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 | |
| parent | 58de21f1e7cde4c32bfd03b545ac3ede6b6a8e38 (diff) | |
''
| -rw-r--r-- | implement.h | 6 | ||||
| -rw-r--r-- | pthread.h | 6 | ||||
| -rw-r--r-- | pthread_once.c | 176 | 
3 files changed, 62 insertions, 126 deletions
| diff --git a/implement.h b/implement.h index eeb7c88..670d948 100644 --- a/implement.h +++ b/implement.h @@ -325,9 +325,9 @@ struct pthread_rwlockattr_t_  };  enum ptw32_once_state { -  PTW32_ONCE_CLEAR     = 0x0, -  PTW32_ONCE_DONE      = 0x1, -  PTW32_ONCE_CANCELLED = 0x2 +  PTW32_ONCE_INIT      = 0x0, +  PTW32_ONCE_STARTED   = 0x1, +  PTW32_ONCE_DONE      = 0x2  };  typedef struct { @@ -653,12 +653,12 @@ enum {   * ====================   * ====================   */ -#define PTHREAD_ONCE_INIT       { PTW32_FALSE, PTW32_FALSE, 0, 0} +#define PTHREAD_ONCE_INIT       { 0, 0, 0, 0}  struct pthread_once_t_  { -  int          done;        /* indicates if user function has been executed */ -  int          started; +  int          state;        /* indicates if user function has been executed */ +  int          reserved;    int          numSemaphoreUsers;    HANDLE       semaphore;  }; 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 */ | 
