summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog-99b6
-rw-r--r--ace/Process_Manager.cpp538
-rw-r--r--ace/Process_Manager.h196
-rw-r--r--tests/Reactor_Notify_Test.cpp2
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);