/usr/include/ace/Process_Manager.h is in libace-dev 6.3.3+dfsg-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 | // -*- C++ -*-
//=============================================================================
/**
* @file Process_Manager.h
*
* @author Douglas C. Schmidt <schmidt@cs.wustl.edu>
*/
//=============================================================================
#ifndef ACE_PROCESS_MANAGER_H
#define ACE_PROCESS_MANAGER_H
#include /**/ "ace/pre.h"
#include /**/ "ace/ACE_export.h"
#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
#include "ace/Process.h"
#include "ace/Event_Handler.h"
#include "ace/Time_Value.h"
#if defined (ACE_HAS_THREADS)
# include "ace/Recursive_Thread_Mutex.h"
#endif /* ACE_HAS_THREADS */
ACE_BEGIN_VERSIONED_NAMESPACE_DECL
class ACE_Reactor;
/**
* @class ACE_Process_Manager
*
* @brief Manages a group of processes.
*
* This class allows applications to control groups of processes,
* similar to the way 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 multiple processes.
* There are two main ways of using ACE_Process_Manager,
* depending on how involved you wish to be with the termination
* of managed processes. If you want processes to simply
* go away when they're finished, register the ACE_Process_Manager with
* an ACE_Reactor that can handle notifications of child process exit:
* @code
* ACE_Process_Manager mgr;
* // ...
* mgr.open (100, ACE_Reactor::instance ());
* @endcode
* In this usage scenario, the ACE_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.)
*
* @note When you register a ACE_Process_Manager with a
* ACE_Reactor, the reactor's notification pipe is used to help reap the
* available process exit statuses. Therefore, you must not use a
* reactor whose notify pipe has been disabled. Here's the
* sequence of steps used to reap the exit statuses in this case:
* -# The ACE_Process_Manager registers a signal handler for
* SIGCHLD.
* -# The SIGCHLD handler, when invoked, uses the ACE_Reactor's
* notify() method to inform the ACE_Reactor to wake up.
* -# The ACE_Reactor calls the ACE_Process_Manager's
* handle_input() method; this happens synchronously, not in
* signal context.
* -# The handle_input() method collects all available exit
* statuses.
*
* If, on the other hand you want to wait "in line" to handle the
* terminated process cleanup code, call one of the wait functions
* whenever there might be managed processes that have exited.
*
* Note that in either case, ACE_Process_Manager allows you to
* register an ACE_Event_Handler to be called when a specific
* spawned process exits, or when any process without a specific
* ACE_Event_Handler exits. When a process exits, the
* appropriate ACE_Event_Handler's handle_input() method is called; the
* ACE_HANDLE passed is either the process's HANDLE (on Windows),
* or its pid cast to an ACE_HANDLE (on POSIX).
* It is also possible to call the wait() functions even when the
* ACE_Process_Manager is registered with a reactor.
*
* @note Be aware 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 ACE_Process_Manager whose wait() you invoked. It's best to
* only use a single ACE_Process_Manager, and to create all
* subprocesses by calling that manager's spawn() method.
*/
class ACE_Export ACE_Process_Manager : protected ACE_Event_Handler
{
public:
friend class ACE_Process_Control;
enum
{
DEFAULT_SIZE = 100
};
/**
* @name Initialization and termination methods
*/
//@{
/**
* Initialize an ACE_Process_Manager with a table containing up to
* @a size processes. This table resizes itself automatically as
* needed. If a @a 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.
*/
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
* @a size processes. This table resizes itself automatically as
* needed. If a @a 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 = ACE_Process_Manager::DEFAULT_SIZE,
ACE_Reactor *r = 0);
/// Release all resources. Do not wait for processes to exit.
int close (void);
/// Destructor releases all resources and does not wait for processes
/// to exit.
virtual ~ACE_Process_Manager (void);
//@}
/**
* @name Singleton access and control
*/
//@{
/// Get pointer to a process-wide ACE_Process_Manager.
static ACE_Process_Manager *instance (void);
/// Set pointer to a process-wide ACE_Process_Manager and return
/// existing pointer.
static ACE_Process_Manager *instance (ACE_Process_Manager *);
/// Delete the dynamically allocated singleton.
static void close_singleton (void);
/// Cleanup method, used by the ACE_Object_Manager to destroy the
/// singleton.
static void cleanup (void *instance, void *arg);
//@}
/**
* @name Process creation methods
*/
//@{
/**
* Create a new process with specified @a options.
* Register @a event_handler to be called back when the process exits.
* The @a proc object's ACE_Process::unmanage() method is called when
* the process is removed from ACE_Process_Manager.
*
* On success, returns the process id of the child that was created.
* On failure, returns ACE_INVALID_PID.
*/
pid_t spawn (ACE_Process *proc,
ACE_Process_Options &options,
ACE_Event_Handler *event_handler = 0);
/**
* Create a new process with the specified @a options.
* Register @a event_handler to be called back when the process exits.
*
* 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,
ACE_Event_Handler *event_handler = 0);
/**
* Create @a n new processes with the same @a options.
* If @a child_pids is non-0 it is expected to be an array of at least
* @a n pid_t, which are filled in with the process IDs of the spawned
* processes.
* Register @a event_handler to be called back when each process exits.
* Returns 0 on success and -1 on failure.
*/
int spawn_n (size_t n,
ACE_Process_Options &options,
pid_t *child_pids = 0,
ACE_Event_Handler *event_Handler = 0);
//@}
/**
* @name Process synchronization operations
*/
//@{
/**
* Abruptly terminate a single process with id @a pid using the
* ACE::terminate_process() method which works on both signal-capable
* systems and on Windows.
*
* @note This call is potentially dangerous to use since the process
* being terminated may not have a chance to cleanup before it shuts down.
* The process's entry is also not removed from this class's process
* table. Calling either wait() or remove() after terminate() is
* advisable.
*
* @retval 0 on success and -1 on failure.
*/
int terminate (pid_t pid);
/**
* Sends the specified signal to the specified process.
*
* @note This only works on platforms that have signal capability. In
* particular, it doesn't work on Windows.
*
* @retval 0 on success and -1 on failure.
*/
int terminate (pid_t pid, int sig);
/**
* Block until there are no more child processes running that were
* spawned by this ACE_Process_Manager. Unlike the wait() method
* below, this method does not require a signal handler or use of
* 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.
*
* @param timeout Relative time to wait for processes to terminate.
*
* @retval 0 on success; -1 on failure.
*/
int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time);
#if defined (ACE_HAS_CPP11)
/// @sa wait
template< class Rep, class Period >
int wait (const std::chrono::duration<Rep, Period>& timeout)
{
ACE_Time_Value const tv (timeout);
return this->wait (tv);
}
#endif
/**
* Wait up to @a timeout for a single specified process to terminate.
* If @a pid is 0, this method waits for any of the managed processes
* (but see the note concerning "sloppy process cleanup on unix").
* If @a pid != 0, waits for that process only.
*
* @param pid Process ID
* @param timeout Relative time to wait for process to terminate
* @param status Exit status of terminated process
*
* @retval The pid of the process which exited, 0
* if a timeout occurred, or ACE_INVALID_PID on error.
*/
pid_t wait (pid_t pid,
const ACE_Time_Value &timeout,
ACE_exitcode *status = 0);
#if defined (ACE_HAS_CPP11)
/// @sa wait
template< class Rep, class Period >
pid_t wait (pid_t pid,
const std::chrono::duration<Rep, Period>& timeout,
ACE_exitcode *status = 0)
{
ACE_Time_Value const tv (timeout);
return this->wait (pid, tv, status);
}
#endif
/**
* Wait indefinitely for a single, specified process to terminate.
* If @a pid is 0, waits for any of the managed processes (but see the
* note concerning "sloppy process cleanup on unix").
* If @a pid != 0, this method waits for that process only.
*
* @retval The pid of the process which exited, or
* ACE_INVALID_PID on error.
*/
pid_t wait (pid_t pid,
ACE_exitcode *status = 0);
//@}
/**
* @name Utility methods
*/
//@{
/**
* Register an event handler to be called back when the specified
* process exits. If @a pid == ACE_INVALID_PID this handler is called
* when any process with no specific handler exits.
*
* @warning In multithreaded applications, there is a race condition
* if a process exits between the time it is spawned and when its
* handler is registered. To avoid this, register the handler at
* the time the process is spawned.
*/
int register_handler (ACE_Event_Handler *event_handler,
pid_t pid = ACE_INVALID_PID);
/**
* Remove process @a pid from the ACE_Process_Manager's internal records.
* This is called automatically by the wait() method if the waited process
* exits. This method can also be called after calling terminate() if
* there's no need to wait() for the terminated process.
*/
int remove (pid_t pid);
/// Return the number of managed processes.
size_t managed (void) const;
/**
* Sets the scheduling parameters for process identified by @a pid by
* passing @a params, @a pid to ACE_OS::sched_params().
*
* @retval 0 on success, -1 on failure, and ACE_INVALID_PID when the
* specified @a pid is not managed by this ACE_Process_Manager.
*/
int set_scheduler (const ACE_Sched_Params ¶ms, pid_t pid);
/**
* Sets the scheduling parameters for all the processes managed by
* this ACE_Process_Manager by passing @a params to
* ACE_OS::sched_params().
*
* @retval 0 on success, -1 on failure.
*/
int set_scheduler_all (const ACE_Sched_Params ¶ms);
/// Dump the state of an object.
void dump (void) const;
/// Declare the dynamic allocation hooks.
ACE_ALLOC_HOOK_DECLARE;
//@}
protected:
// = These methods allow a <Process_Manager> to be an Event_Handler.
// As an Event_Handler, the <Process_Manager> automagically
// detects child Processes exiting and calls notify_proc_handler()
// and remove(). This means that you don't have to (shouldn't!)
// call the wait(...) methods yourself.
// On Unix, we can't detect individual process termination very
// well; the best method is to catch SIGCHLD and then call the
// polling wait() function to collect any available exit statuses.
// However, we don't want to do this from within a signal handler
// because of the restrictions associated. Therefore (following the
// lead in examples/mumble) we open a bogus handle (to ACE_DEV_NULL)
// and register that handle with our Reactor. Then, when our
// SIGCHLD handler gets invoked, we tell the Reactor that the bogus
// handle is readable. That will cause the handle_input() function
// to be called once we're out of the interrupt context, and
// handle_input() collects exit statuses.
// On Win32, we simply register ourself with the Reactor to deal
// with the Process handle becoming signaled. No muss, no fuss, no
// signal handler, and no dummy handle.
#if !defined(ACE_WIN32)
/// Collect one (or more, on unix) process exit status.
virtual int handle_input (ACE_HANDLE proc);
/// If registered with a reactor for SIGCHLD and the reactor closes, this
/// will get called to notify.
virtual int handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask);
#endif // !defined(ACE_WIN32)
/**
* On Unix, this routine is called asynchronously when a SIGCHLD is
* received. We just tweak the reactor so that it'll call back our
* <handle_input> function, which allows us to handle Process exits
* synchronously.
*
* On Win32, this routine is called synchronously, and is passed the
* HANDLE of the Process that exited, so we can do all our work here
*/
virtual int handle_signal (int signum,
siginfo_t * = 0,
ucontext_t * = 0);
private:
/**
* @struct Process_Descriptor
*
* @internal This struct is for internal use only by ACE_Process_Manager.
*
* @brief Information describing each process that's controlled by an
* ACE_Process_Manager.
*/
struct Process_Descriptor
{
/// Default ctor/dtor.
Process_Descriptor (void);
~Process_Descriptor (void);
/// Describes the process itself.
ACE_Process *process_;
/// Function to call when process exits
ACE_Event_Handler *exit_notify_;
/// Dump the state of an object.
void dump (void) const;
};
/// Resize the pool of Process_Descriptors.
int resize (size_t);
/// Locate the index of the table slot occupied by @a process_id.
/// Returns -1 if @a process_id is not in the @c process_table_
ssize_t find_proc (pid_t process_id);
#if defined (ACE_WIN32)
/// Locate the index of the table slot occupied by @a process_handle.
/// Returns ~0 if @a process_handle is not in the @c process_table_
ssize_t find_proc (ACE_HANDLE process_handle);
#endif /* ACE_WIN32 */
/// Insert a process in the table (checks for duplicates). Omitting
/// the process handle won't work on Win32...
/// Register @a event_handler to be called back when the process exits.
int insert_proc (ACE_Process *process,
ACE_Event_Handler *event_handler = 0);
/**
* Append information about a process, i.e., its <process_id> in the
* @c process_table_. Each entry is added at the end, growing the
* table if necessary.
* Register @a event_handler to be called back when the process exits.
*/
int append_proc (ACE_Process *process,
ACE_Event_Handler *event_handler = 0);
/// Actually removes the process at index @a n from the table. This method
/// must be called with locks held.
int remove_proc (size_t n);
/// If there's a specific handler for the Process at index @a n in the
/// table, or there's a default handler, call it.
int notify_proc_handler (size_t n, ACE_exitcode status);
/// Vector that describes process state within the Process_Manager.
Process_Descriptor *process_table_;
/// Maximum number of processes we can manage (should be dynamically
/// allocated).
size_t max_process_table_size_;
/// Current number of processes we are managing.
size_t current_count_;
/// This event handler is used to notify when a process we control
/// exits.
ACE_Event_Handler *default_exit_handler_;
/// Singleton pointer.
static ACE_Process_Manager *instance_;
/// Controls whether the <Process_Manager> is deleted when we shut
/// down (we can only delete it safely if we created it!)
static bool delete_instance_;
#if defined (ACE_HAS_THREADS)
/// This lock protects access/ops on @c process_table_.
ACE_Recursive_Thread_Mutex lock_;
#endif /* ACE_HAS_THREADS */
};
ACE_END_VERSIONED_NAMESPACE_DECL
#if defined (__ACE_INLINE__)
#include "ace/Process_Manager.inl"
#endif /* __ACE_INLINE__ */
#include /**/ "ace/post.h"
#endif /* ACE_PROCESS_MANAGER_H */
|