Mercurial > ecos
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 |
