Merge pull request #1844 from jrw972/monterey
[ACE_TAO.git] / ACE / tests / Signal_Test.cpp
blob600e2de5f75ccb3df2ae6c5e6b0e2f8cd9b1210d
2 //=============================================================================
3 /**
4 * @file Signal_Test.cpp
6 * This program tests the signal handling capabilities of ACE on
7 * various OS platforms that support sending signals between
8 * processes.
10 * @author Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
12 //=============================================================================
15 #include "test_config.h"
16 #include "ace/Thread_Manager.h"
17 #include "ace/Process.h"
18 #include "ace/Signal.h"
19 #include "ace/Get_Opt.h"
20 #include "ace/ARGV.h"
21 #include "ace/ACE.h"
22 #include "ace/OS_NS_signal.h"
23 #include "ace/OS_NS_stdio.h"
24 #include "ace/OS_NS_unistd.h"
25 #include "ace/OS_NS_stdlib.h"
26 #include "ace/SString.h"
30 #if !defined (ACE_LACKS_UNIX_SIGNALS) && !defined ACE_LACKS_SIGNAL
32 // Global options.
33 static size_t n_iterations = 10000;
35 // Keeps track of whether we're the child or not.
36 static int child = 0;
38 // Keep track of the child pid.
39 static pid_t child_pid = 0;
41 // Keep track of the (original) parent pid.
42 static pid_t parent_pid = 0;
44 // Keep track of which test we're running.
45 static int test_number = 0;
47 // Coordinate the shutdown between threads.
48 static sig_atomic_t shut_down = 0;
50 static int
51 handle_signal (int signum)
53 // ACE_DEBUG / ACE_ERROR invocations have been #if'd out because
54 // they are "unsafe" when handler is invoked asynchronously. On
55 // NetBSD 3.X, calls to change the thread's signal mask block as
56 // a lock seems to be held by the signal trampoline code.
58 #if 0
59 ACE_DEBUG ((LM_DEBUG,
60 ACE_TEXT ("(%P|%t) received signal %S\n"),
61 signum));
62 #endif
64 switch (signum)
66 case SIGCHLD:
67 // Signal to the main thread to shut down.
68 shut_down = 1;
70 // This should only occur for the asynchronous case, so we don't
71 // need to return -1!
72 return 0;
73 case SIGINT:
74 /* FALLTHRU */
75 case SIGTERM:
76 // Shut down our thread using <ACE_Thread_Manager::exit>.
77 #if 0
78 ACE_DEBUG ((LM_DEBUG,
79 ACE_TEXT ("(%P|%t) shutting down due to %S\n"),
80 signum));
81 #endif
83 // Signal to the worker thread to shut down.
84 shut_down = 1;
86 // Bail out and close down.
87 return -1;
88 /* NOTREACHED */
90 case SIGHUP:
92 // Shutdown the child.
94 #if 0
95 ACE_DEBUG ((LM_DEBUG,
96 ACE_TEXT ("(%P|%t) killing child pid %d\n"),
97 child_pid));
98 #endif
99 int const result = ACE_OS::kill (child_pid,
100 SIGTERM);
101 ACE_TEST_ASSERT (result != -1);
103 return -1;
105 /* NOTREACHED */
106 case -1:
107 #if 0
108 ACE_ERROR ((LM_ERROR,
109 ACE_TEXT ("(%P|%t) %p\n"),
110 "sigwait"));
111 #endif
112 return -1;
113 /* NOTREACHED */
114 default:
115 #if 0
116 ACE_ERROR ((LM_ERROR,
117 ACE_TEXT ("(%P|%t) signal %S unexpected\n"),
118 signum));
119 #endif
120 return -1;
121 /* NOTREACHED */
125 // This function handles signals synchronously.
127 static ACE_THR_FUNC_RETURN
128 synchronous_signal_handler (void *)
130 ACE_Sig_Set sigset;
132 // Register signal handlers.
133 if (child)
135 sigset.sig_add (SIGINT);
136 sigset.sig_add (SIGTERM);
138 else
139 sigset.sig_add (SIGHUP);
141 for (;;)
143 // Block waiting for SIGINT, SIGTERM, or SIGHUP, depending on
144 // whether we're the parent or child process.
145 int signr = ACE_OS::sigwait (sigset);
146 if (signr == -1)
148 if (errno != EINTR)
149 ACE_ERROR ((LM_ERROR,
150 ACE_TEXT ("(%P|%t) %p\n"),
151 ACE_TEXT ("sigwait")));
152 continue;
154 if (handle_signal (signr) == -1)
155 break;
156 ACE_DEBUG ((LM_DEBUG,
157 ACE_TEXT ("(%P|%t) handled signal\n")));
160 ACE_DEBUG ((LM_DEBUG,
161 ACE_TEXT ("(%P|%t) synchronous signal handler done\n")));
163 return 0;
166 // This function arranges to handle signals asynchronously, which is
167 // necessary if an OS platform lacks threads.
169 static ACE_THR_FUNC_RETURN
170 asynchronous_signal_handler (void *)
172 ACE_Sig_Set sigset;
174 // Register signal handlers.
175 if (child)
177 sigset.sig_add (SIGINT);
178 sigset.sig_add (SIGTERM);
180 else
182 sigset.sig_add (SIGCHLD);
183 sigset.sig_add (SIGHUP);
186 // Register the <handle_signal> method to process all the signals in
187 // <sigset>.
188 ACE_Sig_Action sa (sigset,
189 (ACE_SignalHandler) handle_signal);
190 ACE_UNUSED_ARG (sa);
192 return 0;
195 // Function that runs in the child process in its own worker thread.
197 static ACE_THR_FUNC_RETURN
198 worker_child (void *arg)
200 long handle_signals_synchronously =
201 reinterpret_cast <long> (arg);
203 for (size_t i = 0; i < n_iterations; i++)
205 if (shut_down > 0)
207 ACE_DEBUG ((LM_DEBUG,
208 ACE_TEXT ("(%P|%t) we've been shutdown!\n")));
209 break;
212 // Every 100 iterations sleep for 2 seconds.
213 if ((i % 100) == 0)
215 ACE_DEBUG ((LM_DEBUG,
216 ACE_TEXT ("(%P|%t) sleeping for 2 seconds\n")));
217 ACE_OS::sleep (2);
220 // After 1000 iterations sent a SIGHUP to our parent.
221 if ((i % 1000) == 0)
223 ACE_DEBUG ((LM_DEBUG,
224 ACE_TEXT ("(%P|%t) sending SIGHUP to parent process %d\n"),
225 parent_pid));
226 int const result = ACE_OS::kill (parent_pid,
227 SIGHUP);
228 if (result == -1)
230 ACE_ERROR ((LM_ERROR,
231 ACE_TEXT ("(%P|%t) %p\n"),
232 ACE_TEXT ("kill")));
233 ACE_TEST_ASSERT (result != -1);
238 if (handle_signals_synchronously)
240 if (!shut_down)
242 ACE_DEBUG ((LM_DEBUG,
243 ACE_TEXT ("(%P|%t) sending SIGINT to ourselves\n")));
244 // We need to do this to dislodge the signal handling thread if
245 // it hasn't shut down on its own accord yet.
246 int const result = ACE_OS::kill (ACE_OS::getpid (), SIGINT);
247 ACE_TEST_ASSERT (result != -1);
250 ACE_DEBUG ((LM_DEBUG,
251 ACE_TEXT ("(%P|%t) finished running child\n")));
252 return 0;
255 // This function runs the parent process in a separate worker thread.
256 static ACE_THR_FUNC_RETURN
257 worker_parent (void *arg)
259 long handle_signals_synchronously =
260 reinterpret_cast <long> (arg);
261 ACE_Process_Options options;
263 ACE_TCHAR *l_argv[3];
264 ACE_TCHAR pid_str[100];
265 // Store the parent's process id so we can pass it to the child
266 // portably. Also, pass the test number, as well.
267 ACE_OS::snprintf (pid_str, 100, ACE_TEXT ("-p %ld -t %d"),
268 static_cast<long> (parent_pid), test_number);
270 // We're going to create a new process that runs this program again,
271 // so we need to indicate that it's the child.
272 const ACE_TCHAR *t = ACE_TEXT (".")
273 ACE_DIRECTORY_SEPARATOR_STR
274 ACE_TEXT ("%sSignal_Test")
275 ACE_PLATFORM_EXE_SUFFIX
276 ACE_TEXT (" -c");
277 l_argv[0] = const_cast <ACE_TCHAR *> (t);
278 l_argv[1] = pid_str;
279 l_argv[2] = 0;
281 ACE_ARGV argv (l_argv);
283 // Generate a command-line!
284 ACE_TString exe_sub_dir;
285 const char *subdir_env = ACE_OS::getenv ("ACE_EXE_SUB_DIR");
286 if (subdir_env)
288 exe_sub_dir = ACE_TEXT_CHAR_TO_TCHAR (subdir_env);
289 exe_sub_dir += ACE_DIRECTORY_SEPARATOR_STR;
292 options.command_line (argv.buf (), exe_sub_dir.c_str ());
293 ACE_Process pm;
295 child_pid = pm.spawn (options);
297 if (child_pid == ACE_INVALID_PID)
298 ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) spawning child process failed\n"), 0);
299 else
300 ACE_DEBUG ((LM_DEBUG,
301 ACE_TEXT ("(%P|%t) spawning child process %d\n"),
302 child_pid));
304 // Perform a <wait> until our child process has exited.
306 if (handle_signals_synchronously)
308 int status;
309 // Wait for the child process to exit.
310 pm.wait (&status);
311 ACE_DEBUG ((LM_DEBUG,
312 ACE_TEXT ("(%P|%t) reaped child with status %d\n"),
313 status));
315 else
316 while (shut_down == 0)
318 // Wait for a signal to arrive.
319 if (ACE_OS::sigsuspend (0) == -1 && errno != EINTR)
320 ACE_ERROR ((LM_ERROR,
321 ACE_TEXT ("(%P|%t) %p\n"),
322 ACE_TEXT ("sigsuspend")));
323 ACE_DEBUG ((LM_DEBUG,
324 ACE_TEXT ("(%P|%t) got signal!\n")));
327 ACE_DEBUG ((LM_DEBUG,
328 ACE_TEXT ("(%P|%t) parent worker done\n")));
329 return 0;
332 // This is the driver function that spawns threads to run the test for
333 // the parent and the child process.
335 static void
336 run_test (ACE_THR_FUNC worker,
337 long handle_signals_in_separate_thread,
338 long handle_signals_synchronously)
340 #if defined (ACE_HAS_THREADS)
341 if (handle_signals_synchronously)
343 // For the synchronous signal tests, block signals to prevent
344 // asynchronous delivery to default handler (at least necessary
345 // on linux and solaris; POSIX spec also states that signal(s)
346 // should be blocked before call to sigwait())
347 ACE_Sig_Guard guard;
349 int result;
351 ACE_DEBUG ((LM_DEBUG,
352 ACE_TEXT ("(%P|%t) spawning worker thread\n")));
353 result = ACE_Thread_Manager::instance ()->spawn
354 (worker,
355 reinterpret_cast <void *> (handle_signals_synchronously),
356 THR_DETACHED);
357 ACE_TEST_ASSERT (result != -1);
359 if (handle_signals_in_separate_thread)
361 ACE_DEBUG ((LM_DEBUG,
362 ACE_TEXT ("(%P|%t) spawning signal handler thread\n")));
364 result = ACE_Thread_Manager::instance ()->spawn
365 (synchronous_signal_handler,
367 THR_DETACHED);
368 ACE_TEST_ASSERT (result != -1);
370 else
372 synchronous_signal_handler (0);
375 // Wait for the thread(s) to finish.
376 result = ACE_Thread_Manager::instance ()->wait ();
377 ACE_TEST_ASSERT (result != -1);
379 else
380 #else
381 // Don't remove this since otherwise some compilers give warnings
382 // when ACE_HAS_THREADS is disabled!
383 ACE_UNUSED_ARG (synchronous_signal_handler);
384 #endif /* ACE_HAS_THREADS */
386 ACE_UNUSED_ARG (handle_signals_in_separate_thread);
387 // Arrange to handle signals asynchronously.
388 asynchronous_signal_handler (0);
389 (*worker) (reinterpret_cast <void *> (handle_signals_synchronously));
393 // Parse the command-line arguments and set options.
395 static void
396 parse_args (int argc, ACE_TCHAR *argv[])
398 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:chp:t:"));
400 int c;
402 while ((c = get_opt ()) != -1)
403 switch (c)
405 case 'i':
406 n_iterations = ACE_OS::atoi (get_opt.opt_arg ());
407 break;
408 case 'c':
409 child = 1;
410 break;
411 case 'p':
412 parent_pid = ACE_OS::atoi (get_opt.opt_arg ());
413 break;
414 case 't':
415 test_number = ACE_OS::atoi (get_opt.opt_arg ());
416 break;
417 case 'h':
418 default:
419 ACE_DEBUG ((LM_DEBUG,
420 ACE_TEXT ("(%P|%t) usage:\n")
421 ACE_TEXT ("-i <iterations>\n")
422 ACE_TEXT ("-c\n")
423 ACE_TEXT ("-p <parent_pid>\n")
424 ACE_TEXT ("-t <test_number>\n")));
425 break;
430 run_main (int argc, ACE_TCHAR *argv[])
432 ACE_OS::signal(SIGHUP, SIG_DFL);
434 if (argc > 1)
436 ACE_APPEND_LOG (ACE_TEXT ("Signal_Test-child"));
437 parse_args (argc, argv);
439 if (test_number == 1)
441 ACE_DEBUG ((LM_DEBUG,
442 ACE_TEXT ("(%P|%t) **** test 1: handle signals synchronously in separate thread\n")));
444 // First, handle signals synchronously in separate thread.
445 run_test (worker_child, 1, 1);
447 else if (test_number == 2)
449 ACE_DEBUG ((LM_DEBUG,
450 ACE_TEXT ("(%P|%t) **** test 2: handle signals synchronously in this thread\n")));
452 // Next, handle signals synchronously in this thread.
453 run_test (worker_child, 0, 1);
455 else if (test_number == 3)
457 ACE_DEBUG ((LM_DEBUG,
458 ACE_TEXT ("(%P|%t) **** test 3: handle signals asynchronously in this thread\n")));
460 // Finally, handle signals asynchronously in this thread.
461 run_test (worker_child, 0, 0);
464 ACE_END_LOG;
466 else
468 ACE_START_TEST (ACE_TEXT ("Signal_Test"));
469 ACE_INIT_LOG (ACE_TEXT ("Signal_Test-child"));
471 // We need to get the process id here to work around "features"
472 // of Linux threads...
473 parent_pid = ACE_OS::getpid ();
475 ACE_DEBUG ((LM_DEBUG,
476 ACE_TEXT ("(%P|%t) **** test 1: handle signals synchronously in a separate thread\n")));
478 #ifdef ACE_HAS_THREADS
479 ++test_number;
480 // Run the parent logic for the signal test, first by handling
481 // signals synchronously in a separate thread.
482 run_test (worker_parent, 1L, 1L);
484 ACE_DEBUG ((LM_DEBUG,
485 ACE_TEXT ("(%P|%t) **** test 2: handle signals synchronously in this thread\n")));
487 ++test_number;
488 // And next by handling synchronously signals in this thread.
489 run_test (worker_parent, 0L, 1L);
491 ACE_DEBUG ((LM_DEBUG,
492 ACE_TEXT ("(%P|%t) **** test 3: handle signals asynchronously in this thread\n")));
493 #else
494 test_number += 2;
495 #endif /* ACE_HAS_THREADS */
497 ++test_number;
498 // And finally by handling asynchronously signals in this thread.
499 run_test (worker_parent, 0L, 0L);
501 ACE_END_TEST;
503 return 0;
506 #else
508 run_main (int, ACE_TCHAR *[])
510 ACE_START_TEST (ACE_TEXT ("Signal_Test"));
511 ACE_ERROR ((LM_INFO,
512 ACE_TEXT ("The Unix Signals capability is not supported on this platform\n")));
513 ACE_END_TEST;
514 return 0;
516 #endif /* !defined (ACE_LACKS_UNIX_SIGNALS) */