/* -*- C++ -*- */ //============================================================================= /** * @file Process_Manager.h * * $Id$ * * @author Douglas C. Schmidt */ //============================================================================= #ifndef ACE_PROCESS_MANAGER_H #define ACE_PROCESS_MANAGER_H #include "ace/pre.h" #include "ace/Synch.h" #include "ace/Reactor.h" #include "ace/Event_Handler.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once #endif /* ACE_LACKS_PRAGMA_ONCE */ #include "ace/Process.h" /** * @class ACE_Process_Descriptor * * @brief Information describing each process that's controlled by an * . */ class ACE_Export ACE_Process_Descriptor { private: friend class ACE_Process_Manager; /// Default ctor/dtor. ACE_Process_Descriptor (void); ~ACE_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; }; /** * @class ACE_Process_Manager * * @brief Manages a group of processes. * * This class allows applications to control groups of processes, * similar to how the 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 , * depending on how involved you wish to be with the termination * of managed es. If you just want es to * go away when they're finished, simply register the * with an : * ACE_Process_Manager mgr( 100, some_reactor ) * -or- * ACE_Process_Manager mgr; * ... * mgr.open( 100, some_reactor ); * Then, the will clean up after any * 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 cleanup * code, then *don't* register the with a * Reactor, and be sure to call one of the * functions whenever there might be * managed es that have exited. * Note that in either case, allows you to * register "" to be called when a specific * exits, or when any without a specific * exits. When a exits, the * appropriate 's is called; the * passed is either the Process' HANDLE (on Win32), * or its pid cast to an (on unix). * It is also possible to call the * functions even though the is registered with * a . * 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 whose you invoked. It's best to * only use a single , and to create all * subprocesses by calling that 's * method. * Incidentally, when you register your with a * its notification pipe is used to help "reap" the * available exit statuses. Therefore, you must not use a * whose notify pipe has been disabled. Here's the * sequence of steps used to reap the exit statuses in this case: * + The registers a signal handler for * SIGCHLD. * + The SIGCHLD handler, when invoked, uses the 's * method to inform the to wake up. * + Next, the calls the 's * , this happens synchronously, not in * sighandler-space. * + The method collects all available exit * statuses. */ class ACE_Export ACE_Process_Manager : protected ACE_Event_Handler { public: friend class ACE_Process_Control; enum { DEFAULT_SIZE = 100 }; // = Initialization and termination methods. /** * Initialize an with a table containing up to * processes. This table resizes itself automatically as * needed. If a non-NULL is provided, this * uses it to notify an application when a * process it controls exits. By default, however, we don't use an * . */ ACE_Process_Manager (size_t size = ACE_Process_Manager::DEFAULT_SIZE, ACE_Reactor *reactor = 0); /** * Initialize an with a table containing up to * processes. This table resizes itself automatically as * needed. If a non-NULL is provided, this * uses it to notify an application when a * process it controls exits. By default, however, we don't use an * . */ int open (size_t size = 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); // = Singleton accessors. /// Get pointer to a process-wide . static ACE_Process_Manager *instance (void); /// Set pointer to a process-wide 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 to destroy the /// singleton. static void cleanup (void *instance, void *arg); // = Process creation methods. /** * Create a new process by passing to . 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); /** * Create a new process by passing to * . 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 new processes by passing to * , which is called times. If * is non-0 it is expected to be an array of 's, which * are filled in with the process ids of each newly created process. * Returns 0 on success and -1 on failure. */ int spawn_n (size_t n, ACE_Process_Options &options, pid_t *child_pids = 0); // = Process synchronization operations. /** * Block until there are no more child processes running that were * ed by this . Unlike the call * below, this method does not require a signal handler or * because it simply blocks synchronously waiting * for all the children managed by this 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 s the corresponding entries * from the ; otherwise, returns -1 on failure. */ int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time); /** * Wait up to for a single process to terminate. If * pid==0, waits for any of the managed es (but see the * note in the class documentation above for caveats about this -- * "sloppy process cleanup on unix") If pid != 0, waits for that * 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, const ACE_Time_Value &timeout, ACE_exitcode *status = 0); /** * Wait indefinitely for a single process to terminate. If pid==0, * waits for any of the managed es (but see the note in * the class documentation for caveats about this -- "sloppy Process * cleanup on unix") If pid != 0, waits for that only. * Returns the pid of the process whose exit was handled, or * ACE_INVALID_PID on error. */ pid_t wait (pid_t pid, ACE_exitcode *status = 0); /** * Reap the result of a single process by calling , * therefore, this method is not portable to Win32. If the child is * successfully reaped, is called automatically. This * method does the same thing that the method directly above * it does -- It's just here for backwards compatibility. */ int reap (pid_t pid = -1, ACE_exitcode *stat_loc = 0, int options = WNOHANG); // = Utility methods. /** * 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 register_handler (ACE_Event_Handler *event_handler, pid_t pid = ACE_INVALID_PID); /** * Remove process from the table. This is called * automatically by the method after it successfully reaped a * signal. It's also possible to call this method * directly from a signal handler, but don't call both and * ! */ int remove (pid_t pid); /** * Abruptly terminate a single process with id using the * 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); /// On OSs that support signals, send the signal to the specified /// process. Returns 0 on success and -1 on failure. int terminate (pid_t pid, int sig); /// Return the number of managed Processes. size_t managed (void) const; /// Dump the state of an object. void dump (void) const; /// Declare the dynamic allocation hooks. ACE_ALLOC_HOOK_DECLARE; protected: // = These methods allow a to be an . // As an , the 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); #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 * 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: /// Resize the pool of Process_Descriptors. int resize (size_t); /// Locate the index of the table slot occupied by . /// Returns -1 if is not in the ssize_t find_proc (pid_t process_id); #if defined (ACE_WIN32) /// Locate the index of the table slot occupied by . /// Returns ~0 if is not in the 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... int insert_proc (ACE_Process *process); /** * Append information about a process, i.e., its in the * . Each entry is added at the end, growing the * table if necessary. */ int append_proc (ACE_Process *process); /// Actually removes the process at index 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 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. ACE_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 is deleted when we shut /// down (we can only delete it safely if we created it!) static int delete_instance_; #if defined (ACE_HAS_THREADS) /// This lock protects access/ops on . ACE_Recursive_Thread_Mutex lock_; #endif /* ACE_HAS_THREADS */ }; #if defined (__ACE_INLINE__) #include "ace/Process_Manager.i" #endif /* __ACE_INLINE__ */ #include "ace/post.h" #endif /* ACE_PROCESS_MANAGER_H */