2 //=============================================================================
4 * @file Thread_Manager_Test.cpp
6 * This program tests the group management mechanisms provided by
7 * the <ACE_Thread_Manager>, including the group signal handling,
8 * group suspension and resumption, and cooperative thread
9 * cancellation mechanisms.
11 * @author Prashant Jain <pjain@cs.wustl.edu> and Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
13 //=============================================================================
15 #include "test_config.h"
16 #include "ace/Thread_Manager.h"
17 #include "ace/Signal.h"
19 #include "ace/OS_NS_unistd.h"
20 #include "ace/OS_NS_sys_time.h"
22 #if defined (ACE_HAS_THREADS)
23 #include "ace/Barrier.h"
25 // Each thread keeps track of whether it has been signalled by using a
26 // global array. It must be dynamically allocated to allow sizing at
27 // runtime, based on the number of threads.
28 static ACE_thread_t
*signalled
= 0;
29 static size_t n_threads
= ACE_MAX_THREADS
;
31 // Helper function that looks for an existing entry in the signalled
32 // array. Also finds the position of the first unused entry in the
33 // array, and updates if requested with the t_id.
35 been_signalled (const ACE_thread_t t_id
,
36 const u_int update
= 0)
38 u_int unused_slot
= n_threads
;
40 for (u_int i
= 0; i
< n_threads
; ++i
)
42 if (ACE_OS::thr_equal (signalled
[i
], t_id
))
47 unused_slot
== n_threads
&&
48 ACE_OS::thr_equal (signalled
[i
],
53 if (update
&& unused_slot
< n_threads
)
54 // Update the array using the first unused_slot.
55 signalled
[unused_slot
] = t_id
;
60 // Synchronize starts of threads, so that they all start before the
61 // main thread cancels them. To avoid creating a static object, it is
62 // dynamically allocated, before spawning any threads.
63 static ACE_Barrier
*thread_start
= 0;
66 handler (int /* signum */)
70 // No printout here, to be safe. Signal handlers must not
71 // acquire locks, etc.
72 const ACE_thread_t t_id
= ACE_OS::thr_self ();
74 // Update the signalled indication.
75 (void) been_signalled (t_id
, 1u /* update */);
80 worker (int iterations
)
82 #if !defined (ACE_LACKS_UNIX_SIGNALS)
83 // Cache this thread's ID.
84 const ACE_thread_t t_id
= ACE_OS::thr_self ();
85 #endif /* ! ACE_LACKS_UNIX_SIGNAL */
87 ACE_Thread_Manager
*thr_mgr
= ACE_Thread_Manager::instance ();
89 // After setting up the signal catcher, block on the start barrier.
90 thread_start
->wait ();
93 ACE_TEXT ("(%t) worker starting loop\n")));
95 for (int i
= 0; i
< iterations
; i
++)
99 #if !defined (ACE_LACKS_UNIX_SIGNALS)
100 if (been_signalled (t_id
))
102 ACE_DEBUG ((LM_DEBUG
,
103 ACE_TEXT ("(%t) had received signal\n")));
105 // Only test for cancellation after we've been signaled,
106 // to avoid race conditions for suspend() and resume().
107 if (thr_mgr
->testcancel (ACE_Thread::self ()) != 0)
110 // Test out the ACE_OS::thr_id() method.
111 ACE_OS::thr_id (thr_id
, sizeof thr_id
);
112 ACE_DEBUG ((LM_DEBUG
,
113 ACE_TEXT ("(%C) has been cancelled ")
114 ACE_TEXT ("before iteration %d!\n"),
121 if (thr_mgr
->testcancel (ACE_Thread::self ()) != 0)
124 // Test out the ACE_OS::thr_id() method.
125 ACE_OS::thr_id (thr_id
, sizeof thr_id
);
126 ACE_DEBUG ((LM_DEBUG
,
127 ACE_TEXT ("(%C) has been cancelled ")
128 ACE_TEXT ("before iteration %d!\n"),
133 #endif /* ! ACE_LACKS_UNIX_SIGNAL */
138 // Destructor removes thread from Thread_Manager.
142 static const int DEFAULT_ITERATIONS
= 10000;
144 // Define a ACE_Task that will serve in the tests related to tasks.
146 class ThrMgr_Task
: public ACE_Task_Base
{
148 ThrMgr_Task (ACE_Thread_Manager
*);
155 int ThrMgr_Task::errors
= 0;
157 // Just be sure to set the ACE_Thread_Manager correctly.
158 ThrMgr_Task::ThrMgr_Task (ACE_Thread_Manager
*mgr
)
159 : ACE_Task_Base (mgr
)
163 // svc just waits til it's been cancelled, then exits.
167 ACE_DEBUG ((LM_DEBUG
,
168 ACE_TEXT ("Task 0x%x, thread %t waiting to be cancelled\n"),
170 ACE_thread_t me
= ACE_Thread::self ();
171 for (int i
= 0; i
< 30 && !this->thr_mgr ()->testcancel (me
); ++i
)
174 if (this->thr_mgr ()->testcancel (me
))
176 ACE_DEBUG ((LM_DEBUG
,
177 ACE_TEXT ("Task 0x%x, thread %t cancelled; exiting\n"),
182 ACE_ERROR ((LM_ERROR
,
183 ACE_TEXT ("Task 0x%x, thread %t was not cancelled\n"),
185 ++ThrMgr_Task::errors
;
191 // This function tests the task-based record keeping functions.
193 test_task_record_keeping (ACE_Thread_Manager
*mgr
)
197 ThrMgr_Task
t1 (mgr
), t2 (mgr
);
198 int t1_grp (20), t2_grp (30);
200 // Start two tasks, with multiple threads per task. Make sure that
201 // task_all_list() works.
202 if (-1 == t1
.activate (THR_JOINABLE
, 2, 0,
203 ACE_DEFAULT_THREAD_PRIORITY
, t1_grp
))
204 ACE_ERROR_RETURN ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("activate")), 1);
205 if (-1 == t2
.activate (THR_JOINABLE
, 3, 0,
206 ACE_DEFAULT_THREAD_PRIORITY
, t2_grp
))
207 ACE_ERROR_RETURN ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("activate")), 1);
209 ACE_Task_Base
*task_list
[10];
210 int num_tasks
= mgr
->task_all_list (task_list
, 10);
213 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("Expected 2 tasks; got %d\n"),
219 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("Got %d tasks - correct\n"), num_tasks
));
220 if (((task_list
[0] == &t1
&& task_list
[1] == &t2
)
221 || (task_list
[1] == &t1
&& task_list
[0] == &t2
))
222 && task_list
[0] != task_list
[1])
223 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("The Task IDs are correct\n")));
225 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("But Task ID values are wrong!\n")));
227 ACE_DEBUG ((LM_DEBUG
, "Canceling grp %d\n", t1_grp
));
228 if (-1 == mgr
->cancel_grp (t1_grp
))
229 ACE_ERROR_RETURN ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("cancel_grp")),
231 ACE_DEBUG ((LM_DEBUG
, "Canceling grp %d\n", t2_grp
));
232 if (-1 == mgr
->cancel_grp (t2_grp
))
233 ACE_ERROR_RETURN ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("cancel_grp")),
237 if (ThrMgr_Task::errors
> 0 && status
== 0)
243 #endif /* ACE_HAS_THREADS */
246 run_main (int, ACE_TCHAR
*[])
248 ACE_START_TEST (ACE_TEXT ("Thread_Manager_Test"));
251 #if defined (ACE_HAS_THREADS)
252 size_t n_iterations
= DEFAULT_ITERATIONS
;
256 // Dynamically allocate signalled so that we can control when it
257 // gets deleted. Specifically, we need to delete it before the main
258 // thread's TSS is cleaned up.
259 ACE_NEW_RETURN (signalled
,
260 ACE_thread_t
[n_threads
],
262 // Initialize each ACE_thread_t to avoid Purify UMR's.
263 for (i
= 0; i
< n_threads
; ++i
)
264 signalled
[i
] = ACE_OS::NULL_thread
;
266 // And similarly, dynamically allocate the thread_start barrier.
267 ACE_NEW_RETURN (thread_start
,
268 ACE_Barrier (n_threads
+ 1),
271 // Register a signal handler.
272 ACE_Sig_Action
sa ((ACE_SignalHandler
) handler
, SIGINT
);
275 ACE_Thread_Manager
*thr_mgr
= ACE_Thread_Manager::instance ();
278 // Assign thread (VxWorks task) names to test that feature.
279 ACE_hthread_t
*thread_name
= 0;
280 ACE_NEW_RETURN (thread_name
,
281 ACE_hthread_t
[n_threads
],
284 // And test the ability to specify stack size.
285 size_t *stack_size
= 0;
286 ACE_NEW_RETURN (stack_size
,
290 for (i
= 0; i
< n_threads
; ++i
)
292 if (i
< n_threads
- 1)
294 ACE_NEW_RETURN (thread_name
[i
], char[32], -1);
295 ACE_OS::snprintf (thread_name
[i
], 32, ACE_TEXT ("thread%u"), i
);
298 // Pass an ACE_thread_t pointer of 0 for the last thread name.
299 thread_name
[n_threads
- 1] = 0;
301 stack_size
[i
] = 40000;
303 #endif /* ACE_HAS_VXTHREADS */
305 int grp_id
= thr_mgr
->spawn_n
308 (ACE_THR_FUNC
) worker
,
309 reinterpret_cast <void *> (n_iterations
),
311 , ACE_DEFAULT_THREAD_PRIORITY
318 #endif /* ACE_HAS_VXTHREADS */
321 ACE_TEST_ASSERT (grp_id
!= -1);
322 thread_start
->wait ();
324 // Wait for 1 second and then suspend every thread in the group.
326 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT ("(%t) suspending group\n")));
327 if (thr_mgr
->suspend_grp (grp_id
) == -1)
329 // Pthreads w/o UNIX 98 extensions doesn't support suspend/resume,
330 // so it's allowed to ENOTSUP as long as the config indicates this.
331 if (errno
== ENOTSUP
)
333 #if defined (ACE_HAS_PTHREADS) && (defined (ACE_HAS_PTHREAD_SUSPEND) || \
334 defined (ACE_HAS_PTHREAD_SUSPEND_NP))
335 ACE_ERROR ((LM_ERROR
,
336 ACE_TEXT ("suspend_grp: ENOTSUP but config ")
337 ACE_TEXT ("says it should work.\n")));
340 ACE_TEXT (" OK: suspend_grp isn't supported with ")
341 ACE_TEXT ("Pthreads\n")));
342 #endif /* ACE_HAS_PTHREADS && should be able to suspend */
346 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("suspend_grp")));
350 // Wait for 1 more second and then resume every thread in the
352 ACE_OS::sleep (ACE_Time_Value (1));
354 ACE_DEBUG ((LM_DEBUG
,
355 ACE_TEXT ("(%t) resuming group\n")));
357 if (thr_mgr
->resume_grp (grp_id
) == -1)
359 // Pthreads w/o UNIX 98 extensions doesn't support suspend/resume,
360 // so it's allowed to ENOTSUP as long as the config indicates this.
361 if (errno
== ENOTSUP
)
363 #if defined (ACE_HAS_PTHREADS) && (defined (ACE_HAS_PTHREAD_CONTINUE) || \
364 defined (ACE_HAS_PTHREAD_CONTINUE_NP) || \
365 defined (ACE_HAS_PTHREAD_RESUME_NP))
366 ACE_ERROR ((LM_ERROR
,
367 ACE_TEXT ("resume_grp: ENOTSUP but config ")
368 ACE_TEXT ("says it should work.\n")));
371 ACE_TEXT (" OK: resume_grp isn't supported with ")
372 ACE_TEXT ("Pthreads\n")));
373 #endif /* ACE_HAS_PTHREADS && should be able to continue/resume */
377 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p\n"), ACE_TEXT ("resume_grp")));
381 // Wait for 1 more second and then send a SIGINT to every thread in
383 ACE_OS::sleep (ACE_Time_Value (1));
385 ACE_DEBUG ((LM_DEBUG
,
386 ACE_TEXT ("(%t) signaling group\n")));
388 #if defined (ACE_HAS_WTHREADS)
389 thr_mgr
->kill_grp (grp_id
,
391 #elif !defined (ACE_HAS_PTHREADS_DRAFT4) && !defined(ACE_LACKS_PTHREAD_KILL)
392 ACE_TEST_ASSERT (thr_mgr
->kill_grp (grp_id
,
395 if (thr_mgr
->kill_grp (grp_id
,
397 ACE_TEST_ASSERT (errno
== ENOTSUP
);
398 #endif /* ACE_HAS_WTHREADS */
400 // Wait and then cancel all the threads.
401 ACE_OS::sleep (ACE_Time_Value (1));
403 ACE_DEBUG ((LM_DEBUG
,
404 ACE_TEXT ("(%t) cancelling group\n")));
406 ACE_TEST_ASSERT (thr_mgr
->cancel_grp (grp_id
) != -1);
408 // Perform a barrier wait until all the threads have shut down.
409 // But, wait for a limited time, just in case.
410 ACE_Time_Value
const max_wait (600);
411 ACE_Time_Value
const wait_time (ACE_OS::gettimeofday () + max_wait
);
412 if (thr_mgr
->wait (&wait_time
) == -1)
415 ACE_ERROR ((LM_ERROR
,
416 ACE_TEXT ("maximum wait time of %d msec exceeded\n"),
419 ACE_ERROR ((LM_ERROR
,
420 ACE_TEXT ("%p\n"), ACE_TEXT ("wait")));
424 ACE_DEBUG ((LM_DEBUG
,
425 ACE_TEXT ("(%t) main thread finished\n")));
428 for (i
= 0; i
< n_threads
- 1; ++i
)
430 delete [] thread_name
[i
];
431 // Don't delete the last thread_name, because it points to the
432 // name in the TCB. It was initially 0.
434 delete [] thread_name
;
435 delete [] stack_size
;
436 #endif /* ACE_HAS_VXTHREADS */
443 // Now test task record keeping
444 if (test_task_record_keeping (thr_mgr
) != 0)
449 ACE_TEXT ("threads not supported on this platform\n")));
450 #endif /* ACE_HAS_THREADS */