NAME

ACE_Process_Manager - Manages a group of processes.

SYNOPSIS

#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_; };

DESCRIPTION

This class allows applications to control groups of processes, similar to how the 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.

Initialization and termination methods.

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);

Singleton accessors.

static ACE_Process_Manager *instance (void);

static ACE_Process_Manager *instance (ACE_Process_Manager *);

Process creation methods.

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
    );

Process synchronization operations.

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
    );

Utility methods.

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;

ACE_Thread_Mutex for access/ops on process_table_

ACE_Thread_Mutex lock_;

AUTHOR

Douglas C. Schmidt schmidt@cs.wustl.edu

LIBRARY

ace