Cleanup Solaris support
[ACE_TAO.git] / ACE / performance-tests / Misc / preempt.cpp
blob20b70117e5ac038562fbe8af8981f85060aa9d1d
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 static const char usage [] = "[-? |\n"
41 #if defined (ACE_HAS_THREADS)
42 " [-f use fork instead of spawn]\n"
43 #endif /* ACE_HAS_THREADS */
44 " [-h <high pri iterations, default 10>]\n"
45 " [-l <low pri iterations, default 25000>]\n"
46 " [-n <high priority threads, default 1>]\n"
47 " [-p <read period, default 500000 usec>]\n"
48 " [-y to yield the low priority thread]]\n";
50 // Configuration options.
51 #if defined (ACE_HAS_THREADS)
52 u_int use_fork = 0;
53 #else /* ! ACE_HAS_THREADS */
54 u_int use_fork = 1;
55 #endif /* ! ACE_HAS_THREADS */
56 u_int high_iterations = 10;
57 u_int high_priority_tasks = 1;
58 u_int low_iterations = 25000; /* Big enough to keep the low priority task
59 cranking for a while */
60 u_long read_period = 500000; /* usec */
61 u_int low_yield = 0;
63 // To permit calculation of relative times.
64 ACE_hrtime_t starttime;
67 ///////////////////////////////////////////////////////////////////////////////
68 ///////////////////////////////////////////////////////////////////////////////
69 // class High_Priority_Task
70 ///////////////////////////////////////////////////////////////////////////////
71 ///////////////////////////////////////////////////////////////////////////////
73 class High_Priority_Task : public ACE_Task<ACE_SYNCH>
75 public:
76 High_Priority_Task ();
77 ~High_Priority_Task ();
79 //FUZZ: disable check_for_lack_ACE_OS
80 int open (void *);
81 //FUZZ: enable check_for_lack_ACE_OS
83 int svc ();
84 int done () const { return done_; }
85 void print_times () const;
87 private:
88 int priority_;
89 int done_;
90 u_long *time_;
93 High_Priority_Task::High_Priority_Task ()
94 : ACE_Task<ACE_SYNCH> (ACE_Thread_Manager::instance ()),
95 priority_ (ACE_Sched_Params::next_priority (
96 ACE_SCHED_FIFO,
97 ACE_Sched_Params::priority_min (ACE_SCHED_FIFO,
98 ACE_SCOPE_THREAD),
99 ACE_SCOPE_THREAD)),
100 done_ (0)
102 ACE_NEW (time_, u_long[high_iterations]);
105 High_Priority_Task::~High_Priority_Task ()
107 delete [] time_;
108 time_ = 0;
112 High_Priority_Task::open (void *)
114 if (use_fork == 1)
116 ACE_hthread_t thr_handle;
117 ACE_Thread::self (thr_handle);
119 if (ACE_Thread::setprio (thr_handle, priority_) == -1)
120 ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "setprio failed"), -1);
122 return svc ();
124 else
126 long flags = THR_BOUND | THR_NEW_LWP | THR_SCHED_FIFO;
128 // Become an active object.
129 if (this->activate (flags, 1, 0, this->priority_) == -1)
131 ACE_DEBUG ((LM_ERROR, "(%P|%t) task activation failed, exiting!\n"));
132 ACE_OS::exit (1);
135 return 0;
140 High_Priority_Task::svc ()
142 ACE_hthread_t thr_handle;
143 ACE_Thread::self (thr_handle);
144 int prio;
146 if (ACE_Thread::getprio (thr_handle, prio) == -1)
147 ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getprio failed"), -1);
149 ACE_DEBUG ((LM_DEBUG, "(%P|%t) High: prio = %d, priority_ = %d\n",
150 prio, this->priority_));
152 ACE_ASSERT (this->priority_ == prio);
154 ACE_Time_Value pause (0, read_period);
156 for (u_int i = 0; i < high_iterations; ++i)
158 time_ [i] = (u_long) ((ACE_OS::gethrtime () - starttime)/
159 (ACE_UINT32) 1000000u);
160 ACE_OS::select (0, 0, 0, 0, &pause);
163 done_ = 1;
165 return 0;
168 void
169 High_Priority_Task::print_times () const
171 for (u_int i = 0; i < high_iterations; ++i)
173 ACE_DEBUG ((LM_INFO, " iteration %u, time %u msec\n",
174 i, time_ [i]));
179 ///////////////////////////////////////////////////////////////////////////////
180 ///////////////////////////////////////////////////////////////////////////////
181 // class Low_Priority_Task
182 ///////////////////////////////////////////////////////////////////////////////
183 ///////////////////////////////////////////////////////////////////////////////
185 class Low_Priority_Task : public ACE_Task<ACE_SYNCH>
187 public:
188 Low_Priority_Task (const High_Priority_Task &);
190 //FUZZ: disable check_for_lack_ACE_OS
191 int open (void *);
192 //FUZZ: enable check_for_lack_ACE_OS
194 int svc ();
196 private:
197 int priority_;
198 const High_Priority_Task &high_priority_task_;
201 Low_Priority_Task::Low_Priority_Task (
202 const High_Priority_Task &high_priority_task)
203 : ACE_Task<ACE_SYNCH> (ACE_Thread_Manager::instance ()),
204 priority_ (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO,
205 ACE_SCOPE_THREAD)),
206 high_priority_task_ (high_priority_task)
211 Low_Priority_Task::open (void *)
213 if (use_fork == 1)
215 ACE_hthread_t thr_handle;
216 ACE_Thread::self (thr_handle);
218 if (ACE_Thread::setprio (thr_handle, priority_) == -1)
219 ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "setprio failed"), -1);
221 return svc ();
223 else
225 long flags = THR_BOUND | THR_NEW_LWP | THR_SCHED_FIFO;
227 // Become an active object.
228 if (this->activate (flags, 1, 0, this->priority_) == -1)
229 ACE_ERROR ((LM_ERROR,
230 "(%P|%t) task activation failed, exiting!\n%a",
231 -1));
232 return 0;
237 Low_Priority_Task::svc ()
239 ACE_hthread_t thr_handle;
240 ACE_Thread::self (thr_handle);
241 int prio;
243 if (ACE_Thread::getprio (thr_handle, prio) == -1)
244 ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getprio failed"), -1);
246 ACE_DEBUG ((LM_DEBUG, "(%P|%t) Low: prio = %d, priority_ = %d\n",
247 prio, this->priority_));
249 ACE_ASSERT (this->priority_ == prio);
251 u_long iterations = 0;
253 // Chew up CPU for the entire interval.
254 for (;
255 ! high_priority_task_.done () && iterations < low_iterations;
256 ++iterations)
258 u_long n = 1279ul; /* Takes about 40.2 usecs on a 168 MHz Ultra2. */
259 ACE::is_prime (n,
260 2ul /* min_factor */,
261 n/2 /* max_factor */);
263 if (low_yield)
264 ACE_OS::thr_yield ();
267 ACE_DEBUG ((LM_INFO, "Low completed %u iterations\n", iterations));
269 return 0;
273 ///////////////////////////////////////////////////////////////////////////////
274 ///////////////////////////////////////////////////////////////////////////////
275 // function get_options
276 ///////////////////////////////////////////////////////////////////////////////
277 ///////////////////////////////////////////////////////////////////////////////
279 static int
280 get_options (int argc, ACE_TCHAR *argv[])
282 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("fh:l:n:p:y?"));
283 int opt;
284 while ((opt = get_opt ()) != EOF) {
285 switch (opt) {
286 case 'f':
287 use_fork = 1;
288 break;
289 case 'h':
290 if (ACE_OS::atoi (get_opt.opt_arg ()) >= 2)
291 high_iterations = ACE_OS::atoi (get_opt.opt_arg ());
292 else
293 ACE_ERROR_RETURN ((LM_ERROR, "%n: high iterations must be >= 2\n"),
294 -1);
295 break;
296 case 'l':
297 if (ACE_OS::atoi (get_opt.opt_arg ()) >= 2)
298 low_iterations = ACE_OS::atoi (get_opt.opt_arg ());
299 else
300 ACE_ERROR_RETURN ((LM_ERROR, "%n: low iterations must be >= 2\n"), -1);
301 break;
302 case 'n':
303 if (ACE_OS::atoi (get_opt.opt_arg ()) >= 1)
304 high_priority_tasks = ACE_OS::atoi (get_opt.opt_arg ());
305 else
306 ACE_ERROR_RETURN ((LM_ERROR, "%n: number of high priority threads "
307 "must be >= 1\n"), -1);
308 break;
309 case 'p':
310 if (ACE_OS::atoi (get_opt.opt_arg ()) > 0)
311 read_period = ACE_OS::atoi (get_opt.opt_arg ());
312 else
313 ACE_ERROR_RETURN ((LM_ERROR, "%n: read period > 0\n"), -1);
314 break;
315 case 'y':
316 low_yield = 1;
317 break;
318 case '?':
319 ACE_DEBUG ((LM_ERROR, "usage: %n %s\n%a", usage, 0));
320 break;
321 default:
322 ACE_ERROR_RETURN ((LM_ERROR,
323 "%n: unknown arg, %c\nusage: %n %s\n", opt, usage),
324 -1);
328 switch (argc - get_opt.opt_ind ()) {
329 case 0:
330 // OK, no non-flag arguments.
331 break;
332 default:
333 ACE_ERROR_RETURN ((LM_ERROR,
334 "%n: too many arguments\nusage: %n %s\n", usage),
335 -1);
338 return 0;
341 #endif /* ACE_HAS_THREADS || ! ACE_LACKS_FORK */
344 ///////////////////////////////////////////////////////////////////////////////
345 ///////////////////////////////////////////////////////////////////////////////
346 // function main
347 ///////////////////////////////////////////////////////////////////////////////
348 ///////////////////////////////////////////////////////////////////////////////
351 ACE_TMAIN (int argc, ACE_TCHAR *argv[])
353 ACE_LOG_MSG->open (argv[0] ? argv[0] : ACE_TEXT("preempt"));
355 #if defined (ACE_HAS_THREADS) || !defined (ACE_LACKS_FORK)
357 u_int i;
359 if (get_options (argc, argv))
360 ACE_OS::exit (-1);
362 // Enable FIFO scheduling
363 if (ACE_OS::sched_params (
364 ACE_Sched_Params (
365 ACE_SCHED_FIFO,
366 ACE_Sched_Params::priority_min (ACE_SCHED_FIFO),
367 ACE_SCOPE_PROCESS)) != 0)
369 if (ACE_OS::last_error () == EPERM)
370 ACE_DEBUG ((LM_MAX, "preempt: user is not superuser, "
371 "so remain in time-sharing class\n"));
372 else
373 ACE_ERROR_RETURN ((LM_ERROR, "%n: ACE_OS::sched_params failed\n%a"),
374 -1);
377 High_Priority_Task *high_priority_task;
378 ACE_NEW_RETURN (high_priority_task, High_Priority_Task [high_priority_tasks],
381 Low_Priority_Task low_priority_task (high_priority_task[0]);
383 if (! use_fork)
385 ACE_DEBUG ((LM_DEBUG,
386 "(%P|%t) main (), wait for threads to exit . . .\n"));
389 // Save the start time, so that deltas can be displayed later.
390 starttime = ACE_OS::gethrtime ();
392 // Spawn the threads/processes . . .
393 pid_t child = 0;
394 if (use_fork == 1)
396 switch ((child = ACE_OS::fork (ACE_TEXT("preempt-low_priority_process"))))
398 case -1:
399 ACE_ERROR ((LM_ERROR, "%p\n%a", "fork failed"));
400 return -1;
401 case 0: // In child
403 low_priority_task.open (0);
404 break;
406 default: // In parent
407 for (i = 0; i < high_priority_tasks; ++i)
409 high_priority_task[i].open (0);
411 break;
414 else
416 for (i = 0; i < high_priority_tasks; ++i)
418 high_priority_task[i].open (0);
420 low_priority_task.open (0);
422 #if defined (ACE_HAS_THREADS)
423 // Wait for all threads to exit.
424 ACE_Thread_Manager::instance ()->wait ();
425 #endif /* ACE_HAS_THREADS */
428 // Display the time deltas. They should be about a half second apart.
429 if (child || ! use_fork)
431 for (i = 0; i < high_priority_tasks; ++i)
433 ACE_DEBUG ((LM_DEBUG, "High priority task %u:\n", i + 1));
434 high_priority_task[i].print_times ();
438 delete [] high_priority_task;
440 #else /* ! ACE_HAS_THREADS && ACE_LACKS_FORK */
441 ACE_UNUSED_ARG (argc);
442 ACE_UNUSED_ARG (argv);
443 ACE_ERROR ((LM_ERROR, "threads and fork not supported on this platform\n"));
444 #endif /* ! ACE_HAS_THREADS && ACE_LACKS_FORK */
446 return 0;