2 //=============================================================================
6 * This is a simple test to illustrate OS thread preemption. One
7 * ore more high priority threads periodically (every half
8 * second, by default) reads the clock. They use select () to
9 * block for that duration. Meanwhile, a low priority thread
10 * continually chews up the CPU. Without preemption, the high
11 * priority thread won't have a chance to read the clock in a
14 * At the end of the test, the actual clock read intervals by the
15 * high priority task(s) are printed out. With proper
16 * preemption, the intervals should correspond to the requested
17 * clock read interval.
19 * There is a -y option for the low priority thread to periodically
20 * yield. It shouldn't be necessary to use that option, if preemption
21 * is supported. It's a handy option for testing.
23 * @author David L. Levine
25 //=============================================================================
28 #include "ace/OS_main.h"
31 #include "ace/Sched_Params.h"
32 #include "ace/Get_Opt.h"
33 #include "ace/OS_NS_sys_select.h"
34 #include "ace/OS_NS_time.h"
35 #include "ace/OS_NS_errno.h"
36 #include "ace/OS_NS_unistd.h"
38 #if defined (ACE_HAS_THREADS) || ! defined (ACE_LACKS_FORK)
40 #if defined (ACE_HAS_STHREADS)
41 # include <sys/lwp.h> /* for _lwp_self () */
42 #endif /* ACE_HAS_STHREADS */
44 static const char usage
[] = "[-? |\n"
45 #if defined (ACE_HAS_THREADS)
46 " [-f use fork instead of spawn]\n"
47 #endif /* ACE_HAS_THREADS */
48 " [-h <high pri iterations, default 10>]\n"
49 " [-l <low pri iterations, default 25000>]\n"
50 " [-n <high priority threads, default 1>]\n"
51 " [-p <read period, default 500000 usec>]\n"
52 " [-y to yield the low priority thread]]\n";
54 // Configuration options.
55 #if defined (ACE_HAS_THREADS)
57 #else /* ! ACE_HAS_THREADS */
59 #endif /* ! ACE_HAS_THREADS */
60 u_int high_iterations
= 10;
61 u_int high_priority_tasks
= 1;
62 u_int low_iterations
= 25000; /* Big enough to keep the low priority task
63 cranking for a while */
64 u_long read_period
= 500000; /* usec */
67 // To permit calculation of relative times.
68 ACE_hrtime_t starttime
;
71 ///////////////////////////////////////////////////////////////////////////////
72 ///////////////////////////////////////////////////////////////////////////////
73 // class High_Priority_Task
74 ///////////////////////////////////////////////////////////////////////////////
75 ///////////////////////////////////////////////////////////////////////////////
77 class High_Priority_Task
: public ACE_Task
<ACE_SYNCH
>
80 High_Priority_Task (void);
81 ~High_Priority_Task (void);
83 //FUZZ: disable check_for_lack_ACE_OS
85 //FUZZ: enable check_for_lack_ACE_OS
88 int done () const { return done_
; }
89 void print_times () const;
97 High_Priority_Task::High_Priority_Task (void)
98 : ACE_Task
<ACE_SYNCH
> (ACE_Thread_Manager::instance ()),
99 priority_ (ACE_Sched_Params::next_priority (
101 ACE_Sched_Params::priority_min (ACE_SCHED_FIFO
,
106 ACE_NEW (time_
, u_long
[high_iterations
]);
109 High_Priority_Task::~High_Priority_Task (void)
116 High_Priority_Task::open (void *)
120 ACE_hthread_t thr_handle
;
121 ACE_Thread::self (thr_handle
);
123 if (ACE_Thread::setprio (thr_handle
, priority_
) == -1)
124 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "setprio failed"), -1);
130 long flags
= THR_BOUND
| THR_NEW_LWP
| THR_SCHED_FIFO
;
132 // Become an active object.
133 if (this->activate (flags
, 1, 0, this->priority_
) == -1)
135 ACE_DEBUG ((LM_ERROR
, "(%P|%t) task activation failed, exiting!\n"));
144 High_Priority_Task::svc (void)
146 ACE_hthread_t thr_handle
;
147 ACE_Thread::self (thr_handle
);
150 if (ACE_Thread::getprio (thr_handle
, prio
) == -1)
151 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "getprio failed"), -1);
153 #if defined (ACE_HAS_STHREADS)
154 ACE_DEBUG ((LM_DEBUG
, "(%P|%u|%t) High: prio = %d, priority_ = %d\n",
155 _lwp_self (), prio
, this->priority_
));
156 #else /* ! ACE_HAS_STHREADS */
157 ACE_DEBUG ((LM_DEBUG
, "(%P|%t) High: prio = %d, priority_ = %d\n",
158 prio
, this->priority_
));
159 #endif /* ! ACE_HAS_STHREADS */
160 ACE_ASSERT (this->priority_
== prio
);
162 ACE_Time_Value
pause (0, read_period
);
164 for (u_int i
= 0; i
< high_iterations
; ++i
)
166 time_
[i
] = (u_long
) ((ACE_OS::gethrtime () - starttime
)/
167 (ACE_UINT32
) 1000000u);
168 ACE_OS::select (0, 0, 0, 0, &pause
);
177 High_Priority_Task::print_times () const
179 for (u_int i
= 0; i
< high_iterations
; ++i
)
181 ACE_DEBUG ((LM_INFO
, " iteration %u, time %u msec\n",
187 ///////////////////////////////////////////////////////////////////////////////
188 ///////////////////////////////////////////////////////////////////////////////
189 // class Low_Priority_Task
190 ///////////////////////////////////////////////////////////////////////////////
191 ///////////////////////////////////////////////////////////////////////////////
193 class Low_Priority_Task
: public ACE_Task
<ACE_SYNCH
>
196 Low_Priority_Task (const High_Priority_Task
&);
198 //FUZZ: disable check_for_lack_ACE_OS
200 //FUZZ: enable check_for_lack_ACE_OS
206 const High_Priority_Task
&high_priority_task_
;
209 Low_Priority_Task::Low_Priority_Task (
210 const High_Priority_Task
&high_priority_task
)
211 : ACE_Task
<ACE_SYNCH
> (ACE_Thread_Manager::instance ()),
212 priority_ (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO
,
214 high_priority_task_ (high_priority_task
)
219 Low_Priority_Task::open (void *)
223 ACE_hthread_t thr_handle
;
224 ACE_Thread::self (thr_handle
);
226 if (ACE_Thread::setprio (thr_handle
, priority_
) == -1)
227 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "setprio failed"), -1);
233 long flags
= THR_BOUND
| THR_NEW_LWP
| THR_SCHED_FIFO
;
235 // Become an active object.
236 if (this->activate (flags
, 1, 0, this->priority_
) == -1)
237 ACE_ERROR ((LM_ERROR
,
238 "(%P|%t) task activation failed, exiting!\n%a",
245 Low_Priority_Task::svc (void)
247 ACE_hthread_t thr_handle
;
248 ACE_Thread::self (thr_handle
);
251 if (ACE_Thread::getprio (thr_handle
, prio
) == -1)
252 ACE_ERROR_RETURN ((LM_ERROR
, "%p\n", "getprio failed"), -1);
254 #if defined (ACE_HAS_STHREADS)
255 ACE_DEBUG ((LM_DEBUG
, "(%P|%u|%t) Low: prio = %d, priority_ = %d\n",
256 _lwp_self (), prio
, this->priority_
));
257 #else /* ! ACE_HAS_STHREADS */
258 ACE_DEBUG ((LM_DEBUG
, "(%P|%t) Low: prio = %d, priority_ = %d\n",
259 prio
, this->priority_
));
260 #endif /* ! ACE_HAS_STHREADS */
261 ACE_ASSERT (this->priority_
== prio
);
263 u_long iterations
= 0;
265 // Chew up CPU for the entire interval.
267 ! high_priority_task_
.done () && iterations
< low_iterations
;
270 u_long n
= 1279ul; /* Takes about 40.2 usecs on a 168 MHz Ultra2. */
272 2ul /* min_factor */,
273 n
/2 /* max_factor */);
276 ACE_OS::thr_yield ();
279 ACE_DEBUG ((LM_INFO
, "Low completed %u iterations\n", iterations
));
285 ///////////////////////////////////////////////////////////////////////////////
286 ///////////////////////////////////////////////////////////////////////////////
287 // function get_options
288 ///////////////////////////////////////////////////////////////////////////////
289 ///////////////////////////////////////////////////////////////////////////////
292 get_options (int argc
, ACE_TCHAR
*argv
[])
294 ACE_Get_Opt
get_opt (argc
, argv
, ACE_TEXT("fh:l:n:p:y?"));
296 while ((opt
= get_opt ()) != EOF
) {
302 if (ACE_OS::atoi (get_opt
.opt_arg ()) >= 2)
303 high_iterations
= ACE_OS::atoi (get_opt
.opt_arg ());
305 ACE_ERROR_RETURN ((LM_ERROR
, "%n: high iterations must be >= 2\n"),
309 if (ACE_OS::atoi (get_opt
.opt_arg ()) >= 2)
310 low_iterations
= ACE_OS::atoi (get_opt
.opt_arg ());
312 ACE_ERROR_RETURN ((LM_ERROR
, "%n: low iterations must be >= 2\n"), -1);
315 if (ACE_OS::atoi (get_opt
.opt_arg ()) >= 1)
316 high_priority_tasks
= ACE_OS::atoi (get_opt
.opt_arg ());
318 ACE_ERROR_RETURN ((LM_ERROR
, "%n: number of high priority threads "
319 "must be >= 1\n"), -1);
322 if (ACE_OS::atoi (get_opt
.opt_arg ()) > 0)
323 read_period
= ACE_OS::atoi (get_opt
.opt_arg ());
325 ACE_ERROR_RETURN ((LM_ERROR
, "%n: read period > 0\n"), -1);
331 ACE_DEBUG ((LM_ERROR
, "usage: %n %s\n%a", usage
, 0));
334 ACE_ERROR_RETURN ((LM_ERROR
,
335 "%n: unknown arg, %c\nusage: %n %s\n", opt
, usage
),
340 switch (argc
- get_opt
.opt_ind ()) {
342 // OK, no non-flag arguments.
345 ACE_ERROR_RETURN ((LM_ERROR
,
346 "%n: too many arguments\nusage: %n %s\n", usage
),
353 #endif /* ACE_HAS_THREADS || ! ACE_LACKS_FORK */
356 ///////////////////////////////////////////////////////////////////////////////
357 ///////////////////////////////////////////////////////////////////////////////
359 ///////////////////////////////////////////////////////////////////////////////
360 ///////////////////////////////////////////////////////////////////////////////
363 ACE_TMAIN (int argc
, ACE_TCHAR
*argv
[])
365 ACE_LOG_MSG
->open (argv
[0] ? argv
[0] : ACE_TEXT("preempt"));
367 #if defined (ACE_HAS_THREADS) || !defined (ACE_LACKS_FORK)
371 if (get_options (argc
, argv
))
374 // Enable FIFO scheduling, e.g., RT scheduling class on Solaris.
375 if (ACE_OS::sched_params (
378 ACE_Sched_Params::priority_min (ACE_SCHED_FIFO
),
379 ACE_SCOPE_PROCESS
)) != 0)
381 if (ACE_OS::last_error () == EPERM
)
382 ACE_DEBUG ((LM_MAX
, "preempt: user is not superuser, "
383 "so remain in time-sharing class\n"));
385 ACE_ERROR_RETURN ((LM_ERROR
, "%n: ACE_OS::sched_params failed\n%a"),
389 High_Priority_Task
*high_priority_task
;
390 ACE_NEW_RETURN (high_priority_task
, High_Priority_Task
[high_priority_tasks
],
393 Low_Priority_Task
low_priority_task (high_priority_task
[0]);
397 ACE_DEBUG ((LM_DEBUG
,
398 "(%P|%t) main (), wait for threads to exit . . .\n"));
401 // Save the start time, so that deltas can be displayed later.
402 starttime
= ACE_OS::gethrtime ();
404 // Spawn the threads/processes . . .
408 switch ((child
= ACE_OS::fork (ACE_TEXT("preempt-low_priority_process"))))
411 ACE_ERROR ((LM_ERROR
, "%p\n%a", "fork failed"));
415 low_priority_task
.open (0);
418 default: // In parent
419 for (i
= 0; i
< high_priority_tasks
; ++i
)
421 high_priority_task
[i
].open (0);
428 for (i
= 0; i
< high_priority_tasks
; ++i
)
430 high_priority_task
[i
].open (0);
432 low_priority_task
.open (0);
434 #if defined (ACE_HAS_THREADS)
435 // Wait for all threads to exit.
436 ACE_Thread_Manager::instance ()->wait ();
437 #endif /* ACE_HAS_THREADS */
440 // Display the time deltas. They should be about a half second apart.
441 if (child
|| ! use_fork
)
443 for (i
= 0; i
< high_priority_tasks
; ++i
)
445 ACE_DEBUG ((LM_DEBUG
, "High priority task %u:\n", i
+ 1));
446 high_priority_task
[i
].print_times ();
450 delete [] high_priority_task
;
452 #else /* ! ACE_HAS_THREADS && ACE_LACKS_FORK */
453 ACE_UNUSED_ARG (argc
);
454 ACE_UNUSED_ARG (argv
);
455 ACE_ERROR ((LM_ERROR
, "threads and fork not supported on this platform\n"));
456 #endif /* ! ACE_HAS_THREADS && ACE_LACKS_FORK */