summaryrefslogtreecommitdiff
path: root/condvar.c
diff options
context:
space:
mode:
Diffstat (limited to 'condvar.c')
-rw-r--r--condvar.c705
1 files changed, 705 insertions, 0 deletions
diff --git a/condvar.c b/condvar.c
index 3dc76d4..f12c5e3 100644
--- a/condvar.c
+++ b/condvar.c
@@ -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 */