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 //=============================================================================
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"
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
30 static size_t n_iterations
= 10000;
32 // Keeps track of whether we're the child or not.
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;
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.
56 ACE_TEXT ("(%P|%t) received signal %S\n"),
63 // Signal to the main thread to shut down.
66 // This should only occur for the asynchronous case, so we don't
72 // Shut down our thread using <ACE_Thread_Manager::exit>.
75 ACE_TEXT ("(%P|%t) shutting down due to %S\n"),
79 // Signal to the worker thread to shut down.
82 // Bail out and close down.
88 // Shutdown the child.
92 ACE_TEXT ("(%P|%t) killing child pid %d\n"),
95 int const result
= ACE_OS::kill (child_pid
,
97 ACE_TEST_ASSERT (result
!= -1);
104 ACE_ERROR ((LM_ERROR
,
105 ACE_TEXT ("(%P|%t) %p\n"),
112 ACE_ERROR ((LM_ERROR
,
113 ACE_TEXT ("(%P|%t) signal %S unexpected\n"),
121 // This function handles signals synchronously.
123 static ACE_THR_FUNC_RETURN
124 synchronous_signal_handler (void *)
128 // Register signal handlers.
131 sigset
.sig_add (SIGINT
);
132 sigset
.sig_add (SIGTERM
);
135 sigset
.sig_add (SIGHUP
);
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
);
145 ACE_ERROR ((LM_ERROR
,
146 ACE_TEXT ("(%P|%t) %p\n"),
147 ACE_TEXT ("sigwait")));
150 if (handle_signal (signr
) == -1)
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")));
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 *)
169 // Register signal handlers.
172 sigset
.sig_add (SIGINT
);
173 sigset
.sig_add (SIGTERM
);
177 sigset
.sig_add (SIGCHLD
);
178 sigset
.sig_add (SIGHUP
);
181 // Register the <handle_signal> method to process all the signals in
183 ACE_Sig_Action
sa (sigset
, (ACE_SignalHandler
)handle_signal
);
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
++)
201 ACE_DEBUG ((LM_DEBUG
,
202 ACE_TEXT ("(%P|%t) we've been shutdown!\n")));
206 // Every 100 iterations sleep for 2 seconds.
209 ACE_DEBUG ((LM_DEBUG
,
210 ACE_TEXT ("(%P|%t) sleeping for 2 seconds\n")));
214 // After 1000 iterations sent a SIGHUP to our parent.
217 ACE_DEBUG ((LM_DEBUG
,
218 ACE_TEXT ("(%P|%t) sending SIGHUP to parent process %d\n"),
220 int const result
= ACE_OS::kill (parent_pid
,
224 ACE_ERROR ((LM_ERROR
,
225 ACE_TEXT ("(%P|%t) %p\n"),
227 ACE_TEST_ASSERT (result
!= -1);
232 if (handle_signals_synchronously
)
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")));
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
271 l_argv
[0] = const_cast <ACE_TCHAR
*> (t
);
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");
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 ());
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);
294 ACE_DEBUG ((LM_DEBUG
,
295 ACE_TEXT ("(%P|%t) spawning child process %d\n"),
298 // Perform a <wait> until our child process has exited.
300 if (handle_signals_synchronously
)
303 // Wait for the child process to exit.
305 ACE_DEBUG ((LM_DEBUG
,
306 ACE_TEXT ("(%P|%t) reaped child with status %d\n"),
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")));
326 // This is the driver function that spawns threads to run the test for
327 // the parent and the child process.
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())
345 ACE_DEBUG ((LM_DEBUG
,
346 ACE_TEXT ("(%P|%t) spawning worker thread\n")));
347 result
= ACE_Thread_Manager::instance ()->spawn
349 reinterpret_cast <void *> (handle_signals_synchronously
),
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
,
362 ACE_TEST_ASSERT (result
!= -1);
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);
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.
390 parse_args (int argc
, ACE_TCHAR
*argv
[])
392 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT ("i:chp:t:"));
396 while ((c
= get_opt ()) != -1)
400 n_iterations
= ACE_OS::atoi (get_opt
.opt_arg ());
406 parent_pid
= ACE_OS::atoi (get_opt
.opt_arg ());
409 test_number
= ACE_OS::atoi (get_opt
.opt_arg ());
413 ACE_DEBUG ((LM_DEBUG
,
414 ACE_TEXT ("(%P|%t) usage:\n")
415 ACE_TEXT ("-i <iterations>\n")
417 ACE_TEXT ("-p <parent_pid>\n")
418 ACE_TEXT ("-t <test_number>\n")));
424 run_main (int argc
, ACE_TCHAR
*argv
[])
426 ACE_OS::signal(SIGHUP
, SIG_DFL
);
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);
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
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")));
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")));
489 #endif /* ACE_HAS_THREADS */
492 // And finally by handling asynchronously signals in this thread.
493 run_test (worker_parent
, 0L, 0L);
502 run_main (int, ACE_TCHAR
*[])
504 ACE_START_TEST (ACE_TEXT ("Signal_Test"));
506 ACE_TEXT ("The Unix Signals capability is not supported on this platform\n")));
510 #endif /* !defined (ACE_LACKS_UNIX_SIGNALS) */