Compile fixes
[ACE_TAO.git] / ACE / tests / Signal_Test.cpp
blob25cf75a6e5d8e3ad063266bc45f9c767720f2c1d
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 //=============================================================================
14 #include "test_config.h"
15 #include "ace/Thread_Manager.h"
16 #include "ace/Process.h"
17 #include "ace/Signal.h"
18 #include "ace/Get_Opt.h"
19 #include "ace/ARGV.h"
20 #include "ace/ACE.h"
21 #include "ace/OS_NS_signal.h"
22 #include "ace/OS_NS_stdio.h"
23 #include "ace/OS_NS_unistd.h"
24 #include "ace/OS_NS_stdlib.h"
25 #include "ace/SString.h"
27 #if !defined (ACE_LACKS_UNIX_SIGNALS) && !defined ACE_LACKS_SIGNAL
29 // Global options.
30 static size_t n_iterations = 10000;
32 // Keeps track of whether we're the child or not.
33 static int child = 0;
35 // Keep track of the child pid.
36 static pid_t child_pid = 0;
38 // Keep track of the (original) parent pid.
39 static pid_t parent_pid = 0;
41 // Keep track of which test we're running.
42 static int test_number = 0;
44 // Coordinate the shutdown between threads.
45 static sig_atomic_t shut_down = 0;
47 static int
48 handle_signal (int signum)
50 // ACE_DEBUG / ACE_ERROR invocations have been #if'd out because
51 // they are "unsafe" when handler is invoked asynchronously. On
52 // NetBSD 3.X, calls to change the thread's signal mask block as
53 // a lock seems to be held by the signal trampoline code.
54 #if 0
55 ACE_DEBUG ((LM_DEBUG,
56 ACE_TEXT ("(%P|%t) received signal %S\n"),
57 signum));
58 #endif
60 switch (signum)
62 case SIGCHLD:
63 // Signal to the main thread to shut down.
64 shut_down = 1;
66 // This should only occur for the asynchronous case, so we don't
67 // need to return -1!
68 return 0;
69 case SIGINT:
70 ACE_FALLTHROUGH;
71 case SIGTERM:
72 // Shut down our thread using <ACE_Thread_Manager::exit>.
73 #if 0
74 ACE_DEBUG ((LM_DEBUG,
75 ACE_TEXT ("(%P|%t) shutting down due to %S\n"),
76 signum));
77 #endif
79 // Signal to the worker thread to shut down.
80 shut_down = 1;
82 // Bail out and close down.
83 return -1;
84 /* NOTREACHED */
86 case SIGHUP:
88 // Shutdown the child.
90 #if 0
91 ACE_DEBUG ((LM_DEBUG,
92 ACE_TEXT ("(%P|%t) killing child pid %d\n"),
93 child_pid));
94 #endif
95 int const result = ACE_OS::kill (child_pid,
96 SIGTERM);
97 ACE_TEST_ASSERT (result != -1);
99 return -1;
101 /* NOTREACHED */
102 case -1:
103 #if 0
104 ACE_ERROR ((LM_ERROR,
105 ACE_TEXT ("(%P|%t) %p\n"),
106 "sigwait"));
107 #endif
108 return -1;
109 /* NOTREACHED */
110 default:
111 #if 0
112 ACE_ERROR ((LM_ERROR,
113 ACE_TEXT ("(%P|%t) signal %S unexpected\n"),
114 signum));
115 #endif
116 return -1;
117 /* NOTREACHED */
121 // This function handles signals synchronously.
123 static ACE_THR_FUNC_RETURN
124 synchronous_signal_handler (void *)
126 ACE_Sig_Set sigset;
128 // Register signal handlers.
129 if (child)
131 sigset.sig_add (SIGINT);
132 sigset.sig_add (SIGTERM);
134 else
135 sigset.sig_add (SIGHUP);
137 for (;;)
139 // Block waiting for SIGINT, SIGTERM, or SIGHUP, depending on
140 // whether we're the parent or child process.
141 int signr = ACE_OS::sigwait (sigset);
142 if (signr == -1)
144 if (errno != EINTR)
145 ACE_ERROR ((LM_ERROR,
146 ACE_TEXT ("(%P|%t) %p\n"),
147 ACE_TEXT ("sigwait")));
148 continue;
150 if (handle_signal (signr) == -1)
151 break;
152 ACE_DEBUG ((LM_DEBUG,
153 ACE_TEXT ("(%P|%t) handled signal\n")));
156 ACE_DEBUG ((LM_DEBUG,
157 ACE_TEXT ("(%P|%t) synchronous signal handler done\n")));
159 return 0;
162 // This function arranges to handle signals asynchronously, which is
163 // necessary if an OS platform lacks threads.
164 static ACE_THR_FUNC_RETURN
165 asynchronous_signal_handler (void *)
167 ACE_Sig_Set sigset;
169 // Register signal handlers.
170 if (child)
172 sigset.sig_add (SIGINT);
173 sigset.sig_add (SIGTERM);
175 else
177 sigset.sig_add (SIGCHLD);
178 sigset.sig_add (SIGHUP);
181 // Register the <handle_signal> method to process all the signals in
182 // <sigset>.
183 ACE_Sig_Action sa (sigset, (ACE_SignalHandler)handle_signal);
184 ACE_UNUSED_ARG (sa);
186 return 0;
189 // Function that runs in the child process in its own worker thread.
191 static ACE_THR_FUNC_RETURN
192 worker_child (void *arg)
194 long handle_signals_synchronously =
195 reinterpret_cast <long> (arg);
197 for (size_t i = 0; i < n_iterations; i++)
199 if (shut_down > 0)
201 ACE_DEBUG ((LM_DEBUG,
202 ACE_TEXT ("(%P|%t) we've been shutdown!\n")));
203 break;
206 // Every 100 iterations sleep for 2 seconds.
207 if ((i % 100) == 0)
209 ACE_DEBUG ((LM_DEBUG,
210 ACE_TEXT ("(%P|%t) sleeping for 2 seconds\n")));
211 ACE_OS::sleep (2);
214 // After 1000 iterations sent a SIGHUP to our parent.
215 if ((i % 1000) == 0)
217 ACE_DEBUG ((LM_DEBUG,
218 ACE_TEXT ("(%P|%t) sending SIGHUP to parent process %d\n"),
219 parent_pid));
220 int const result = ACE_OS::kill (parent_pid,
221 SIGHUP);
222 if (result == -1)
224 ACE_ERROR ((LM_ERROR,
225 ACE_TEXT ("(%P|%t) %p\n"),
226 ACE_TEXT ("kill")));
227 ACE_TEST_ASSERT (result != -1);
232 if (handle_signals_synchronously)
234 if (!shut_down)
236 ACE_DEBUG ((LM_DEBUG,
237 ACE_TEXT ("(%P|%t) sending SIGINT to ourselves\n")));
238 // We need to do this to dislodge the signal handling thread if
239 // it hasn't shut down on its own accord yet.
240 int const result = ACE_OS::kill (ACE_OS::getpid (), SIGINT);
241 ACE_TEST_ASSERT (result != -1);
244 ACE_DEBUG ((LM_DEBUG,
245 ACE_TEXT ("(%P|%t) finished running child\n")));
246 return 0;
249 // This function runs the parent process in a separate worker thread.
250 static ACE_THR_FUNC_RETURN
251 worker_parent (void *arg)
253 long handle_signals_synchronously =
254 reinterpret_cast <long> (arg);
255 ACE_Process_Options options;
257 ACE_TCHAR *l_argv[3];
258 ACE_TCHAR pid_str[100];
259 // Store the parent's process id so we can pass it to the child
260 // portably. Also, pass the test number, as well.
261 ACE_OS::snprintf (pid_str, 100, ACE_TEXT ("-p %ld -t %d"),
262 static_cast<long> (parent_pid), test_number);
264 // We're going to create a new process that runs this program again,
265 // so we need to indicate that it's the child.
266 const ACE_TCHAR *t = ACE_TEXT (".")
267 ACE_DIRECTORY_SEPARATOR_STR
268 ACE_TEXT ("%sSignal_Test")
269 ACE_PLATFORM_EXE_SUFFIX
270 ACE_TEXT (" -c");
271 l_argv[0] = const_cast <ACE_TCHAR *> (t);
272 l_argv[1] = pid_str;
273 l_argv[2] = 0;
275 ACE_ARGV argv (l_argv);
277 // Generate a command-line!
278 ACE_TString exe_sub_dir;
279 const char *subdir_env = ACE_OS::getenv ("ACE_EXE_SUB_DIR");
280 if (subdir_env)
282 exe_sub_dir = ACE_TEXT_CHAR_TO_TCHAR (subdir_env);
283 exe_sub_dir += ACE_DIRECTORY_SEPARATOR_STR;
286 options.command_line (argv.buf (), exe_sub_dir.c_str ());
287 ACE_Process pm;
289 child_pid = pm.spawn (options);
291 if (child_pid == ACE_INVALID_PID)
292 ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) spawning child process failed\n"), 0);
293 else
294 ACE_DEBUG ((LM_DEBUG,
295 ACE_TEXT ("(%P|%t) spawning child process %d\n"),
296 child_pid));
298 // Perform a <wait> until our child process has exited.
300 if (handle_signals_synchronously)
302 int status;
303 // Wait for the child process to exit.
304 pm.wait (&status);
305 ACE_DEBUG ((LM_DEBUG,
306 ACE_TEXT ("(%P|%t) reaped child with status %d\n"),
307 status));
309 else
310 while (shut_down == 0)
312 // Wait for a signal to arrive.
313 if (ACE_OS::sigsuspend (0) == -1 && errno != EINTR)
314 ACE_ERROR ((LM_ERROR,
315 ACE_TEXT ("(%P|%t) %p\n"),
316 ACE_TEXT ("sigsuspend")));
317 ACE_DEBUG ((LM_DEBUG,
318 ACE_TEXT ("(%P|%t) got signal!\n")));
321 ACE_DEBUG ((LM_DEBUG,
322 ACE_TEXT ("(%P|%t) parent worker done\n")));
323 return 0;
326 // This is the driver function that spawns threads to run the test for
327 // the parent and the child process.
329 static void
330 run_test (ACE_THR_FUNC worker,
331 long handle_signals_in_separate_thread,
332 long handle_signals_synchronously)
334 #if defined (ACE_HAS_THREADS)
335 if (handle_signals_synchronously)
337 // For the synchronous signal tests, block signals to prevent
338 // asynchronous delivery to default handler (at least necessary
339 // on linux ; POSIX spec also states that signal(s)
340 // should be blocked before call to sigwait())
341 ACE_Sig_Guard guard;
343 int result;
345 ACE_DEBUG ((LM_DEBUG,
346 ACE_TEXT ("(%P|%t) spawning worker thread\n")));
347 result = ACE_Thread_Manager::instance ()->spawn
348 (worker,
349 reinterpret_cast <void *> (handle_signals_synchronously),
350 THR_DETACHED);
351 ACE_TEST_ASSERT (result != -1);
353 if (handle_signals_in_separate_thread)
355 ACE_DEBUG ((LM_DEBUG,
356 ACE_TEXT ("(%P|%t) spawning signal handler thread\n")));
358 result = ACE_Thread_Manager::instance ()->spawn
359 (synchronous_signal_handler,
361 THR_DETACHED);
362 ACE_TEST_ASSERT (result != -1);
364 else
366 synchronous_signal_handler (0);
369 // Wait for the thread(s) to finish.
370 result = ACE_Thread_Manager::instance ()->wait ();
371 ACE_TEST_ASSERT (result != -1);
373 else
374 #else
375 // Don't remove this since otherwise some compilers give warnings
376 // when ACE_HAS_THREADS is disabled!
377 ACE_UNUSED_ARG (synchronous_signal_handler);
378 #endif /* ACE_HAS_THREADS */
380 ACE_UNUSED_ARG (handle_signals_in_separate_thread);
381 // Arrange to handle signals asynchronously.
382 asynchronous_signal_handler (0);
383 (*worker) (reinterpret_cast <void *> (handle_signals_synchronously));
387 // Parse the command-line arguments and set options.
389 static void
390 parse_args (int argc, ACE_TCHAR *argv[])
392 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:chp:t:"));
394 int c;
396 while ((c = get_opt ()) != -1)
397 switch (c)
399 case 'i':
400 n_iterations = ACE_OS::atoi (get_opt.opt_arg ());
401 break;
402 case 'c':
403 child = 1;
404 break;
405 case 'p':
406 parent_pid = ACE_OS::atoi (get_opt.opt_arg ());
407 break;
408 case 't':
409 test_number = ACE_OS::atoi (get_opt.opt_arg ());
410 break;
411 case 'h':
412 default:
413 ACE_DEBUG ((LM_DEBUG,
414 ACE_TEXT ("(%P|%t) usage:\n")
415 ACE_TEXT ("-i <iterations>\n")
416 ACE_TEXT ("-c\n")
417 ACE_TEXT ("-p <parent_pid>\n")
418 ACE_TEXT ("-t <test_number>\n")));
419 break;
424 run_main (int argc, ACE_TCHAR *argv[])
426 ACE_OS::signal(SIGHUP, SIG_DFL);
428 if (argc > 1)
430 ACE_APPEND_LOG (ACE_TEXT ("Signal_Test-child"));
431 parse_args (argc, argv);
433 if (test_number == 1)
435 ACE_DEBUG ((LM_DEBUG,
436 ACE_TEXT ("(%P|%t) **** test 1: handle signals synchronously in separate thread\n")));
438 // First, handle signals synchronously in separate thread.
439 run_test (worker_child, 1, 1);
441 else if (test_number == 2)
443 ACE_DEBUG ((LM_DEBUG,
444 ACE_TEXT ("(%P|%t) **** test 2: handle signals synchronously in this thread\n")));
446 // Next, handle signals synchronously in this thread.
447 run_test (worker_child, 0, 1);
449 else if (test_number == 3)
451 ACE_DEBUG ((LM_DEBUG,
452 ACE_TEXT ("(%P|%t) **** test 3: handle signals asynchronously in this thread\n")));
454 // Finally, handle signals asynchronously in this thread.
455 run_test (worker_child, 0, 0);
458 ACE_END_LOG;
460 else
462 ACE_START_TEST (ACE_TEXT ("Signal_Test"));
463 ACE_INIT_LOG (ACE_TEXT ("Signal_Test-child"));
465 // We need to get the process id here to work around "features"
466 // of Linux threads...
467 parent_pid = ACE_OS::getpid ();
469 ACE_DEBUG ((LM_DEBUG,
470 ACE_TEXT ("(%P|%t) **** test 1: handle signals synchronously in a separate thread\n")));
472 #ifdef ACE_HAS_THREADS
473 ++test_number;
474 // Run the parent logic for the signal test, first by handling
475 // signals synchronously in a separate thread.
476 run_test (worker_parent, 1L, 1L);
478 ACE_DEBUG ((LM_DEBUG,
479 ACE_TEXT ("(%P|%t) **** test 2: handle signals synchronously in this thread\n")));
481 ++test_number;
482 // And next by handling synchronously signals in this thread.
483 run_test (worker_parent, 0L, 1L);
485 ACE_DEBUG ((LM_DEBUG,
486 ACE_TEXT ("(%P|%t) **** test 3: handle signals asynchronously in this thread\n")));
487 #else
488 test_number += 2;
489 #endif /* ACE_HAS_THREADS */
491 ++test_number;
492 // And finally by handling asynchronously signals in this thread.
493 run_test (worker_parent, 0L, 0L);
495 ACE_END_TEST;
497 return 0;
500 #else
502 run_main (int, ACE_TCHAR *[])
504 ACE_START_TEST (ACE_TEXT ("Signal_Test"));
505 ACE_ERROR ((LM_INFO,
506 ACE_TEXT ("The Unix Signals capability is not supported on this platform\n")));
507 ACE_END_TEST;
508 return 0;
510 #endif /* !defined (ACE_LACKS_UNIX_SIGNALS) */