diff options
author | rpj <rpj> | 2005-05-27 07:23:59 +0000 |
---|---|---|
committer | rpj <rpj> | 2005-05-27 07:23:59 +0000 |
commit | ce73fb344fb6c075465c5989cafdf8b26cc57a7e (patch) | |
tree | 37a54200e7d1c603e046c4d606372fb1d91a51a2 /pthread_once.c | |
parent | 58de21f1e7cde4c32bfd03b545ac3ede6b6a8e38 (diff) |
''
Diffstat (limited to 'pthread_once.c')
-rw-r--r-- | pthread_once.c | 176 |
1 files changed, 56 insertions, 120 deletions
diff --git a/pthread_once.c b/pthread_once.c index b42036f..39ddcce 100644 --- a/pthread_once.c +++ b/pthread_once.c @@ -34,77 +34,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -/* - * NOTES: - * pthread_once() performs a very simple task. So why is this implementation - * so complicated? - * - * The original implementation WAS very simple, but it relied on Windows random - * priority boosting to resolve starvation problems. Windows priority boosting - * does not occur for realtime priority classes (levels 16 to 31). - * - * You can check back to previous versions of code in the CVS repository or - * search the mailing list archives for discussion. - * - * Version A - * --------- - * Waiting threads would resume and suspend again using Sleep(0) until the - * init_routine had completed, but a higher priority waiter could hog the CPU and - * starve the initter thread until Windows randomly boosted it's priority, or forever - * for realtime applications. - * - * Version B - * --------- - * This was fixed by introducing a per once_control manual-reset event that is - * created and destroyed dynamically only if there are waiters. The design did not - * need global critical sections. Each once_control remained independent. A waiter - * could be confident that if the event was not null then it did not need to create - * the event. - * - * Version C - * --------- - * Since a change in ABI would result from version B, it was decided to take - * the opportunity and make pthread_once() fully compliant with the Single Unix - * Specification (version 3 at the time). This required allowing the init_routine - * to be a cancelation point. A cancelation meant that at least some waiting threads - * if any had to be woken so that one might become the new initter thread. - * Waiters could no longer simply assume that, if the event was not null, it did - * not need to create an event. - * - * Also, the cancelled init thread needed to set the event, and the - * new init thread (the winner of the race between any newly arriving threads and - * waking waiters) would need to reset it again. In the meantime, threads could be - * happily looping around until they either suspended on the reset event, or exited - * because the init thread had completed. It was also once again possible for a higher - * priority waiter to starve the init thread. - * - * Version D - * --------- - * There were now two options considered: - * - use an auto-reset event; OR - * - add our own priority boosting. - * - * An auto-reset event would stop threads from looping ok, but it makes threads - * dependent on earlier threads to successfully set the event in turn when it's time - * to wake up, and this serialises threads unecessarily on MP systems. It also adds - * an extra kernel call for each waking thread. If one waiter wakes and dies (async - * cancelled or killed) before it can set the event, then all remaining waiters are - * stranded. - * - * Priority boosting is a standard method for solving priority inversion and - * starvation problems. Furthermore, all of the priority boost logic can - * be restricted to the post cancellation tracks. That is, it need not slow - * the normal cancel-free behaviour. Threads remain independent of other threads. - * - * Version E - * --------- - * Substituting a semaphore in place of the event achieves the same effect as an - * auto-reset event in the post cancellation phase, and a manual-reset event in the - * normal exit phase. The new initter thread does not need to do any post-cancellation - * operations, and waiters only need to check that there is a new initter running - * before starting to wait. All priority issues and adjustments disappear. - */ - #include "pthread.h" #include "implement.h" @@ -114,7 +43,7 @@ ptw32_once_init_routine_cleanup(void * arg) { pthread_once_t * once_control = (pthread_once_t *) arg; - (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started, (LONG)PTW32_FALSE); + (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_INIT); if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ { @@ -162,6 +91,7 @@ pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) */ { int result; + int state; HANDLE sema; if (once_control == NULL || init_routine == NULL) @@ -174,69 +104,75 @@ pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) result = 0; } - while (!InterlockedExchangeAdd((LPLONG)&once_control->done, 0L)) /* Atomic Read */ + while ((state = + PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)&once_control->state, + (PTW32_INTERLOCKED_LONG)PTW32_ONCE_STARTED, + (PTW32_INTERLOCKED_LONG)PTW32_ONCE_INIT)) + != PTW32_ONCE_DONE) { - if (!PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started, (LONG)PTW32_TRUE)) - { + if (PTW32_ONCE_INIT == state) + { #ifdef _MSC_VER #pragma inline_depth(0) #endif - pthread_cleanup_push(ptw32_once_init_routine_cleanup, (void *) once_control); - (*init_routine)(); - pthread_cleanup_pop(0); + pthread_cleanup_push(ptw32_once_init_routine_cleanup, (void *) once_control); + (*init_routine)(); + pthread_cleanup_pop(0); #ifdef _MSC_VER #pragma inline_depth() #endif - (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->done, (LONG)PTW32_TRUE); - - /* - * we didn't create the semaphore. - * it is only there if there is someone waiting. - */ - if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ - { - ReleaseSemaphore(once_control->semaphore, once_control->numSemaphoreUsers, NULL); - } - } + (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, + (LONG)PTW32_ONCE_DONE); + + /* + * we didn't create the semaphore. + * it is only there if there is someone waiting. + */ + if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ + { + ReleaseSemaphore(once_control->semaphore, + once_control->numSemaphoreUsers, NULL); + } + } else - { - InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers); + { + InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers); - if (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ - { - sema = CreateSemaphore(NULL, 0, INT_MAX, NULL); + if (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ + { + sema = CreateSemaphore(NULL, 0, INT_MAX, NULL); - if (PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)&once_control->semaphore, + if (PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)&once_control->semaphore, (PTW32_INTERLOCKED_LONG)sema, (PTW32_INTERLOCKED_LONG)0)) - { - CloseHandle(sema); - } - } - - /* - * Check 'done' and 'started' again in case the initting thread has finished or cancelled - * and left before seeing that there was a semaphore to release. - */ - if (InterlockedExchangeAdd((LPLONG)&once_control->done, 0L) /* Done immediately, or */ - || !InterlockedExchangeAdd((LPLONG)&once_control->started, 0L) /* No initter yet, or */ - || WaitForSingleObject(once_control->semaphore, INFINITE)) /* Done or Cancelled */ - { - if (0 == InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers)) - { - /* we were last */ - if ((sema = (HANDLE) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->semaphore, - (LONG)0))) - { - CloseHandle(sema); - } - } - } - } + { + CloseHandle(sema); + } + } + + /* + * Check 'state' again in case the initting thread has finished or + * cancelled and left before seeing that there was a semaphore. + */ + if (InterlockedExchangeAdd((LPLONG)&once_control->state, 0L) == PTW32_ONCE_STARTED) + { + WaitForSingleObject(once_control->semaphore, INFINITE); + } + + if (0 == InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers)) + { + /* we were last */ + if ((sema = + (HANDLE) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->semaphore, (LONG)0))) + { + CloseHandle(sema); + } + } + } } /* @@ -246,4 +182,4 @@ pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) */ FAIL0: return (result); -} /* pthread_once */ +} /* pthread_once */ |