fish-shell/src/wait_handle.h
2021-10-28 10:37:43 -07:00

89 lines
3.0 KiB
C++

// Support for handling pids that are no longer fish jobs.
// This includes pids that have been disowned ("forgotten") and background jobs which have finished,
// but may be wait'ed.
#ifndef FISH_WAIT_HANDLE_H
#define FISH_WAIT_HANDLE_H
#include "config.h" // IWYU pragma: keep
#include <unistd.h>
#include <list>
#include <unordered_map>
#include <vector>
#include "common.h"
/// The bits of a job necessary to support 'wait' and '--on-process-exit'.
/// This may outlive the job.
struct wait_handle_t {
/// Construct from a pid, job id, and base name.
wait_handle_t(pid_t pid, internal_job_id_t jid, wcstring name)
: pid(pid), internal_job_id(jid), base_name(std::move(name)) {}
/// The pid of this process.
const pid_t pid{};
/// The internal job id of the job which contained this process.
const internal_job_id_t internal_job_id{};
/// The "base name" of this process.
/// For example if the process is "/bin/sleep" then this will be 'sleep'.
const wcstring base_name{};
/// The value appropriate for populating $status, if completed.
int status{0};
/// Set to true when the process is completed.
bool completed{false};
};
using wait_handle_ref_t = std::shared_ptr<wait_handle_t>;
/// Support for storing a list of wait handles, with a max limit set at initialization.
/// Note this class is not safe for concurrent access.
class wait_handle_store_t : noncopyable_t {
public:
// Our wait handles are arranged in a linked list for its iterator invalidation semantics: we
// may remove one without needing to update the map from pid -> handle.
using wait_handle_list_t = std::list<wait_handle_ref_t>;
/// Construct with a max limit on the number of handles we will remember.
/// The default is 1024, which is zsh's default.
explicit wait_handle_store_t(size_t limit = 1024);
/// Add a wait handle to the store. This may remove the oldest handle, if our limit is exceeded.
/// It may also remove any existing handle with that pid.
/// For convenience, this does nothing if wh is null.
void add(wait_handle_ref_t wh);
/// \return the wait handle for a pid, or nullptr if there is none.
/// This is a fast lookup.
wait_handle_ref_t get_by_pid(pid_t pid) const;
/// Remove a given wait handle, if present in this store.
void remove(const wait_handle_ref_t &wh);
/// Remove the wait handle for a pid, if present in this store.
void remove_by_pid(pid_t pid);
/// Get the list of all wait handles.
const wait_handle_list_t &get_list() const { return handles_; }
/// Convenience to return the size, for testing.
size_t size() const { return handles_.size(); }
private:
using list_node_t = typename wait_handle_list_t::iterator;
/// The list of all wait handles. New ones come on the front, the last one is oldest.
wait_handle_list_t handles_{};
/// Map from pid to the wait handle's position in the list.
std::unordered_map<pid_t, list_node_t> handle_map_{};
/// Max supported wait handles.
const size_t limit_;
};
#endif