summaryrefslogtreecommitdiff
path: root/pthread_once.c
diff options
context:
space:
mode:
authorrpj <rpj>2005-05-27 07:23:59 +0000
committerrpj <rpj>2005-05-27 07:23:59 +0000
commitce73fb344fb6c075465c5989cafdf8b26cc57a7e (patch)
tree37a54200e7d1c603e046c4d606372fb1d91a51a2 /pthread_once.c
parent58de21f1e7cde4c32bfd03b545ac3ede6b6a8e38 (diff)
''
Diffstat (limited to 'pthread_once.c')
-rw-r--r--pthread_once.c176
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 */