summaryrefslogtreecommitdiff
path: root/ace/Process_Manager.h
diff options
context:
space:
mode:
authorschmidt <douglascraigschmidt@users.noreply.github.com>1999-09-01 06:05:10 +0000
committerschmidt <douglascraigschmidt@users.noreply.github.com>1999-09-01 06:05:10 +0000
commit8677b6d622876a6e68f1ccddeaed5912beb05639 (patch)
tree734244f189c322bc24dae63defb393455bb817a5 /ace/Process_Manager.h
parent7c9df2b94260de0683498f181e8550f6382ad702 (diff)
downloadATCD-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.h196
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 */
};