diff options
author | schmidt <douglascraigschmidt@users.noreply.github.com> | 1999-08-30 21:26:48 +0000 |
---|---|---|
committer | schmidt <douglascraigschmidt@users.noreply.github.com> | 1999-08-30 21:26:48 +0000 |
commit | 82455c2029d9946c5ccfa12a1c3a38d3d15270e5 (patch) | |
tree | 263e2600bcc0702ebce60fcf5e53bdb630803b28 | |
parent | 2df08d0bea3dda3312f99cf54ae31e138d97565e (diff) | |
download | ATCD-82455c2029d9946c5ccfa12a1c3a38d3d15270e5.tar.gz |
ChangeLogTag:Mon Aug 30 16:12:36 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu>
-rw-r--r-- | ChangeLog-99b | 67 | ||||
-rw-r--r-- | ace/Msg_WFMO_Reactor.h | 13 | ||||
-rw-r--r-- | ace/OS.h | 9 | ||||
-rw-r--r-- | ace/OS.i | 18 | ||||
-rw-r--r-- | ace/Process.cpp | 134 | ||||
-rw-r--r-- | ace/Process.h | 40 | ||||
-rw-r--r-- | ace/Process.i | 59 | ||||
-rw-r--r-- | ace/Process_Manager.cpp | 377 | ||||
-rw-r--r-- | ace/Process_Manager.h | 139 | ||||
-rw-r--r-- | ace/Signal.h | 3 | ||||
-rw-r--r-- | ace/config-chorus.h | 1 | ||||
-rw-r--r-- | ace/config-cray.h | 19 | ||||
-rw-r--r-- | ace/config-cygwin32-common.h | 1 | ||||
-rw-r--r-- | ace/config-freebsd-pthread.h | 1 | ||||
-rw-r--r-- | ace/config-freebsd.h | 1 | ||||
-rw-r--r-- | ace/config-hpux-9.x.h | 2 | ||||
-rw-r--r-- | ace/config-lynxos.h | 1 | ||||
-rw-r--r-- | ace/config-netbsd.h | 1 | ||||
-rw-r--r-- | ace/config-sunos4-g++.h | 1 | ||||
-rw-r--r-- | ace/config-sunos4-lucid3.2.h | 1 | ||||
-rw-r--r-- | ace/config-sunos4-sun3.x.h | 1 | ||||
-rw-r--r-- | ace/config-sunos4-sun4.1.4.h | 1 | ||||
-rw-r--r-- | ace/config-sunos4-sun4.x-orbix.h | 1 | ||||
-rw-r--r-- | ace/config-sunos4-sun4.x.h | 1 | ||||
-rw-r--r-- | ace/config-win32-common.h | 1 | ||||
-rw-r--r-- | examples/OS/Process/process.cpp | 218 |
26 files changed, 879 insertions, 232 deletions
diff --git a/ChangeLog-99b b/ChangeLog-99b index fbe21bf797d..b81015ce43b 100644 --- a/ChangeLog-99b +++ b/ChangeLog-99b @@ -1,3 +1,12 @@ +Mon Aug 30 16:12:36 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu> + + * ace/Process.i: Oops, the implementation of gethandle() was + in a Win32-specific part of the code... + + * ace/Msg_WFMO_Reactor.h: Fixed a problem that was causing + ACE_Msg_WFMO_Reactor from being included in the documentation + windex files. Thanks to Jody Hagins for reporting this. + Mon Aug 30 16:16:46 1999 Ossama Othman <othman@cs.wustl.edu> * PROBLEM-REPORT-FORM: @@ -11,11 +20,59 @@ Mon Aug 30 14:27:26 1999 David L. Levine <levine@cs.wustl.edu> use the explicit template instantiations even with ACE_HAS_GNU_REPO. g++ 2.91.66 misses some of them with -frepo. -Mon Aug 30 12:53:48 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu> - - * ace/Local_Tokens.h: Added a comment explaining that these locking - classes aren't intended as general-purpose synchronization - mechanisms. Thanks to Brian Wright <bwright@paladyne.com> +Mon Aug 30 15:23:12 1999 Douglas C. Schmidt <schmidt@tango.cs.wustl.edu> + + * ace/Process_Manager: Integrated the new features that make it + possible to notify a Reactor when a process exits. Thanks to + Dave Madden <dhm@mersenne.com> for contributing this. + + * ace/Process: Added a more portable timed wait() operation for + UNIX platforms. However, this method has the following two + limitations: (1) on UNIX platforms this function uses <ualarm>, + i.e., it overwrites any existing alarm and (2) it steals all + SIGCHLDs during the timeout period, which will break another + ACE_Process_Manager in the same process that's expecting SIGCHLD + to kick off process reaping. If you know a good solution to + this please let us know. Thanks to Dave Madden + <dhm@mersenne.com> for contributing this. + + * ace/Process: Added setgroup() and getgroup() methods to + ACE_Process_Options so that we can manage groups of processes. + Thanks to Dave Madden <dhm@mersenne.com> for contributing this. + + * examples/OS/Process/process.cpp: Added a new version that + tests the various wait() methods on ACE_Process. Thanks to Dave + Madden <dhm@mersenne.com> for contributing this. + + * config-chorus.h, + config-cray.h, + config-cygwin32-common.h, + config-freebsd-pthread.h, + config-freebsd.h, + config-hpux-9.x.h, + config-hpux-9.x.h, + config-lynxos.h, + config-netbsd.h, + config-sunos4-g++.h, + config-sunos4-lucid3.2.h, + config-sunos4-sun3.x.h, + config-sunos4-sun4.1.4.h, + config-sunos4-sun4.x-orbix.h, + config-sunos4-sun4.x.h, + config-win32-common.h: + Added ACE_LACKS_SETPGID on the assumption that platforms lacking + getpgid() won't have setpgid() either. If this turns out to be + incorrect, please let me know. + + * ace/OS: Added a new wrapper facade method for setpgid(). Thanks to + Dave Madden <dhm@mersenne.com> for contributing this. + + * ace/OS.h: Added a new macro called ACE_INVALID_PID. Thanks to + Dave Madden <dhm@mersenne.com> for contributing this. + + * ace/Local_Tokens.h: Added a comment explaining that these locking + classes aren't intended as general-purpose synchronization + mechanisms. Thanks to Brian Wright <bwright@paladyne.com> * examples/Misc/test_trace.cpp (main): Added a call to ACE_OS::atexit() to make sure we test this someplace. diff --git a/ace/Msg_WFMO_Reactor.h b/ace/Msg_WFMO_Reactor.h index 0002c6f6c45..10fdc10b4ef 100644 --- a/ace/Msg_WFMO_Reactor.h +++ b/ace/Msg_WFMO_Reactor.h @@ -29,15 +29,14 @@ class ACE_Export ACE_Msg_WFMO_Reactor : public ACE_WFMO_Reactor { // = TITLE - // An object oriented event demultiplexor and event handler - // dispatcher for Win32 MsgWaitForMultipleObjects. + // An OO event demultiplexor and event handler dispatcher for + // Win32 <MsgWaitForMultipleObjects>. // // = DESCRIPTION - // The ACE_Msg_WFMO_Reactor is an object-oriented event - // demultiplexor and event handler Reactor. It differs from - // WFMO_Reactor by its ability to react on Windows messages. It - // is needed when the task should serve also as a COM/DCOM - // server. + // The ACE_Msg_WFMO_Reactor is an OO event demultiplexor and + // event handler Reactor. It differs from <ACE_WFMO_Reactor> by + // its ability to react on Windows messages. It is needed when + // the task should serve also as a COM/DCOM server. public: // = Initialization and termination methods. ACE_Msg_WFMO_Reactor (ACE_Sig_Handler * = 0, @@ -851,11 +851,12 @@ typedef int key_t; # if defined (ACE_PSOS_DIAB_PPC) typedef unsigned long pid_t; +# define ACE_INVALID_PID ((pid_t) ~0) # else /* !defined (ACE_PSOS_DIAB_PPC) */ typedef long pid_t; +# define ACE_INVALID_PID ((pid_t) -1) # endif /* defined (ACE_PSOS_DIAB_PPC) */ - // typedef unsigned char wchar_t; # endif @@ -3250,6 +3251,7 @@ typedef OVERLAPPED ACE_OVERLAPPED; typedef DWORD ACE_thread_t; typedef HANDLE ACE_hthread_t; typedef long pid_t; +#define ACE_INVALID_PID ((pid_t) -1) # if defined (ACE_HAS_TSS_EMULATION) typedef DWORD ACE_OS_thread_key_t; typedef u_int ACE_thread_key_t; @@ -3492,6 +3494,10 @@ typedef char TCHAR; # define ACE_SEH_EXCEPT(X) while (0) # define ACE_SEH_FINALLY if (1) +# if !defined (ACE_INVALID_PID) +# define ACE_INVALID_PID ((pid_t) -1) +# endif /* ACE_INVALID_PID */ + // The "null" device on UNIX. # define ACE_DEV_NULL "/dev/null" @@ -5545,6 +5551,7 @@ public: static uid_t getuid (void); static int setuid (uid_t); static pid_t setsid (void); + static int setpgid (pid_t pid, pid_t pgid); static int system (const char *s); static pid_t waitpid (pid_t, int *status = 0, @@ -10125,6 +10125,24 @@ ACE_OS::getppid (void) #endif /* ACE_WIN32 */ } +ACE_INLINE int +ACE_OS::setpgid (pid_t pid, pid_t pgid) +{ + ACE_TRACE ("ACE_OS::setpgid"); +#if defined (ACE_LACKS_SETPGID) + ACE_UNUSED_ARG (pid); + ACE_UNUSED_ARG (pgid); + ACE_NOTSUP_RETURN (-1); +#elif defined (VXWORKS) || defined (ACE_PSOS) + // setpgid() is not supported, only one process anyway. + ACE_UNUSED_ARG (pid); + ACE_UNUSED_ARG (pgid); + return 0; +#else + ACE_OSCALL_RETURN (::setpgid (pid, pgid), pid_t, -1); +#endif /* ACE_WIN32 */ +} + ACE_INLINE off_t ACE_OS::lseek (ACE_HANDLE handle, off_t offset, int whence) { diff --git a/ace/Process.cpp b/ace/Process.cpp index a8d32cc33d8..c610e746f3d 100644 --- a/ace/Process.cpp +++ b/ace/Process.cpp @@ -1,6 +1,7 @@ // $Id$ #define ACE_BUILD_DLL +#include "ace/OS.h" #include "ace/Process.h" #include "ace/ARGV.h" #include "ace/SString.h" @@ -9,16 +10,17 @@ #include "ace/Process.i" #endif /* __ACE_INLINE__ */ -ACE_RCSID(ace, Process, "$Id$") +ACE_RCSID (ace, Process, "$Id$") ACE_Process::ACE_Process (void) #if !defined (ACE_WIN32) - : child_id_ (0) + : child_id_ (ACE_INVALID_PID) #endif /* !defined (ACE_WIN32) */ { #if defined (ACE_WIN32) ACE_OS::memset ((void *) &this->process_info_, - 0, sizeof this->process_info_); + 0, + sizeof this->process_info_); #endif /* ACE_WIN32 */ } @@ -47,27 +49,26 @@ ACE_Process::spawn (ACE_Process_Options &options) options.startup_info (), &this->process_info_); - if (fork_result) // If success. + if (fork_result) return this->getpid (); else - // CreateProcess failed. - return -1; + return ACE_INVALID_PID; #elif defined (CHORUS) - // This only works if we <exec>. Chorus does not really support - // <fork>. + // This only works if we exec. Chorus does not really support + // forking. if (ACE_BIT_ENABLED (options.creation_flags (), ACE_Process_Options::NO_EXEC)) - ACE_NOTSUP_RETURN (-1); + ACE_NOTSUP_RETURN (ACE_INVALID_PID); // These are all currently unsupported. if (options.get_stdin () != ACE_INVALID_HANDLE) - ACE_NOTSUP_RETURN (-1); + ACE_NOTSUP_RETURN (ACE_INVALID_PID); if (options.get_stdout () != ACE_INVALID_HANDLE) - ACE_NOTSUP_RETURN (-1); + ACE_NOTSUP_RETURN (ACE_INVALID_PID); if (options.get_stderr () != ACE_INVALID_HANDLE) - ACE_NOTSUP_RETURN (-1); + ACE_NOTSUP_RETURN (ACE_INVALID_PID); if (options.working_directory () != 0) - ACE_NOTSUP_RETURN (-1); + ACE_NOTSUP_RETURN (ACE_INVALID_PID); if (options.env_argv ()[0] == 0) // command-line args @@ -81,7 +82,7 @@ ACE_Process::spawn (ACE_Process_Options &options) *user_env != 0; user_env++) if (ACE_OS::putenv (*user_env) != 0) - return -1; + return ACE_INVALID_PID; // Now the forked process has both inherited variables and the // user's supplied variables. @@ -95,6 +96,14 @@ ACE_Process::spawn (ACE_Process_Options &options) this->child_id_ = ACE::fork (options.command_line_argv ()[0], options.avoid_zombies ()); + // If we're the child and the options specified a non-default + // process group, try to set our pgid to it. (This will allow + // Process_Manager to wait for Processes by process-group.) + if (options.getgroup () != ACE_INVALID_PID + && this->child_id_ == 0) + ACE_OS::setpgid (0, + options.getgroup ()); + // If we're not supposed to exec, return the process id. if (ACE_BIT_ENABLED (options.creation_flags (), ACE_Process_Options::NO_EXEC)) @@ -104,9 +113,9 @@ ACE_Process::spawn (ACE_Process_Options &options) { case -1: // Error. - return -1; + return ACE_INVALID_PID; case 0: - // Child process. + // Child process...exec the { if (options.get_stdin () != ACE_INVALID_HANDLE && ACE_OS::dup2 (options.get_stdin (), @@ -130,6 +139,7 @@ ACE_Process::spawn (ACE_Process_Options &options) // process. if (options.working_directory () != 0) ACE_OS::chdir (options.working_directory ()); + // Should check for error here! // Child process executes the command. int result = 0; @@ -140,10 +150,10 @@ ACE_Process::spawn (ACE_Process_Options &options) options.command_line_argv ()); else { -#if defined( ghs ) +#if defined (ghs) // GreenHills 1.8.8 (for VxWorks 5.3.x) can't compile this // code. Processes aren't supported on VxWorks anyways. - ACE_NOTSUP_RETURN (-1); + ACE_NOTSUP_RETURN (ACE_INVALID_PID); #else // Add the new environment variables to the environment // context of the context before doing an <execvp>. @@ -151,7 +161,7 @@ ACE_Process::spawn (ACE_Process_Options &options) *user_env != 0; user_env++) if (ACE_OS::putenv (*user_env) != 0) - return -1; + return ACE_INVALID_PID; // Now the forked process has both inherited variables and // the user's supplied variables. @@ -159,7 +169,6 @@ ACE_Process::spawn (ACE_Process_Options &options) options.command_line_argv ()); #endif /* ghs */ } - if (result == -1) { // If the execv fails, this child needs to exit. @@ -168,7 +177,7 @@ ACE_Process::spawn (ACE_Process_Options &options) // catch this and figure out what went wrong. ACE_OS::exit (errno); } - + // ... otherwise, this is never reached. return 0; } default: @@ -178,6 +187,71 @@ ACE_Process::spawn (ACE_Process_Options &options) #endif /* ACE_WIN32 */ } +pid_t +ACE_Process::wait (const ACE_Time_Value &tv, + int *status) +{ +#if defined (ACE_WIN32) + // Don't try to get the process exit status if wait failed so we can + // keep the original error code intact. + switch (::WaitForSingleObject (process_info_.hProcess, + tv.msec ())) + { + case WAIT_OBJECT_0: + if (status != 0) + // The error status of <GetExitCodeProcess> is nonetheless not + // tested because we don't know how to return the value. + ::GetExitCodeProcess (process_info_.hProcess, + (LPDWORD) status); + return 0; + case WAIT_TIMEOUT: + errno = ETIME; + return 0; + default: + ACE_OS::set_errno_to_last_error (); + return -1; + } +#else /* ACE_WIN32 */ + if (tv == ACE_Time_Value::zero) + ACE_OSCALL_RETURN (ACE_OS::waitpid (this->child_id_, + status, + WNOHANG), + int, ACE_INVALID_PID); + + if (tv == ACE_Time_Value::max_time) + return this->wait (status); + + ACE_Time_Value wait_until = ACE_OS::gettimeofday () + tv; + + for (;;) + { + int result = ACE_OS::waitpid (this->getpid (), + status, + WNOHANG); + if (result != 0) + return result; + + 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) + return 0; // timeout + + ACE_OS::ualarm (time_left); + if (ACE_OS::sigwait (alarm_or_child) == -1) + return ACE_INVALID_PID; + } +#endif /* ACE_WIN32 */ +} + ACE_Process_Options::ACE_Process_Options (int ie, int cobl, int ebl, @@ -208,7 +282,8 @@ ACE_Process_Options::ACE_Process_Options (int ie, max_environ_argv_index_ (mea - 1), #endif /* !ACE_HAS_WINCE */ command_line_argv_calculated_ (0), - command_line_buf_ (0) + command_line_buf_ (0), + process_group_ (ACE_INVALID_PID) { ACE_NEW (command_line_buf_, TCHAR[cobl]); @@ -256,7 +331,8 @@ ACE_Process_Options::inherit_environment (void) // Add the string to our env buffer. if (this->setenv_i (existing_environment + slot, len) == -1) { - ACE_ERROR ((LM_ERROR, ASYS_TEXT ("%p.\n"), + ACE_ERROR ((LM_ERROR, + ASYS_TEXT ("%p.\n"), ASYS_TEXT ("ACE_Process_Options::ACE_Process_Options"))); break; } @@ -434,27 +510,27 @@ ACE_Process_Options::set_handles (ACE_HANDLE std_in, if (std_err == ACE_INVALID_HANDLE) std_err = ACE_STDERR; - if (!::DuplicateHandle (::GetCurrentProcess(), + if (!::DuplicateHandle (::GetCurrentProcess (), std_in, - ::GetCurrentProcess(), + ::GetCurrentProcess (), &this->startup_info_.hStdInput, NULL, TRUE, DUPLICATE_SAME_ACCESS)) return -1; - if (!::DuplicateHandle (::GetCurrentProcess(), + if (!::DuplicateHandle (::GetCurrentProcess (), std_out, - ::GetCurrentProcess(), + ::GetCurrentProcess (), &this->startup_info_.hStdOutput, NULL, TRUE, DUPLICATE_SAME_ACCESS)) return -1; - if (!::DuplicateHandle (::GetCurrentProcess(), + if (!::DuplicateHandle (::GetCurrentProcess (), std_err, - ::GetCurrentProcess(), + ::GetCurrentProcess (), &this->startup_info_.hStdError, NULL, TRUE, diff --git a/ace/Process.h b/ace/Process.h index 9d272e2c0b7..045d38810cc 100644 --- a/ace/Process.h +++ b/ace/Process.h @@ -142,6 +142,12 @@ public: // is an environment assignment "VARIABLE=value". This buffer // should end with two null characters. + // = Get/set process group. + pid_t getgroup (void) const; + pid_t setgroup (pid_t pgrp); + // On UNIX, these methods are used by the <ACE_Process_Manager> to + // manage groups of processes. + #if defined (ACE_WIN32) // = Non-portable accessors for when you "just have to use them." @@ -277,6 +283,9 @@ protected: LPTSTR command_line_argv_[MAX_COMMAND_LINE_OPTIONS]; // Argv-style command-line arguments. + + pid_t process_group_; + // Process-group on Unix; unused on Win32. }; class ACE_Export ACE_Process @@ -295,7 +304,7 @@ class ACE_Export ACE_Process // program file in the PATH variable. public: ACE_Process (void); - // Default construction. Must use ACE_Process::start. + // Default construction. Must use <ACE_Process::spawn> to start. virtual ~ACE_Process (void); // Destructor. @@ -307,18 +316,26 @@ public: pid_t wait (int *status = 0, int wait_options = 0); - // Wait for the process we just created to exit. If <status> != 0, - // it points to an integer where the function store the exit status - // of child process to. If <wait_options> == <WNOHANG> then return - // 0 and don't block if the child process hasn't exited yet. A - // return value of -1 represents the <wait> operation failed, - // otherwise, the child process id is returned. + // Wait for the process we've created to exit. If <status> != 0, it + // points to an integer where the function store the exit status of + // child process to. If <wait_options> == <WNOHANG> then return 0 + // and don't block if the child process hasn't exited yet. A return + // value of -1 represents the <wait> operation failed, otherwise, + // the child process id is returned. pid_t wait (const ACE_Time_Value &tv, int *status = 0); - // Timed wait for the process we just created to exit. This - // operation is only supported on Win32 platforms because UNIX - // platforms don't support a timed <wait> operation. + // Timed wait for the process we've created to exit. A return value + // of -1 indicates that the something failed; 0 indicates that a + // timeout occurred. Otherwise, the child's process id is returned. + // If <status> != 0, it points to an integer where the function + // stores the child's exit status. + // + // NOTE: on UNIX platforms this function uses <ualarm>, i.e., it + // overwrites any existing alarm. In addition, it steals all + // <SIGCHLD>s during the timeout period, which will break another + // <ACE_Process_Manager> in the same process that's expecting + // <SIGCHLD> to kick off process reaping. int kill (int signum = SIGINT); // Send the process a signal. This is only portable to operating @@ -332,6 +349,9 @@ public: pid_t getpid (void); // Return the process id of the new child process. + ACE_HANDLE gethandle (void); + // Return the handle of the process, if it has one. + #if defined (ACE_WIN32) PROCESS_INFORMATION process_info (void); #endif /* ACE_WIN32 */ diff --git a/ace/Process.i b/ace/Process.i index a89d87746a5..cdb3653a154 100644 --- a/ace/Process.i +++ b/ace/Process.i @@ -2,6 +2,7 @@ // $Id$ #if defined (ACE_WIN32) + ACE_INLINE PROCESS_INFORMATION ACE_Process::process_info (void) { @@ -9,6 +10,15 @@ ACE_Process::process_info (void) } #endif /* ACE_WIN32 */ +ACE_INLINE ACE_HANDLE +ACE_Process::gethandle (void) +{ +#if defined (ACE_WIN32) + return process_info_.hProcess; +#else + return ACE_HANDLE (child_id_); +#endif /* ACE_WIN32 */ +} ACE_INLINE pid_t ACE_Process::getpid (void) @@ -22,42 +32,11 @@ ACE_Process::getpid (void) ACE_INLINE pid_t ACE_Process::wait (int *status, - int options) + int wait_options) { return ACE_OS::wait (this->getpid (), status, - options); -} - -ACE_INLINE pid_t -ACE_Process::wait (const ACE_Time_Value &tv, - int *status) -{ -#if defined (ACE_WIN32) - // Don't try to get the process exit status if wait failed so we can - // keep the original error code intact. - switch (::WaitForSingleObject (process_info_.hProcess, - tv.msec ())) - { - case WAIT_OBJECT_0: - if (status != 0) - // The error status of <GetExitCodeProcess> is nonetheless not - // tested because we don't know how to return the value. - ::GetExitCodeProcess (process_info_.hProcess, - (LPDWORD) status); - return 0; - case WAIT_TIMEOUT: - errno = ETIME; - return 0; - default: - ACE_OS::set_errno_to_last_error (); - return -1; - } -#else /* ACE_WIN32 */ - ACE_UNUSED_ARG (tv); - ACE_UNUSED_ARG (status); - ACE_NOTSUP_RETURN (-1); -#endif /* ACE_WIN32 */ + wait_options); } ACE_INLINE int @@ -89,6 +68,20 @@ ACE_Process_Options::creation_flags (u_long cf) creation_flags_ = cf; } +ACE_INLINE pid_t +ACE_Process_Options::getgroup (void) const +{ + return process_group_; +} + +ACE_INLINE pid_t +ACE_Process_Options::setgroup (pid_t pgrp) +{ + pid_t old = process_group_; + process_group_ = pgrp; + return old; +} + #if defined (ACE_WIN32) ACE_INLINE STARTUPINFO * diff --git a/ace/Process_Manager.cpp b/ace/Process_Manager.cpp index 5cc8ca9f2a1..e18129d1e7a 100644 --- a/ace/Process_Manager.cpp +++ b/ace/Process_Manager.cpp @@ -11,9 +11,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; @@ -29,16 +29,13 @@ ACE_Process_Descriptor::dump (void) const ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); - ACE_DEBUG ((LM_DEBUG, ASYS_TEXT ("\nproc_id_ = %d"), this->proc_id_)); - ACE_DEBUG ((LM_DEBUG, ASYS_TEXT ("\ngrp_id_ = %d"), this->grp_id_)); + ACE_DEBUG ((LM_DEBUG, + ASYS_TEXT ("\nproc_id_ = %d"), + this->process_->getpid())); ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); } -ACE_Process_Descriptor::~ACE_Process_Descriptor (void) -{ -} - void ACE_Process_Manager::dump (void) const { @@ -56,8 +53,9 @@ ACE_Process_Manager::dump (void) const } ACE_Process_Descriptor::ACE_Process_Descriptor (void) - : proc_id_ (-1), - grp_id_ (0) + : delete_process_ (0), + process_ (0), + exit_notify_ (0) { ACE_TRACE ("ACE_Process_Descriptor::ACE_Process_Descriptor"); } @@ -105,7 +103,7 @@ ACE_Process_Manager::resize (size_t size) { ACE_TRACE ("ACE_Process_Manager::resize"); - ACE_Process_Descriptor *temp = 0; + ACE_Process_Descriptor *temp; ACE_NEW_RETURN (temp, ACE_Process_Descriptor[size], @@ -128,31 +126,72 @@ 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_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) + // (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 + // the ACE_Event_Handler::NULL_MASK when registering this with + // the ACE_Reactor (see below). + this->dummy_handle_ = ACE_OS::open (ACE_DEV_NULL, + O_WRONLY); + ACE_ASSERT (this->dummy_handle_ != ACE_INVALID_HANDLE); + + // Register signal handler object. Note that NULL_MASK is used + // to keep the ACE_Reactor from calling us back on the + // "/dev/null" descriptor. NULL_MASK just reserves a "slot" in + // the Reactor's internal demuxing table, but doesn't cause it + // to dispatch the event handler directly. Instead, we use the + // signal handler to do this. + if (reactor ()->register_handler + (this, + ACE_Event_Handler::NULL_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "register_handler", + 1)); + + if (reactor ()->register_handler + (SIGCHLD, + this) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "register_handler", + 1)); +#endif // !defined (ACE_WIN32) + } + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); if (this->max_process_table_size_ < size) - return this->resize (size); - else - return 0; + this->resize (size); + return 0; + } // Initialize the synchronization variables. -ACE_Process_Manager::ACE_Process_Manager (size_t size) - : 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) + current_count_ (0), + dummy_handle_ (ACE_INVALID_HANDLE), + default_exit_handler_ (0) #if defined (ACE_HAS_THREADS) , zero_cond_ (lock_) #endif /* ACE_HAS_THREADS */ { ACE_TRACE ("ACE_Process_Manager::ACE_Process_Manager"); - if (this->open (size) == -1) + if (this->open (size,r) == -1) ACE_ERROR ((LM_ERROR, ASYS_TEXT ("%p\n"), ASYS_TEXT ("ACE_Process_Manager"))); @@ -165,10 +204,22 @@ ACE_Process_Manager::close (void) { ACE_TRACE ("ACE_Process_Manager::close"); + if (this->reactor ()) + { + this->reactor ()->remove_handler (this, 0); + this->reactor (0); + } + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); 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); + delete [] this->process_table_; this->process_table_ = 0; this->max_process_table_size_ = 0; @@ -183,15 +234,151 @@ ACE_Process_Manager::~ACE_Process_Manager (void) this->close (); } -// Create a new process. *Must* be called with the lock_ held... +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' +// 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 +// children as are dead.) + +int +ACE_Process_Manager::handle_input (ACE_HANDLE proc) +{ + ACE_TRACE ("ACE_Process_Manager::handle_input"); + +#if defined (ACE_WIN32) + DWORD status = 0; + 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; + this->notify_proc_handler (proc, pid, status); + this->remove_proc (pid); + } + ACE_Reactor *r = reactor(); + if (r) + r->remove_handler ( proc, 0 ); + } + else + { + // Huh? Process still active -- shouldn't have been called yet! + } + } + else + { + // <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) + continue; +#endif /* ACE_WIN32 */ + return 0; +} + +int +ACE_Process_Manager::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + return reactor ()->ready_ops + (this->dummy_handle_, + ACE_Event_Handler::READ_MASK, + ACE_Reactor::ADD_MASK); +} + +int +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 (this->default_exit_handler_ != 0) + this->default_exit_handler_->handle_close (ACE_INVALID_HANDLE, + 0); + this->default_exit_handler_ = eh; + return 0; + } + + int i = this->find_proc (pid); + + if (i == -1) + 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_ = eh; + return 0; + } +} + +int +ACE_Process_Manager::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask) +{ + ACE_TRACE ("ACE_Process_Manager::handle_close"); + + ACE_ASSERT (handle==this->dummy_handle_ ); + + ACE_OS::close ( dummy_handle_ ); + + 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); + + pid_t pid = spawn (process, + options); + + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int i = ACE_INVALID_PID; + + if (pid != -1) + i = this->find_proc (pid); + + if (i != -1) + this->process_table_[i].delete_process_ = 1; + + return pid; +} + +// Create a new process. + +pid_t +ACE_Process_Manager::spawn (ACE_Process *process, + ACE_Process_Options &options) +{ ACE_TRACE ("ACE_Process_Manager::spawn"); - ACE_Process process; - pid_t pid = process.spawn (options); + pid_t pid = process->spawn (options); // Only include the pid in the parent's table. if (pid == -1 || pid == 0) @@ -200,10 +387,10 @@ ACE_Process_Manager::spawn (ACE_Process_Options &options) { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); - if (this->append_proc (pid) == -1) - return -1; + if (this->append_proc (process) == -1) + return ACE_INVALID_PID; else - return pid; + return pid; } } @@ -234,7 +421,7 @@ ACE_Process_Manager::spawn_n (size_t n, // Must be called with locks held. int -ACE_Process_Manager::append_proc (pid_t pid) +ACE_Process_Manager::append_proc (ACE_Process *proc) { ACE_TRACE ("ACE_Process_Manager::append_proc"); @@ -248,8 +435,20 @@ ACE_Process_Manager::append_proc (pid_t pid) ACE_Process_Descriptor &proc_desc = this->process_table_[this->current_count_]; - proc_desc.proc_id_ = pid; - proc_desc.grp_id_ = ACE_OS::getpgid (pid); + proc_desc.delete_process_ = 0; // pending better info from caller + 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. + + ACE_Reactor *r = this->reactor (); + if (r != 0) + r->register_handler (proc->gethandle (), + this, + ACE_Event_Handler::READ_MASK); +#endif /* ACE_WIN32 */ this->current_count_++; return 0; @@ -260,19 +459,19 @@ ACE_Process_Manager::append_proc (pid_t pid) // allow them to be inserted twice). int -ACE_Process_Manager::insert_proc (pid_t pid) +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 (pid) != -1) + if (this->find_proc (proc->getpid ()) != -1) return -1; - return this->append_proc (pid); + return this->append_proc (proc); #else - ACE_UNUSED_ARG (pid); + ACE_UNUSED_ARG (proc); ACE_NOTSUP_RETURN (-1); #endif } @@ -301,6 +500,9 @@ ACE_Process_Manager::remove_proc (pid_t pid) return -1; else { + if (this->process_table_[i].delete_process_) + delete this->process_table_[i].process_; + this->current_count_--; if (this->current_count_ > 0) @@ -333,13 +535,13 @@ ACE_Process_Manager::terminate (pid_t pid) return -1; else { - int result = ACE::terminate_process (this->process_table_[i].proc_id_); + 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].proc_id_); + this->remove (this->process_table_[i].process_->getpid ()); return 0; } else @@ -347,6 +549,23 @@ ACE_Process_Manager::terminate (pid_t pid) } } +int +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); + + if (i == -1) + return -1; + else + { + return ACE_OS::kill (this->process_table_[i].process_->getpid (), sig); + } +} + // Locate the index in the table associated with <pid>. Must be // called with the lock held. @@ -356,12 +575,29 @@ 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++) - if (pid == this->process_table_[i].proc_id_) + if (pid == this->process_table_[i].process_->getpid ()) return i; return -1; } +#if defined (ACE_WIN32) +// Locate the index in the table associated with <h>. Must be called +// with the lock held. + +int +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++) + if (h == this->process_table_[i].process_->gethandle ()) + return i; + + return -1; +} +#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. @@ -398,40 +634,83 @@ ACE_Process_Manager::wait (void) ACE_Process_Descriptor &proc_desc = this->process_table_[i]; - pid_t pid = ACE_OS::wait (proc_desc.proc_id_, 0); + pid_t pid = proc_desc.process_->wait (); - if (pid == -1) - return -1; - else + if (pid != -1) this->remove_proc (pid); + return pid; } return 0; } -// Reap a single child processes' exit status by calling -// <ACE_OS::wait>. +// Collect a single child processes' exit status by calling +// <ACE_OS::wait>. Calls the appropriate exit_notify, if registered. int -ACE_Process_Manager::reap (pid_t pid, +ACE_Process_Manager::wait (pid_t pid, int *stat_loc, int options) { - ACE_TRACE ("ACE_Process_Manager::reap"); + ACE_TRACE ("ACE_Process_Manager::wait"); - pid = ACE_OS::wait (pid, stat_loc, options); + int local_stat = 0; + + if (stat_loc == 0) + stat_loc = &local_stat; + + pid = ACE_OS::waitpid (pid, stat_loc, options); + + if (pid > 0) + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + this->notify_proc_handler (ACE_HANDLE (pid), + pid, + *stat_loc); + } - if (pid != -1) - this->remove (pid); return pid; } int -ACE_Process_Manager::wait (pid_t pid, +ACE_Process_Manager::reap (pid_t pid, int *stat_loc, int options) { - ACE_TRACE ("ACE_Process_Manager::wait"); + ACE_TRACE ("ACE_Process_Manager::reap"); + + return this->wait (pid, stat_loc, options); +} + +// Notify either the process-specific handler or the generic handler. +// If process-specific, call handle_close on the handler. Returns 1 +// 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 status) +{ + int i = this->find_proc (pid); + + if (i != -1) + { + ACE_Process_Descriptor &proc_desc = this->process_table_[i]; + + if (proc_desc.exit_notify_ != 0) + { + proc_desc.exit_notify_->handle_input (h); + proc_desc.exit_notify_->handle_close (h, 0); + proc_desc.exit_notify_ = 0; + } + else if (this->default_exit_handler_ != 0 + && this->default_exit_handler_->handle_input (h) < 0) + this->register_handler (0); + } + else + ACE_DEBUG ((LM_DEBUG, + "(%P:%t|%T) ACE_Process_Manager::notify_proc_handler: unknown process %d reaped\n", + pid)); - return this->reap (pid, stat_loc, options); + return i != -1; } diff --git a/ace/Process_Manager.h b/ace/Process_Manager.h index b852ff4a5ec..8f970cebe5b 100644 --- a/ace/Process_Manager.h +++ b/ace/Process_Manager.h @@ -18,6 +18,8 @@ #define ACE_PROCESS_MANAGER_H #include "ace/Synch.h" +#include "ace/Reactor.h" +#include "ace/Event_Handler.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once @@ -28,7 +30,7 @@ class ACE_Export ACE_Process_Descriptor { // = TITLE - // Information describing each process that's controlled by the + // Information describing each process that's controlled by an // <ACE_Process_Manager>. private: friend class ACE_Process_Manager; @@ -37,17 +39,20 @@ private: ~ACE_Process_Descriptor (void); // Default ctor/dtor. - pid_t proc_id_; - // Unique process ID. + int delete_process_; + // Do we need to delete the Process, or just close() it? - gid_t grp_id_; - // Unique group ID. + ACE_Process *process_; + // Describes the process itself. + + ACE_Event_Handler *exit_notify_; + // function to call when process exits void dump (void) const; // Dump the state of an object. }; -class ACE_Export ACE_Process_Manager +class ACE_Export ACE_Process_Manager : protected ACE_Event_Handler { // = TITLE // Manages a group of processes. @@ -74,9 +79,32 @@ public: }; // = Initialization and termination methods. - ACE_Process_Manager (size_t size = ACE_Process_Manager::DEFAULT_SIZE); + ACE_Process_Manager (size_t size = ACE_Process_Manager::DEFAULT_SIZE, + ACE_Reactor *reactor = 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 + // <ACE_Process_Manager> uses it to notify an application when a + // process it controls exits. By default, however, we don't use an + // <ACE_Reactor>. + + int open (size_t size = DEFAULT_SIZE, + ACE_Reactor *r = ACE_Reactor::instance ()); + // 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 + // <ACE_Process_Manager> uses it to notify an application when a + // process it controls exits. By default, however, we don't use an + // <ACE_Reactor>. + + int close (void); + // Release all resources. Do not wait for processes to exit. + virtual ~ACE_Process_Manager (void); + // Destructor releases all resources and does not wait for processes + // to exit. + // = Singleton accessors. static ACE_Process_Manager *instance (void); // Get pointer to a process-wide <ACE_Process_Manager>. @@ -84,13 +112,13 @@ public: // Set pointer to a process-wide <ACE_Process_Manager> and return // existing pointer. - int open (size_t size = DEFAULT_SIZE); - // Initialize an <ACE_Process_Manager> with a table containing up to - // <size> processes. This table resizes itself automatically as - // needed. + // = Process creation methods. - int close (void); - // Release all resources. + pid_t spawn (ACE_Process *proc, + 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. pid_t spawn (ACE_Process_Options &options); // Create a new process by passing <options> to @@ -106,6 +134,8 @@ public: // are filled in with the process ids of each newly created process. // Returns 0 on success and -1 on failure. + // = Process synchronization operations. + int wait (void); // Block until there are no more child processes running that were // <spawn>ed by this <ACE_Process_Manager>. Unlike the <wait> call @@ -113,23 +143,24 @@ public: // <ACE_OS::sigwait> because it simply blocks synchronously waiting // 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. Returns + // 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> entry from the <Process_Manager>; + // <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. In - // order for this <wait> method to work you'll need to register a - // signal handler or use <ACE_OS::sigwait> to call the <reap> or - // <remove> method when a <SIGCHILD> signal occurs. Therefore, this - // method is not portable to Win32... Returns 0 on success and -1 - // on failure. - - int wait (pid_t pid, - int *stat_loc, - int options); + // <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 = -(ACE_OS::getpid( )), + int *stat_loc = 0, + int options = WNOHANG ); // 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 @@ -148,6 +179,13 @@ public: // method does the same thing that the <wait> method directly above // it does -- It's just here for backwards compatibility. + // = Utility methods. + int register_handler (ACE_Event_Handler *event_handler, + pid_t pid = ACE_INVALID_PID); + // 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 @@ -162,26 +200,51 @@ 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); + // On OSs that support signals, send the signal to the specified + // process. Returns 0 on success and -1 on failure. + void dump (void) const; // Dump the state of an object. ACE_ALLOC_HOOK_DECLARE; // Declare the dynamic allocation hooks. +protected: + // = These methods allow a <Process_Manager> to be an <Event_Handler>. + + // @@ Dave, can you please add comments for these methods. + virtual ACE_HANDLE get_handle (void) const; + // Return the handle. + + virtual int handle_input (ACE_HANDLE); + // + virtual int handle_signal (int signum, siginfo_t * = 0, + ucontext_t * = 0); + // + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + // + private: int resize (size_t); // Resize the pool of Process_Descriptors. int 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_> doesn't - // contain <process_id>. + // Returns -1 if <process_id> is not in the <process_table_> + +#if defined (ACE_WIN32) + int 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_> +#endif /* ACE_WIN32 */ - int insert_proc (pid_t process_id); - // Insert a process in the table (checks for duplicates). - // Omitting the process handle won't work on Win32... + int insert_proc (ACE_Process *process); + // Insert a process in the table (checks for duplicates). Omitting + // the process handle won't work on Win32... - int append_proc (pid_t process_id); + int append_proc (ACE_Process *process); // Append information about a process, i.e., its <process_id> in the // <process_table_>. Each entry is added at the end, growing the // table if necessary. @@ -190,6 +253,12 @@ private: // Actually removes the process <pid> from the table. This method // must be called with locks held. + int notify_proc_handler (ACE_HANDLE proc, + pid_t pid, + int status); + // If there's a specific handler for <pid>'s exit, or a default + // handler, call it. + ACE_Process_Descriptor *process_table_; // Vector that describes process state within the Process_Manager. @@ -200,6 +269,13 @@ private: size_t current_count_; // Current number of processes we are managing. + ACE_HANDLE dummy_handle_; + // Allows SIGCHLD to be handled synchronously. + + ACE_Event_Handler *default_exit_handler_; + // This event handler is used to notify when a process we control + // exits. + static ACE_Process_Manager *instance_; // Singleton pointer. @@ -219,4 +295,3 @@ private: #endif /* __ACE_INLINE__ */ #endif /* ACE_PROCESS_MANAGER_H */ - diff --git a/ace/Signal.h b/ace/Signal.h index 2d972a35039..3d5a67bd750 100644 --- a/ace/Signal.h +++ b/ace/Signal.h @@ -52,7 +52,8 @@ public: // the set. ACE_Sig_Set (int fill = 0); - // If <fill> == 0 then initialize the <sigset_> empty, else full. + // If <fill> == 0 then initialize the <sigset_> to be empty, else + // full. ~ACE_Sig_Set (void); diff --git a/ace/config-chorus.h b/ace/config-chorus.h index ee92c196b12..5e10f2811c4 100644 --- a/ace/config-chorus.h +++ b/ace/config-chorus.h @@ -66,6 +66,7 @@ #define ACE_LACKS_FSYNC #define ACE_LACKS_GETHOSTENT #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_LACKS_GETSERVBYNAME #define ACE_LACKS_KEY_T #define ACE_LACKS_LONGLONG_T diff --git a/ace/config-cray.h b/ace/config-cray.h index e9ab5630593..9bb277fc7a3 100644 --- a/ace/config-cray.h +++ b/ace/config-cray.h @@ -143,49 +143,32 @@ // working. Of course, all other Cray PVP and MPP systems do NOT support it, // so it's probably good to just define like this for consistency #define ACE_LACKS_SYSV_SHMEM - #define ACE_LACKS_MMAP - #define ACE_LACKS_CONST_TIMESPEC_PTR - #define ACE_LACKS_SYSCALL - #define ACE_LACKS_STRRECVFD - #define ACE_LACKS_MADVISE - #define ACE_LACKS_NETDB_REENTRANT_FUNCTIONS - #define ACE_LACKS_LINEBUFFERED_STREAMBUF - #define ACE_LACKS_PTHREAD_CLEANUP - #define ACE_LACKS_CONDATTR_PSHARED - #define ACE_LACKS_THREAD_PROCESS_SCOPING #if !defined(_CRAYMPP) #define ACE_LACKS_PTHREAD_CANCEL - #define ACE_LACKS_PTHREAD_KILL #endif #define ACE_LACKS_MUTEXATTR_PSHARED - #define ACE_LACKS_RWLOCK_T - #define ACE_LACKS_PRI_T - #define ACE_LACKS_GETPGID - +#define ACE_LACKS_SETPGID #define ACE_LACKS_MPROTECT - #define ACE_LACKS_MSYNC - #define ACE_LACKS_READV - #define ACE_LACKS_RLIMIT // we probably want to fake not having this, since Cray memory mgmt is different diff --git a/ace/config-cygwin32-common.h b/ace/config-cygwin32-common.h index 923f8952b79..01b1962adf1 100644 --- a/ace/config-cygwin32-common.h +++ b/ace/config-cygwin32-common.h @@ -59,6 +59,7 @@ // but that may depend on the version of glibc that is used. # define ACE_HAS_DLFCN_H_BROKEN_EXTERN_C # define ACE_HAS_VOIDPTR_SOCKOPT +#define ACE_LACKS_SETPGID # define ACE_LACKS_GETPGID // The strtok_r declaration is protected in string.h. extern "C" char *strtok_r __P ((char *__s, __const char *__delim, diff --git a/ace/config-freebsd-pthread.h b/ace/config-freebsd-pthread.h index 6758aee1db5..853a6eaab8e 100644 --- a/ace/config-freebsd-pthread.h +++ b/ace/config-freebsd-pthread.h @@ -36,6 +36,7 @@ #define ACE_HAS_RECURSIVE_THR_EXIT_SEMANTICS #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_LACKS_RWLOCK_T #define ACE_LACKS_READDIR_R #define ACE_HAS_SIG_MACROS diff --git a/ace/config-freebsd.h b/ace/config-freebsd.h index 1c82991a098..5ed3b1fe135 100644 --- a/ace/config-freebsd.h +++ b/ace/config-freebsd.h @@ -30,6 +30,7 @@ // Platform specific directives #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_LACKS_RWLOCK_T #define ACE_LACKS_READDIR_R #define ACE_HAS_SIG_MACROS diff --git a/ace/config-hpux-9.x.h b/ace/config-hpux-9.x.h index 21acfd86e55..c77788f0df7 100644 --- a/ace/config-hpux-9.x.h +++ b/ace/config-hpux-9.x.h @@ -18,6 +18,7 @@ #define ACE_HAS_IP_MULTICAST #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_HAS_BROKEN_CONVERSIONS // Optimize ACE_Handle_Set for select(). #define ACE_HAS_HANDLE_SET_OPTIMIZED_FOR_SELECT @@ -26,6 +27,7 @@ #define ACE_LACKS_CONST_TIMESPEC_PTR #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_HAS_IP_MULTICAST #define ACE_LACKS_SYSCALL #define ACE_LACKS_STRRECVFD diff --git a/ace/config-lynxos.h b/ace/config-lynxos.h index 3bd869b8848..0058523328e 100644 --- a/ace/config-lynxos.h +++ b/ace/config-lynxos.h @@ -90,6 +90,7 @@ #define ACE_LACKS_GETHOSTENT #define ACE_LACKS_GETOPT_PROTO #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_LACKS_MADVISE #define ACE_LACKS_MKTEMP #define ACE_LACKS_RWLOCK_T diff --git a/ace/config-netbsd.h b/ace/config-netbsd.h index ff9f00717f8..4862af16990 100644 --- a/ace/config-netbsd.h +++ b/ace/config-netbsd.h @@ -29,6 +29,7 @@ // Platform specific directives #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_LACKS_RWLOCK_T #define ACE_HAS_SIG_MACROS #define ACE_HAS_CHARPTR_DL diff --git a/ace/config-sunos4-g++.h b/ace/config-sunos4-g++.h index 739e78174bc..65f5d2625d9 100644 --- a/ace/config-sunos4-g++.h +++ b/ace/config-sunos4-g++.h @@ -28,6 +28,7 @@ #define ACE_LACKS_SYSTIME_H #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_HAS_CHARPTR_SPRINTF #define ACE_HAS_UNION_WAIT diff --git a/ace/config-sunos4-lucid3.2.h b/ace/config-sunos4-lucid3.2.h index 2c4317b8722..f30c8cde26d 100644 --- a/ace/config-sunos4-lucid3.2.h +++ b/ace/config-sunos4-lucid3.2.h @@ -8,6 +8,7 @@ #define ACE_CONFIG_H #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID // Maximum compensation (10 ms) for early return from timed ::select (). #if !defined (ACE_TIMER_SKEW) diff --git a/ace/config-sunos4-sun3.x.h b/ace/config-sunos4-sun3.x.h index 4c91d18de30..9115692b70b 100644 --- a/ace/config-sunos4-sun3.x.h +++ b/ace/config-sunos4-sun3.x.h @@ -7,6 +7,7 @@ #ifndef ACE_CONFIG_H #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID // Maximum compensation (10 ms) for early return from timed ::select (). #if !defined (ACE_TIMER_SKEW) diff --git a/ace/config-sunos4-sun4.1.4.h b/ace/config-sunos4-sun4.1.4.h index c9775e4fd9c..7275e63d648 100644 --- a/ace/config-sunos4-sun4.1.4.h +++ b/ace/config-sunos4-sun4.1.4.h @@ -8,6 +8,7 @@ #define ACE_CONFIG_H #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID // Maximum compensation (10 ms) for early return from timed ::select (). #if !defined (ACE_TIMER_SKEW) diff --git a/ace/config-sunos4-sun4.x-orbix.h b/ace/config-sunos4-sun4.x-orbix.h index c429a951c72..b3baa967bd3 100644 --- a/ace/config-sunos4-sun4.x-orbix.h +++ b/ace/config-sunos4-sun4.x-orbix.h @@ -8,6 +8,7 @@ #define ACE_CONFIG_H #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID // Maximum compensation (10 ms) for early return from timed ::select (). #if !defined (ACE_TIMER_SKEW) diff --git a/ace/config-sunos4-sun4.x.h b/ace/config-sunos4-sun4.x.h index 701599ab480..9fcb4025ba7 100644 --- a/ace/config-sunos4-sun4.x.h +++ b/ace/config-sunos4-sun4.x.h @@ -9,6 +9,7 @@ #define ACE_LACKS_SYSTIME_H #define ACE_LACKS_GETPGID +#define ACE_LACKS_SETPGID #define ACE_HAS_CHARPTR_SPRINTF #define ACE_LACKS_POSIX_PROTOTYPES diff --git a/ace/config-win32-common.h b/ace/config-win32-common.h index 4082f616051..ab4dc4dd8dd 100644 --- a/ace/config-win32-common.h +++ b/ace/config-win32-common.h @@ -28,6 +28,7 @@ #define ACE_HAS_SOCKADDR_MSG_NAME #define ACE_LACKS_GETPGID #define ACE_LACKS_GETPPID +#define ACE_LACKS_SETPGID #define ACE_HAS_THREAD_SAFE_ACCEPT #define ACE_HAS_EXCEPTIONS #define ACE_HAS_BROKEN_NAMESPACES diff --git a/examples/OS/Process/process.cpp b/examples/OS/Process/process.cpp index 6ba5d5454b2..0467c5fb7b3 100644 --- a/examples/OS/Process/process.cpp +++ b/examples/OS/Process/process.cpp @@ -1,6 +1,6 @@ -// ============================================================================ // $Id$ +// ============================================================================ // // = LIBRARY // examples @@ -9,11 +9,11 @@ // process.cpp // // = DESCRIPTION -// This example tests the ACE_Process. For more info, check the +// This example tests the <ACE_Process>. For more info, check the // README file in this directory. // // = AUTHOR -// Tim Harrison. +// Tim Harrison <harrison@cs.wustl.edu>. // // ============================================================================ @@ -27,10 +27,12 @@ ACE_RCSID(Process, process, "$Id$") #define EXEC_NAME "MORE.COM" const char *DATE_PATH = "date.exe"; const char *LS_PATH = "ls.exe"; +const char *SLEEP_PATH = "sleep.exe"; #else #define EXEC_NAME "less" const char *DATE_PATH = "date"; const char *LS_PATH = "ls"; +const char *SLEEP_PATH = "sleep"; #endif /* ACE_WIN32 */ static char *executable = EXEC_NAME; @@ -42,12 +44,13 @@ static int run_ls = 0; static int run_all = 0; static int run_setenv = 0; static int run_tokenizer = 0; +static int run_wait = 0; // Parse the command-line arguments and set options. static int parse_args (int argc, char **argv) { - ACE_Get_Opt get_opt (argc, argv, "dlx:p:e:gastu"); + ACE_Get_Opt get_opt (argc, argv, "dlx:p:e:gastuw"); int c; while ((c = get_opt ()) != -1) @@ -81,6 +84,9 @@ parse_args (int argc, char **argv) case 'g': get_env = 1; break; + case 'w': + run_wait = 1; + break; case 'u': default: ACE_ERROR_RETURN ((LM_ERROR, "Usage:\n" @@ -92,6 +98,7 @@ parse_args (int argc, char **argv) "-s setenv ACE_PROCESS_ENV and spawn -g\n" "-g get_env ACE_PROCESS_ENV\n" "-t test tokenizer\n" + "-w test wait functions\n" "-a run all (d,l,e \"running\")\n"), -1); break; } @@ -120,19 +127,25 @@ test_more (void) if (new_process.spawn (options) == -1) { int error = ACE_OS::last_error (); - ACE_ERROR ((LM_ERROR, "%p errno = %d.\n", - "test_more", error)); + ACE_ERROR ((LM_ERROR, + "%p errno = %d.\n", + "test_more", + error)); } int status; new_process.wait (&status); - ACE_DEBUG ((LM_DEBUG, "Process exit with status %d\n", status)); + ACE_DEBUG ((LM_DEBUG, + "Process exit with status %d\n", + status)); ACE_OS::close (infile); - ACE_DEBUG ((LM_DEBUG, "More succeeded.\n")); + ACE_DEBUG ((LM_DEBUG, + "More succeeded.\n")); } // This is a simple usage of ACE_Process. + static void test_date (void) { @@ -144,15 +157,20 @@ test_date (void) if (new_process.spawn (options) == -1) { int error = ACE_OS::last_error (); - ACE_ERROR ((LM_ERROR, "%p errno = %d.\n", - "test_date", error)); + ACE_ERROR ((LM_ERROR, + "%p errno = %d.\n", + "test_date", + error)); return; } int status; new_process.wait (&status); - ACE_DEBUG ((LM_DEBUG, "Process exit with status %d\n", status)); - ACE_DEBUG ((LM_DEBUG, "date succeeded.\n")); + ACE_DEBUG ((LM_DEBUG, + "Process exit with status %d\n", + status)); + ACE_DEBUG ((LM_DEBUG, + "date succeeded.\n")); } static void @@ -165,13 +183,83 @@ test_ls (void) if (new_process.spawn (options) == -1) { int error = ACE_OS::last_error (); - ACE_ERROR ((LM_ERROR, "%p errno = %d.\n", - "test_ls", error)); + ACE_ERROR ((LM_ERROR, + "%p errno = %d.\n", + "test_ls", + error)); } int status; new_process.wait (&status); - ACE_DEBUG ((LM_DEBUG, "Process exit with status %d\n", status)); + ACE_DEBUG ((LM_DEBUG, + "Process exit with status %d\n", + status)); +} + +static void +test_wait (void) +{ + ACE_Process_Options options; + options.command_line ("%s 10", SLEEP_PATH); + + ACE_Process process1; + if (process1.spawn (options) == -1) + { + int error = ACE_OS::last_error (); + ACE_ERROR ((LM_ERROR, + "%p errno = %d.\n", + "test_ls", + error)); + } + + int result; + int status; + + ACE_DEBUG ((LM_DEBUG, + "[%T] New process sleeping 10; try wait(2)\n", + status)); + + result = process1.wait (ACE_Time_Value (2), + &status); + + ACE_DEBUG ((LM_DEBUG, + "[%T] wait(2) returns %d(%d)...now try regular wait\n", + result, + status)); + + result = process1.wait (&status); + ACE_DEBUG ((LM_DEBUG, + "[%T] wait() returns %d(%d)\n", + result, + status)); + + ACE_Process process2; + if (process2.spawn (options) == -1) + { + int error = ACE_OS::last_error (); + ACE_ERROR ((LM_ERROR, + "%p errno = %d.\n", + "test_ls", + error)); + } + + ACE_DEBUG ((LM_DEBUG, + "[%T] New process sleeping 10; try wait(12)\n", + status)); + + result = process2.wait (ACE_Time_Value (12), + &status); + + ACE_DEBUG ((LM_DEBUG, + "[%T] wait(12) returns %d(%d)...now try regular wait\n", + result, + status)); + + result = process2.wait (&status); + ACE_DEBUG ((LM_DEBUG, + "[%T] wait returns %d(%d)\n", + result, + status)); } #if defined (ACE_WIN32) @@ -183,23 +271,27 @@ win32_test_ls (void) PROCESS_INFORMATION process_info; STARTUPINFO startup_info; ACE_OS::memset ((void *) &startup_info, - 0, sizeof startup_info); + 0, + sizeof startup_info); ACE_OS::memset ((void *) &process_info, - 0, sizeof process_info); - startup_info.cb = sizeof (startup_info); + 0, + sizeof process_info); + startup_info.cb = sizeof startup_info; startup_info.dwFlags = STARTF_USESTDHANDLES; ACE_HANDLE std_out = ACE_STDOUT; - if (!::DuplicateHandle (::GetCurrentProcess(), + if (!::DuplicateHandle (::GetCurrentProcess (), std_out, - ::GetCurrentProcess(), + ::GetCurrentProcess (), &startup_info.hStdOutput, NULL, TRUE, DUPLICATE_SAME_ACCESS)) { - ACE_ERROR ((LM_ERROR, "%p duplicate failed.\n", "test_ls")); + ACE_ERROR ((LM_ERROR, + "%p duplicate failed.\n", + "test_ls")); return; } @@ -218,11 +310,15 @@ win32_test_ls (void) ::CloseHandle (startup_info.hStdOutput); if (fork_result == 0) - ACE_ERROR ((LM_ERROR, "%p CreateProcess failed.\n", "test_ls")); + ACE_ERROR ((LM_ERROR, + "%p CreateProcess failed.\n", + "test_ls")); else { - ::WaitForSingleObject (process_info.hProcess, INFINITE); - ACE_DEBUG ((LM_ERROR, "ls succeeded.\n")); + ::WaitForSingleObject (process_info.hProcess, + INFINITE); + ACE_DEBUG ((LM_ERROR, + "ls succeeded.\n")); } } @@ -230,15 +326,18 @@ win32_test_ls (void) // existing environment, plus one more. This has to be done by hand // since CreateProcess does not allow us to inherit AND add // environment variables. + static void win32_spawn_environment_process (void) { PROCESS_INFORMATION process_info; STARTUPINFO startup_info; ACE_OS::memset ((void *) &startup_info, - 0, sizeof startup_info); + 0, + sizeof startup_info); ACE_OS::memset ((void *) &process_info, - 0, sizeof process_info); + 0, + sizeof process_info); startup_info.cb = sizeof (startup_info); startup_info.dwFlags = STARTF_USESTDHANDLES; @@ -254,7 +353,8 @@ win32_spawn_environment_process (void) TRUE, DUPLICATE_SAME_ACCESS)) { - ACE_ERROR ((LM_ERROR, "%p duplicate failed.\n", "spawn_environment_process")); + ACE_ERROR ((LM_ERROR, + "%p duplicate failed.\n", "spawn_environment_process")); return; } @@ -266,7 +366,9 @@ win32_spawn_environment_process (void) TRUE, DUPLICATE_SAME_ACCESS)) { - ACE_ERROR ((LM_ERROR, "%p duplicate failed.\n", "spawn_environment_process")); + ACE_ERROR ((LM_ERROR, + "%p duplicate failed.\n", + "spawn_environment_process")); return; } @@ -278,13 +380,16 @@ win32_spawn_environment_process (void) TRUE, DUPLICATE_SAME_ACCESS)) { - ACE_ERROR ((LM_ERROR, "%p duplicate failed.\n", "spawn_environment_process")); + ACE_ERROR ((LM_ERROR, + "%p duplicate failed.\n", + "spawn_environment_process")); return; } char *existing_environment = ::GetEnvironmentStrings (); char environment[10240]; - ACE_OS::sprintf (environment, "ACE_PROCESS_TEST=%s", + ACE_OS::sprintf (environment, + "ACE_PROCESS_TEST=%s", environment_string); int size = 0; @@ -314,11 +419,15 @@ win32_spawn_environment_process (void) ::CloseHandle (startup_info.hStdError); if (fork_result == 0) - ACE_ERROR ((LM_ERROR, "%p.\n", "spawn_environment_process")); + ACE_ERROR ((LM_ERROR, + "%p.\n", + "spawn_environment_process")); else { - ::WaitForSingleObject (process_info.hProcess, INFINITE); - ACE_DEBUG ((LM_ERROR, "spawn_environment_process succeeded.\n")); + ::WaitForSingleObject (process_info.hProcess, + INFINITE); + ACE_DEBUG ((LM_ERROR, + "spawn_environment_process succeeded.\n")); } } #endif @@ -334,13 +443,17 @@ test_setenv (const char *argv0) ACE_Process process; if (process.spawn (options) == -1) { - ACE_ERROR ((LM_ERROR, "%p.\n", "test_setenv")); + ACE_ERROR ((LM_ERROR, + "%p.\n", + "test_setenv")); return; } int status; process.wait (&status); - ACE_DEBUG ((LM_DEBUG, "Process exit with status %d\n", status)); + ACE_DEBUG ((LM_DEBUG, + "Process exit with status %d\n", + status)); } // Tests the ACE_Tokenizer. @@ -354,15 +467,15 @@ tokenize (char *buffer) parser.preserve_designators ('\"', '\"'); // " This quote is for emacs parser.preserve_designators ('\'', '\''); - const char *temp; - - while (1) + for (const char *temp; ;-) { temp = parser.next (); if (temp == 0) break; - ACE_DEBUG ((LM_DEBUG, temp)); - ACE_DEBUG ((LM_DEBUG, "\n")); + ACE_DEBUG ((LM_DEBUG, + temp)); + ACE_DEBUG ((LM_DEBUG, + "\n")); } } @@ -370,9 +483,11 @@ int main (int argc, char *argv[]) { if (ACE_LOG_MSG->open (argv[0]) == -1) - ACE_ERROR ((LM_ERROR, "cannot open logger!!!\n")); + ACE_ERROR ((LM_ERROR, + "cannot open logger!!!\n")); - ACE_DEBUG ((LM_DEBUG, "starting...\n")); + ACE_DEBUG ((LM_DEBUG, + "starting...\n")); if (::parse_args (argc, argv) == -1) return -1; @@ -380,14 +495,19 @@ main (int argc, char *argv[]) if (run_all) { ACE_Process_Options options; - options.command_line ("%s -d -l -s", argv[0]); + options.command_line ("%s -d -l -s -w", + argv[0]); ACE_Process process; if (process.spawn (options) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "main"), -1); - + ACE_ERROR_RETURN ((LM_ERROR, + "%p.\n", + "main"), + -1); int status; process.wait (&status); - ACE_DEBUG ((LM_DEBUG, "Process exit with status %d\n", status)); + ACE_DEBUG ((LM_DEBUG, + "Process exit with status %d\n", + status)); } if (run_date) @@ -401,7 +521,8 @@ main (int argc, char *argv[]) ACE_DEBUG ((LM_DEBUG, "checking ACE_PROCESS_TEST\n")); char *value = ACE_OS::getenv ("ACE_PROCESS_TEST"); char *value2 = ACE_OS::getenv ("ACE_PROCESS_TEST2"); - ACE_DEBUG ((LM_DEBUG, "ACE_PROCESS_TEST = %s.\n" + ACE_DEBUG ((LM_DEBUG, + "ACE_PROCESS_TEST = %s.\n" "ACE_PROCESS_TEST2 = %s.\n", value == 0 ? "no value" : value, value2 == 0 ? "no value" : value2)); @@ -410,6 +531,9 @@ main (int argc, char *argv[]) if (run_ls) ::test_ls (); + if (run_wait) + ::test_wait (); + #if defined (ACE_WIN32) if (environment_string != 0) win32_spawn_environment_process (); |