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 ~Exit_Handler () override
{ }
44 int handle_close (ACE_HANDLE
, ACE_Reactor_Mask
) override
50 int handle_exit (ACE_Process
*proc
) override
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
,
78 #if defined (ACE_WIN32)
79 const ACE_TCHAR
*cmdline_format
= ACE_TEXT("\"%s\" %s %d");
80 #elif !defined (ACE_USES_WCHAR)
81 const ACE_TCHAR
*cmdline_format
= ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR
ACE_TEXT("%s %s %d");
83 const ACE_TCHAR
*cmdline_format
= ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR
ACE_TEXT("%ls %ls %d");
85 ACE_Process_Options opts
;
91 ACE_OS::strcpy (cmd
, ACE_TEXT ("-d"));
93 cmd
[0] = ACE_TEXT ('\0');
95 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
96 if (my_process_id
== 1)
98 opts
.creation_flags (ABOVE_NORMAL_PRIORITY_CLASS
);
99 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'above normal'"));
101 else if (my_process_id
== 2)
103 opts
.creation_flags (BELOW_NORMAL_PRIORITY_CLASS
);
104 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'below normal'"));
106 else if (my_process_id
== 3)
108 opts
.creation_flags (IDLE_PRIORITY_CLASS
);
109 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'idle'"));
111 else if (my_process_id
== 4)
113 opts
.creation_flags (HIGH_PRIORITY_CLASS
);
114 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'high'"));
116 else if (my_process_id
== 5)
118 opts
.creation_flags (NORMAL_PRIORITY_CLASS
);
119 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'normal'"));
122 prio
[0] = ACE_TEXT ('\0');
125 ACE_OS::snprintf (pd
, 16, ACE_TEXT (" -p %d"), my_process_id
);
126 ACE_OS::strcat (cmd
, pd
);
128 ACE_UNUSED_ARG (my_process_id
);
129 prio
[0] = ACE_TEXT ('\0');
132 opts
.process_name (argv0
);
133 #ifndef ACE_LACKS_VA_FUNCTIONS
134 opts
.command_line (cmdline_format
,
139 ACE_UNUSED_ARG (cmdline_format
);
140 #endif /* ACE_LACKS_VA_FUNCTIONS */
142 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT("Spawning <%s> <%s>\n"),
144 opts
.command_line_buf ()));
146 pid_t result
= mgr
.spawn (opts
);
148 if (result
!= ACE_INVALID_PID
)
149 ACE_DEBUG ((LM_DEBUG
,
150 ACE_TEXT ("(%P) spawned child: pid %d time %d %s\n"),
151 int (result
), sleep_time
, prio
));
153 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn failed")));
160 ACE_Atomic_Op
<ACE_SYNCH_MUTEX
, int> running_tasks
= 0;
162 class Process_Task
: public ACE_Task
<ACE_SYNCH
>
165 Process_Task (const ACE_TCHAR
*argv0
,
166 ACE_Process_Manager
&mgr
,
170 sleep_time_ (sleep_time
) { }
172 // FUZZ: disable check_for_lack_ACE_OS
173 /// FUZZ: enable check_for_lack_ACE_OS
174 int open (void*) override
177 order
+= ACE_OS::itoa (sleep_time_
, tmp
, 10);
186 ACE_exitcode exitcode
;
187 pid_t my_child
= spawn_child (argv0_
,
191 result
= mgr_
.wait (my_child
,
193 if (result
!= my_child
)
195 ACE_ERROR ((LM_ERROR
,
196 ACE_TEXT ("(%P) Error: expected to reap child (%d); got %d\n"),
199 if (result
== ACE_INVALID_PID
)
200 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
204 ACE_DEBUG ((LM_DEBUG
,
205 ACE_TEXT ("(%P) reaped child, pid %d: %d\n"),
209 order
+= ACE_OS::itoa (sleep_time_
, tmp
, 10);
213 // FUZZ: disable check_for_lack_ACE_OS
214 /// FUZZ: enable check_for_lack_ACE_OS
215 int close (u_long
) override
222 const ACE_TCHAR
*argv0_
;
223 ACE_Process_Manager
&mgr_
;
227 #ifdef ACE_HAS_PROCESS_SPAWN
231 ACE_DEBUG ((LM_DEBUG
,
232 ACE_TEXT ("Testing for last character of command line\n")));
234 const ACE_TCHAR
*command
= ACE_TEXT ("test Hello");
235 size_t command_len
= ACE_OS::strlen (command
);
236 ACE_Process_Options
options (1, command_len
+ 1);
238 #ifndef ACE_LACKS_VA_FUNCTIONS
239 options
.command_line (ACE_TEXT ("%") ACE_TEXT_PRIs
, command
);
242 ACE_TCHAR
* const *procargv
= options
.command_line_argv ();
243 if (ACE_OS::strcmp (procargv
[1], ACE_TEXT ("Hello")) != 0)
245 ACE_ERROR ((LM_ERROR
,
246 ACE_TEXT ("command_line_test failed: expected \"%s\"; got \"%s\"\n"),
255 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
257 check_process_priority (DWORD priority
)
259 if ((process_id
== 0) ||
260 (process_id
== 1 && priority
== ABOVE_NORMAL_PRIORITY_CLASS
) ||
261 (process_id
== 2 && priority
== BELOW_NORMAL_PRIORITY_CLASS
) ||
262 (process_id
== 3 && priority
== IDLE_PRIORITY_CLASS
) ||
263 (process_id
== 4 && priority
== HIGH_PRIORITY_CLASS
) ||
264 (process_id
== 5 && priority
== NORMAL_PRIORITY_CLASS
) ||
265 (process_id
== 7 && priority
== NORMAL_PRIORITY_CLASS
))
266 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Process ID (%d) and priority (%d) match\n"),
267 process_id
, priority
));
269 ACE_ERROR ((LM_ERROR
,
270 ACE_TEXT ("Given process priority (%d) and real priority (%d) differ.\n"),
271 process_id
, priority
));
276 run_main (int argc
, ACE_TCHAR
*argv
[])
278 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
279 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT ("dp:"));
281 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT ("d"));
284 while ((opt
= get_opt ()) != EOF
)
291 #if defined (ACE_HAS_WIN32_PRIORITY_CLASS)
293 process_id
= ACE_OS::atoi (get_opt
.opt_arg ());
299 if (get_opt
.opt_ind () == argc
- 1)
301 // child process: sleep & exit
302 ACE_TCHAR lognm
[MAXPATHLEN
];
303 int const mypid (ACE_OS::getpid ());
304 ACE_OS::snprintf (lognm
, MAXPATHLEN
,
305 ACE_TEXT ("Process_Manager_Test-child-%d"), mypid
);
307 ACE_START_TEST (lognm
);
308 int const secs
= ACE_OS::atoi (argv
[get_opt
.opt_ind ()]);
309 ACE_OS::sleep (secs
? secs
: 1);
312 #if defined (ACE_WIN32_HAS_PRIORITY_CLASS)
313 DWORD priority
= ::GetPriorityClass (::GetCurrentProcess());
315 check_process_priority(priority
);
317 if (priority
== ABOVE_NORMAL_PRIORITY_CLASS
)
318 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'above normal'"));
319 else if (priority
== BELOW_NORMAL_PRIORITY_CLASS
)
320 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'below normal'"));
321 else if (priority
== HIGH_PRIORITY_CLASS
)
322 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'high'"));
323 else if (priority
== IDLE_PRIORITY_CLASS
)
324 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'idle'"));
325 else if (priority
== NORMAL_PRIORITY_CLASS
)
326 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'normal'"));
327 else if (priority
== REALTIME_PRIORITY_CLASS
)
328 ACE_OS::snprintf (prio
, 64, ACE_TEXT ("and priority 'realtime'"));
330 prio
[0] = ACE_TEXT ('\0');
333 ACE_DEBUG ((LM_DEBUG
,
334 ACE_TEXT ("%T: pid %P about to exit with code %d %s\n"),
342 if (get_opt
.opt_ind () != argc
) // incorrect usage
345 ACE_START_TEST (ACE_TEXT ("Process_Manager_Test"));
349 #ifdef ACE_HAS_PROCESS_SPAWN
352 if ((result
= command_line_test ()) != 0)
353 test_status
= result
;
355 // Try the explicit <ACE_Process_Manager::wait> functions
356 ACE_Process_Manager mgr
;
358 mgr
.register_handler (new Exit_Handler ("default"));
360 ACE_exitcode exitcode
;
362 // --------------------------------------------------
363 // wait for a specific PID
364 pid_t child1
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
368 result
= mgr
.wait (child1
,
371 if (result
!= child1
)
373 ACE_ERROR ((LM_ERROR
,
374 ACE_TEXT ("(%P) Error: expected to reap child1 (%d); got %d\n"),
377 if (result
== ACE_INVALID_PID
)
378 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
382 ACE_DEBUG ((LM_DEBUG
,
383 ACE_TEXT ("(%P) reaped child1, pid %d: %d\n"),
387 // --------------------------------------------------
388 // wait for a specific PID; another should finish first
389 pid_t child2
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
393 pid_t child3
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
397 result
= mgr
.wait (child3
,
400 if (result
!= child3
)
402 ACE_ERROR ((LM_ERROR
,
403 ACE_TEXT ("(%P) Error: expected to reap child3 (%d); got %d\n"),
406 if (result
== ACE_INVALID_PID
)
407 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
411 ACE_DEBUG ((LM_DEBUG
,
412 ACE_TEXT ("(%P) reaped child 3, pid %d: %d\n"),
416 // Now wait for any...should get the one that finished earlier.
418 result
= mgr
.wait (0, &exitcode
);
420 if (result
!= child2
)
422 ACE_ERROR ((LM_ERROR
,
423 ACE_TEXT ("(%P) Error: expected to reap child2 (%d); got %d\n"),
426 if (result
== ACE_INVALID_PID
)
427 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
431 ACE_DEBUG ((LM_DEBUG
,
432 ACE_TEXT ("(%P) reaped child 2, pid %d: %d\n"),
436 // --------------------------------------------------
437 // Try the timed wait functions
439 // This one shouldn't timeout:
440 pid_t child4
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
444 result
= mgr
.wait (0, std::chrono::seconds (4), &exitcode
);
446 if (result
!= child4
)
448 ACE_ERROR ((LM_ERROR
,
449 ACE_TEXT ("(%P) Error: expected to reap child4 (%d); got %d\n"),
452 if (result
== ACE_INVALID_PID
)
453 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
457 ACE_DEBUG ((LM_DEBUG
,
458 ACE_TEXT ("(%P) reaped child 4 pid %d: %d\n"),
462 // This one should timeout:
463 pid_t child5
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
467 result
= mgr
.wait (0, ACE_Time_Value (1), &exitcode
);
470 ACE_ERROR ((LM_ERROR
,
471 ACE_TEXT ("(%P) Error: expected wait to time out; got %d\n"),
473 if (result
== ACE_INVALID_PID
)
474 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
478 ACE_DEBUG ((LM_DEBUG
,
479 ACE_TEXT ("(%P) Correctly timed out wait at child 5\n")));
481 // Now wait indefinitely to clean up...
482 result
= mgr
.wait (0, &exitcode
);
484 if (result
!= child5
)
486 ACE_ERROR ((LM_ERROR
,
487 ACE_TEXT ("Error: expected to reap child5 pid %d; got %d\n"),
490 if (result
== ACE_INVALID_PID
)
491 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("error")));
495 ACE_DEBUG ((LM_DEBUG
,
496 ACE_TEXT ("(%P) reaped child 5, pid %d: %d\n"),
500 // Terminate a child process and make sure we can wait for it.
501 pid_t child6
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
505 ACE_exitcode status6
;
506 if (-1 == mgr
.terminate (child6
))
508 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("(%P) %p\n"), ACE_TEXT ("terminate child6")));
510 mgr
.wait (child6
, &status6
); // Wait for child to exit just to clean up
514 if (-1 == mgr
.wait (child6
, &status6
))
516 ACE_ERROR ((LM_ERROR
,
517 ACE_TEXT ("(%P) wait on child6 reported ACE_INVALID_PID\n")));
522 // Get the results of the termination.
523 #if !defined(ACE_WIN32)
524 if (WIFSIGNALED (status6
) != 0)
525 ACE_DEBUG ((LM_DEBUG
,
526 ACE_TEXT ("(%P) child6 died on signal %d - correct\n"),
527 WTERMSIG (status6
)));
529 ACE_ERROR ((LM_ERROR
,
530 ACE_TEXT ("(%P) child6 should have died on signal, ")
531 ACE_TEXT ("but didn't; exit status %d\n"),
532 WEXITSTATUS (status6
)));
536 ACE_TEXT ("(%P) The process terminated with exit code %d\n"),
542 #ifdef ACE_HAS_THREADS
543 Process_Task
task1 (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"), mgr
, 3);
544 Process_Task
task2 (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"), mgr
, 2);
545 Process_Task
task3 (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"), mgr
, 1);
550 while (running_tasks
!=0)
553 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("(%P) still running tasks\n")));
556 ACE_DEBUG ((LM_DEBUG
,
557 ACE_TEXT ("(%P) result: '%C'\n"),
560 if (order
!= "321123")
562 ACE_ERROR ((LM_ERROR
,
563 ACE_TEXT ("(%P) wrong order of spawns ('%C', should be '321123')\n"),
567 #endif /* ACE_HAS_THREADS */
569 #if defined ACE_WIN32 || !defined ACE_LACKS_UNIX_SIGNALS
570 // --------------------------------------------------
571 // Finally, try the reactor stuff...
572 mgr
.open (ACE_Process_Manager::DEFAULT_SIZE
,
573 ACE_Reactor::instance ());
575 pid_t child7
= spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
579 /* pid_t child8 = */ spawn_child (argc
> 0 ? argv
[0] : ACE_TEXT ("Process_Manager_Test"),
584 mgr
.register_handler (new Exit_Handler ("specific"),
587 ACE_Time_Value
how_long (10);
589 ACE_Reactor::instance ()->run_reactor_event_loop (how_long
);
591 ACE_DEBUG ((LM_DEBUG
,
592 ACE_TEXT ("(%P) Reactor loop done!\n") ));
594 size_t const nr_procs
= mgr
.managed ();
596 ACE_ERROR ((LM_ERROR
,
597 ACE_TEXT ("(%P) %d processes left in manager\n"),
599 #endif /* defined (ACE_WIN32) */
600 #endif // ACE_HAS_PROCESS_SPAWN