2 //=============================================================================
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"
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;
37 class Exit_Handler
: public ACE_Event_Handler
40 Exit_Handler (const char *msg
): msg_ (msg
) { }
42 virtual ~Exit_Handler (void) { }
44 virtual int handle_close (ACE_HANDLE
, ACE_Reactor_Mask
)
50 virtual int handle_exit (ACE_Process
*proc
)
53 ACE_TEXT ("(%P) Exit_Handler(%C) got %d: %d\n"),
55 int (proc
->getpid ()),
56 int (proc
->exit_code ()) ));
64 usage (const ACE_TCHAR
*argv0
)
67 ACE_TEXT ("usage: %s [-d] [sleep-seconds]\n"),
73 spawn_child (const ACE_TCHAR
*argv0
,
74 ACE_Process_Manager
&mgr
,
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");
86 const ACE_TCHAR
*cmdline_format
= ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR
ACE_TEXT("%ls %ls %d");
88 ACE_Process_Options opts
;
94 ACE_OS::strcpy (cmd
, ACE_TEXT ("-d"));
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'"));
125 prio
[0] = ACE_TEXT ('\0');
128 ACE_OS::snprintf (pd
, 16, ACE_TEXT (" -p %d"), my_process_id
);
129 ACE_OS::strcat (cmd
, pd
);
131 ACE_UNUSED_ARG (my_process_id
);
132 prio
[0] = ACE_TEXT ('\0');
135 opts
.process_name (argv0
);
136 #ifndef ACE_LACKS_VA_FUNCTIONS
137 opts
.command_line (cmdline_format
,
138 #if !defined (ACE_HAS_WINCE)
140 #endif /* !ACE_HAS_WINCE */
144 ACE_UNUSED_ARG (cmdline_format
);
145 #endif /* ACE_LACKS_VA_FUNCTIONS */
147 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT("Spawning <%s> <%s>\n"),
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
));
158 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn failed")));
165 ACE_Atomic_Op
<ACE_SYNCH_MUTEX
, int> running_tasks
= 0;
167 class Process_Task
: public ACE_Task
<ACE_SYNCH
>
170 Process_Task (const ACE_TCHAR
*argv0
,
171 ACE_Process_Manager
&mgr
,
175 sleep_time_ (sleep_time
) { }
177 // FUZZ: disable check_for_lack_ACE_OS
178 /// FUZZ: enable check_for_lack_ACE_OS
182 order
+= ACE_OS::itoa (sleep_time_
, tmp
, 10);
191 ACE_exitcode exitcode
;
192 pid_t my_child
= spawn_child (argv0_
,
196 result
= mgr_
.wait (my_child
,
198 if (result
!= my_child
)
200 ACE_ERROR ((LM_ERROR
,
201 ACE_TEXT ("(%P) Error: expected to reap child (%d); got %d\n"),
204 if (result
== ACE_INVALID_PID
)
205 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
209 ACE_DEBUG ((LM_DEBUG
,
210 ACE_TEXT ("(%P) reaped child, pid %d: %d\n"),
214 order
+= ACE_OS::itoa (sleep_time_
, tmp
, 10);
218 // FUZZ: disable check_for_lack_ACE_OS
219 /// FUZZ: enable check_for_lack_ACE_OS
227 const ACE_TCHAR
*argv0_
;
228 ACE_Process_Manager
&mgr_
;
232 #ifdef ACE_HAS_PROCESS_SPAWN
234 command_line_test (void)
236 ACE_DEBUG ((LM_DEBUG
,
237 ACE_TEXT ("Testing for last character of command line\n")));
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
);
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"),
260 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
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
));
274 ACE_ERROR ((LM_ERROR
,
275 ACE_TEXT ("Given process priority (%d) and real priority (%d) differ.\n"),
276 process_id
, priority
));
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:"));
286 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT ("d"));
289 while ((opt
= get_opt ()) != EOF
)
296 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
298 process_id
= ACE_OS::atoi (get_opt
.opt_arg ());
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);
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'"));
335 prio
[0] = ACE_TEXT ('\0');
338 ACE_DEBUG ((LM_DEBUG
,
339 ACE_TEXT ("%T: pid %P about to exit with code %d %s\n"),
347 if (get_opt
.opt_ind () != argc
) // incorrect usage
350 ACE_START_TEST (ACE_TEXT ("Process_Manager_Test"));
354 #ifdef ACE_HAS_PROCESS_SPAWN
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"),
373 result
= mgr
.wait (child1
,
376 if (result
!= child1
)
378 ACE_ERROR ((LM_ERROR
,
379 ACE_TEXT ("(%P) Error: expected to reap child1 (%d); got %d\n"),
382 if (result
== ACE_INVALID_PID
)
383 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
387 ACE_DEBUG ((LM_DEBUG
,
388 ACE_TEXT ("(%P) reaped child1, pid %d: %d\n"),
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"),
398 pid_t child3
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
402 result
= mgr
.wait (child3
,
405 if (result
!= child3
)
407 ACE_ERROR ((LM_ERROR
,
408 ACE_TEXT ("(%P) Error: expected to reap child3 (%d); got %d\n"),
411 if (result
== ACE_INVALID_PID
)
412 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
416 ACE_DEBUG ((LM_DEBUG
,
417 ACE_TEXT ("(%P) reaped child 3, pid %d: %d\n"),
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"),
431 if (result
== ACE_INVALID_PID
)
432 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
436 ACE_DEBUG ((LM_DEBUG
,
437 ACE_TEXT ("(%P) reaped child 2, pid %d: %d\n"),
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"),
449 #if defined (ACE_HAS_CPP11)
450 result
= mgr
.wait (0, std::chrono::seconds (4), &exitcode
);
452 result
= mgr
.wait (0, ACE_Time_Value (4), &exitcode
);
455 if (result
!= child4
)
457 ACE_ERROR ((LM_ERROR
,
458 ACE_TEXT ("(%P) Error: expected to reap child4 (%d); got %d\n"),
461 if (result
== ACE_INVALID_PID
)
462 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
466 ACE_DEBUG ((LM_DEBUG
,
467 ACE_TEXT ("(%P) reaped child 4 pid %d: %d\n"),
471 // This one should timeout:
472 pid_t child5
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
476 result
= mgr
.wait (0, ACE_Time_Value (1), &exitcode
);
479 ACE_ERROR ((LM_ERROR
,
480 ACE_TEXT ("(%P) Error: expected wait to time out; got %d\n"),
482 if (result
== ACE_INVALID_PID
)
483 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
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"),
499 if (result
== ACE_INVALID_PID
)
500 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
504 ACE_DEBUG ((LM_DEBUG
,
505 ACE_TEXT ("(%P) reaped child 5, pid %d: %d\n"),
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"),
514 ACE_exitcode status6
;
515 if (-1 == mgr
.terminate (child6
))
517 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("terminate child6")));
519 mgr
.wait (child6
, &status6
); // Wait for child to exit just to clean up
523 if (-1 == mgr
.wait (child6
, &status6
))
525 ACE_ERROR ((LM_ERROR
,
526 ACE_TEXT ("(%P) wait on child6 reported ACE_INVALID_PID\n")));
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
)));
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
)));
545 ACE_TEXT ("(%P) The process terminated with exit code %d\n"),
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);
559 while (running_tasks
!=0)
562 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("(%P) still running tasks\n")));
565 ACE_DEBUG ((LM_DEBUG
,
566 ACE_TEXT ("(%P) result: '%C'\n"),
569 if (order
!= "321123")
571 ACE_ERROR ((LM_ERROR
,
572 ACE_TEXT ("(%P) wrong order of spawns ('%C', should be '321123')\n"),
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"),
589 /* pid_t child8 = */ spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
594 mgr
.register_handler (new Exit_Handler ("specific"),
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 ();
606 ACE_ERROR ((LM_ERROR
,
607 ACE_TEXT ("(%P) %d processes left in manager\n"),
609 #endif /* !defined (ACE_OPENVMS) */
610 #endif // ACE_HAS_PROCESS_SPAWN