|  | /* | 
|  | * once4.c | 
|  | * | 
|  | * | 
|  | * -------------------------------------------------------------------------- | 
|  | * | 
|  | *      Pthreads-win32 - POSIX Threads Library for Win32 | 
|  | *      Copyright(C) 1998 John E. Bossom | 
|  | *      Copyright(C) 1999,2005 Pthreads-win32 contributors | 
|  | * | 
|  | *      Contact Email: rpj@callisto.canberra.edu.au | 
|  | * | 
|  | *      The current list of contributors is contained | 
|  | *      in the file CONTRIBUTORS included with the source | 
|  | *      code distribution. The list can also be seen at the | 
|  | *      following World Wide Web location: | 
|  | *      http://sources.redhat.com/pthreads-win32/contributors.html | 
|  | * | 
|  | *      This library is free software; you can redistribute it and/or | 
|  | *      modify it under the terms of the GNU Lesser General Public | 
|  | *      License as published by the Free Software Foundation; either | 
|  | *      version 2 of the License, or (at your option) any later version. | 
|  | * | 
|  | *      This library is distributed in the hope that it will be useful, | 
|  | *      but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | *      Lesser General Public License for more details. | 
|  | * | 
|  | *      You should have received a copy of the GNU Lesser General Public | 
|  | *      License along with this library in the file COPYING.LIB; | 
|  | *      if not, write to the Free Software Foundation, Inc., | 
|  | *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | 
|  | * | 
|  | * -------------------------------------------------------------------------- | 
|  | * | 
|  | * Create several pthread_once objects and channel several threads | 
|  | * through each. Make the init_routine cancelable and cancel them | 
|  | * waiters waiting. Vary the priorities. | 
|  | * | 
|  | * Depends on API functions: | 
|  | *	pthread_once() | 
|  | *	pthread_create() | 
|  | *      pthread_testcancel() | 
|  | *      pthread_cancel() | 
|  | *      pthread_once() | 
|  | */ | 
|  |  | 
|  | #include "test.h" | 
|  |  | 
|  | #define NUM_THREADS 100 /* Targeting each once control */ | 
|  | #define NUM_ONCE    10 | 
|  |  | 
|  | pthread_once_t o = PTHREAD_ONCE_INIT; | 
|  | pthread_once_t once[NUM_ONCE]; | 
|  |  | 
|  | typedef struct { | 
|  | int i; | 
|  | CRITICAL_SECTION cs; | 
|  | } sharedInt_t; | 
|  |  | 
|  | static sharedInt_t numOnce = {0, {0}}; | 
|  | static sharedInt_t numThreads = {0, {0}}; | 
|  |  | 
|  | typedef struct { | 
|  | int threadnum; | 
|  | int oncenum; | 
|  | int myPrio; | 
|  | HANDLE w32Thread; | 
|  | } bag_t; | 
|  |  | 
|  | static bag_t threadbag[NUM_THREADS][NUM_ONCE]; | 
|  |  | 
|  | CRITICAL_SECTION print_lock; | 
|  |  | 
|  | void | 
|  | mycleanupfunc(void * arg) | 
|  | { | 
|  | bag_t * bag = (bag_t *) arg; | 
|  | EnterCriticalSection(&print_lock); | 
|  | /*      once thrd  prio error */ | 
|  | printf("%4d %4d %4d %4d\n", | 
|  | bag->oncenum, | 
|  | bag->threadnum, | 
|  | bag->myPrio, | 
|  | bag->myPrio - GetThreadPriority(bag->w32Thread)); | 
|  | LeaveCriticalSection(&print_lock); | 
|  | } | 
|  |  | 
|  | void | 
|  | myinitfunc(void) | 
|  | { | 
|  | EnterCriticalSection(&numOnce.cs); | 
|  | numOnce.i++; | 
|  | LeaveCriticalSection(&numOnce.cs); | 
|  | /* Simulate slow once routine so that following threads pile up behind it */ | 
|  | Sleep(10); | 
|  | /* test for cancelation late so we're sure to have waiters. */ | 
|  | pthread_testcancel(); | 
|  | } | 
|  |  | 
|  | void * | 
|  | mythread(void * arg) | 
|  | { | 
|  | bag_t * bag = (bag_t *) arg; | 
|  | struct sched_param param; | 
|  |  | 
|  | /* | 
|  | * Cancel every thread. These threads are deferred cancelable only, so | 
|  | * only the thread performing the init_routine will see it (there are | 
|  | * no other cancelation points here). The result will be that every thread | 
|  | * eventually cancels only when it becomes the new initter. | 
|  | */ | 
|  | pthread_t self = pthread_self(); | 
|  | bag->w32Thread = pthread_getw32threadhandle_np(self); | 
|  | /* | 
|  | * Set priority between -2 and 2 inclusive. | 
|  | */ | 
|  | bag->myPrio = (bag->threadnum % 5) - 2; | 
|  | param.sched_priority = bag->myPrio; | 
|  | pthread_setschedparam(self, SCHED_OTHER, ¶m); | 
|  |  | 
|  | /* Trigger a cancellation at the next cancellation point in this thread */ | 
|  | pthread_cancel(self); | 
|  | #if 0 | 
|  | pthread_cleanup_push(mycleanupfunc, arg); | 
|  | assert(pthread_once(&once[bag->oncenum], myinitfunc) == 0); | 
|  | pthread_cleanup_pop(1); | 
|  | #else | 
|  | assert(pthread_once(&once[bag->oncenum], myinitfunc) == 0); | 
|  | #endif | 
|  | EnterCriticalSection(&numThreads.cs); | 
|  | numThreads.i++; | 
|  | LeaveCriticalSection(&numThreads.cs); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | main() | 
|  | { | 
|  | pthread_t t[NUM_THREADS][NUM_ONCE]; | 
|  | int i, j, cpus; | 
|  |  | 
|  | //fprintf(stderr, "Skipped (hangs)\n"); | 
|  | //return 1; | 
|  | InitializeCriticalSection(&print_lock); | 
|  | InitializeCriticalSection(&numThreads.cs); | 
|  | InitializeCriticalSection(&numOnce.cs); | 
|  |  | 
|  | #if 0 | 
|  | /*       once thrd  prio change */ | 
|  | printf("once thrd  prio  error\n"); | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Set the priority class to realtime - otherwise normal | 
|  | * Windows random priority boosting will obscure any problems. | 
|  | */ | 
|  | cpus = pthread_num_processors_np(); | 
|  | fprintf(stderr,"CPU count: %d\n", cpus); | 
|  | if (cpus <= 1) { | 
|  | fprintf(stderr, "This test uses realtime scheduling and requires a multi-core to prevent system hang.\n"); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | pthread_set_num_processors_np(cpus-1); | 
|  | //SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); | 
|  | SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); | 
|  | /* Set main thread to lower prio than threads */ | 
|  | SetThreadPriority(GetCurrentThread(), -2); | 
|  | printf("CPU count (reduced): %d\n", pthread_num_processors_np()); | 
|  |  | 
|  | for (j = 0; j < NUM_ONCE; j++) | 
|  | { | 
|  | once[j] = o; | 
|  |  | 
|  | for (i = 0; i < NUM_THREADS; i++) | 
|  | { | 
|  | int r1; | 
|  | bag_t * bag = &threadbag[i][j]; | 
|  | bag->threadnum = i; | 
|  | bag->oncenum = j; | 
|  | r1 = pthread_create(&t[i][j], NULL, mythread, (void *) bag); | 
|  | if (r1 == EAGAIN) { --i; Sleep(0); continue; } | 
|  | assert (r1 == 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (j = 0; j < NUM_ONCE; j++) | 
|  | for (i = 0; i < NUM_THREADS; i++) | 
|  | if (pthread_join(t[i][j], NULL) != 0) | 
|  | printf("Join failed for [thread,once] = [%d,%d]\n", i, j); | 
|  |  | 
|  | /* | 
|  | * All threads will cancel, none will return normally from | 
|  | * pthread_once and so numThreads should never be incremented. However, | 
|  | * numOnce should be incremented by every thread (NUM_THREADS*NUM_ONCE). | 
|  | */ | 
|  | assert(numOnce.i == NUM_ONCE * NUM_THREADS); | 
|  | assert(numThreads.i == 0); | 
|  |  | 
|  | DeleteCriticalSection(&numOnce.cs); | 
|  | DeleteCriticalSection(&numThreads.cs); | 
|  | DeleteCriticalSection(&print_lock); | 
|  |  | 
|  | return 0; | 
|  | } |