Merge pull request #1844 from jrw972/monterey
[ACE_TAO.git] / ACE / tests / Process_Manager_Test.cpp
blobd08152588d6c43d62db72380e5a32dd400346df1
2 //=============================================================================
3 /**
4 * @file Process_Manager_Test.cpp
6 * This program tests the various methods provided by
7 * <ACE_Process_Manager>. It illustrates both the explicit <wait>
8 * functions and the Reactor-style auto-reaping. There's an
9 * Exit_Handler class that can print out (in Debug mode) when a
10 * child has been reaped.
12 * The child processes spawned are simply this program itself, with
13 * an integer argument specifying how long to "process" (actually,
14 * the child just sleeps for the specified length of time).
16 * @author Douglas C. Schmidt <d.schmidt@vanderbilt.edu> and Dave Madden <dhm@mersenne.com>
18 //=============================================================================
20 #include "test_config.h"
21 #include "ace/SString.h"
22 #include "ace/Atomic_Op.h"
23 #include "ace/Task.h"
24 #include "ace/OS_NS_unistd.h"
25 #include "ace/OS_NS_string.h"
26 #include "ace/Process_Manager.h"
27 #include "ace/Synch_Traits.h"
28 #include "ace/Get_Opt.h"
29 #include "ace/Thread.h"
30 #include "ace/Reactor.h"
32 static u_int debug_test = 0;
33 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
34 static u_int process_id = 0;
35 #endif
37 class Exit_Handler : public ACE_Event_Handler
39 public:
40 Exit_Handler (const char *msg): msg_ (msg) { }
42 virtual ~Exit_Handler (void) { }
44 virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask)
46 delete this;
47 return 0;
50 virtual int handle_exit (ACE_Process *proc)
52 ACE_DEBUG ((LM_DEBUG,
53 ACE_TEXT ("(%P) Exit_Handler(%C) got %d: %d\n"),
54 msg_,
55 int (proc->getpid ()),
56 int (proc->exit_code ()) ));
57 return 0;
59 private:
60 const char *msg_;
63 static void
64 usage (const ACE_TCHAR *argv0)
66 ACE_ERROR ((LM_ERROR,
67 ACE_TEXT ("usage: %s [-d] [sleep-seconds]\n"),
68 argv0));
69 ACE_OS::exit (0);
72 static pid_t
73 spawn_child (const ACE_TCHAR *argv0,
74 ACE_Process_Manager &mgr,
75 int sleep_time,
76 int my_process_id)
79 #if defined (ACE_HAS_WINCE)
80 const ACE_TCHAR *cmdline_format = ACE_TEXT("%s %d");
81 #elif defined (ACE_WIN32)
82 const ACE_TCHAR *cmdline_format = ACE_TEXT("\"%s\" %s %d");
83 #elif !defined (ACE_USES_WCHAR)
84 const ACE_TCHAR *cmdline_format = ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR ACE_TEXT("%s %s %d");
85 #else
86 const ACE_TCHAR *cmdline_format = ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR ACE_TEXT("%ls %ls %d");
87 #endif
88 ACE_Process_Options opts;
90 ACE_TCHAR prio[64];
91 ACE_TCHAR cmd[16];
93 if (debug_test)
94 ACE_OS::strcpy (cmd, ACE_TEXT ("-d"));
95 else
96 cmd[0] = ACE_TEXT ('\0');
98 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
99 if (my_process_id == 1)
101 opts.creation_flags (ABOVE_NORMAL_PRIORITY_CLASS);
102 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'above normal'"));
104 else if (my_process_id == 2)
106 opts.creation_flags (BELOW_NORMAL_PRIORITY_CLASS);
107 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'below normal'"));
109 else if (my_process_id == 3)
111 opts.creation_flags (IDLE_PRIORITY_CLASS);
112 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'idle'"));
114 else if (my_process_id == 4)
116 opts.creation_flags (HIGH_PRIORITY_CLASS);
117 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'high'"));
119 else if (my_process_id == 5)
121 opts.creation_flags (NORMAL_PRIORITY_CLASS);
122 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'normal'"));
124 else
125 prio[0] = ACE_TEXT ('\0');
127 ACE_TCHAR pd [16];
128 ACE_OS::snprintf (pd, 16, ACE_TEXT (" -p %d"), my_process_id);
129 ACE_OS::strcat (cmd, pd);
130 #else
131 ACE_UNUSED_ARG (my_process_id);
132 prio[0] = ACE_TEXT ('\0');
133 #endif
135 opts.process_name (argv0);
136 #ifndef ACE_LACKS_VA_FUNCTIONS
137 opts.command_line (cmdline_format,
138 #if !defined (ACE_HAS_WINCE)
139 argv0,
140 #endif /* !ACE_HAS_WINCE */
141 cmd,
142 sleep_time);
143 #else
144 ACE_UNUSED_ARG (cmdline_format);
145 #endif /* ACE_LACKS_VA_FUNCTIONS */
147 ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Spawning <%s> <%s>\n"),
148 opts.process_name(),
149 opts.command_line_buf ()));
151 pid_t result = mgr.spawn (opts);
153 if (result != ACE_INVALID_PID)
154 ACE_DEBUG ((LM_DEBUG,
155 ACE_TEXT ("(%P) spawned child: pid %d time %d %s\n"),
156 int (result), sleep_time, prio));
157 else
158 ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn failed")));
160 return result;
163 ACE_CString order;
165 ACE_Atomic_Op<ACE_SYNCH_MUTEX, int> running_tasks = 0;
167 class Process_Task : public ACE_Task<ACE_SYNCH>
169 public:
170 Process_Task (const ACE_TCHAR *argv0,
171 ACE_Process_Manager &mgr,
172 int sleep_time)
173 : argv0_ (argv0),
174 mgr_ (mgr),
175 sleep_time_ (sleep_time) { }
177 // FUZZ: disable check_for_lack_ACE_OS
178 /// FUZZ: enable check_for_lack_ACE_OS
179 int open (void*)
181 char tmp[10];
182 order += ACE_OS::itoa (sleep_time_, tmp, 10);
183 ++running_tasks;
184 activate ();
185 return 0;
188 int svc (void)
190 int result = 0;
191 ACE_exitcode exitcode;
192 pid_t my_child = spawn_child (argv0_,
193 mgr_,
194 sleep_time_,
196 result = mgr_.wait (my_child,
197 &exitcode);
198 if (result != my_child)
200 ACE_ERROR ((LM_ERROR,
201 ACE_TEXT ("(%P) Error: expected to reap child (%d); got %d\n"),
202 my_child,
203 result));
204 if (result == ACE_INVALID_PID)
205 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
206 //test_status = 1;
208 else
209 ACE_DEBUG ((LM_DEBUG,
210 ACE_TEXT ("(%P) reaped child, pid %d: %d\n"),
211 my_child,
212 exitcode));
213 char tmp[10];
214 order += ACE_OS::itoa (sleep_time_, tmp, 10);
215 return 0;
218 // FUZZ: disable check_for_lack_ACE_OS
219 /// FUZZ: enable check_for_lack_ACE_OS
220 int close (u_long)
222 --running_tasks;
223 return 0;
226 private:
227 const ACE_TCHAR *argv0_;
228 ACE_Process_Manager &mgr_;
229 int sleep_time_;
232 #ifdef ACE_HAS_PROCESS_SPAWN
233 static int
234 command_line_test (void)
236 ACE_DEBUG ((LM_DEBUG,
237 ACE_TEXT ("Testing for last character of command line\n")));
238 int result = 0;
239 const ACE_TCHAR *command = ACE_TEXT ("test Hello");
240 size_t command_len = ACE_OS::strlen (command);
241 ACE_Process_Options options (1, command_len + 1);
243 #ifndef ACE_LACKS_VA_FUNCTIONS
244 options.command_line (command);
245 #endif
247 ACE_TCHAR * const *procargv = options.command_line_argv ();
248 if (ACE_OS::strcmp (procargv [1], ACE_TEXT ("Hello")) != 0)
250 ACE_ERROR ((LM_ERROR,
251 ACE_TEXT ("command_line_test failed: expected \"%s\"; got \"%s\"\n"),
252 ACE_TEXT ("Hello"),
253 procargv [1]));
254 result = 1;
256 return result;
258 #endif
260 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
261 void
262 check_process_priority (DWORD priority)
264 if ((process_id == 0) ||
265 (process_id == 1 && priority == ABOVE_NORMAL_PRIORITY_CLASS) ||
266 (process_id == 2 && priority == BELOW_NORMAL_PRIORITY_CLASS) ||
267 (process_id == 3 && priority == IDLE_PRIORITY_CLASS) ||
268 (process_id == 4 && priority == HIGH_PRIORITY_CLASS) ||
269 (process_id == 5 && priority == NORMAL_PRIORITY_CLASS) ||
270 (process_id == 7 && priority == NORMAL_PRIORITY_CLASS))
271 ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Process ID (%d) and priority (%d) match\n"),
272 process_id, priority));
273 else
274 ACE_ERROR ((LM_ERROR,
275 ACE_TEXT ("Given process priority (%d) and real priority (%d) differ.\n"),
276 process_id, priority));
278 #endif
281 run_main (int argc, ACE_TCHAR *argv[])
283 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
284 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("dp:"));
285 #else
286 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("d"));
287 #endif
288 int opt;
289 while ((opt = get_opt ()) != EOF)
291 switch (opt)
293 case 'd':
294 debug_test = 1u;
295 break;
296 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
297 case 'p':
298 process_id = ACE_OS::atoi (get_opt.opt_arg ());
299 break;
300 #endif
304 if (get_opt.opt_ind () == argc - 1)
306 // child process: sleep & exit
307 ACE_TCHAR lognm[MAXPATHLEN];
308 int const mypid (ACE_OS::getpid ());
309 ACE_OS::snprintf (lognm, MAXPATHLEN,
310 ACE_TEXT ("Process_Manager_Test-child-%d"), mypid);
312 ACE_START_TEST (lognm);
313 int const secs = ACE_OS::atoi (argv[get_opt.opt_ind ()]);
314 ACE_OS::sleep (secs ? secs : 1);
316 ACE_TCHAR prio[64];
317 #if defined (ACE_WIN32_HAS_PRIORITY_CLASS)
318 DWORD priority = ::GetPriorityClass (::GetCurrentProcess());
320 check_process_priority(priority);
322 if (priority == ABOVE_NORMAL_PRIORITY_CLASS)
323 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'above normal'"));
324 else if (priority == BELOW_NORMAL_PRIORITY_CLASS)
325 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'below normal'"));
326 else if (priority == HIGH_PRIORITY_CLASS)
327 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'high'"));
328 else if (priority == IDLE_PRIORITY_CLASS)
329 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'idle'"));
330 else if (priority == NORMAL_PRIORITY_CLASS)
331 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'normal'"));
332 else if (priority == REALTIME_PRIORITY_CLASS)
333 ACE_OS::snprintf (prio, 64, ACE_TEXT ("and priority 'realtime'"));
334 #else
335 prio[0] = ACE_TEXT ('\0');
336 #endif
337 if (debug_test)
338 ACE_DEBUG ((LM_DEBUG,
339 ACE_TEXT ("%T: pid %P about to exit with code %d %s\n"),
340 secs,
341 prio));
342 ACE_END_LOG;
344 return secs;
347 if (get_opt.opt_ind () != argc) // incorrect usage
348 usage (argv[0]);
350 ACE_START_TEST (ACE_TEXT ("Process_Manager_Test"));
352 int test_status = 0;
354 #ifdef ACE_HAS_PROCESS_SPAWN
356 int result = 0;
357 if ((result = command_line_test ()) != 0)
358 test_status = result;
360 // Try the explicit <ACE_Process_Manager::wait> functions
361 ACE_Process_Manager mgr;
363 mgr.register_handler (new Exit_Handler ("default"));
365 ACE_exitcode exitcode;
367 // --------------------------------------------------
368 // wait for a specific PID
369 pid_t child1 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
370 mgr,
373 result = mgr.wait (child1,
374 &exitcode);
376 if (result != child1)
378 ACE_ERROR ((LM_ERROR,
379 ACE_TEXT ("(%P) Error: expected to reap child1 (%d); got %d\n"),
380 child1,
381 result));
382 if (result == ACE_INVALID_PID)
383 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
384 test_status = 1;
386 else
387 ACE_DEBUG ((LM_DEBUG,
388 ACE_TEXT ("(%P) reaped child1, pid %d: %d\n"),
389 child1,
390 exitcode));
392 // --------------------------------------------------
393 // wait for a specific PID; another should finish first
394 pid_t child2 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
395 mgr,
398 pid_t child3 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
399 mgr,
402 result = mgr.wait (child3,
403 &exitcode);
405 if (result != child3)
407 ACE_ERROR ((LM_ERROR,
408 ACE_TEXT ("(%P) Error: expected to reap child3 (%d); got %d\n"),
409 child3,
410 result));
411 if (result == ACE_INVALID_PID)
412 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
413 test_status = 1;
415 else
416 ACE_DEBUG ((LM_DEBUG,
417 ACE_TEXT ("(%P) reaped child 3, pid %d: %d\n"),
418 child3,
419 exitcode));
421 // Now wait for any...should get the one that finished earlier.
423 result = mgr.wait (0, &exitcode);
425 if (result != child2)
427 ACE_ERROR ((LM_ERROR,
428 ACE_TEXT ("(%P) Error: expected to reap child2 (%d); got %d\n"),
429 child2,
430 result));
431 if (result == ACE_INVALID_PID)
432 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
433 test_status = 1;
435 else
436 ACE_DEBUG ((LM_DEBUG,
437 ACE_TEXT ("(%P) reaped child 2, pid %d: %d\n"),
438 result,
439 exitcode));
441 // --------------------------------------------------
442 // Try the timed wait functions
444 // This one shouldn't timeout:
445 pid_t child4 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
446 mgr,
449 #if defined (ACE_HAS_CPP11)
450 result = mgr.wait (0, std::chrono::seconds (4), &exitcode);
451 #else
452 result = mgr.wait (0, ACE_Time_Value (4), &exitcode);
453 #endif
455 if (result != child4)
457 ACE_ERROR ((LM_ERROR,
458 ACE_TEXT ("(%P) Error: expected to reap child4 (%d); got %d\n"),
459 child4,
460 result));
461 if (result == ACE_INVALID_PID)
462 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
463 test_status = 1;
465 else
466 ACE_DEBUG ((LM_DEBUG,
467 ACE_TEXT ("(%P) reaped child 4 pid %d: %d\n"),
468 result,
469 exitcode));
471 // This one should timeout:
472 pid_t child5 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
473 mgr,
476 result = mgr.wait (0, ACE_Time_Value (1), &exitcode);
477 if (result != 0)
479 ACE_ERROR ((LM_ERROR,
480 ACE_TEXT ("(%P) Error: expected wait to time out; got %d\n"),
481 result));
482 if (result == ACE_INVALID_PID)
483 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
484 test_status = 1;
486 else
487 ACE_DEBUG ((LM_DEBUG,
488 ACE_TEXT ("(%P) Correctly timed out wait at child 5\n")));
490 // Now wait indefinitely to clean up...
491 result = mgr.wait (0, &exitcode);
493 if (result != child5)
495 ACE_ERROR ((LM_ERROR,
496 ACE_TEXT ("Error: expected to reap child5 pid %d; got %d\n"),
497 child5,
498 result));
499 if (result == ACE_INVALID_PID)
500 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
501 test_status = 1;
503 else
504 ACE_DEBUG ((LM_DEBUG,
505 ACE_TEXT ("(%P) reaped child 5, pid %d: %d\n"),
506 result,
507 exitcode));
509 // Terminate a child process and make sure we can wait for it.
510 pid_t child6 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
511 mgr,
514 ACE_exitcode status6;
515 if (-1 == mgr.terminate (child6))
517 ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("terminate child6")));
518 test_status = 1;
519 mgr.wait (child6, &status6); // Wait for child to exit just to clean up
521 else
523 if (-1 == mgr.wait (child6, &status6))
525 ACE_ERROR ((LM_ERROR,
526 ACE_TEXT ("(%P) wait on child6 reported ACE_INVALID_PID\n")));
527 test_status = 1;
529 else
531 // Get the results of the termination.
532 #if !defined(ACE_WIN32)
533 if (WIFSIGNALED (status6) != 0)
534 ACE_DEBUG ((LM_DEBUG,
535 ACE_TEXT ("(%P) child6 died on signal %d - correct\n"),
536 WTERMSIG (status6)));
537 else
538 ACE_ERROR ((LM_ERROR,
539 ACE_TEXT ("(%P) child6 should have died on signal, ")
540 ACE_TEXT ("but didn't; exit status %d\n"),
541 WEXITSTATUS (status6)));
542 #else
543 ACE_DEBUG
544 ((LM_DEBUG,
545 ACE_TEXT ("(%P) The process terminated with exit code %d\n"),
546 status6));
547 #endif /*ACE_WIN32*/
551 #ifdef ACE_HAS_THREADS
552 Process_Task task1 (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"), mgr, 3);
553 Process_Task task2 (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"), mgr, 2);
554 Process_Task task3 (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"), mgr, 1);
555 task1.open (0);
556 task2.open (0);
557 task3.open (0);
559 while (running_tasks!=0)
561 ACE_OS::sleep (1);
562 ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) still running tasks\n")));
565 ACE_DEBUG ((LM_DEBUG,
566 ACE_TEXT ("(%P) result: '%C'\n"),
567 order.c_str ()));
569 if (order != "321123")
571 ACE_ERROR ((LM_ERROR,
572 ACE_TEXT ("(%P) wrong order of spawns ('%C', should be '321123')\n"),
573 order.c_str ()));
574 test_status = 1;
576 #endif /* ACE_HAS_THREADS */
578 #if !defined (ACE_OPENVMS) && \
579 (defined ACE_WIN32 || !defined ACE_LACKS_UNIX_SIGNALS)
580 // --------------------------------------------------
581 // Finally, try the reactor stuff...
582 mgr.open (ACE_Process_Manager::DEFAULT_SIZE,
583 ACE_Reactor::instance ());
585 pid_t child7 = spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
586 mgr,
589 /* pid_t child8 = */ spawn_child (argc > 0 ? argv[0] : ACE_TEXT ("Process_Manager_Test"),
590 mgr,
594 mgr.register_handler (new Exit_Handler ("specific"),
595 child7);
597 ACE_Time_Value how_long (10);
599 ACE_Reactor::instance ()->run_reactor_event_loop (how_long);
601 ACE_DEBUG ((LM_DEBUG,
602 ACE_TEXT ("(%P) Reactor loop done!\n") ));
604 size_t const nr_procs = mgr.managed ();
605 if (nr_procs != 0)
606 ACE_ERROR ((LM_ERROR,
607 ACE_TEXT ("(%P) %d processes left in manager\n"),
608 nr_procs));
609 #endif /* !defined (ACE_OPENVMS) */
610 #endif // ACE_HAS_PROCESS_SPAWN
611 ACE_END_TEST;
612 return test_status;