3 //=============================================================================
5 * @file Process_Manager.h
7 * @author Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
9 //=============================================================================
11 #ifndef ACE_PROCESS_MANAGER_H
12 #define ACE_PROCESS_MANAGER_H
14 #include /**/ "ace/pre.h"
16 #include /**/ "ace/ACE_export.h"
18 #if !defined (ACE_LACKS_PRAGMA_ONCE)
20 #endif /* ACE_LACKS_PRAGMA_ONCE */
22 #include "ace/Process.h"
23 #include "ace/Event_Handler.h"
24 #include "ace/Time_Value.h"
26 #if defined (ACE_HAS_THREADS)
27 # include "ace/Recursive_Thread_Mutex.h"
28 #endif /* ACE_HAS_THREADS */
30 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
35 * @class ACE_Process_Manager
37 * @brief Manages a group of processes.
39 * This class allows applications to control groups of processes,
40 * similar to the way ACE_Thread_Manager controls groups of
41 * threads. Naturally, it doesn't work at all on platforms, such
42 * as VxWorks or pSoS, that don't support multiple processes.
43 * There are two main ways of using ACE_Process_Manager,
44 * depending on how involved you wish to be with the termination
45 * of managed processes. If you want processes to simply
46 * go away when they're finished, register the ACE_Process_Manager with
47 * an ACE_Reactor that can handle notifications of child process exit:
49 * ACE_Process_Manager mgr;
51 * mgr.open (100, ACE_Reactor::instance ());
53 * In this usage scenario, the ACE_Process_Manager will clean up after any
54 * processes that it spawns. (On Unix, this means executing a
55 * wait(2) to collect the exit status and avoid zombie
56 * processes; on Win32, it means closing the process and thread
57 * HANDLEs that are created when CreateProcess is called.)
59 * @note When you register a ACE_Process_Manager with a
60 * ACE_Reactor, the reactor's notification pipe is used to help reap the
61 * available process exit statuses. Therefore, you must not use a
62 * reactor whose notify pipe has been disabled. Here's the
63 * sequence of steps used to reap the exit statuses in this case:
64 * -# The ACE_Process_Manager registers a signal handler for
66 * -# The SIGCHLD handler, when invoked, uses the ACE_Reactor's
67 * notify() method to inform the ACE_Reactor to wake up.
68 * -# The ACE_Reactor calls the ACE_Process_Manager's
69 * handle_input() method; this happens synchronously, not in
71 * -# The handle_input() method collects all available exit
74 * If, on the other hand you want to wait "in line" to handle the
75 * terminated process cleanup code, call one of the wait functions
76 * whenever there might be managed processes that have exited.
78 * Note that in either case, ACE_Process_Manager allows you to
79 * register an ACE_Event_Handler to be called when a specific
80 * spawned process exits, or when any process without a specific
81 * ACE_Event_Handler exits. When a process exits, the
82 * appropriate ACE_Event_Handler's handle_input() method is called; the
83 * ACE_HANDLE passed is either the process's HANDLE (on Windows),
84 * or its pid cast to an ACE_HANDLE (on POSIX).
85 * It is also possible to call the wait() functions even when the
86 * ACE_Process_Manager is registered with a reactor.
88 * @note Be aware that the wait functions are "sloppy" on Unix,
89 * because there's no good way to wait for a subset of the
90 * children of a process. The wait functions may end up
91 * collecting the exit status of a process that's not managed by
92 * the ACE_Process_Manager whose wait() you invoked. It's best to
93 * only use a single ACE_Process_Manager, and to create all
94 * subprocesses by calling that manager's spawn() method.
96 class ACE_Export ACE_Process_Manager
: protected ACE_Event_Handler
99 friend class ACE_Process_Control
;
107 * @name Initialization and termination methods
111 * Initialize an ACE_Process_Manager with a table containing up to
112 * @a size processes. This table resizes itself automatically as
113 * needed. If a @a reactor is provided, this
114 * ACE_Process_Manager uses it to notify an application when a
115 * process it controls exits. By default, however, we don't use an
118 ACE_Process_Manager (size_t size
= ACE_Process_Manager::DEFAULT_SIZE
,
119 ACE_Reactor
*reactor
= 0);
122 * Initialize an ACE_Process_Manager with a table containing up to
123 * @a size processes. This table resizes itself automatically as
124 * needed. If a @a reactor is provided, this
125 * ACE_Process_Manager uses it to notify an application when a
126 * process it controls exits. By default, however, we don't use an
129 int open (size_t size
= ACE_Process_Manager::DEFAULT_SIZE
,
132 /// Release all resources. Do not wait for processes to exit.
135 /// Destructor releases all resources and does not wait for processes
137 virtual ~ACE_Process_Manager ();
142 * @name Singleton access and control
145 /// Get pointer to a process-wide ACE_Process_Manager.
146 static ACE_Process_Manager
*instance ();
148 /// Set pointer to a process-wide ACE_Process_Manager and return
149 /// existing pointer.
150 static ACE_Process_Manager
*instance (ACE_Process_Manager
*);
152 /// Delete the dynamically allocated singleton.
153 static void close_singleton ();
155 /// Cleanup method, used by the ACE_Object_Manager to destroy the
157 static void cleanup (void *instance
, void *arg
);
162 * @name Process creation methods
166 * Create a new process with specified @a options.
167 * Register @a event_handler to be called back when the process exits.
168 * The @a proc object's ACE_Process::unmanage() method is called when
169 * the process is removed from ACE_Process_Manager.
171 * On success, returns the process id of the child that was created.
172 * On failure, returns ACE_INVALID_PID.
174 pid_t
spawn (ACE_Process
*proc
,
175 ACE_Process_Options
&options
,
176 ACE_Event_Handler
*event_handler
= 0);
179 * Create a new process with the specified @a options.
180 * Register @a event_handler to be called back when the process exits.
182 * On success, returns the process id of the child that was created.
183 * On failure, returns ACE_INVALID_PID.
185 pid_t
spawn (ACE_Process_Options
&options
,
186 ACE_Event_Handler
*event_handler
= 0);
189 * Create @a n new processes with the same @a options.
190 * If @a child_pids is non-0 it is expected to be an array of at least
191 * @a n pid_t, which are filled in with the process IDs of the spawned
193 * Register @a event_handler to be called back when each process exits.
194 * Returns 0 on success and -1 on failure.
196 int spawn_n (size_t n
,
197 ACE_Process_Options
&options
,
198 pid_t
*child_pids
= 0,
199 ACE_Event_Handler
*event_Handler
= 0);
203 * @name Process synchronization operations
207 * Abruptly terminate a single process with id @a pid using the
208 * ACE::terminate_process() method which works on both signal-capable
209 * systems and on Windows.
211 * @note This call is potentially dangerous to use since the process
212 * being terminated may not have a chance to cleanup before it shuts down.
213 * The process's entry is also not removed from this class's process
214 * table. Calling either wait() or remove() after terminate() is
217 * @retval 0 on success and -1 on failure.
219 int terminate (pid_t pid
);
222 * Sends the specified signal to the specified process.
224 * @note This only works on platforms that have signal capability. In
225 * particular, it doesn't work on Windows.
227 * @retval 0 on success and -1 on failure.
229 int terminate (pid_t pid
, int sig
);
232 * Block until there are no more child processes running that were
233 * spawned by this ACE_Process_Manager. Unlike the wait() method
234 * below, this method does not require a signal handler or use of
235 * ACE_OS::sigwait() because it simply blocks synchronously waiting
236 * for all the children managed by this ACE_Process_Manager to
237 * exit. Note that this does not return any status information
238 * about the success or failure of exiting child processes, although
239 * any registered exit handlers are called.
241 * @param timeout Relative time to wait for processes to terminate.
243 * @retval 0 on success; -1 on failure.
245 int wait (const ACE_Time_Value
&timeout
= ACE_Time_Value::max_time
);
248 template< class Rep
, class Period
>
249 int wait (const std::chrono::duration
<Rep
, Period
>& timeout
)
251 ACE_Time_Value
const tv (timeout
);
252 return this->wait (tv
);
256 * Wait up to @a timeout for a single specified process to terminate.
257 * If @a pid is 0, this method waits for any of the managed processes
258 * (but see the note concerning "sloppy process cleanup on unix").
259 * If @a pid != 0, waits for that process only.
261 * @param pid Process ID
262 * @param timeout Relative time to wait for process to terminate
263 * @param status Exit status of terminated process
265 * @retval The pid of the process which exited, 0
266 * if a timeout occurred, or ACE_INVALID_PID on error.
268 pid_t
wait (pid_t pid
,
269 const ACE_Time_Value
&timeout
,
270 ACE_exitcode
*status
= 0);
273 template< class Rep
, class Period
>
274 pid_t
wait (pid_t pid
,
275 const std::chrono::duration
<Rep
, Period
>& timeout
,
276 ACE_exitcode
*status
= 0)
278 ACE_Time_Value
const tv (timeout
);
279 return this->wait (pid
, tv
, status
);
283 * Wait indefinitely for a single, specified process to terminate.
284 * If @a pid is 0, waits for any of the managed processes (but see the
285 * note concerning "sloppy process cleanup on unix").
286 * If @a pid != 0, this method waits for that process only.
288 * @retval The pid of the process which exited, or
289 * ACE_INVALID_PID on error.
291 pid_t
wait (pid_t pid
,
292 ACE_exitcode
*status
= 0);
296 * @name Utility methods
300 * Register an event handler to be called back when the specified
301 * process exits. If @a pid == ACE_INVALID_PID this handler is called
302 * when any process with no specific handler exits.
304 * @warning In multithreaded applications, there is a race condition
305 * if a process exits between the time it is spawned and when its
306 * handler is registered. To avoid this, register the handler at
307 * the time the process is spawned.
309 int register_handler (ACE_Event_Handler
*event_handler
,
310 pid_t pid
= ACE_INVALID_PID
);
313 * Remove process @a pid from the ACE_Process_Manager's internal records.
314 * This is called automatically by the wait() method if the waited process
315 * exits. This method can also be called after calling terminate() if
316 * there's no need to wait() for the terminated process.
318 int remove (pid_t pid
);
320 /// Return the number of managed processes.
321 size_t managed () const;
324 * Sets the scheduling parameters for process identified by @a pid by
325 * passing @a params, @a pid to ACE_OS::sched_params().
327 * @retval 0 on success, -1 on failure, and ACE_INVALID_PID when the
328 * specified @a pid is not managed by this ACE_Process_Manager.
330 int set_scheduler (const ACE_Sched_Params
¶ms
, pid_t pid
);
333 * Sets the scheduling parameters for all the processes managed by
334 * this ACE_Process_Manager by passing @a params to
335 * ACE_OS::sched_params().
337 * @retval 0 on success, -1 on failure.
339 int set_scheduler_all (const ACE_Sched_Params
¶ms
);
341 /// Dump the state of an object.
344 /// Declare the dynamic allocation hooks.
345 ACE_ALLOC_HOOK_DECLARE
;
349 // = These methods allow a <Process_Manager> to be an Event_Handler.
351 // As an Event_Handler, the <Process_Manager> automagically
352 // detects child Processes exiting and calls notify_proc_handler()
353 // and remove(). This means that you don't have to (shouldn't!)
354 // call the wait(...) methods yourself.
356 // On Unix, we can't detect individual process termination very
357 // well; the best method is to catch SIGCHLD and then call the
358 // polling wait() function to collect any available exit statuses.
359 // However, we don't want to do this from within a signal handler
360 // because of the restrictions associated. Therefore (following the
361 // lead in examples/mumble) we open a bogus handle (to ACE_DEV_NULL)
362 // and register that handle with our Reactor. Then, when our
363 // SIGCHLD handler gets invoked, we tell the Reactor that the bogus
364 // handle is readable. That will cause the handle_input() function
365 // to be called once we're out of the interrupt context, and
366 // handle_input() collects exit statuses.
368 // On Win32, we simply register ourself with the Reactor to deal
369 // with the Process handle becoming signaled. No muss, no fuss, no
370 // signal handler, and no dummy handle.
372 #if !defined(ACE_WIN32)
373 /// Collect one (or more, on unix) process exit status.
374 virtual int handle_input (ACE_HANDLE proc
);
376 /// If registered with a reactor for SIGCHLD and the reactor closes, this
377 /// will get called to notify.
378 virtual int handle_close (ACE_HANDLE handle
,
379 ACE_Reactor_Mask close_mask
);
381 #endif // !defined(ACE_WIN32)
384 * On Unix, this routine is called asynchronously when a SIGCHLD is
385 * received. We just tweak the reactor so that it'll call back our
386 * <handle_input> function, which allows us to handle Process exits
389 * On Win32, this routine is called synchronously, and is passed the
390 * HANDLE of the Process that exited, so we can do all our work here
392 virtual int handle_signal (int signum
, siginfo_t
* = 0, ucontext_t
* = 0);
396 * @struct Process_Descriptor
398 * @internal This struct is for internal use only by ACE_Process_Manager.
400 * @brief Information describing each process that's controlled by an
401 * ACE_Process_Manager.
403 struct Process_Descriptor
405 /// Default ctor/dtor.
406 Process_Descriptor ();
407 ~Process_Descriptor ();
409 /// Describes the process itself.
410 ACE_Process
*process_
;
412 /// Function to call when process exits
413 ACE_Event_Handler
*exit_notify_
;
415 /// Dump the state of an object.
418 ACE_ALLOC_HOOK_DECLARE
;
421 /// Resize the pool of Process_Descriptors.
424 /// Locate the index of the table slot occupied by @a process_id.
425 /// Returns -1 if @a process_id is not in the @c process_table_
426 ssize_t
find_proc (pid_t process_id
);
428 #if defined (ACE_WIN32)
429 /// Locate the index of the table slot occupied by @a process_handle.
430 /// Returns ~0 if @a process_handle is not in the @c process_table_
431 ssize_t
find_proc (ACE_HANDLE process_handle
);
432 #endif /* ACE_WIN32 */
434 /// Insert a process in the table (checks for duplicates). Omitting
435 /// the process handle won't work on Win32...
436 /// Register @a event_handler to be called back when the process exits.
437 int insert_proc (ACE_Process
*process
,
438 ACE_Event_Handler
*event_handler
= 0);
441 * Append information about a process, i.e., its <process_id> in the
442 * @c process_table_. Each entry is added at the end, growing the
443 * table if necessary.
444 * Register @a event_handler to be called back when the process exits.
446 int append_proc (ACE_Process
*process
,
447 ACE_Event_Handler
*event_handler
= 0);
449 /// Actually removes the process at index @a n from the table. This method
450 /// must be called with locks held.
451 int remove_proc (size_t n
);
453 /// If there's a specific handler for the Process at index @a n in the
454 /// table, or there's a default handler, call it.
455 int notify_proc_handler (size_t n
, ACE_exitcode status
);
457 /// Vector that describes process state within the Process_Manager.
458 Process_Descriptor
*process_table_
;
460 /// Maximum number of processes we can manage (should be dynamically
462 size_t max_process_table_size_
;
464 /// Current number of processes we are managing.
465 size_t current_count_
;
467 /// This event handler is used to notify when a process we control
469 ACE_Event_Handler
*default_exit_handler_
;
471 /// Singleton pointer.
472 static ACE_Process_Manager
*instance_
;
474 /// Controls whether the <Process_Manager> is deleted when we shut
475 /// down (we can only delete it safely if we created it!)
476 static bool delete_instance_
;
478 #if defined (ACE_HAS_THREADS)
479 /// This lock protects access/ops on @c process_table_.
480 ACE_Recursive_Thread_Mutex lock_
;
481 #endif /* ACE_HAS_THREADS */
484 ACE_END_VERSIONED_NAMESPACE_DECL
486 #if defined (__ACE_INLINE__)
487 #include "ace/Process_Manager.inl"
488 #endif /* __ACE_INLINE__ */
490 #include /**/ "ace/post.h"
491 #endif /* ACE_PROCESS_MANAGER_H */