Update NEWS
[ACE_TAO.git] / ACE / performance-tests / Misc / preempt.cpp
blobf2331b979f9f647a7947a53629aedd9124bbf6cc
2 //=============================================================================
3 /**
4 * @file preempt.cpp
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
12 * timely manner.
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"
29 #include "ace/ACE.h"
30 #include "ace/Task.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)
56 u_int use_fork = 0;
57 #else /* ! ACE_HAS_THREADS */
58 u_int use_fork = 1;
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 */
65 u_int low_yield = 0;
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>
79 public:
80 High_Priority_Task (void);
81 ~High_Priority_Task (void);
83 //FUZZ: disable check_for_lack_ACE_OS
84 int open (void *);
85 //FUZZ: enable check_for_lack_ACE_OS
87 int svc (void);
88 int done () const { return done_; }
89 void print_times () const;
91 private:
92 int priority_;
93 int done_;
94 u_long *time_;
97 High_Priority_Task::High_Priority_Task (void)
98 : ACE_Task<ACE_SYNCH> (ACE_Thread_Manager::instance ()),
99 priority_ (ACE_Sched_Params::next_priority (
100 ACE_SCHED_FIFO,
101 ACE_Sched_Params::priority_min (ACE_SCHED_FIFO,
102 ACE_SCOPE_THREAD),
103 ACE_SCOPE_THREAD)),
104 done_ (0)
106 ACE_NEW (time_, u_long[high_iterations]);
109 High_Priority_Task::~High_Priority_Task (void)
111 delete [] time_;
112 time_ = 0;
116 High_Priority_Task::open (void *)
118 if (use_fork == 1)
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);
126 return svc ();
128 else
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"));
136 ACE_OS::exit (1);
139 return 0;
144 High_Priority_Task::svc (void)
146 ACE_hthread_t thr_handle;
147 ACE_Thread::self (thr_handle);
148 int prio;
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);
171 done_ = 1;
173 return 0;
176 void
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",
182 i, time_ [i]));
187 ///////////////////////////////////////////////////////////////////////////////
188 ///////////////////////////////////////////////////////////////////////////////
189 // class Low_Priority_Task
190 ///////////////////////////////////////////////////////////////////////////////
191 ///////////////////////////////////////////////////////////////////////////////
193 class Low_Priority_Task : public ACE_Task<ACE_SYNCH>
195 public:
196 Low_Priority_Task (const High_Priority_Task &);
198 //FUZZ: disable check_for_lack_ACE_OS
199 int open (void *);
200 //FUZZ: enable check_for_lack_ACE_OS
202 int svc (void);
204 private:
205 int priority_;
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,
213 ACE_SCOPE_THREAD)),
214 high_priority_task_ (high_priority_task)
219 Low_Priority_Task::open (void *)
221 if (use_fork == 1)
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);
229 return svc ();
231 else
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",
239 -1));
240 return 0;
245 Low_Priority_Task::svc (void)
247 ACE_hthread_t thr_handle;
248 ACE_Thread::self (thr_handle);
249 int prio;
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.
266 for (;
267 ! high_priority_task_.done () && iterations < low_iterations;
268 ++iterations)
270 u_long n = 1279ul; /* Takes about 40.2 usecs on a 168 MHz Ultra2. */
271 ACE::is_prime (n,
272 2ul /* min_factor */,
273 n/2 /* max_factor */);
275 if (low_yield)
276 ACE_OS::thr_yield ();
279 ACE_DEBUG ((LM_INFO, "Low completed %u iterations\n", iterations));
281 return 0;
285 ///////////////////////////////////////////////////////////////////////////////
286 ///////////////////////////////////////////////////////////////////////////////
287 // function get_options
288 ///////////////////////////////////////////////////////////////////////////////
289 ///////////////////////////////////////////////////////////////////////////////
291 static int
292 get_options (int argc, ACE_TCHAR *argv[])
294 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("fh:l:n:p:y?"));
295 int opt;
296 while ((opt = get_opt ()) != EOF) {
297 switch (opt) {
298 case 'f':
299 use_fork = 1;
300 break;
301 case 'h':
302 if (ACE_OS::atoi (get_opt.opt_arg ()) >= 2)
303 high_iterations = ACE_OS::atoi (get_opt.opt_arg ());
304 else
305 ACE_ERROR_RETURN ((LM_ERROR, "%n: high iterations must be >= 2\n"),
306 -1);
307 break;
308 case 'l':
309 if (ACE_OS::atoi (get_opt.opt_arg ()) >= 2)
310 low_iterations = ACE_OS::atoi (get_opt.opt_arg ());
311 else
312 ACE_ERROR_RETURN ((LM_ERROR, "%n: low iterations must be >= 2\n"), -1);
313 break;
314 case 'n':
315 if (ACE_OS::atoi (get_opt.opt_arg ()) >= 1)
316 high_priority_tasks = ACE_OS::atoi (get_opt.opt_arg ());
317 else
318 ACE_ERROR_RETURN ((LM_ERROR, "%n: number of high priority threads "
319 "must be >= 1\n"), -1);
320 break;
321 case 'p':
322 if (ACE_OS::atoi (get_opt.opt_arg ()) > 0)
323 read_period = ACE_OS::atoi (get_opt.opt_arg ());
324 else
325 ACE_ERROR_RETURN ((LM_ERROR, "%n: read period > 0\n"), -1);
326 break;
327 case 'y':
328 low_yield = 1;
329 break;
330 case '?':
331 ACE_DEBUG ((LM_ERROR, "usage: %n %s\n%a", usage, 0));
332 break;
333 default:
334 ACE_ERROR_RETURN ((LM_ERROR,
335 "%n: unknown arg, %c\nusage: %n %s\n", opt, usage),
336 -1);
340 switch (argc - get_opt.opt_ind ()) {
341 case 0:
342 // OK, no non-flag arguments.
343 break;
344 default:
345 ACE_ERROR_RETURN ((LM_ERROR,
346 "%n: too many arguments\nusage: %n %s\n", usage),
347 -1);
350 return 0;
353 #endif /* ACE_HAS_THREADS || ! ACE_LACKS_FORK */
356 ///////////////////////////////////////////////////////////////////////////////
357 ///////////////////////////////////////////////////////////////////////////////
358 // function main
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)
369 u_int i;
371 if (get_options (argc, argv))
372 ACE_OS::exit (-1);
374 // Enable FIFO scheduling, e.g., RT scheduling class on Solaris.
375 if (ACE_OS::sched_params (
376 ACE_Sched_Params (
377 ACE_SCHED_FIFO,
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"));
384 else
385 ACE_ERROR_RETURN ((LM_ERROR, "%n: ACE_OS::sched_params failed\n%a"),
386 -1);
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]);
395 if (! use_fork)
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 . . .
405 pid_t child = 0;
406 if (use_fork == 1)
408 switch ((child = ACE_OS::fork (ACE_TEXT("preempt-low_priority_process"))))
410 case -1:
411 ACE_ERROR ((LM_ERROR, "%p\n%a", "fork failed"));
412 return -1;
413 case 0: // In child
415 low_priority_task.open (0);
416 break;
418 default: // In parent
419 for (i = 0; i < high_priority_tasks; ++i)
421 high_priority_task[i].open (0);
423 break;
426 else
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 */
458 return 0;