diff options
author | schmidt <douglascraigschmidt@users.noreply.github.com> | 1999-09-01 06:05:10 +0000 |
---|---|---|
committer | schmidt <douglascraigschmidt@users.noreply.github.com> | 1999-09-01 06:05:10 +0000 |
commit | 8677b6d622876a6e68f1ccddeaed5912beb05639 (patch) | |
tree | 734244f189c322bc24dae63defb393455bb817a5 /ace/Process_Manager.h | |
parent | 7c9df2b94260de0683498f181e8550f6382ad702 (diff) | |
download | ATCD-8677b6d622876a6e68f1ccddeaed5912beb05639.tar.gz |
ChangeLogTag:Wed Sep 1 00:05:04 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu>
Diffstat (limited to 'ace/Process_Manager.h')
-rw-r--r-- | ace/Process_Manager.h | 196 |
1 files changed, 141 insertions, 55 deletions
diff --git a/ace/Process_Manager.h b/ace/Process_Manager.h index 0cf697e2d38..68126602389 100644 --- a/ace/Process_Manager.h +++ b/ace/Process_Manager.h @@ -40,14 +40,13 @@ private: // Default ctor/dtor. int delete_process_; - // Keeps track of whether we need to delete the Process or just - // <close> it? + // Do we need to delete the Process, or just close() it? ACE_Process *process_; // Describes the process itself. ACE_Event_Handler *exit_notify_; - // function to call when this process exits. + // function to call when process exits void dump (void) const; // Dump the state of an object. @@ -62,15 +61,77 @@ class ACE_Export ACE_Process_Manager : protected ACE_Event_Handler // 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. Moreover, - // it's best to avoid the <wait> methods in this class that - // require the use of signals since these aren't portable to - // Win32. In addition, if you choose to use signals, try to use - // the <ACE_OS::sigwait> variant of <{synchronous}> signal - // handling, rather than <{asynchronous}> signal handling. See - // $ACE_ROOT/tests/Process_Manager_Test.cpp for an illustrate of - // how to use <ACE_OS::sigwait> in conjunction with the - // <ACE_Process_Manager>. + // 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_Process>es. If you just want <Process>es 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 + // <Process>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 <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 <Process>es 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. public: friend class ACE_Process_Control; @@ -90,7 +151,7 @@ public: // <ACE_Reactor>. int open (size_t size = DEFAULT_SIZE, - ACE_Reactor *r = ACE_Reactor::instance ()); + 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 @@ -119,12 +180,12 @@ public: 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 -1. + // 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 -1. + // child that was created. On failure, returns ACE_INVALID_PID. int spawn_n (size_t n, ACE_Process_Options &options, @@ -137,7 +198,7 @@ public: // = Process synchronization operations. - int wait (void); + int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time); // Block until there are no more child processes running that were // <spawn>ed by this <ACE_Process_Manager>. Unlike the <wait> call // below, this method does not require a signal handler or @@ -145,31 +206,28 @@ public: // 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 <remove>s the corresponding - // <ACE_Process_Descriptor> entries from the <Process_Manager>; - // otherwise, returns -1 on failure. - - int wait (ACE_Time_Value *timeout); - // Block until there are no more child processes running that were - // <spawn>ed by this <ACE_Process_Manager> or <timeout> expires. - // Returns 0 on success and -1 on failure. - - int wait (pid_t pid, ACE_Time_Value *timeout); - // Block until pid exits or <timeout> expires. - // Returns 0 on success and -1 on failure. - - int wait (pid_t pid, - int *stat_loc, - int options); - // Reap the result of a single process by calling <ACE_OS::wait>. - // If the child is successfully reaped, <remove> is called - // automatically. Note that this method can be portably called - // within an asynchronous signal handler only if the platform allows - // signal handlers to <acquire>/<release> threads. This method - // works portably on platforms that support <ACE_OS::sigwait> -- in - // general, it's better to use <ACE_OS::sigwait> to wait for signals - // synchronously rather than asynchronously anyhow. + // any registered exit_handlers are called. Returns 0 on success + // (and <remove>s 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, + int *status = 0); + // Wait up to <timeout> for a single process to terminate. If + // pid==0, waits for any of the managed <Process>es (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, + int *status = 0); + // Wait indefinitely for a single process to terminate. If pid==0, + // waits for any of the managed <Process>es (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, int *stat_loc = 0, @@ -186,7 +244,7 @@ public: // 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 @@ -201,7 +259,8 @@ public: // 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); + 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. @@ -214,31 +273,57 @@ public: protected: // = These methods allow a <Process_Manager> to be an <Event_Handler>. - // @@ Dave, can you please add comments for these methods. + // 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. + + virtual int handle_input (ACE_HANDLE proc); + // Collect one (or more, on unix) Process exit status + +#if !defined(ACE_WIN32) virtual ACE_HANDLE get_handle (void) const; - // Return the handle. + // (unix only) : return dummy handle - virtual int handle_input (ACE_HANDLE); - // - virtual int handle_signal (int signum, siginfo_t * = 0, + virtual int handle_signal (int signum, + siginfo_t * = 0, ucontext_t * = 0); - // + // (unix only) : called to handle SIGCHLD; tweaks Reactor so that + // it'll call handle_input() synchronously +#endif // !defined(ACE_WIN32) + virtual int handle_close (ACE_HANDLE handle, ACE_Reactor_Mask close_mask); - // + // we're being removed from Reactor...on unix, close bogus handle. private: int resize (size_t); // Resize the pool of Process_Descriptors. - int find_proc (pid_t process_id); + ssize_t find_proc (pid_t process_id); // Locate the index of the table slot occupied by <process_id>. // Returns -1 if <process_id> is not in the <process_table_> #if defined (ACE_WIN32) - int find_proc (ACE_HANDLE process_handle); + size_t find_proc (ACE_HANDLE process_handle); // Locate the index of the table slot occupied by <process_handle>. - // Returns -1 if <process_handle> is not in the <process_table_> + // Returns ~0 if <process_handle> is not in the <process_table_> #endif /* ACE_WIN32 */ int insert_proc (ACE_Process *process); @@ -270,8 +355,10 @@ private: size_t current_count_; // Current number of processes we are managing. +#if !defined(ACE_WIN32) ACE_HANDLE dummy_handle_; // Allows SIGCHLD to be handled synchronously. +#endif ACE_Event_Handler *default_exit_handler_; // This event handler is used to notify when a process we control @@ -285,9 +372,8 @@ private: // down (we can only delete it safely if we created it!) #if defined (ACE_HAS_THREADS) - // = ACE_Thread_Mutex and condition variable for synchronizing termination. + // = ACE_Thread_Mutex for access/ops on process_table_ ACE_Thread_Mutex lock_; - ACE_Condition_Thread_Mutex zero_cond_; #endif /* ACE_HAS_THREADS */ }; |