#include <ace/Process_Manager.h>
class ACE_Process_Manager : protected ACE_Event_Handler {
public:friend class ACE_Process_Control;enum{ DEFAULT_SIZE = 100 };ACE_Process_Manager ( size_t size = ACE_Process_Manager::DEFAULT_SIZE, ACE_Reactor *reactor = 0 );int open (size_t size = DEFAULT_SIZE, ACE_Reactor *r = 0);int close (void);virtual ~ACE_Process_Manager (void);static ACE_Process_Manager *instance (void);static ACE_Process_Manager *instance (ACE_Process_Manager *);pid_t spawn (ACE_Process *proc, ACE_Process_Options &options);pid_t spawn (ACE_Process_Options &options);int spawn_n ( size_t n, ACE_Process_Options &options, pid_t *child_pids = 0 );int wait ( const ACE_Time_Value &timeout = ACE_Time_Value::max_time );pid_t wait ( pid_t pid, const ACE_Time_Value &timeout, ACE_exitcode *status = 0 );pid_t wait (pid_t pid, ACE_exitcode *status = 0);int reap ( pid_t pid = -1, ACE_exitcode *stat_loc = 0, int options = WNOHANG );int register_handler ( ACE_Event_Handler *event_handler, pid_t pid = ACE_INVALID_PID );int remove (pid_t pid);int terminate (pid_t pid);int terminate (pid_t pid, int sig);size_t managed (void) const;void dump (void) const;ACE_ALLOC_HOOK_DECLARE;protected:virtual int handle_input (ACE_HANDLE proc);virtual ACE_HANDLE get_handle (void) const;virtual int handle_signal ( int signum, siginfo_t * = 0, ucontext_t * = 0 );virtual int handle_close ( ACE_HANDLE handle, ACE_Reactor_Mask close_mask );private:int resize (size_t);ssize_t find_proc (pid_t process_id);ssize_t find_proc (ACE_HANDLE process_handle);int insert_proc (ACE_Process *process);int append_proc (ACE_Process *process);int remove_proc (size_t n);int notify_proc_handler (size_t n, ACE_exitcode status);ACE_Process_Descriptor *process_table_;size_t max_process_table_size_;size_t current_count_;ACE_HANDLE dummy_handle_;ACE_Event_Handler *default_exit_handler_;static ACE_Process_Manager *instance_;static int delete_instance_;ACE_Thread_Mutex lock_;};
ACE_Thread_Manager controls groups of
threads. Naturally, it doesn't work at all on platforms, such
as VxWorks or pSoS, that don't support process.
There are two (main) ways of using ACE_Process_Manager,
depending on how involved you wish to be with the termination
of managed ACE_Processes. If you just want Processes to
go away when they're finished, simply register the
Process_Manager with an ACE_Reactor:
ACE_Process_Manager mgr( 100, some_reactor ) -or- ACE_Process_Manager mgr; ... mgr.open( 100, some_reactor );
Then, the Process_Manager will clean up after any
Processes that it spawns. (On Unix, this means executing a
wait(2) to collect the exit status -- and avoid zombie
processes; on Win32, it means closing the process and thread
HANDLEs that are created when CreateProcess is called.)
If, on the other hand (and for some inexplicable reason) you
want to explicitly invoke the terminated Process cleanup
code, then *don't* register the Process_Manager with a
Reactor, and be sure to call one of the
Process_Manager::wait functions whenever there might be
managed Processes that have exited.
Note that in either case, Process_Manager allows you to
register "Event_Handlers" to be called when a specific
Process exits, or when any Process without a specific
Event_Handler exits. When a Process exits, the
appropriate Event_Handler's handle_input is called; the
ACE_HANDLE passed is either the Process' HANDLE (on Win32),
or its pid cast to an ACE_HANDLE (on unix).
It is also possible to call the Process_Manager::wait
functions even though the Process_Manager is registered with
a Reactor. I don't know what happens in this case, but it's
probably not *too* bad.
Note also that the wait functions are "sloppy" on Unix,
because there's no good way to wait for a subset of the
children of a process. The wait functions may end up
collecting the exit status of a process that's not managed by
the Process_Manager whose wait you invoked. It's best to
only use a single Process_Manager, and to create all
subprocesses by calling that Process_Manager's spawn
method. (I have some ideas for workarounds to improve this
situation, but I consider it fairly low priority because I
think the "single Process_Manager" pattern will be
sufficient in most cases.)
Incidentally, here's how the auto-reaping works on unix when
you register your Process_Manager with a Reactor:
* the Process_Manager opens ACE_DEV_NULL to get a dummy
HANDLE.
* the dummy HANDLE is registered with the Reactor, but
with a NULL_MASK so that it's never normally active.
* the Process_Manager also registers a signal handler for
SIGCHLD.
* the SIGCHLD handler, when invoked, marks the dummy HANDLE
as ready for input.
* the Reactor calls the Process_Manager's handle_input
(this happens synchronously, not in sighandler-space).
* handle_input collects all available exit statuses.
ACE_Process_Manager (
size_t size = ACE_Process_Manager::DEFAULT_SIZE,
ACE_Reactor *reactor = 0
);
ACE_Process_Manager with a table containing up to
size processes. This table resizes itself automatically as
needed. If a non-NULL reactor is provided, this
ACE_Process_Manager uses it to notify an application when a
process it controls exits. By default, however, we don't use an
ACE_Reactor.
int open (size_t size = DEFAULT_SIZE, ACE_Reactor *r = 0);
ACE_Process_Manager with a table containing up to
size processes. This table resizes itself automatically as
needed. If a non-NULL reactor is provided, this
ACE_Process_Manager uses it to notify an application when a
process it controls exits. By default, however, we don't use an
ACE_Reactor.
int close (void);
virtual ~ACE_Process_Manager (void);
static ACE_Process_Manager *instance (void);
ACE_Process_Manager.
static ACE_Process_Manager *instance (ACE_Process_Manager *);
ACE_Process_Manager and return
existing pointer.
pid_t spawn (ACE_Process *proc, ACE_Process_Options &options);
options to proc.spawn. On
success, returns the process id of the child that was created.
On failure, returns ACE_INVALID_PID.
pid_t spawn (ACE_Process_Options &options);
options to
ACE_Process::spawn. On success, returns the process id of the
child that was created. On failure, returns ACE_INVALID_PID.
int spawn_n (
size_t n,
ACE_Process_Options &options,
pid_t *child_pids = 0
);
n new processes by passing options to
ACE_Process::spawn, which is called n times. If child_pids
is non-0 it is expected to be an array of n pid_t's, which
are filled in with the process ids of each newly created process.
Returns 0 on success and -1 on failure.
int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time);
spawned by this ACE_Process_Manager. Unlike the wait call
below, this method does not require a signal handler or
ACE_OS::sigwait because it simply blocks synchronously waiting
for all the children managed by this ACE_Process_Manager to
exit. Note that this does not return any status information
about the success or failure of exiting child processes, although
any registered exit_handlers are called. Returns 0 on success
(and removes the corresponding ACE_Process_Descriptor entries
from the Process_Manager; otherwise, returns -1 on failure.
pid_t wait (
pid_t pid,
const ACE_Time_Value &timeout,
ACE_exitcode *status = 0
);
timeout for a single process to terminate. If
pid==0, waits for any of the managed Processes (but see the
note in DESCRIPTION above for caveats about this -- "sloppy
Process cleanup on unix") If pid != 0, waits for that Process
only. Returns the pid of the Process whose exit was handled, 0
if a timeout occurred, or ACE_INVALID_PID on error.
pid_t wait (pid_t pid, ACE_exitcode *status = 0);
Processes (but see the note in
DESCRIPTION above for caveats about this -- "sloppy Process
cleanup on unix") If pid != 0, waits for that Process only.
Returns the pid of the process whose exit was handled, or
ACE_INVALID_PID on error.
int reap (
pid_t pid = -1,
ACE_exitcode *stat_loc = 0,
int options = WNOHANG
);
ACE_OS::waitpid,
therefore, this method is not portable to Win32. If the child is
successfully reaped, remove is called automatically. This
method does the same thing that the wait method directly above
it does -- It's just here for backwards compatibility.
int register_handler (
ACE_Event_Handler *event_handler,
pid_t pid = ACE_INVALID_PID
);
int remove (pid_t pid);
pid from the table. This is called
automatically by the reap method after it successfully reaped a
SIGCHLD signal. It's also possible to call this method
directly from a signal handler, but don't call both reap and
remove!
int terminate (pid_t pid);
pid using the
ACE::terminate_process method. Note that this call is
potentially dangerous to use since the process being terminated
may not have a chance to cleanup before it shuts down. Returns 0
on success and -1 on failure.
int terminate (pid_t pid, int sig);
size_t managed (void) const;
void dump (void) const;
ACE_ALLOC_HOOK_DECLARE;
ACE_Thread_Mutex lock_;
schmidt@cs.wustl.edu