#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_Process
es. If you just want Process
es 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
Process
es 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 Process
es 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);
spawn
ed 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 remove
s 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 Process
es (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);
Process
es (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