child_proc_watcher, child_proc_watcher_impl
// Members of dasynq::event_loop<T> instantiation: class child_proc_watcher; template <class Derived> class child_proc_watcher_impl; // : public child_proc_watcher;
child_proc_watcher
Brief: child_proc_watcher is a member type of the event_loop template class. It represents an event watcher for child process termination. The child_proc_watcher class should not be subclassed directly; the child_proc_watcher_impl template provides a means for subclassing.
Members
Types
- event_loop_t — the event loop type that this watcher registers with.
- proc_status_t — status information about a watched proces.
Constructors
- child_proc_watcher() — default constructor.
Functions
- void add_watch(event_loop_t &eloop, pid_t child, int prio = DEFAULT_PRIORITY)
— register a watcher with an event loop. child specifies the child process to watch. May throw std::bad_alloc or std::system_error. - void add_reserved(event_loop_t &eloop, pid_t child, int prio = DEFAULT_PRIORITY) noexcept
— add a watch on a child process from a watcher which is already holding a watch reservation on the event loop (via the reserve_watch function). - void deregister(event_loop_t &eloop) noexcept
— request removal from the event loop. - pid_t fork(event_loop_t &eloop, bool from_reserved = false, int prio = DEFAULT_PRIORITY)
— fork (as per POSIX fork) and add a watch on the child process. If a watch cannot be created, the child process is either not created or is terminated immediately (before the function returns) and std::bad_alloc or std::system_error is thrown; similarly if the fork cannot be performed. Otherwise, the return is the child process ID in the parent process, and 0 in the child process. - void reserve_watch(event_loop_t &eloop) — reserve a watch on the specified event loop. The add_reserved function can then be used at a later point in time to watch a child process with no risk of failure due to resource limits. This function may itself throw std::bad_alloc or std::system_error.
- int send_signal(event_loop_t &loop, int signo) noexcept — send the specified signal to the process, if it has not yet terminated. Return value is as per POSIX kill function (including setting of errno).
- void stop_watch(event_loop_t &eloop) noexcept — stop watching a child process, but retain watch reservation so that another process can be watched at a later point in time using the add_reserved function. This can only be called if the watcher is currently watching a process, but does not require that a watch was reserved previously.
- void unreserve(event_loop_t &eloop) — release a watch reservation.
- virtual void watch_removed() noexcept — called when the watcher has been removed from the event loop.
Details and Usage
Note: also see the watcher constraints section.
An application may wish to handle child process termination at the same time as other events. The child_proc_watcher allows child process termination to be detected by an event loop.
Note that creating multiple watchers for a single child process is not supported.
Subclassing child_proc_watcher
To specify callback behaviour, child_proc_watcher can be subclassed — however, it should not be directly subclassed; instead, use the child_proc_watcher_impl implementation wrapper template.
Reaping of child processes
Processes which terminate may becomes "zombie" processes until their termination is acknowledged by their parent process, i.e. until they are reaped.
Child processes for which a watcher is added will be reaped automatically, either when their termination is detected or (if possible) immediately after the callback function returns. Note that the event loop may reap all child processes, even those that are not watched (as a necessity of implementation).
Reserving watchers
Since adding a watcher using add_watch can fail, and because it requires that the child process has already been created, the awkward situation can arise that a child process is created but cannot be monitored. There are two ways to avoid this: the first is to reserve a watch (using the reserve_watch function) and add the watch after creating the child process using the add_reserved function instead of the add_watch function; the second way is to perform an "atomic" fork using the fork member function.
"Atomic" fork
The fork member function avoids the problem of creating unwatchable child processes by forking and adding the watch simultaneously. If the watch cannot be created, the child process is either not created or is terminated immediately (before the fork function returns).
Note that, with a multi-threaded event loop, there is a race when creating a child process and watching it, because the child might be reaped before the watch is added (resulting either in failure when adding the watch, or the child termination never being reported via the callback). Using the fork member function to create the child process also avoids this race.
Signalling child processes
Since child processes may be reaped "behind the scenes" by the event loop, signalling them becomes inherently race-prone; any signal sent to a process identified by its numerical process id (pid) could potentially be delivered instead to a new process which has been given the same pid after the original process terminated and was reaped. To avoid this, use the send_signal member function, which guarantees to send a signal only if the process has not yet been reaped.
child_proc_watcher_impl
Brief: The child_proc_watcher_impl provides a basis for implementing child_proc_watcher, using the "curiously recurring template pattern". Instead of subclassing child_proc_watcher directly, subclass an instantiation of child_proc_watcher_impl with the template parameter specified as the subclass itself. For example:
class my_watcher : public event_loop_t::child_proc_watcher_impl<my_watcher> { // ... }
Details and Usage
The callback function must be provided in the subclass and named status_change, with a signature compatible with the following:
rearm status_change(loop_t &, pid_t child, int status);
The status_change function must be public, but need not be virtual. It will be called with the following parameters:
- loop — a reference to the event loop.
- child — the process ID (pid) of the watched child which has terminated.
- status — the status of the process, as returned by the POSIX wait function. The macros WIFEXITED, WEXITSTATUS etc. can be used to determine the cause of termination and the exit status code if applicable. POSIX guarantees that the value will be 0 if and only if the process terminates "normally" (i.e. not via a signal, and not with an exit status other than 0).
The return value specifies the rearm action.
A child_proc_watcher_impl instantiation has no public or protected members, other than those inherited from child_proc_watcher, which it publicly derives from.
proc_status_t
Brief: The proc_status_t class wraps information about the (termination) status of a watched process.
Note: this may be a typedef'd alias of another type internal to Dasynq.
Members
Constructors
- proc_status_t() noexcept — default constructor; contents unspecified
- proc_status_t(const proc_status_t &) noexcept — copy constructor
Functions
- proc_status_t &operator=(const proc_status_t &) noexcept
— assignment - bool did_exit() noexcept
— whether the process terminated normally - bool did_exit_clean() noexcept
— whether the process terminated normally, with an exit status of 0 - bool was_signalled() noexcept
— whether the process was terminated via a signal - int get_exit_status() noexcept
— if the process exited normally, the exit status it returned; otherwise unspecified. Note that on some systems (notably Linux) only 8 bits of the exit status are available. - int get_signal() noexcept
— if the process was terminated via a signal, the relevant signal number; otherwise unspecified - int get_si_code() noexcept
— returns value indicating cause of process termination; can be CLD_EXITED (process called exit(...) or returned from main), CLD_KILLED if the process was terminated by reception of a signal. - int get_si_status() noexcept
— if the process exited, returns the exit status (passed to exit(...) or returned from main); note that some systems (such as Linux) may limit the result to 8 bits. If the process was terminated by reception of a signal, returns the signal number.
Details and Usage
The proc_status_t class is a straight-forward wrapper around process status information. For convenience, a default constructor, copy constructor and assignment operator are provided.