diff options
author | schmidt <douglascraigschmidt@users.noreply.github.com> | 1999-02-01 01:25:06 +0000 |
---|---|---|
committer | schmidt <douglascraigschmidt@users.noreply.github.com> | 1999-02-01 01:25:06 +0000 |
commit | cecf8ae614b687b165257de7246d75b823593d00 (patch) | |
tree | 68000c06cee8e67c7d3ed2377fbb8866b2dfec97 | |
parent | 3f86474fbd339a45d6614526870b11b7fd0f6920 (diff) | |
download | ATCD-cecf8ae614b687b165257de7246d75b823593d00.tar.gz |
.
-rw-r--r-- | ChangeLog-99b | 7 | ||||
-rw-r--r-- | ace/ACE.cpp | 54 | ||||
-rw-r--r-- | ace/ACE.h | 7 | ||||
-rw-r--r-- | ace/Strategies_T.cpp | 16 | ||||
-rw-r--r-- | ace/Strategies_T.h | 21 | ||||
-rw-r--r-- | ace/Strategies_T.i | 7 | ||||
-rw-r--r-- | tests/Process_Strategy_Test.cpp | 182 |
7 files changed, 213 insertions, 81 deletions
diff --git a/ChangeLog-99b b/ChangeLog-99b index c3d945f87a4..e646dd7937a 100644 --- a/ChangeLog-99b +++ b/ChangeLog-99b @@ -1,5 +1,12 @@ Sun Jan 31 18:19:45 1999 Douglas C. Schmidt <schmidt@ace.cs.wustl.edu> + * tests/Process_Strategy_Test: Updated the test to use ACE::fork() + and the new "avoid zombies" feature of ACE_Process_Strategy. + + * ace/ACE: Added a new version of fork() that can avoid creating + zombies. Thanks to Garry Brother <gmbroth@romulus.ncsc.mil> for + this code. + * ace/Strategies_T: Changed the ACE_Process_Strategy so that programmers can designate to not create zombies. Thanks to Garry Brother <gmbroth@romulus.ncsc.mil> for this suggestion. diff --git a/ace/ACE.cpp b/ace/ACE.cpp index 90c0b85cb29..a61904184ff 100644 --- a/ace/ACE.cpp +++ b/ace/ACE.cpp @@ -1997,6 +1997,60 @@ ACE::daemonize (const char pathname[], #endif /* ACE_LACKS_FORK */ } +pid_t +ACE::fork (const char *program_name, + int avoid_zombies) +{ + if (avoid_zombies == 0) + return ACE_OS::fork (program_name); + else + { + // This algorithm is adapted from an example in the Stevens book + // "Advanced Programming in the Unix Environment" and an item in + // Andrew Gierth's Unix Programming FAQ. It creates an orphan + // process that's inherited by the init process; init cleans up + // when the orphan process terminates. + // + // Another way to avoid zombies is to ignore or catch the + // SIGCHLD signal; we don't use that approach here. + + pid_t pid = ACE_OS::fork (); + if (pid == 0) + { + // The child process forks again to create a grandchild. + switch (ACE_OS::fork (program_name)) + { + case 0: // grandchild returns 0. + return 0; + case -1: // assumes all errnos are < 256 + ACE_OS::_exit (errno); + default: // child terminates, orphaning grandchild + ACE_OS::_exit (0); + } + } + + // Parent process waits for child to terminate. + int status; + if (pid < 0 || ACE_OS::waitpid (pid, &status, 0) < 0) + return -1; + + // child terminated by calling exit()? + if (WIFEXITED (status)) + { + // child terminated normally? + if (WEXITSTATUS (status) == 0) + return 1; + else + errno = WEXITSTATUS (status); + } + else + // child didn't call exit(); perhaps it received a signal? + errno = EINTR; + + return -1; + } +} + int ACE::max_handles (void) { diff --git a/ace/ACE.h b/ace/ACE.h index ffe89ca8c72..60d0d7417c5 100644 --- a/ace/ACE.h +++ b/ace/ACE.h @@ -537,6 +537,13 @@ public: // 0 if unsuccessful, else returns pointer to beginning of the // "time" portion of <day_and_time>. + static pid_t fork (const char *program_name = "<unknown>", + int avoid_zombies = 0); + // if <avoid_zombies> == 0 call <ACE_OS::fork> directly, else create + // an orphan process that's inherited by the init process; init + // cleans up when the orphan process terminates so we don't create + // zombies. + static int daemonize (const char pathname[] = "/", int close_all_handles = ACE_DEFAULT_CLOSE_ALL_HANDLES, const char program_name[] = "<unknown>"); diff --git a/ace/Strategies_T.cpp b/ace/Strategies_T.cpp index 8e6469f817f..d9c8c3a8843 100644 --- a/ace/Strategies_T.cpp +++ b/ace/Strategies_T.cpp @@ -295,13 +295,13 @@ template <class SVC_HANDLER> int ACE_Process_Strategy<SVC_HANDLER>::open (size_t n_processes, ACE_Event_Handler *acceptor, ACE_Reactor *reactor, - int daemonize) + int avoid_zombies) { ACE_TRACE ("ACE_Process_Strategy<SVC_HANDLER>::open"); this->n_processes_ = n_processes; this->acceptor_ = acceptor; this->reactor_ = reactor; - this->flags_ = daemonize; + this->flags_ = avoid_zombies; return 0; } @@ -312,16 +312,8 @@ ACE_Process_Strategy<SVC_HANDLER>::activate_svc_handler (SVC_HANDLER *svc_handle { ACE_TRACE ("ACE_Process_Strategy<SVC_HANDLER>::activate_svc_handler"); - int result; - - // If <flags_> is non-0 then we'll use <ACE::daemonize> to avoid - // creating zombies. - if (this->flags_) - result = ACE::daemonize (0, 0, "child"); - else - result = ACE_OS::fork ("child"); - - switch (result) + // If <flags_> is non-0 then we won't create zombies. + switch (ACE::fork ("child", this->flags_)) { case -1: ACE_ERROR_RETURN ((LM_ERROR, diff --git a/ace/Strategies_T.h b/ace/Strategies_T.h index 397945cd7bb..d8703ce1b47 100644 --- a/ace/Strategies_T.h +++ b/ace/Strategies_T.h @@ -348,28 +348,25 @@ class ACE_Process_Strategy : public ACE_Concurrency_Strategy<SVC_HANDLER> // = DESCRIPTION // This class provides a strategy that manages the creation of // processes to handle requests from clients concurrently. It - // behaves as a "process factory", using <ACE_OS::fork> or - // <ACE::daemonize> to fork threads "on-demand" to run the - // service specified by a user-supplied <SVC_HANDLER> in a - // separate process. + // behaves as a "process factory", using <ACE::fork> to fork + // threads "on-demand" to run the service specified by a + // user-supplied <SVC_HANDLER> in a separate process. public: // = Intialization and termination methods. ACE_Process_Strategy (size_t n_processes = 1, ACE_Event_Handler *acceptor = 0, ACE_Reactor * = 0, - int daemonize = 0); - // Initialize the strategy. If <daemonize> is non-0 then use the - // <ACE::daemonize> method instead of <ACE_OS::fork> to avoid - // zombies. + int avoid_zombies = 0); + // Initialize the strategy. If <avoid_zombies> is non-0 then set a + // flag to <ACE::fork> to avoid zombies. virtual int open (size_t n_processes = 1, ACE_Event_Handler *acceptor = 0, ACE_Reactor * = 0, - int daemonize = 0); - // Initialize the strategy. If <daemonize> is non-0 then use the - // <ACE::daemonize> method instead of <ACE_OS::fork> to avoid - // zombies. + int avoid_zombies = 0); + // Initialize the strategy. If <avoid_zombies> is non-0 then set a + // flag to <ACE::fork> to avoid zombies. virtual ~ACE_Process_Strategy (void); diff --git a/ace/Strategies_T.i b/ace/Strategies_T.i index 419f3ad02e9..d86bfca51cf 100644 --- a/ace/Strategies_T.i +++ b/ace/Strategies_T.i @@ -238,10 +238,13 @@ template <class SVC_HANDLER> ASYS_INLINE ACE_Process_Strategy<SVC_HANDLER>::ACE_Process_Strategy (size_t n_processes, ACE_Event_Handler *acceptor, ACE_Reactor *reactor, - int flags) + int avoid_zombies) { ACE_TRACE ("ACE_Process_Strategy<SVC_HANDLER>::ACE_Process_Strategy"); - this->open (n_processes, acceptor, reactor, flags); + this->open (n_processes, + acceptor, + reactor, + avoid_zombies); } template <class SVC_HANDLER> ASYS_INLINE diff --git a/tests/Process_Strategy_Test.cpp b/tests/Process_Strategy_Test.cpp index 37779c86125..ca939ea2fec 100644 --- a/tests/Process_Strategy_Test.cpp +++ b/tests/Process_Strategy_Test.cpp @@ -68,18 +68,19 @@ static int connections = 0; // Use this to show down the process gracefully. static void -connection_completed () +connection_completed (void) { - // Increment connection counter + // Increment connection counter. connections++; - // If all connections have been serviced + // If all connections have been serviced. if (connections == ACE_MAX_ITERATIONS + 1) - // Make sure that the event loop is interrupted + // Make sure that the event loop is interrupted. ACE_Reactor::instance()->wakeup_all_threads (); } // Have all connections been serviced? + static int done (void) { @@ -90,26 +91,32 @@ done (void) Process_Strategy::Process_Strategy (size_t n_processes, ACE_Event_Handler *acceptor, ACE_Reactor *r, - int flags) - : ACE_Process_Strategy<Counting_Service> (n_processes, acceptor, r, flags) + int avoid_zombies) + : ACE_Process_Strategy<Counting_Service> (n_processes, + acceptor, + r, + avoid_zombies) { } // Destructor. g++ 2.7.2.3 gets very confused ("Internal compiler // error") without it. + Process_Strategy::~Process_Strategy (void) { } // Overwrite the process creation method to include connection -// counting +// counting. + int Process_Strategy::activate_svc_handler (Counting_Service *svc_handler, void *arg) { // Call down to the base class - int result = ACE_Process_Strategy<Counting_Service>::activate_svc_handler (svc_handler, arg); - + int result = + ACE_Process_Strategy<Counting_Service>::activate_svc_handler (svc_handler, + arg); // Connection is now complete connection_completed (); @@ -164,14 +171,17 @@ Options::parse_args (int argc, char *argv[]) switch (c) { case 'c': - if (ACE_OS::strcmp (get_opt.optarg, "REACTIVE") == 0) + if (ACE_OS::strcmp (get_opt.optarg, + "REACTIVE") == 0) OPTIONS::instance ()->concurrency_type (Options::REACTIVE); #if !defined (ACE_LACKS_FORK) - else if (ACE_OS::strcmp (get_opt.optarg, "PROCESS") == 0) + else if (ACE_OS::strcmp (get_opt.optarg, + "PROCESS") == 0) OPTIONS::instance ()->concurrency_type (Options::PROCESS); #endif /* !ACE_LACKS_FORK */ #if defined (ACE_HAS_THREADS) - else if (ACE_OS::strcmp (get_opt.optarg, "THREAD") == 0) + else if (ACE_OS::strcmp (get_opt.optarg, + "THREAD") == 0) OPTIONS::instance ()->concurrency_type (Options::THREAD); #endif /* ACE_HAS_THREADS */ else @@ -193,11 +203,15 @@ Options::parse_args (int argc, char *argv[]) if (this->file_lock_.open (ACE_WIDE_STRING (this->filename_), O_RDWR | O_CREAT, ACE_DEFAULT_FILE_PERMS) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open"), + -1); ACE_DEBUG ((LM_DEBUG, "(%P|%t) opening %s on handle %d.\n", - this->filename_, this->file_lock_.get_handle ())); + this->filename_, + this->file_lock_.get_handle ())); int count = 0; @@ -205,7 +219,9 @@ Options::parse_args (int argc, char *argv[]) if (ACE_OS::write (this->file_lock_.get_handle (), (const void *) &count, sizeof count) != sizeof count) - ACE_ERROR ((LM_ERROR, "(%P|%t) %p\n", "write")); + ACE_ERROR ((LM_ERROR, + "(%P|%t) %p\n", + "write")); // Initialize the Concurrency strategy. switch (this->concurrency_type_) @@ -213,7 +229,10 @@ Options::parse_args (int argc, char *argv[]) case Options::PROCESS: #if !defined (ACE_LACKS_FORK) ACE_NEW_RETURN (this->concurrency_strategy_, - Process_Strategy (1, this, ACE_Reactor::instance()), + Process_Strategy (1, + this, + ACE_Reactor::instance (), + 1), // Avoid zombies. -1); break; #else @@ -224,7 +243,8 @@ Options::parse_args (int argc, char *argv[]) ACE_NEW_RETURN (this->concurrency_strategy_, ACE_Thread_Strategy<Counting_Service> (ACE_Thread_Manager::instance (), - THR_NEW_LWP, 1), + THR_NEW_LWP, + 1), -1); break; #else @@ -234,7 +254,7 @@ Options::parse_args (int argc, char *argv[]) // Settle for the purely Reactive strategy. ACE_NEW_RETURN (this->concurrency_strategy_, ACE_Reactive_Strategy<Counting_Service> - (ACE_Reactor::instance()), + (ACE_Reactor::instance ()), -1); break; } @@ -256,7 +276,8 @@ Options::concurrency_type (Options::Concurrency_Type cs) Counting_Service::Counting_Service (ACE_Thread_Manager *) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) creating the Counting_Service\n")); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) creating the Counting_Service\n")); } // Read the current value from the shared file and return it to the @@ -267,7 +288,8 @@ Counting_Service::read (void) { ACE_READ_GUARD_RETURN (ACE_File_Lock, ace_mon, OPTIONS::instance ()->file_lock (), -1); - ACE_DEBUG ((LM_DEBUG, "(%P|%t) reading on handle %d.\n", + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) reading on handle %d.\n", OPTIONS::instance ()->file_lock ().get_handle ())); int count; @@ -275,19 +297,24 @@ Counting_Service::read (void) (void *) &count, sizeof count, 0) != sizeof count) - ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) %p\n", "read"), -1); - + ACE_ERROR_RETURN ((LM_ERROR, + "(%P|%t) %p\n", + "read"), + -1); char buf[BUFSIZ]; - int n = ACE_OS::sprintf (buf, "count = %d\n", count); - + int n = ACE_OS::sprintf (buf, + "count = %d\n", + count); ACE_DEBUG ((LM_DEBUG, "(%P|%t) count = %d\n", count)); if (this->peer ().send_n (buf, n) != n) - ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) %p\n", "send_n"), -1); - + ACE_ERROR_RETURN ((LM_ERROR, + "(%P|%t) %p\n", + "send_n"), + -1); return 0; } @@ -307,18 +334,25 @@ Counting_Service::inc (void) (void *) &count, sizeof count, 0) != sizeof count) - ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) %p\n", "read"), -1); + ACE_ERROR_RETURN ((LM_ERROR, + "(%P|%t) %p\n", + "read"), + -1); ACE_DEBUG ((LM_DEBUG, "(%P|%t) incrementing count from %d to %d\n", - count, count + 1)); + count, + count + 1)); count++; if (ACE_OS::pwrite (OPTIONS::instance ()->file_lock ().get_handle (), (const void *) &count, sizeof count, 0) != sizeof count) - ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) %p\n", "write"), -1); + ACE_ERROR_RETURN ((LM_ERROR, + "(%P|%t) %p\n", + "write"), + -1); return 0; } @@ -336,7 +370,8 @@ Counting_Service::handle_input (ACE_HANDLE) size_t len; // Read the PDU length first. - ssize_t bytes = this->peer ().recv ((void *) &len, sizeof len); + ssize_t bytes = this->peer ().recv ((void *) &len, + sizeof len); if (bytes <= 0) return -1; @@ -349,17 +384,22 @@ Counting_Service::handle_input (ACE_HANDLE) { ACE_DEBUG ((LM_DEBUG, "(%P|%t) %d bytes of input on %d is %*s\n", - bytes, this->peer ().get_handle (), bytes, buf)); + bytes, + this->peer ().get_handle (), + bytes, + buf)); // Read and return the current value in the file. - if (ACE_OS::strncmp (buf, "read", 4) == 0) + if (ACE_OS::strncmp (buf, + "read", + 4) == 0) return this->read (); // Increment the current value in the file. else if (ACE_OS::strncmp (buf, "inc", 3) == 0) return this->inc (); else - ACE_DEBUG ((LM_DEBUG, "(%P|%t) no match...\n")); - + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) no match...\n")); return 0; } } @@ -367,7 +407,8 @@ Counting_Service::handle_input (ACE_HANDLE) int Counting_Service::svc (void) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) handling thread\n")); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) handling thread\n")); while (this->handle_input () >= 0) continue; @@ -388,6 +429,7 @@ Counting_Service::handle_close (ACE_HANDLE, // This method is called back by the <Acceptor> once the client has // connected and the process is forked or spawned. + int Counting_Service::open (void *) { @@ -433,35 +475,49 @@ client (void *arg) for (i = 0; i < ACE_MAX_ITERATIONS; i++) { - ACE_DEBUG ((LM_DEBUG, "(%P|%t) client iteration %d\n", i)); - + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) client iteration %d\n", + i)); if (connector.connect (stream, server_addr) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), 0); - + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open"), + 0); command = "inc"; command_len = ACE_OS::strlen (command); if (stream.send (4, &command_len, sizeof command_len, command, command_len) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"), 0); - + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send"), + 0); command = "read"; command_len = ACE_OS::strlen (command); if (stream.send (4, &command_len, sizeof command_len, command, command_len) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send"), + 0); else if (stream.recv (buf, sizeof buf) <= 0) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "recv"), + 0); // ACE_DEBUG ((LM_DEBUG, // "(%P|%t) client iteration %d, buf = %s\n", // i, buf)); if (stream.close () == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "close"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "close"), + 0); } command = "read"; @@ -469,29 +525,42 @@ client (void *arg) int bytes_read = 0; if (connector.connect (stream, server_addr) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "open"), + 0); else if (stream.send (4, &command_len, sizeof command_len, command, command_len) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send"), + 0); else if ((bytes_read = stream.recv (buf, sizeof buf)) <= 0) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "recv"), + 0); else { - // Null terminate buf to avoid an uninitialized memory read in the - // call to ACE_OS::strrchr (). + // Null terminate buf to avoid an uninitialized memory read in + // the call to ACE_OS::strrchr (). buf [bytes_read] = '\0'; size_t count = ACE_OS::atoi (ACE_OS::strrchr (buf, ' ')); - ACE_DEBUG ((LM_DEBUG, "(%P|%t) count = %d\n", count)); - + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) count = %d\n", + count)); // Make sure that the count is correct. ACE_ASSERT (count == ACE_MAX_ITERATIONS); } if (stream.close () == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "close"), 0); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "close"), + 0); // Remove the filename. ACE_OS::unlink (OPTIONS::instance ()->filename ()); @@ -520,7 +589,10 @@ main (int argc, char *argv[]) ACE_START_TEST ("Process_Strategy_Test"); if (OPTIONS::instance ()->parse_args (argc, argv) == -1) - ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "parse_args"), -1); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "parse_args"), + -1); ACCEPTOR acceptor; @@ -546,7 +618,8 @@ main (int argc, char *argv[]) #if !defined (ACE_LACKS_FORK) // We're running the client and serve as separate processes. - pid_t pid = ACE_OS::fork ("child"); + pid_t pid = ACE::fork ("child", + 1); // Avoid zombies. switch (pid) { @@ -557,7 +630,6 @@ main (int argc, char *argv[]) exit (-1); /* NOTREACHED */ case 0: - ACE_OS::signal (SIGCHLD, SIG_IGN); server (0); break; /* NOTREACHED */ |