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 | |
parent | 7c9df2b94260de0683498f181e8550f6382ad702 (diff) | |
download | ATCD-8677b6d622876a6e68f1ccddeaed5912beb05639.tar.gz |
ChangeLogTag:Wed Sep 1 00:05:04 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu>
-rw-r--r-- | ChangeLog-99b | 6 | ||||
-rw-r--r-- | ace/Process_Manager.cpp | 538 | ||||
-rw-r--r-- | ace/Process_Manager.h | 196 | ||||
-rw-r--r-- | tests/Reactor_Notify_Test.cpp | 2 |
4 files changed, 491 insertions, 251 deletions
diff --git a/ChangeLog-99b b/ChangeLog-99b index d6a3906880c..bd95d4f9f34 100644 --- a/ChangeLog-99b +++ b/ChangeLog-99b @@ -1,5 +1,11 @@ Wed Sep 1 00:05:04 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu> + * ace/Process_Manager: Integrated the latest and greatest + ACE_Process_Manager from Dave Madden <dhm@mersenne.com>. This + should now work relatively transparently on all OS platforms + that support processes. We'll be adding a new + Process_Manager_Test.cpp shortly... + * tests/Reactor_Notify_Test.cpp: Modified this test to exercise the new user-level notification queueing in the ACE_Reactor. diff --git a/ace/Process_Manager.cpp b/ace/Process_Manager.cpp index 111c2d19ef8..0012e0eb9c8 100644 --- a/ace/Process_Manager.cpp +++ b/ace/Process_Manager.cpp @@ -4,6 +4,7 @@ #define ACE_BUILD_DLL #include "ace/Synch_T.h" #include "ace/Process.h" +#include "ace/Signal.h" #include "ace/Process_Manager.h" #include "ace/Object_Manager.h" @@ -11,9 +12,9 @@ #include "ace/Process_Manager.i" #endif /* __ACE_INLINE__ */ -ACE_RCSID (ace, Process_Manager, "$Id$") +ACE_RCSID(ace, Process_Manager, "$Id$") -ACE_ALLOC_HOOK_DEFINE (ACE_Process_Manager) +ACE_ALLOC_HOOK_DEFINE(ACE_Process_Manager) // Singleton instance. ACE_Process_Manager *ACE_Process_Manager::instance_ = 0; @@ -22,6 +23,10 @@ ACE_Process_Manager *ACE_Process_Manager::instance_ = 0; // (we can only delete it safely if we created it!) int ACE_Process_Manager::delete_instance_ = 0; +ACE_Process_Descriptor::~ACE_Process_Descriptor (void) +{ +} + void ACE_Process_Descriptor::dump (void) const { @@ -29,9 +34,8 @@ ACE_Process_Descriptor::dump (void) const ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); - ACE_DEBUG ((LM_DEBUG, - ASYS_TEXT ("\nproc_id_ = %d"), - this->process_->getpid())); + ACE_DEBUG ((LM_DEBUG, ASYS_TEXT ("\nproc_id_ = %d"), + this->process_->getpid( ))); ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); } @@ -52,14 +56,10 @@ ACE_Process_Manager::dump (void) const ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); } -ACE_Process_Descriptor::~ACE_Process_Descriptor (void) -{ -} - ACE_Process_Descriptor::ACE_Process_Descriptor (void) : delete_process_ (0), - process_ (0), - exit_notify_ (0) + process_ (0), + exit_notify_ (0) { ACE_TRACE ("ACE_Process_Descriptor::ACE_Process_Descriptor"); } @@ -130,15 +130,14 @@ ACE_Process_Manager::resize (size_t size) // Create and initialize the table to keep track of the process pool. int -ACE_Process_Manager::open (size_t size, - ACE_Reactor *r) +ACE_Process_Manager::open (size_t size, ACE_Reactor *r) { ACE_TRACE ("ACE_Process_Manager::open"); if (r) - { - ACE_Event_Handler::reactor (r); -#if !defined (ACE_WIN32) + { + ACE_Event_Handler::reactor( r ); +#if !defined(ACE_WIN32) // (No signals for child-exited on Win32) Assign the // Process_Manager a dummy I/O descriptor. Note that even // though we open this file "Write Only" we still need to use @@ -163,13 +162,12 @@ ACE_Process_Manager::open (size_t size, 1)); if (reactor ()->register_handler - (SIGCHLD, - this) == -1) + (SIGCHLD, this) == -1) ACE_ERROR ((LM_ERROR, "%p\n%a", "register_handler", 1)); -#endif // !defined (ACE_WIN32) +#endif // !defined(ACE_WIN32) } ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); @@ -177,25 +175,28 @@ ACE_Process_Manager::open (size_t size, if (this->max_process_table_size_ < size) this->resize (size); return 0; - } // Initialize the synchronization variables. -ACE_Process_Manager::ACE_Process_Manager (size_t size,ACE_Reactor *r) - : ACE_Event_Handler(), - process_table_ (0), +ACE_Process_Manager::ACE_Process_Manager (size_t size, + ACE_Reactor *r) + : ACE_Event_Handler (), + process_table_ (0), max_process_table_size_ (0), current_count_ (0), - dummy_handle_ (ACE_INVALID_HANDLE), - default_exit_handler_ (0) +#if !defined(ACE_WIN32) + dummy_handle_ (ACE_INVALID_HANDLE), +#endif // !defined(ACE_WIN32) + default_exit_handler_ (0) #if defined (ACE_HAS_THREADS) - , zero_cond_ (lock_) + , lock_ () #endif /* ACE_HAS_THREADS */ { ACE_TRACE ("ACE_Process_Manager::ACE_Process_Manager"); - if (this->open (size,r) == -1) + if (this->open (size, + r) == -1) ACE_ERROR ((LM_ERROR, ASYS_TEXT ("%p\n"), ASYS_TEXT ("ACE_Process_Manager"))); @@ -208,7 +209,7 @@ ACE_Process_Manager::close (void) { ACE_TRACE ("ACE_Process_Manager::close"); - if (this->reactor ()) + if (this->reactor ()) { this->reactor ()->remove_handler (this, 0); this->reactor (0); @@ -219,16 +220,19 @@ ACE_Process_Manager::close (void) if (this->process_table_ != 0) { for (size_t i = 0; i < this->current_count_; ++i) - if (this->process_table_[i].exit_notify_ != 0) - this->process_table_[i].exit_notify_->handle_close - (this->process_table_[i].process_->gethandle (), - 0); + { + if (this->process_table_[i].exit_notify_ != 0) + this->process_table_[i].exit_notify_->handle_close + (this->process_table_[i].process_->gethandle (), + 0); + } delete [] this->process_table_; this->process_table_ = 0; this->max_process_table_size_ = 0; this->current_count_ = 0; } + return 0; } @@ -238,17 +242,11 @@ ACE_Process_Manager::~ACE_Process_Manager (void) this->close (); } -ACE_HANDLE -ACE_Process_Manager::get_handle (void) const -{ - return this->dummy_handle_; -} - -// This is called when the Reactor notices that a process has exited. -// On Windoze, it knows which process it was, and passes the process' +// This is called when the Reactor notices that a Process has exited. +// On Windoze, it knows which Process it was, and passes the Process' // handle. On Unix, what has actually happened is a SIGCHLD invoked // the handle_signal routine, which fooled the Reactor into thinking -// that this routine needed to be called. (On Unix, we reap as many +// that this routine needed to be called. (On Unix, we reap as many // children as are dead.) int @@ -256,62 +254,75 @@ ACE_Process_Manager::handle_input (ACE_HANDLE proc) { ACE_TRACE ("ACE_Process_Manager::handle_input"); -#if defined (ACE_WIN32) +#if defined(ACE_WIN32) DWORD status = 0; - BOOL result = ::GetExitCodeProcess (proc, &status); + BOOL result = ::GetExitCodeProcess (proc, + &status); if (result) { if (status != STILL_ACTIVE) { { - ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex,ace_mon,lock_,-1)); - int i = find_proc ( proc ); - pid_t pid = i != -1 ? process_table_[i].process_->getpid () : -1; + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, lock_, -1)); + + ssize_t i = this->find_proc (proc); + pid_t pid = i != -1 + ? process_table_[i].process_->getpid () + : ACE_INVALID_PID; this->notify_proc_handler (proc, pid, status); this->remove_proc (pid); } - ACE_Reactor *r = reactor(); + ACE_Reactor *r = reactor (); + if (r) - r->remove_handler ( proc, 0 ); + r->remove_handler (proc, 0); } - else - { - // Huh? Process still active -- shouldn't have been called yet! - } + else + ; // Huh? Process still active -- shouldn't have been called yet! } else { - // <GetExitCodeProcess> failed. + // GetExitCodeProcess failed ACE_ERROR ((LM_ERROR, "%p\n%a", "handle_input: GetExitCodeProcess failed", 0)); } -#else // !defined (ACE_WIN32) - ACE_UNUSED_ARG (proc); - // <proc> is <dummy_handle_> on unix. - while (this->reap() > 0) +#else // !defined(ACE_WIN32) + ACE_UNUSED_ARG (proc); // proc is dummy_handle_ on unix. + while (this->reap () > 0) continue; -#endif /* ACE_WIN32 */ +#endif return 0; } +#if !defined (ACE_WIN32) +ACE_HANDLE +ACE_Process_Manager::get_handle (void) const +{ + return this->dummy_handle_; +} + int -ACE_Process_Manager::handle_signal (int, siginfo_t *, ucontext_t *) +ACE_Process_Manager::handle_signal (int, + siginfo_t *, + ucontext_t *) { return reactor ()->ready_ops (this->dummy_handle_, ACE_Event_Handler::READ_MASK, ACE_Reactor::ADD_MASK); } +#endif /* !ACE_WIN32 */ int -ACE_Process_Manager::register_handler (ACE_Event_Handler *eh, pid_t pid) +ACE_Process_Manager::register_handler (ACE_Event_Handler *eh, + pid_t pid) { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); - if (pid == -1) + if (pid == ACE_INVALID_PID) { if (this->default_exit_handler_ != 0) this->default_exit_handler_->handle_close (ACE_INVALID_HANDLE, @@ -320,16 +331,18 @@ ACE_Process_Manager::register_handler (ACE_Event_Handler *eh, pid_t pid) return 0; } - int i = this->find_proc (pid); + ssize_t i = this->find_proc (pid); - if (i == -1) + if (i == -1) + // set "process not found" error return -1; else { ACE_Process_Descriptor &proc_desc = this->process_table_[i]; if (proc_desc.exit_notify_ != 0) - proc_desc.exit_notify_->handle_close (ACE_INVALID_HANDLE, 0); + proc_desc.exit_notify_->handle_close (ACE_INVALID_HANDLE, + 0); proc_desc.exit_notify_ = eh; return 0; } @@ -340,37 +353,35 @@ ACE_Process_Manager::handle_close (ACE_HANDLE handle, ACE_Reactor_Mask) { ACE_TRACE ("ACE_Process_Manager::handle_close"); - ACE_UNUSED_ARG (handle); - - ACE_ASSERT (handle == this->dummy_handle_); +#if !defined(ACE_WIN32) + ACE_ASSERT (handle==this->dummy_handle_); ACE_OS::close (dummy_handle_); - +#endif return 0; } - + // Create a new process. pid_t ACE_Process_Manager::spawn (ACE_Process_Options &options) { - ACE_Process *process; - ACE_NEW_RETURN (process, - ACE_Process, - ACE_INVALID_PID); + ACE_Process *process = new ACE_Process; - pid_t pid = spawn (process, - options); + options.setgroup (ACE_OS::getpid ()); - ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + pid_t pid = spawn (process, options); - int i = ACE_INVALID_PID; + if (pid != ACE_INVALID_PID) + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, ACE_INVALID_PID)); + + ssize_t i = this->find_proc (pid); - if (pid != -1) - i = this->find_proc (pid); + ACE_ASSERT (i != -1); - if (i != -1) - this->process_table_[i].delete_process_ = 1; + this->process_table_[i].delete_process_ = 1; + } return pid; } @@ -378,21 +389,22 @@ ACE_Process_Manager::spawn (ACE_Process_Options &options) // Create a new process. pid_t -ACE_Process_Manager::spawn (ACE_Process *process, - ACE_Process_Options &options) +ACE_Process_Manager::spawn (ACE_Process *process, ACE_Process_Options &options) { ACE_TRACE ("ACE_Process_Manager::spawn"); pid_t pid = process->spawn (options); // Only include the pid in the parent's table. - if (pid == -1 || pid == 0) + if (pid == ACE_INVALID_PID + || pid == 0) return pid; else { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); - if (this->append_proc (process) == -1) + if (this->append_proc (process) == -1) + // bad news: spawned, but not registered in table. return ACE_INVALID_PID; else return pid; @@ -408,11 +420,18 @@ ACE_Process_Manager::spawn_n (size_t n, { ACE_TRACE ("ACE_Process_Manager::spawn_n"); - // This doesn't work (yet). - for (size_t i = 0; i < n; i++) + if (child_pids != 0) + for (size_t i = 0; + i < n; + ++i) + child_pids[i] = ACE_INVALID_PID; + + for (size_t i = 0; + i < n; + i++) { pid_t pid = this->spawn (options); - if (pid <= 0) + if (pid == ACE_INVALID_PID || pid == 0) // We're in the child or something's gone wrong. return pid; else if (child_pids != 0) @@ -438,17 +457,18 @@ ACE_Process_Manager::append_proc (ACE_Process *proc) else { ACE_Process_Descriptor &proc_desc = - this->process_table_[this->current_count_]; + this->process_table_[this->current_count_]; - proc_desc.delete_process_ = 0; // pending better info from caller + // pending better info from caller + proc_desc.delete_process_ = 0; proc_desc.process_ = proc; #if defined (ACE_WIN32) // If we have a Reactor, then we're supposed to reap Processes // automagically. Get a handle to this new Process and tell the - // Reactor we're interested in handling_input () on it. + // Reactor we're interested in <handling_input> on it. - ACE_Reactor *r = this->reactor (); + ACE_Reactor *r = reactor (); if (r != 0) r->register_handler (proc->gethandle (), this, @@ -468,17 +488,12 @@ ACE_Process_Manager::insert_proc (ACE_Process *proc) { ACE_TRACE ("ACE_Process_Manager::insert_proc"); -#if 0 // Check for duplicates and bail out if they're already // registered... if (this->find_proc (proc->getpid ()) != -1) return -1; return this->append_proc (proc); -#else - ACE_UNUSED_ARG (proc); - ACE_NOTSUP_RETURN (-1); -#endif } // Remove a process from the pool. @@ -499,32 +514,36 @@ ACE_Process_Manager::remove_proc (pid_t pid) { ACE_TRACE ("ACE_Process_Manager::remove"); - int i = this->find_proc (pid); + ssize_t i = this->find_proc (pid); if (i == -1) return -1; - else - { - if (this->process_table_[i].delete_process_) - delete this->process_table_[i].process_; - - this->current_count_--; - - if (this->current_count_ > 0) - // Compact the table by moving the last item into the slot - // vacated by the index being removed (this is a structure - // assignment). - this->process_table_[i] = - this->process_table_[this->current_count_]; -#if defined (ACE_HAS_THREADS) - // Tell all waiters when there are no more threads left in the - // pool. - if (this->current_count_ == 0) - this->zero_cond_.broadcast (); -#endif /* ACE_HAS_THREADS */ - return 0; + // If there's an exit_notify_ <Event_Handler> for this pid, call its + // <handle_close> method. + + if (this->process_table_[i].exit_notify_ != 0) + { + this->process_table_[i].exit_notify_->handle_close + (this->process_table_[i].process_->gethandle(), + 0); + this->process_table_[i].exit_notify_ = 0; } + + if (this->process_table_[i].delete_process_) + delete this->process_table_[i].process_; + + this->process_table_[i].process_ = 0; + + this->current_count_--; + + if (this->current_count_ > 0) + // Compact the table by moving the last item into the slot vacated + // by the index being removed (this is a structure assignment). + this->process_table_[i] = + this->process_table_[this->current_count_]; + + return 0; } int @@ -534,68 +553,67 @@ ACE_Process_Manager::terminate (pid_t pid) // Check for duplicates and bail out if they're already // registered... - int i = this->find_proc (pid); + ssize_t i = this->find_proc (pid); if (i == -1) + // set "no such process" error return -1; - else + + int result = ACE::terminate_process (pid); + + if (result != -1) { - int result = ACE::terminate_process (this->process_table_[i].process_->getpid ()); - - if (result != -1) - { - // Save/restore errno. - ACE_Errno_Guard error (errno); - this->remove (this->process_table_[i].process_->getpid ()); - return 0; - } - else - return -1; + // Save/restore errno. + ACE_Errno_Guard error (errno); + this->remove (pid); + return 0; } + else + return -1; } int -ACE_Process_Manager::terminate (pid_t pid, int sig) +ACE_Process_Manager::terminate (pid_t pid, + int sig) { ACE_TRACE ("ACE_Process_Manager::terminate"); // Check for duplicates and bail out if they're already // registered... - int i = this->find_proc (pid); + ssize_t i = this->find_proc (pid); if (i == -1) + // set "no such process" error return -1; - else - { - return ACE_OS::kill (this->process_table_[i].process_->getpid (), sig); - } + + return ACE_OS::kill (pid, sig); } // Locate the index in the table associated with <pid>. Must be // called with the lock held. -int +ssize_t ACE_Process_Manager::find_proc (pid_t pid) { ACE_TRACE ("ACE_Process_Manager::find_proc"); - for (size_t i = 0; i < this->current_count_; i++) + for (size_t i = 0; i < this->current_count_; ++i) if (pid == this->process_table_[i].process_->getpid ()) return i; - return -1; + return -1; } #if defined (ACE_WIN32) -// Locate the index in the table associated with <h>. Must be called -// with the lock held. +// Locate the index in the table associated with <h>. Must be +// called with the lock held. -int +size_t ACE_Process_Manager::find_proc (ACE_HANDLE h) { ACE_TRACE ("ACE_Process_Manager::find_proc"); - for (size_t i = 0; i < this->current_count_; i++) + for (size_t i = 0; i < this->current_count_; ++i) if (h == this->process_table_[i].process_->gethandle ()) return i; @@ -603,80 +621,207 @@ ACE_Process_Manager::find_proc (ACE_HANDLE h) } #endif /* ACE_WIN32 */ -// Wait for all the processs to exit. This implementation requires a -// cooperative signal handler or <ACE_OS::sigwait> thread, so it's not -// portable to Win32. +// Wait for all the Processs to exit, or until <timeout> elapses. +// Returns the number of Processes remaining, or -1 on an error. int -ACE_Process_Manager::wait (ACE_Time_Value *timeout) +ACE_Process_Manager::wait (const ACE_Time_Value &timeout) { ACE_TRACE ("ACE_Process_Manager::wait"); -#if defined (ACE_HAS_THREADS) - ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1); + ACE_Time_Value until = timeout; - while (this->current_count_ > 0) - if (this->zero_cond_.wait (timeout) == -1) - return -1; -#else - ACE_UNUSED_ARG (timeout); -#endif /* ACE_HAS_THREADS */ + if (until < ACE_Time_Value::max_time) + until += ACE_OS::gettimeofday (); - return 0; + while (current_count_ > 0) + { + ACE_Time_Value remaining = until < ACE_Time_Value::max_time + ? until - ACE_OS::gettimeofday () + : ACE_Time_Value::max_time; + if (remaining <= ACE_Time_Value::zero) + break; + + pid_t pid = this->wait (0, remaining); + + if (pid == ACE_INVALID_PID) // wait() failed + return -1; + if (pid == 0) // timeout + break; + // else Process terminated...wait for more... + } + return current_count_; } -// The following method *is* portable all the operating systems that -// support processes. +// Collect a single child process' exit status. Store the exit code +// in *<stat_loc> if non-zero. Call the appropriate exit_notify. If +// <pid> == 0, wait for any of the Process_Manager's children (or as +// near as possible -- on Unix, we might accidentally get some other +// Process_Manager's Process, or an unmanaged Process, or a child +// process started by some other means. -int -ACE_Process_Manager::wait (void) +pid_t +ACE_Process_Manager::wait (pid_t pid, int *status) { ACE_TRACE ("ACE_Process_Manager::wait"); - ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); - - for (size_t i = 0; i < this->current_count_; i++) - { - ACE_Process_Descriptor &proc_desc = - this->process_table_[i]; - pid_t pid = proc_desc.process_->wait (); - - if (pid != -1) - this->remove_proc (pid); - return pid; - } - - return 0; + return wait (pid, + ACE_Time_Value::max_time, + status); } -// Collect a single child processes' exit status by calling -// <ACE_OS::wait>. Calls the appropriate exit_notify, if registered. +// Collect a single child processes' exit status, unless <timeout> +// elapses before the process exits. Same caveats about accidental +// Process reaping on Unix as above. -int +pid_t ACE_Process_Manager::wait (pid_t pid, - int *stat_loc, - int options) + const ACE_Time_Value &timeout, + int *status) { ACE_TRACE ("ACE_Process_Manager::wait"); int local_stat = 0; + if (status == 0) + status = &local_stat; - if (stat_loc == 0) - stat_loc = &local_stat; + ACE_Process *proc = 0; + + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); - pid = ACE_OS::waitpid (pid, stat_loc, options); + if (pid != 0) + { + ssize_t i = this->find_proc (pid); + if (i == -1) + return ACE_INVALID_PID; + else + proc = process_table_[i].process_; + } - if (pid > 0) + if (proc != 0) + pid = proc->wait (timeout, status); + else { - ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); - this->notify_proc_handler (ACE_HANDLE (pid), - pid, - *stat_loc); + // Wait for any Process spawned by this Process_Manager. +#if defined(ACE_WIN32) + + HANDLE *handles; + + ACE_NEW_RETURN (handles, + HANDLE[current_count_], + ACE_INVALID_HANDLE); + + for (size_t i = 0; + i < current_count_; + ++i) + handles[i] = + process_table_[i].process_->gethandle (); + + DWORD result = ::WaitForMultipleObjects (current_count_, + handles, + FALSE, + timeout == ACE_Time_Value::max_time + ? INFINITE + : timeout.msec ()); + if (result == WAIT_FAILED) + pid = ACE_INVALID_PID; + else if (result == WAIT_TIMEOUT) + pid = 0; + else + { + ACE_ASSERT (result >= WAIT_OBJECT_0 + && result < WAIT_OBJECT_0 + current_count_); + + ssize_t i = this->find_proc (handles[result - WAIT_OBJECT_0]); + + if (i != -1) + { + DWORD ulstat = 0; + pid = process_table_[i].process_->getpid (); + result = ::GetExitCodeProcess (handles[result - WAIT_OBJECT_0], + &ulstat); + if (result == 0) + { + // GetExitCodeProcess failed! + this->remove_proc (pid); + pid = ACE_INVALID_PID; + } + else + *status = ulstat; + } + else + // uh oh...handle removed from process_table_, even though + // we're holding a lock! + ; + } + + delete [] handles; +#else /* !defined(ACE_WIN32) */ + if (timeout == ACE_Time_Value::max_time) + pid = ACE_OS::waitpid (-(ACE_OS::getpid ()), + status, 0 ); + else if (timeout == ACE_Time_Value::zero) + pid = ACE_OS::waitpid (-(ACE_OS::getpid ()), + status, + WNOHANG); + else + { + ACE_Time_Value wait_until = timeout + ACE_OS::gettimeofday(); + + for (;;) + { + pid = ACE_OS::waitpid (-(ACE_OS::getpid()), + status, + WNOHANG); + + if (pid != 0) + // "no such children" error, or got one! + break; + + ACE_Sig_Set alarm_or_child; + + alarm_or_child.sig_add (SIGALRM); + alarm_or_child.sig_add (SIGCHLD); + + ACE_Time_Value time_left = wait_until - ACE_OS::gettimeofday (); + + // if ACE_OS::ualarm doesn't have sub-second resolution: + time_left += ACE_Time_Value (0,500000); + time_left.usec (0); + + if (time_left <= ACE_Time_Value::zero) { + pid = 0; + break; + } + + ACE_OS::ualarm (time_left); + ACE_OS::sigwait (alarm_or_child); + } + } +#endif /* !defined (ACE_WIN32) */ } + if (pid != ACE_INVALID_PID && pid != 0) + { + if (proc == 0) + { + ssize_t i = this->find_proc (pid); + if (i == -1) + // oops, reaped an unmanaged process! + return pid; + else + proc = process_table_[i].process_; + } + notify_proc_handler (proc->gethandle (), + pid, + *status); + this->remove_proc (pid); + } + return pid; } +// Legacy method: int ACE_Process_Manager::reap (pid_t pid, int *stat_loc, @@ -684,7 +829,11 @@ ACE_Process_Manager::reap (pid_t pid, { ACE_TRACE ("ACE_Process_Manager::reap"); - return this->wait (pid, stat_loc, options); + return this->wait (pid, + (ACE_BIT_ENABLED (options, WNOHANG) + ? ACE_Time_Value::zero + : ACE_Time_Value::max_time), + stat_loc); } // Notify either the process-specific handler or the generic handler. @@ -692,15 +841,14 @@ ACE_Process_Manager::reap (pid_t pid, // if process found, 0 if not. Must be called with locks held. int -ACE_Process_Manager::notify_proc_handler (ACE_HANDLE h, - pid_t pid, - int) +ACE_Process_Manager::notify_proc_handler (ACE_HANDLE h, pid_t pid, int) { - int i = this->find_proc (pid); + ssize_t i = this->find_proc (pid); - if (i != -1) + if (i != -1) { - ACE_Process_Descriptor &proc_desc = this->process_table_[i]; + ACE_Process_Descriptor &proc_desc = + this->process_table_[i]; if (proc_desc.exit_notify_ != 0) { 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 */ }; diff --git a/tests/Reactor_Notify_Test.cpp b/tests/Reactor_Notify_Test.cpp index 50a4583400a..cd6953d16fb 100644 --- a/tests/Reactor_Notify_Test.cpp +++ b/tests/Reactor_Notify_Test.cpp @@ -174,7 +174,7 @@ Supplier_Task::perform_notifications (int notifications) { ACE_Reactor::instance ()->max_notify_iterations (notifications); - int iterations = ACE_MAX_ITERATIONS; + size_t iterations = ACE_MAX_ITERATIONS; if (this->long_timeout_) iterations *= (iterations * iterations * iterations); |