diff options
| author | rpj <rpj> | 1998-12-22 15:59:24 +0000 | 
|---|---|---|
| committer | rpj <rpj> | 1998-12-22 15:59:24 +0000 | 
| commit | 95aa0a376d93ee021a6c085c71418e9f16513e0a (patch) | |
| tree | f05f436e30d759df3af5cfe9e2cde62e3efa6f1b /condvar.c | |
| parent | 4650bcf1f1efd88a0c8f502c28945bfabd7ef6db (diff) | |
Sun Dec 20 14:51:58 1998  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>
        * misc.c (pthreadCancelableWait): New function by John Bossom. Non-stand
ard
        but provides a hook that can be used to implement cancellation points in
        applications that use this library.
        * pthread.h (pthread_cleanup_pop): C++ (non-WIN32) version uses
        try/catch to emulate John Bossom's WIN32 __try/__finally behaviour.
        In the WIN32 version __finally block, add a test for AbnormalTermination
 otherwise
        cleanup is only run if the cleanup_pop execute arg is non-zero. Cancella
tion
        should cause the cleanup to run irrespective of the execute arg.
        * condvar.c (pthread_condattr_init): Replaced by John Bossom's version.
        (pthread_condattr_destroy): Replaced by John Bossom's version.
        (pthread_condattr_getpshared): Replaced by John Bossom's version.
        (pthread_condattr_setpshared): Replaced by John Bossom's version.
        (pthread_cond_init): Replaced by John Bossom's version.
        Fix comment (refered to mutex rather than condition variable).
        (pthread_cond_destroy): Replaced by John Bossom's version.
        (pthread_cond_wait): Replaced by John Bossom's version.
        (pthread_cond_timedwait): Replaced by John Bossom's version.
        (pthread_cond_signal): Replaced by John Bossom's version.
        (pthread_cond_broadcast): Replaced by John Bossom's version.
Thu Dec 17 19:10:46 1998  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>
        * tsd.c (pthread_key_create): Replaced by John Bossom's version.
        (pthread_key_delete): Replaced by John Bossom's version.
        (pthread_setspecific): Replaced by John Bossom's version.
        (pthread_getspecific): Replaced by John Bossom's version.
Mon Dec  7 09:44:40 1998  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>
        * cancel.c (pthread_setcancelstate): Replaced by John Bossom's version.
        (pthread_setcanceltype): Replaced by John Bossom's version.
        (pthread_testcancel): Replaced by John Bossom's version.
        (pthread_cancel): Replaced by John Bossom's version.
        * exit.c (pthread_exit): Replaced by John Bossom's version.
        * misc.c (pthread_self): Replaced by John Bossom's version.
        (pthread_equal): Replaced by John Bossom's version.
        * sync.c (pthread_detach): Replaced by John Bossom's version.
        (pthread_join): Replaced by John Bossom's version.
        * create.c (pthread_create): Replaced by John Bossom's version.
        * private.c (_pthread_processInitialize): New by John Bossom.
        (_pthread_processTerminate): Non-public function by John Bossom.
        (_pthread_threadStart): Non-public function by John Bossom.
        (_pthread_threadDestroy): Non-public function by John Bossom.
        (_pthread_cleanupStack): Non-public function by John Bossom.
        (_pthread_tkAssocCreate): Non-public function by John Bossom.
        (_pthread_tkAssocDestroy): Non-public function by John Bossom.
        (_pthread_callUserDestroyRoutines): Non-public function by John Bossom.
        * implement.h: Added John Bossom's non-API structures and
        declarations.
        * dll.c (PthreadsEntryPoint): Cast return value of GetProcAddress
        to resolve compile warning from MSVC.
        * dll.c (DLLmain): Replaced by John Bossom's version.
        * dll.c (PthreadsEntryPoint):
        Re-applied Anders Norlander's patch:-
        Initialize _pthread_try_enter_critical_section at startup
        and release kernel32 handle when DLL is being unloaded.
Diffstat (limited to 'condvar.c')
| -rw-r--r-- | condvar.c | 705 | 
1 files changed, 705 insertions, 0 deletions
@@ -5,6 +5,709 @@   * This translation unit implements condition variables and their primitives.   */ +/* + * Code contributed by John E. Bossom <JEB>. + */ + +#include <errno.h> +#include <string.h> + +#include "pthread.h" +#include "implement.h" + +int +pthread_condattr_init (pthread_condattr_t * attr) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Initializes a condition variable attributes object +      *      with default attributes. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_condattr_t +      * +      * +      * DESCRIPTION +      *      Initializes a condition variable attributes object +      *      with default attributes. +      * +      *      NOTES: +      *              1)      Use to define condition variable types +      *              2)      It is up to the application to ensure +      *                      that it doesn't re-init an attribute +      *                      without destroying it first. Otherwise +      *                      a memory leak is created. +      * +      * RESULTS +      *              0               successfully initialized attr, +      *              ENOMEM          insufficient memory for attr. +      * +      * ------------------------------------------------------ +      */ +{ +  pthread_condattr_t attr_result; +  int result = 0; + +  attr_result = calloc (1, sizeof (*attr_result)); + +  if (attr_result == NULL) +    { +      result = ENOMEM; +    } + +  *attr = attr_result; + +  return (result); + +}                               /* pthread_condattr_init */ + + +int +pthread_condattr_destroy (pthread_condattr_t * attr) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Destroys a condition variable attributes object. +      *      The object can no longer be used. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_condattr_t +      * +      * +      * DESCRIPTION +      *      Destroys a condition variable attributes object. +      *      The object can no longer be used. +      * +      *      NOTES: +      *      1)      Does not affect condition variables created +      *              using 'attr' +      * +      * RESULTS +      *              0               successfully released attr, +      *              EINVAL          'attr' is invalid. +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; + +  if (attr == NULL || *attr == NULL) +    { +      result = EINVAL; + +    } +  else +    { +      free (*attr); + +      *attr = NULL; +      result = 0; +    } + +  return (result); + +}                               /* pthread_condattr_destroy */ + + +int +pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Determine whether condition variables created with 'attr' +      *      can be shared between processes. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_condattr_t +      * +      *      pshared +      *              will be set to one of: +      * +      *                      PTHREAD_PROCESS_SHARED +      *                              May be shared if in shared memory +      * +      *                      PTHREAD_PROCESS_PRIVATE +      *                              Cannot be shared. +      * +      * +      * DESCRIPTION +      *      Condition Variables created with 'attr' can be shared +      *      between processes if pthread_cond_t variable is allocated +      *      in memory shared by these processes. +      *      NOTES: +      *      1)      pshared condition variables MUST be allocated in +      *              shared memory. +      * +      *      2)      The following macro is defined if shared mutexes +      *              are supported: +      *                      _POSIX_THREAD_PROCESS_SHARED +      * +      * RESULTS +      *              0               successfully retrieved attribute, +      *              EINVAL          'attr' is invalid, +      * +      * ------------------------------------------------------ +      */ +{ +  int result; + +  if ((attr != NULL && *attr != NULL) && +      (pshared != NULL)) +    { + +      *pshared = (*attr)->pshared; +      result = 0; + +    } +  else +    { +      *pshared = PTHREAD_PROCESS_PRIVATE; +      result = EINVAL; +    } + +  return (result); + +}                               /* pthread_condattr_getpshared */ + + +int +pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Mutexes created with 'attr' can be shared between +      *      processes if pthread_mutex_t variable is allocated +      *      in memory shared by these processes. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_mutexattr_t +      * +      *      pshared +      *              must be one of: +      * +      *                      PTHREAD_PROCESS_SHARED +      *                              May be shared if in shared memory +      * +      *                      PTHREAD_PROCESS_PRIVATE +      *                              Cannot be shared. +      * +      * DESCRIPTION +      *      Mutexes creatd with 'attr' can be shared between +      *      processes if pthread_mutex_t variable is allocated +      *      in memory shared by these processes. +      * +      *      NOTES: +      *              1)      pshared mutexes MUST be allocated in shared +      *                      memory. +      * +      *              2)      The following macro is defined if shared mutexes +      *                      are supported: +      *                              _POSIX_THREAD_PROCESS_SHARED +      * +      * RESULTS +      *              0               successfully set attribute, +      *              EINVAL          'attr' or pshared is invalid, +      *              ENOSYS          PTHREAD_PROCESS_SHARED not supported, +      * +      * ------------------------------------------------------ +      */ +{ +  int result; + +  if ((attr != NULL && *attr != NULL) && +      ((pshared == PTHREAD_PROCESS_SHARED) || +       (pshared == PTHREAD_PROCESS_PRIVATE))) +    { + + +      if (pshared == PTHREAD_PROCESS_SHARED) +        { + +#if !defined( _POSIX_THREAD_PROCESS_SHARED ) +          result = ENOSYS; +          pshared = PTHREAD_PROCESS_PRIVATE; +#else +          result = 0; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + +        } +      else +        { +          result = 0; +        } +      (*attr)->pshared = pshared; + +    } +  else +    { +      result = EINVAL; + +    } + +  return (result); + +}                               /* pthread_condattr_setpshared */ + + +int +pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      This function initializes a condition variable. +      * +      * PARAMETERS +      *      cond +      *              pointer to an instance of pthread_cond_t +      * +      *      attr +      *              specifies optional creation attributes. +      * +      * +      * DESCRIPTION +      *      This function initializes a condition variable. +      * +      * RESULTS +      *              0               successfully created condition variable, +      *              EINVAL          'attr' is invalid, +      *              EAGAIN          insufficient resources (other than +      *                              memory, +      *              ENOMEM          insufficient memory, +      *              EBUSY           'cond' is already initialized, +      * +      * ------------------------------------------------------ +      */ +{ +  int result = EAGAIN; +  pthread_cond_t cv; + +  if ((attr != NULL && *attr != NULL) && +      ((*attr)->pshared == PTHREAD_PROCESS_SHARED)) +    { +      /* +       * Creating condition variable that can be shared between +       * processes. +       */ +      result = ENOSYS; + +      goto FAIL0; +    } + +  cv = (pthread_cond_t) calloc (1, sizeof (*cv)); + +  if (cv != NULL) +    { +      result = ENOMEM; +      goto FAIL0; +    } + +  cv->waiters = 0; +  cv->wasBroadcast = FALSE; + +  if (sem_init (&(cv->sema), 0, 1) != 0) +    { +      goto FAIL0; +    } +  if (pthread_mutex_init (&(cv->waitersLock), NULL) != 0) +    { +      goto FAIL1; +    } + +  cv->waitersDone = CreateEvent ( +                                  0, +                                  (int) FALSE,  /* manualReset  */ +                                  (int) FALSE,  /* setSignaled  */ +                                  NULL); + +  if (cv->waitersDone == NULL) +    { +      goto FAIL2; +    } + +  result = 0; + +  goto DONE; + +  /* +   * ------------- +   * Failure Code +   * ------------- +   */ +FAIL2: +  (void) pthread_mutex_destroy (&(cv->waitersLock)); + +FAIL1: +  (void) sem_destroy (&(cv->sema)); +  free (cv); +  cv = NULL; + +FAIL0: +DONE: +  *cond = cv; +  return (result); + +}                               /* pthread_cond_init */ + + +int +pthread_cond_destroy (pthread_cond_t * cond) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      This function destroys a condition variable +      * +      * +      * PARAMETERS +      *      cond +      *              pointer to an instance of pthread_cond_t +      * +      * +      * DESCRIPTION +      *      This function destroys a condition variable. +      * +      *      NOTES: +      *              1)      Safest after wakeup from 'cond', when +      *                      no other threads will wait. +      * +      * RESULTS +      *              0               successfully released condition variable, +      *              EINVAL          'cond' is invalid, +      *              EBUSY           'cond' is in use, +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; +  pthread_cond_t cv; + +  if (cond != NULL && *cond != NULL) +    { +      cv = *cond; + +      (void) sem_destroy (&(cv->sema)); +      (void) pthread_mutex_destroy (&(cv->waitersLock)); +      (void) CloseHandle (cv->waitersDone); + +      free (cv); + +      *cond = NULL; +    } + +  return (result); +} + +int +pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      This function waits on a condition variable until +      *      awakened by a signal or broadcast. +      * +      *      Caller MUST be holding the mutex lock; the +      *      lock is released and the caller is blocked waiting +      *      on 'cond'. When 'cond' is signaled, the mutex +      *      is re-acquired before returning to the caller. +      * +      * PARAMETERS +      *      cond +      *              pointer to an instance of pthread_cond_t +      * +      *      mutex +      *              pointer to an instance of pthread_mutex_t +      * +      * +      * DESCRIPTION +      *      This function waits on a condition variable until +      *      awakened by a signal or broadcast. +      * +      *      NOTES: +      *      1)      The function must be called with 'mutex' LOCKED +      *               by the calling thread, or undefined behaviour +      *              will result. +      * +      *      2)      This routine atomically releases 'mutex' and causes +      *              the calling thread to block on the condition variable. +      *              The blocked thread may be awakened by  +      *                      pthread_cond_signal or  +      *                      pthread_cond_broadcast. +      * +      * Upon successful completion, the 'mutex' has been locked and  +      * is owned by the calling thread. +      * +      * RESULTS +      *              0               caught condition; mutex released, +      *              EINVAL          'cond' or 'mutex' is invalid, +      *              EINVAL          different mutexes for concurrent waits, +      *              EINVAL          mutex is not held by the calling thread, +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; +  pthread_cond_t cv; +  int lastWaiter; + +  cv = *cond; + +  /* +   * OK to increment  cv->waiters because the caller locked 'mutex' +   * +   * FIXME: This is true. However, it is technically possible to call cond_wait +   * on this cv with a different mutex. The standard leaves the result of such an +   * action as undefined. (RPJ) +   */ +  cv->waiters++; + +  /* +   * We keep the lock held just long enough to increment the count of +   * waiters by one (above). +   * Note that we can't keep it held across the +   * call to sem_wait since that will deadlock other calls +   * to pthread_cond_signal +   */ +  if ((result = pthread_mutex_unlock (mutex)) == 0) +    { +      /* +       * Wait to be awakened by +       *              pthread_cond_signal, or +       *              pthread_cond_broadcast +       * +       * Note:  +       *      sem_wait is a cancellation point, hence providing the +       *      mechanism for making pthread_cond_wait a cancellation +       *      point. We use the cleanup mechanism to ensure we +       *  re-lock the mutex if we are cancelled. +       */ +      pthread_cleanup_push (pthread_mutex_lock, mutex); + +      result = sem_wait (&(cv->sema)); + +      pthread_cleanup_pop (0); +    } + +  if ((result = pthread_mutex_lock (&(cv->waitersLock))) == 0) +    { +      /* +       * By making the waiter responsible for decrementing +       * its count we don't have to worry about having an internal +       * mutex. +       */ +      cv->waiters--; + +      lastWaiter = cv->wasBroadcast && (cv->waiters == 0); + +      result = pthread_mutex_unlock (&(cv->waitersLock)); +    } + +  if (result == 0) +    { +      if (lastWaiter) +        { +          /* +           * If we are the last waiter on this broadcast +           * let the thread doing the broadcast proceed +           */ +          if (!SetEvent (cv->waitersDone)) +            { +              result = EINVAL; +            } +        } +    } + +  /* +   * We must always regain the external mutex, even when +   * errors occur because that's the guarantee that we give +   * to our callers +   */ +  (void) pthread_mutex_lock (mutex); + + +  return (result); + +}                               /* pthread_cond_wait */ + + +int +pthread_cond_timedwait (pthread_cond_t * cond, +                         pthread_mutex_t * mutex, +                         const struct timespec *abstime) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      This function initializes an unnamed semaphore. the +      *      initial value of the semaphore is 'value' +      * +      * PARAMETERS +      *      sem +      *              pointer to an instance of sem_t +      * +      * +      * DESCRIPTION +      *      This function  initializes an unnamed semaphore. The +      *      initial value of the semaphore is set to 'value'. +      * +      * RESULTS +      *              0               successfully created semaphore, +      *              EINVAL          'sem' is not a valid semaphore, +      *              ENOSPC          a required resource has been exhausted, +      *              ENOSYS          semaphores are not supported, +      *              EPERM           the process lacks appropriate privilege +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; +  /* +   * NOT IMPLEMENTED YET!!! +   */ +  return (result); +} + + +int +pthread_cond_signal (pthread_cond_t * cond) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      This function signals a condition variable, waking +      *      one waiting thread. +      *      If SCHED_FIFO or SCHED_RR policy threads are waiting +      *      the highest priority waiter is awakened; otherwise, +      *      an unspecified waiter is awakened. +      * +      * PARAMETERS +      *      cond +      *              pointer to an instance of pthread_cond_t +      * +      * +      * DESCRIPTION +      *      This function signals a condition variable, waking +      *      one waiting thread. +      *      If SCHED_FIFO or SCHED_RR policy threads are waiting +      *      the highest priority waiter is awakened; otherwise, +      *      an unspecified waiter is awakened. +      * +      *      NOTES: +      *      1)      Use when any waiter can respond and only one need +      *              respond (all waiters being equal). +      * +      *      2)      This function MUST be called under the protection  +      *              of the SAME mutex that is used with the condition +      *              variable being signaled; OTHERWISE, the condition +      *              variable may be signaled between the test of the +      *              associated condition and the blocking +      *              pthread_cond_signal. +      *              This can cause an infinite wait. +      * +      * RESULTS +      *              0               successfully signaled condition, +      *              EINVAL          'cond' is invalid, +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; +  pthread_cond_t cv = *cond; + +  /* +   * If there aren't any waiters, then this is a no-op. +   */ +  if (cv->waiters > 0) +    { + +      result = sem_post (&(cv->sema)); +    } + +  return (result); + +}                               /* pthread_cond_signal */ + +int +pthread_cond_broadcast (pthread_cond_t * cond) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      This function broadcasts the condition variable, +      *      waking all current waiters. +      * +      * PARAMETERS +      *      sem +      *              pointer to an instance of pthread_cond_t +      * +      * +      * DESCRIPTION +      *      This function  initializes an unnamed semaphore. The +      *      initial value of the semaphore is set to 'value'. +      * +      *      NOTES: +      *      1)      This function MUST be called under the protection +      *              of the SAME mutex that is used with the condition +      *              variable being signaled; OTHERWISE, the condition +      *              variable may be signaled between the test of the +      *              associated condition and the blocking pthread_cond_wait. +      *              This can cause an infinite wait. +      * +      *      2)      Use when more than one waiter may respond to +      *              predicate change or if any waiting thread may +      *              not be able to respond +      * +      * RESULTS +      *              0               successfully created semaphore, +      *              EINVAL          'cond' is invalid +      *              ENOSPC          a required resource has been exhausted, +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; +  pthread_cond_t cv = *cond; +  int i; + +  cv->wasBroadcast = TRUE; + +  /* +   * Wake up all waiters +   */ +  for (i = cv->waiters; i > 0 && result == 0; i--) +    { + +      result = sem_post (&(cv->sema)); +    } + +  if (result == 0) +    { +      /* +       * Wait for all the awakened threads to acquire their part of +       * the counting semaphore +       */ +      if (WaitForSingleObject (cv->waitersDone, INFINITE) != +          WAIT_OBJECT_0) +        { + +          result = 0; + +        } +      else +        { +          result = EINVAL; +        } + +    } + +  return (result); +} + +/* </JEB> */ + + +#if 0 /* Pre Bossom */ +  #include <errno.h>  #include <windows.h> @@ -202,3 +905,5 @@ pthread_cond_destroy(pthread_cond_t *cv)    return pthread_mutex_destroy(&cv->waiters_count_lock);  } + +#endif /* Pre Bossom */  | 
