2 //=============================================================================
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
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"
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
33 static size_t n_iterations
= 10000;
35 // Keeps track of whether we're the child or not.
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;
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.
60 ACE_TEXT ("(%P|%t) received signal %S\n"),
67 // Signal to the main thread to shut down.
70 // This should only occur for the asynchronous case, so we don't
76 // Shut down our thread using <ACE_Thread_Manager::exit>.
79 ACE_TEXT ("(%P|%t) shutting down due to %S\n"),
83 // Signal to the worker thread to shut down.
86 // Bail out and close down.
92 // Shutdown the child.
96 ACE_TEXT ("(%P|%t) killing child pid %d\n"),
99 int const result
= ACE_OS::kill (child_pid
,
101 ACE_TEST_ASSERT (result
!= -1);
108 ACE_ERROR ((LM_ERROR
,
109 ACE_TEXT ("(%P|%t) %p\n"),
116 ACE_ERROR ((LM_ERROR
,
117 ACE_TEXT ("(%P|%t) signal %S unexpected\n"),
125 // This function handles signals synchronously.
127 static ACE_THR_FUNC_RETURN
128 synchronous_signal_handler (void *)
132 // Register signal handlers.
135 sigset
.sig_add (SIGINT
);
136 sigset
.sig_add (SIGTERM
);
139 sigset
.sig_add (SIGHUP
);
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
);
149 ACE_ERROR ((LM_ERROR
,
150 ACE_TEXT ("(%P|%t) %p\n"),
151 ACE_TEXT ("sigwait")));
154 if (handle_signal (signr
) == -1)
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")));
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 *)
174 // Register signal handlers.
177 sigset
.sig_add (SIGINT
);
178 sigset
.sig_add (SIGTERM
);
182 sigset
.sig_add (SIGCHLD
);
183 sigset
.sig_add (SIGHUP
);
186 // Register the <handle_signal> method to process all the signals in
188 ACE_Sig_Action
sa (sigset
,
189 (ACE_SignalHandler
) handle_signal
);
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
++)
207 ACE_DEBUG ((LM_DEBUG
,
208 ACE_TEXT ("(%P|%t) we've been shutdown!\n")));
212 // Every 100 iterations sleep for 2 seconds.
215 ACE_DEBUG ((LM_DEBUG
,
216 ACE_TEXT ("(%P|%t) sleeping for 2 seconds\n")));
220 // After 1000 iterations sent a SIGHUP to our parent.
223 ACE_DEBUG ((LM_DEBUG
,
224 ACE_TEXT ("(%P|%t) sending SIGHUP to parent process %d\n"),
226 int const result
= ACE_OS::kill (parent_pid
,
230 ACE_ERROR ((LM_ERROR
,
231 ACE_TEXT ("(%P|%t) %p\n"),
233 ACE_TEST_ASSERT (result
!= -1);
238 if (handle_signals_synchronously
)
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")));
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
277 l_argv
[0] = const_cast <ACE_TCHAR
*> (t
);
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");
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 ());
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);
300 ACE_DEBUG ((LM_DEBUG
,
301 ACE_TEXT ("(%P|%t) spawning child process %d\n"),
304 // Perform a <wait> until our child process has exited.
306 if (handle_signals_synchronously
)
309 // Wait for the child process to exit.
311 ACE_DEBUG ((LM_DEBUG
,
312 ACE_TEXT ("(%P|%t) reaped child with status %d\n"),
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")));
332 // This is the driver function that spawns threads to run the test for
333 // the parent and the child process.
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())
351 ACE_DEBUG ((LM_DEBUG
,
352 ACE_TEXT ("(%P|%t) spawning worker thread\n")));
353 result
= ACE_Thread_Manager::instance ()->spawn
355 reinterpret_cast <void *> (handle_signals_synchronously
),
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
,
368 ACE_TEST_ASSERT (result
!= -1);
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);
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.
396 parse_args (int argc
, ACE_TCHAR
*argv
[])
398 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT ("i:chp:t:"));
402 while ((c
= get_opt ()) != -1)
406 n_iterations
= ACE_OS::atoi (get_opt
.opt_arg ());
412 parent_pid
= ACE_OS::atoi (get_opt
.opt_arg ());
415 test_number
= ACE_OS::atoi (get_opt
.opt_arg ());
419 ACE_DEBUG ((LM_DEBUG
,
420 ACE_TEXT ("(%P|%t) usage:\n")
421 ACE_TEXT ("-i <iterations>\n")
423 ACE_TEXT ("-p <parent_pid>\n")
424 ACE_TEXT ("-t <test_number>\n")));
430 run_main (int argc
, ACE_TCHAR
*argv
[])
432 ACE_OS::signal(SIGHUP
, SIG_DFL
);
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);
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
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")));
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")));
495 #endif /* ACE_HAS_THREADS */
498 // And finally by handling asynchronously signals in this thread.
499 run_test (worker_parent
, 0L, 0L);
508 run_main (int, ACE_TCHAR
*[])
510 ACE_START_TEST (ACE_TEXT ("Signal_Test"));
512 ACE_TEXT ("The Unix Signals capability is not supported on this platform\n")));
516 #endif /* !defined (ACE_LACKS_UNIX_SIGNALS) */