/* * condvar.c * * Description: * This translation unit implements condition variables and their primitives. */ /* * Code contributed by John E. Bossom . */ #include #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); } /* */