---
name: "ACE_Process_Manager"
section: "3"
platform: "linux"
package: "libace-doc"
version: "5.0.7-4"
release: "Debian 2.2 (Potato)"
platforms: ["linux"]
url: "https://new.carta.tech/ACE_Process_Manager"
---

# ACE\_Process\_Manager(3)

Manages a group of processes.

## 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)](/wait/2.md) 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
    );
```

Initialize an 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);
```

Initialize an 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);
```

Release all resources.  Do not wait for processes to exit.

```
virtual ~ACE_Process_Manager (void);
```

Destructor releases all resources and does not wait for processes to exit.

## Singleton accessors.

```
static ACE_Process_Manager *instance (void);
```

Get pointer to a process-wide ACE\_Process\_Manager.

```
static ACE_Process_Manager *instance (ACE_Process_Manager *);
```

Set pointer to a process-wide ACE\_Process\_Manager and return existing pointer.

## Process creation methods.

```
pid_t spawn (ACE_Process *proc, ACE_Process_Options &options);
```

Create a new process by passing 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);
```

Create a new process by passing 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
    );
```

Create 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.

## Process synchronization operations.

```
int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time);
```

Block until there are no more child processes running that were 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
    );
```

Wait up to 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);
```

Wait indefinitely 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, or ACE\_INVALID\_PID on error.

```
int reap (
    pid_t pid = -1,
    ACE_exitcode *stat_loc = 0,
    int options = WNOHANG
    );
```

Reap the result of a single process by calling 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.

## Utility methods.

```
int register_handler (
    ACE_Event_Handler *event_handler,
    pid_t pid = ACE_INVALID_PID
    );
```

Register an Event\_Handler to be called back when the specified process exits.  If pid == ACE\_INVALID\_PID this handler is called when any process with no specific handler exits.

```
int remove (pid_t pid);
```

Remove process 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);
```

Abruptly terminate a single process with id 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);
```

On OSs that support signals, send the signal to the specified process.  Returns 0 on success and -1 on failure.

```
size_t managed (void) const;
```

Return the number of managed Processes.

```
void dump (void) const;
```

Dump the state of an object.

```
ACE_ALLOC_HOOK_DECLARE;
```

Declare the dynamic allocation hooks.

## ACE\_Thread\_Mutex for access/ops on process\_table\_

```
ACE_Thread_Mutex lock_;
```

## AUTHOR

Douglas C. Schmidt schmidt@cs.wustl.edu

## LIBRARY

ace
