comparison packages/kernel/current/tests/stress_threads.c @ 2:443894e2e912 ecos-v1_2_1-release

Block commit of eCos version 1.2.1
author jlarmour
date Tue, 11 May 1999 12:24:34 +0000
parents
children 1d7f19c9e4d1
comparison
equal deleted inserted replaced
1:72f549f0d891 2:443894e2e912
1 //==========================================================================
2 //
3 // stress_threads.cxx
4 //
5 // Basic thread stress test
6 //
7 //==========================================================================
8 //####COPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 // The contents of this file are subject to the Cygnus eCos Public License
12 // Version 1.0 (the "License"); you may not use this file except in
13 // compliance with the License. You may obtain a copy of the License at
14 // http://sourceware.cygnus.com/ecos
15 //
16 // Software distributed under the License is distributed on an "AS IS"
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
18 // License for the specific language governing rights and limitations under
19 // the License.
20 //
21 // The Original Code is eCos - Embedded Cygnus Operating System, released
22 // September 30, 1998.
23 //
24 // The Initial Developer of the Original Code is Cygnus. Portions created
25 // by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions. All Rights Reserved.
26 // -------------------------------------------
27 //
28 //####COPYRIGHTEND####
29 //==========================================================================
30 //#####DESCRIPTIONBEGIN####
31 //
32 // Author(s): rosalia
33 // Contributors: rosalia
34 // Date: 1999-04-13
35 // Description: Very simple thread stress test, with some memory
36 // allocation and alarm handling.
37 //####DESCRIPTIONEND####
38
39 #include <pkgconf/system.h>
40 #include <cyg/infra/testcase.h>
41
42 #include <cyg/hal/hal_arch.h>
43
44 #if defined(CYGPKG_KERNEL) && defined(CYGPKG_IO) && defined(CYGPKG_LIBC)
45
46 #include <pkgconf/kernel.h>
47 #include <pkgconf/libc.h>
48
49 #if defined(CYGFUN_KERNEL_API_C)
50
51 #include <cyg/kernel/kapi.h>
52
53 #ifdef CYGPKG_LIBC_STDIO
54
55 #include <stdio.h>
56 #include <stdlib.h>
57
58 #if defined(CYGPKG_LIBM)
59
60 #include <math.h>
61 #include <assert.h>
62
63 #if defined(CYGFUN_KERNEL_THREADS_TIMER)
64 #if defined(CYGPKG_LIBC_MALLOC)
65
66 /* if TIME_LIMIT is defined, it represents the number of seconds this
67 test should last; if it is undefined the test will go forever */
68 #define DEATH_TIME_LIMIT 15
69 /* #undef DEATH_TIME_LIMIT */
70
71 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL)
72 #define STACK_SIZE2 (8*1024 + CYGNUM_HAL_STACK_SIZE_TYPICAL)
73
74 #define N_CLIENTS 4
75 #define N_LISTENERS 4
76 #define MAX_HANDLERS 19
77
78 #if (CYGNUM_KERNEL_SCHED_PRIORITIES < (N_CLIENTS+N_LISTENERS+MAX_HANDLERS))
79 # error "not enough priorities available"
80 #endif
81
82 /* if we use the bitmap scheduler we must make sure we don't use the
83 same priority more than once, so we must store those already in use */
84 static char priority_in_use[N_CLIENTS+N_LISTENERS+MAX_HANDLERS];
85
86 /* now declare (and allocate space for) some kernel objects, like the
87 threads we will use */
88 cyg_thread client_thread_s[N_CLIENTS];
89 cyg_thread listener_thread_s[N_LISTENERS];
90 cyg_thread handler_thread_s[MAX_HANDLERS];
91
92 /* space for stacks for all threads */
93 char client_stack[N_CLIENTS][STACK_SIZE];
94 char listener_stack[N_LISTENERS][STACK_SIZE];
95 char handler_stack[MAX_HANDLERS][STACK_SIZE2];
96
97 /* now the handles for the threads */
98 cyg_handle_t clientH[N_CLIENTS];
99 cyg_handle_t listenerH[N_LISTENERS];
100 cyg_handle_t handlerH[MAX_HANDLERS];
101
102 #ifdef DEATH_TIME_LIMIT
103 /* how many client threads have been killed by the death handler */
104 int n_clients_killed = 0;
105 #endif /* DEATH_TIME_LIMIT */
106
107 /* and now variables for the procedure which is the thread */
108 cyg_thread_entry_t client_program, listener_program, handler_program;
109
110 /* a few mutexes used in the code */
111 cyg_mutex_t client_request_lock, handler_slot_lock, statistics_print_lock;
112
113 /* a global variable with which the client and server coordinate */
114 int client_makes_request = 0;
115
116 /* indicates that it's time to print out a report */
117 int time_to_report = 0;
118
119 /*** now application-specific variables ***/
120 /* an array that stores whether the handler threads are in use */
121 int handler_thread_in_use[MAX_HANDLERS];
122
123 /***** statistics-gathering variables *****/
124 struct s_statistics {
125 /* store the number of times each handler has been invoked */
126 unsigned long handler_invocation_histogram[MAX_HANDLERS];
127
128 /* store how many times malloc has been attempted and how many times
129 it has failed */
130 unsigned long malloc_tries, malloc_failures;
131
132 /* how many threads have been created */
133 unsigned long thread_creations, thread_exits;
134 };
135
136 struct s_statistics statistics;
137
138 /* some function prototypes; those with the sc_ prefix are
139 "statistics-collecting" versions of the cyg_ primitives */
140 void sc_thread_create(
141 cyg_addrword_t sched_info, /* scheduling info (eg pri) */
142 cyg_thread_entry_t *entry, /* entry point function */
143 cyg_addrword_t entry_data, /* entry data */
144 char *name, /* optional thread name */
145 void *stack_base, /* stack base, NULL = alloc */
146 cyg_ucount32 stack_size, /* stack size, 0 = default */
147 cyg_handle_t *handle, /* returned thread handle */
148 cyg_thread *thread /* put thread here */
149 );
150 void sc_thread_exit(void);
151
152 int get_handler_slot(cyg_handle_t current_threadH);
153 void perform_stressful_tasks(void);
154 void permute_array(char a[], int size, int seed);
155 void setup_death_alarm(cyg_addrword_t data, cyg_handle_t *deathHp,
156 cyg_alarm *death_alarm_p, int *killed_p);
157 void handle_death(cyg_handle_t deathH, cyg_handle_t alarmH);
158 void print_statistics(void);
159
160 /* we need to declare the alarm handling function (which is defined
161 below), so that we can pass it to cyg_alarm_initialize() */
162 cyg_alarm_t report_alarm_func, death_alarm_func;
163
164 /* handle and alarm for the report alarm */
165 cyg_handle_t report_alarmH, counterH, system_clockH;
166 cyg_alarm report_alarm;
167
168 /* we install our own startup routine which sets up threads */
169 void cyg_user_start(void)
170 {
171 int i;
172
173 CYG_TEST_INIT();
174 CYG_TEST_INFO("# Entering stress's cyg_user_start() function");
175
176 cyg_mutex_init(&client_request_lock);
177 cyg_mutex_init(&statistics_print_lock);
178
179 /* initialize statistics */
180 memset(&statistics, 0, sizeof(statistics));
181
182 /* initialize all handler threads to not be in use */
183 for (i = 0; i < MAX_HANDLERS; ++i) {
184 handler_thread_in_use[i] = 0;
185 }
186 for (i = 0; i < N_CLIENTS; ++i) {
187 int prio;
188 char thread_name[20];
189 sprintf(thread_name, "client-%02d", i);
190 prio = i;
191 sc_thread_create(prio, client_program, (cyg_addrword_t) i,
192 thread_name, (void *) client_stack[i], STACK_SIZE,
193 &(clientH[i]), &client_thread_s[i]);
194 priority_in_use[prio] = 1;
195 }
196 for (i = 0; i < N_LISTENERS; ++i) {
197 int prio;
198 char thread_name[20];
199 sprintf(thread_name, "listener-%02d", i);
200 prio = N_CLIENTS + i;
201 sc_thread_create(prio, listener_program, (cyg_addrword_t) i,
202 thread_name, (void *) listener_stack[i], STACK_SIZE,
203 &listenerH[i], &listener_thread_s[i]);
204 priority_in_use[prio] = 1;
205 }
206
207 for (i = 0; i < N_CLIENTS; ++i) {
208 cyg_thread_resume(clientH[i]);
209 }
210 for (i = 0; i < N_LISTENERS; ++i) {
211 cyg_thread_resume(listenerH[i]);
212 }
213
214 /* set up the alarm which gives periodic wakeups to say "time to
215 print a report */
216 system_clockH = cyg_real_time_clock();
217 cyg_clock_to_counter(system_clockH, &counterH);
218
219 cyg_alarm_create(counterH, report_alarm_func,
220 (cyg_addrword_t) 4000,
221 &report_alarmH, &report_alarm);
222 if (cyg_test_is_simulator) {
223 cyg_alarm_initialize(report_alarmH, cyg_current_time()+300, 400);
224 } else {
225 cyg_alarm_initialize(report_alarmH, cyg_current_time()+300, 4000);
226 }
227
228 }
229
230 /* client_program() -- an obnoxious client which makes a lot of requests */
231 void client_program(cyg_addrword_t data)
232 {
233 int delay;
234
235 cyg_handle_t counterH, deathH, system_clockH;
236 cyg_alarm death_alarm;
237 int is_dead = 0;
238
239 setup_death_alarm(data, &deathH, &death_alarm, &is_dead);
240
241 printf("# Starting client-%d\n", (int) data);
242
243 system_clockH = cyg_real_time_clock();
244 cyg_clock_to_counter(system_clockH, &counterH);
245
246 for (;;) {
247 delay = (rand() % 3);
248
249 /* now send a request to the server */
250 cyg_mutex_lock(&client_request_lock); {
251 ++client_makes_request;
252 /* printf("client_makes_request %d\n", client_makes_request); */
253 } cyg_mutex_unlock(&client_request_lock);
254
255 cyg_thread_delay(10+delay);
256 /* cyg_thread_delay(0); */
257 #ifdef DEATH_TIME_LIMIT
258 if (is_dead) {
259 handle_death(deathH, report_alarmH);
260 }
261 #endif /* DEATH_TIME_LIMIT */
262 }
263 }
264
265 /* listener_program() -- listens for a request and spawns a handler to
266 take care of the request */
267 void listener_program(cyg_addrword_t data)
268 {
269 /* int message = (int) data; */
270 int handler_slot;
271
272 printf("# Beginning execution; thread data is %d\n", (int) data);
273
274 for (;;) {
275 #ifdef DEATH_TIME_LIMIT
276 /* as an extra task, the listener sees if all clients have been
277 killed off, so it can report that the test is over */
278 if (n_clients_killed == N_CLIENTS) {
279 n_clients_killed = -1; /* so we don't call this again */
280 CYG_TEST_PASS_FINISH("Kernel thread stress test OK");
281 }
282 #endif /* DEATH_TIME_LIMIT */
283 if (client_makes_request > 0) {
284 int prio;
285 /* printf("just got a request from a client (count = %d)\n", */
286 /* client_makes_request); */
287 cyg_mutex_lock(&client_request_lock); {
288 --client_makes_request;
289 } cyg_mutex_unlock(&client_request_lock);
290
291 handler_slot = get_handler_slot(listenerH[(int) data]);
292 prio = N_CLIENTS+N_LISTENERS+handler_slot;
293 priority_in_use[prio] = 1;
294 sc_thread_create(prio, handler_program,
295 (cyg_addrword_t) handler_slot,
296 "handler", (void *) handler_stack[handler_slot],
297 STACK_SIZE2, &handlerH[handler_slot],
298 &handler_thread_s[handler_slot]);
299 cyg_thread_resume(handlerH[handler_slot]);
300 ++statistics.handler_invocation_histogram[handler_slot];
301 }
302 cyg_thread_delay(1);
303 }
304 }
305
306 /* handler_program() -- is spawned to handle each incoming request */
307 void handler_program(cyg_addrword_t data)
308 {
309 /* here is where we perform specific stressful tasks */
310 perform_stressful_tasks();
311
312 if (time_to_report) {
313 time_to_report = 0;
314 print_statistics();
315 }
316
317 cyg_thread_delay(4 + (int) (0.5*log(1.0 + fabs((rand() % 1000000)))));
318 /* cyg_thread_delay(0); */
319
320 /* lock the scheduler before we declare this thread slot available
321 and quit; note that cyg_thread_exit() will unlock the scheduler
322 as many times as necessary */
323 cyg_mutex_lock(&handler_slot_lock); {
324 handler_thread_in_use[data] = 0;
325 priority_in_use[N_CLIENTS + N_LISTENERS + (int) data] = 0;
326 } cyg_mutex_unlock(&handler_slot_lock);
327 /* FIXME: could there be a race condition right here? I unlock the
328 scheduler, so I could get pre-empted out, but meanwhile I have
329 declared this thread available again. must fix it. */
330 sc_thread_exit();
331 }
332
333 /* look for an available handler thread */
334 int get_handler_slot(cyg_handle_t current_threadH)
335 {
336 int i;
337 int found = 0;
338
339 while (!found) {
340 for (i = 0; i < MAX_HANDLERS; ++i) {
341 cyg_mutex_lock(&handler_slot_lock); {
342 if (!handler_thread_in_use[i]) {
343 found = 1;
344 handler_thread_in_use[i] = 1;
345 }
346 } cyg_mutex_unlock(&handler_slot_lock);
347 if (found) {
348 break;
349 }
350 #ifdef DEATH_TIME_LIMIT
351 /* must do a check here to see if all clients have been killed,
352 since otherwise we might end up in an infinite loop */
353 if (n_clients_killed == N_CLIENTS) {
354 n_clients_killed = -1; /* so we don't call this again */
355 CYG_TEST_PASS_FINISH("Kernel thread stress test OK");
356 }
357 #endif
358 }
359 cyg_thread_delay(1);
360 }
361 return i;
362 }
363
364 /* do things which will stress the system */
365 void perform_stressful_tasks()
366 {
367 #define MAX_MALLOCED_SPACES 100 /* do this many mallocs at most */
368 #define MALLOCED_BASE_SIZE 1 /* basic size in bytes */
369 char *spaces[MAX_MALLOCED_SPACES];
370 unsigned int i;
371
372 cyg_mutex_t tmp_lock;
373
374 cyg_uint8 pool_space[10][100];
375 cyg_handle_t mempool_handles[10];
376 cyg_mempool_fix mempool_objects[10];
377
378 cyg_mutex_init(&tmp_lock);
379
380 /* here I use malloc, which uses the kernel's variable memory pools.
381 note that malloc/free is a bit simple-minded here: it does not
382 try to really fragment things, and it does not try to make the
383 allocation/deallocation concurrent with other thread execution
384 (although I'm about to throw in a yield()) */
385 for (i = 0; i < MAX_MALLOCED_SPACES; ++i) {
386 ++statistics.malloc_tries;
387 /* spaces[i] = (char *) malloc(((int)(sqrt(i*2.0))+1)*MALLOCED_BASE_SIZE); */
388 spaces[i] = (char *) malloc(((int)i*2.0+1)*MALLOCED_BASE_SIZE);
389 if (i % 100 == 0) {
390 cyg_thread_yield();
391 }
392 }
393
394 /* now free it all up */
395 for (i = 0; i < MAX_MALLOCED_SPACES; ++i) {
396 if (spaces[i] != NULL) {
397 unsigned int j;
398 for (j = 0; j < (i*2+1)*MALLOCED_BASE_SIZE; ++j) {
399 spaces[i][j] = 0xAA; /* write a bit pattern */
400 }
401 free(spaces[i]);
402 } else {
403 ++statistics.malloc_failures;
404 }
405 }
406 /* now allocate and then free some fixed-size memory pools; for
407 now this is simple-minded because it does not have many threads
408 sharing the memory pools and racing for memory. */
409 for (i = 0; i < 10; ++i) {
410 cyg_mempool_fix_create(pool_space[i], 100, (i+1)*3,
411 &mempool_handles[i], &mempool_objects[i]);
412 }
413
414 for (i = 0; i < 10; ++i) {
415 spaces[i] = cyg_mempool_fix_try_alloc(mempool_handles[i]);
416 }
417
418 for (i = 0; i < 10; ++i) {
419 if (spaces[i]) {
420 cyg_mempool_fix_delete(mempool_handles[i]);
421 }
422 }
423
424 cyg_mutex_destroy(&tmp_lock);
425 }
426
427 /* report_alarm_func() is invoked as an alarm handler, so it should be
428 quick and simple. in this case it sets a global flag which is
429 checked by threads. */
430 void report_alarm_func(cyg_handle_t alarmH, cyg_addrword_t data)
431 {
432 time_to_report = 1;
433 }
434
435 /* this sets up death alarms. it gets the handle and alarm from the
436 caller, since they must persist for the life of the alarm */
437 void setup_death_alarm(cyg_addrword_t data, cyg_handle_t *deathHp,
438 cyg_alarm *death_alarm_p, int *killed_p)
439 {
440 #ifdef DEATH_TIME_LIMIT
441 cyg_handle_t system_clockH, counterH;
442 cyg_resolution_t rtc_res;
443
444 system_clockH = cyg_real_time_clock();
445 cyg_clock_to_counter(system_clockH, &counterH);
446
447 cyg_alarm_create(counterH, death_alarm_func,
448 (cyg_addrword_t) killed_p,
449 deathHp, death_alarm_p);
450 rtc_res = cyg_clock_get_resolution(system_clockH);
451 {
452 cyg_tick_count_t tick_delay;
453 tick_delay = (long long)
454 ((1000000000.0*rtc_res.divisor)
455 *((double)DEATH_TIME_LIMIT)/((double)rtc_res.dividend));
456 if ( cyg_test_is_simulator )
457 tick_delay /= 10;
458 cyg_alarm_initialize(*deathHp, cyg_current_time() + tick_delay, 0);
459 }
460 #endif /* DEATH_TIME_LIMIT */
461 }
462
463 /* death_alarm_func() is the alarm handler that kills the current
464 thread after a specified timeout. It does so by setting a flag the
465 thread is constantly checking. */
466 void death_alarm_func(cyg_handle_t alarmH, cyg_addrword_t data)
467 {
468 int *killed_p;
469 killed_p = (int *) data;
470 *killed_p = 1;
471 }
472
473 #ifdef DEATH_TIME_LIMIT
474 /* handle_death is called by a client thread when it dies; it kills
475 off the alarm */
476 void handle_death(cyg_handle_t deathH, cyg_handle_t alarmH)
477 {
478 ++n_clients_killed;
479 cyg_alarm_delete(deathH);
480 cyg_alarm_delete(alarmH);
481 cyg_thread_exit();
482 }
483 #endif /* DEATH_TIME_LIMIT */
484
485 /* now I write the sc_ versions of the cyg_functions */
486 void sc_thread_create(
487 cyg_addrword_t sched_info, /* scheduling info (eg pri) */
488 cyg_thread_entry_t *entry, /* entry point function */
489 cyg_addrword_t entry_data, /* entry data */
490 char *name, /* optional thread name */
491 void *stack_base, /* stack base, NULL = alloc */
492 cyg_ucount32 stack_size, /* stack size, 0 = default */
493 cyg_handle_t *handle, /* returned thread handle */
494 cyg_thread *thread /* put thread here */
495 )
496 {
497 /*printf("Creating a thread -- priority is %lu\n", (unsigned long) sched_info);*/
498 /* fflush(stdout); */
499 ++statistics.thread_creations;
500 cyg_thread_create(sched_info, entry, entry_data, name,
501 stack_base, stack_size, handle, thread);
502 }
503
504 void sc_thread_exit()
505 {
506 /* printf("exiting\n"); */
507 /* fflush(stdout); */
508 ++statistics.thread_exits;
509 cyg_thread_exit();
510 }
511
512 void print_statistics(void)
513 {
514 int i;
515
516 cyg_mutex_lock(&statistics_print_lock); {
517 printf("Handler-invocations: ");
518 for (i = 0; i < MAX_HANDLERS; ++i) {
519 printf("%4lu ", statistics.handler_invocation_histogram[i]);
520 }
521 printf("\n");
522 printf("malloc()-tries/failures: -- %7lu %7lu\n",
523 statistics.malloc_tries, statistics.malloc_failures);
524 printf("client_makes_request: %d\n", client_makes_request);
525 } cyg_mutex_unlock(&statistics_print_lock);
526 }
527
528 #else /* CYGSEM_LIBC_MALLOC */
529 # define N_A_MSG "this test needs malloc"
530 #endif /* CYGSEM_LIBC_MALLOC */
531
532 #else /* CYGFUN_KERNEL_THREADS_TIMER */
533 # define N_A_MSG "this test needs kernel threads timer"
534 #endif /* CYGFUN_KERNEL_THREADS_TIMER */
535
536 #else /* CYGPKG_LIBM */
537 # define N_A_MSG "this test needs libm"
538 #endif /* CYGPKG_LIBM */
539
540 #else /* CYGSEM_LIBC_STDIO */
541 # define N_A_MSG "this test needs stdio"
542 #endif /* CYGSEM_LIBC_STDIO */
543
544 #else // def CYGFUN_KERNEL_API_C
545 # define N_A_MSG "this test needs Kernel C API"
546 #endif
547
548 #else // def CYGPKG_KERNEL && CYGPKG_IO && CYGPKG_LIBC
549 # define N_A_MSG "this tests needs Kernel, libc and IO"
550 #endif
551
552 #ifdef N_A_MSG
553 externC void
554 cyg_start( void )
555 {
556 CYG_TEST_INIT();
557 CYG_TEST_NA( N_A_MSG);
558 }
559 #endif // N_A_MSG