summaryrefslogtreecommitdiff
path: root/private.c
diff options
context:
space:
mode:
Diffstat (limited to 'private.c')
-rw-r--r--private.c435
1 files changed, 347 insertions, 88 deletions
diff --git a/private.c b/private.c
index 32116ef..901973a 100644
--- a/private.c
+++ b/private.c
@@ -11,133 +11,392 @@
#include "pthread.h"
#include "implement.h"
-/* Thread ID management.
- ---------------------
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
- We started by simply mapping the Win32 thread handle directly to
- pthread_t. However, in order to process pthread_join()'s, we need
- to be able to keep our POSIX thread ID (pthread_t) around after the
- Win32 thread has terminated. Win32 may reuse the Win32 handle during that
- time, which will conflict.
+int
+_pthread_processInitialize (void)
+ /*
+ * ------------------------------------------------------
+ * DOCPRIVATE
+ * This function performs process wide initialization for
+ * the pthread library.
+ *
+ * PARAMETERS
+ * N/A
+ *
+ * DESCRIPTION
+ * This function performs process wide initialization for
+ * the pthread library.
+ * If successful, this routine sets the global variable
+ * _pthread_processInitialized to TRUE.
+ *
+ * RESULTS
+ * TRUE if successful,
+ * FALSE otherwise
+ *
+ * ------------------------------------------------------
+ */
+{
+ _pthread_processInitialized = TRUE;
- The pthread_t value is now actually the pointer to a thread struct:
+ /*
+ * Initialize Keys
+ */
+ if ((pthread_key_create (&_pthread_selfThreadKey, NULL) != 0) ||
+ (pthread_key_create (&_pthread_cleanupKey, NULL) != 0))
+ {
- typedef struct _pthread * pthread_t;
+ _pthread_processTerminate ();
+ }
- which amongst other things stores the Win32 thread handle:
+ return (_pthread_processInitialized);
+
+} /* processInitialize */
+
+void
+_pthread_processTerminate (void)
+ /*
+ * ------------------------------------------------------
+ * DOCPRIVATE
+ * This function performs process wide termination for
+ * the pthread library.
+ *
+ * PARAMETERS
+ * N/A
+ *
+ * DESCRIPTION
+ * This function performs process wide termination for
+ * the pthread library.
+ * This routine sets the global variable
+ * _pthread_processInitialized to FALSE
+ *
+ * RESULTS
+ * N/A
+ *
+ * ------------------------------------------------------
+ */
+{
+ if (_pthread_processInitialized)
+ {
+
+ if (_pthread_selfThreadKey != NULL)
+ {
+ /*
+ * Release _pthread_selfThreadKey
+ */
+ pthread_key_delete (_pthread_selfThreadKey);
- struct _pthread {
- HANDLE win32handle;
- int ptstatus;
- ...
- };
+ _pthread_selfThreadKey = NULL;
+ }
+
+ if (_pthread_cleanupKey != NULL)
+ {
+ /*
+ * Release _pthread_cleanupKey
+ */
+ pthread_key_delete (_pthread_cleanupKey);
- So now whereever we need to use the Win32 handle it can be accessed
- as:
+ _pthread_cleanupKey = NULL;
+ }
- pthread_t T = pthread_this();
- HANDLE H;
+ _pthread_processInitialized = FALSE;
+ }
- H = T->win32handle;
+} /* processTerminate */
- // or (which is NOT preferred, let the compiler optimise to this).
+void *
+_pthread_threadStart (ThreadParms * threadParms)
+{
+ pthread_t tid;
+ void *(*start) (void *);
+ void *arg;
- H = (HANDLE) *T;
+ int status;
+ tid = threadParms->tid;
+ start = threadParms->start;
+ arg = threadParms->arg;
- POSIX Threads Table
- -------------------
+ free (threadParms);
- Having the thread ID as a pointer to the thread struct itself
- avoids the need to search the threads table in all but the initial
- occasion where we create the thread.
+ pthread_setspecific (_pthread_selfThreadKey, tid);
- Initially we used a hash function to select a free thread struct
- from the table, possibly needing a walk through the table if the
- hash collided with an already in-use thread.
+ __try
+ {
+ /*
+ * Run the caller's routine;
+ */
+ (*start) (arg);
+ status = 0;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ /*
+ * A system unexpected exception had occurred running the user's
+ * routine. We get control back within this block.
+ */
+ status = -1;
+ }
- The scheme used now is more efficient and is done as follows:
+ pthread_exit ((void *) status);
- We use two tables and two counters:
+ return ((void *) status);
- struct _pthread _pthread_virgins[PTHREAD_THREADS_MAX];
- pthread_t _pthread_reuse[PTHREAD_THREADS_MAX];
+} /* threadStart */
- int _pthread_virgin_next = 0;
- int _pthread_reuse_top = -1;
+void
+_pthread_threadDestroy (pthread_t thread)
+{
+ if (thread != NULL)
+ {
+ _pthread_callUserDestroyRoutines (thread);
- The counter _pthread_virgin_next is an index into _pthread_virgins[],
- which can be thought of as a list, and _pthread_reuse_top is an
- index into _pthread_reuse[], which can be thought of as a LIFO stack.
+ if (thread->cancelEvent != NULL)
+ {
+ CloseHandle (thread->cancelEvent);
+ }
- Once taken from _pthread_virgins[], used and freed threads are only
- ever pushed back onto _pthread_reuse[].
+ free (thread);
+ }
- */
+} /* threadDestroy */
int
-_pthread_new_thread(pthread_t * thread)
+_pthread_tkAssocCreate (ThreadKeyAssoc ** assocP,
+ pthread_t thread,
+ pthread_key_t key)
+ /*
+ * -------------------------------------------------------------------
+ * This routine creates an association that
+ * is unique for the given (thread,key) combination.The association
+ * is referenced by both the thread and the key.
+ * This association allows us to determine what keys the
+ * current thread references and what threads a given key
+ * references.
+ * See the detailed description
+ * at the beginning of this file for further details.
+ *
+ * Notes:
+ * 1) New associations are pushed to the beginning of the
+ * chain so that the internal _pthread_selfThreadKey association
+ * is always last, thus allowing selfThreadExit to
+ * be implicitly called by pthread_exit last.
+ *
+ * Parameters:
+ * assocP
+ * address into which the association is returned.
+ * thread
+ * current running thread. If NULL, then association
+ * is only added to the key. A NULL thread indicates
+ * that the user called pthread_setspecific prior
+ * to starting a thread. That's ok.
+ * key
+ * key on which to create an association.
+ * Returns:
+ * 0 - if successful,
+ * -1 - general error
+ * -------------------------------------------------------------------
+ */
{
- pthread_t new_thread;
+ int result;
+ ThreadKeyAssoc *assoc;
+
+ /*
+ * Have to create an association and add it
+ * to both the key and the thread.
+ */
+ assoc = (ThreadKeyAssoc *)
+ calloc (1, sizeof (*assoc));
- if (_pthread_reuse_top >= 0)
+ if (assoc == NULL)
{
- new_thread = _pthread_reuse[_pthread_reuse_top--];
+ result = -1;
+ goto FAIL0;
}
- else
+
+ if ((result = pthread_mutex_init (&(assoc->lock), NULL)) !=
+ 0)
{
- if (_pthread_virgin_next < PTHREAD_THREADS_MAX)
- {
- new_thread = (pthread_t) &_pthread_virgins[_pthread_virgin_next++];
- }
- else
- {
- return EAGAIN;
- }
+ goto FAIL1;
}
- new_thread->win32handle = (HANDLE) NULL;
- new_thread->ptstatus = _PTHREAD_NEW;
- pthread_attr_init(&(new_thread->attr));
- new_thread->joinvalueptr = NULL;
- new_thread->cancelstate = PTHREAD_CANCEL_ENABLE;
- new_thread->canceltype = PTHREAD_CANCEL_DEFERRED;
- new_thread->cancel_pending = FALSE;
- new_thread->cleanupstack = NULL;
- new_thread->forkpreparestack = NULL;
- new_thread->forkparentstack = NULL;
- new_thread->forkchildstack = NULL;
+ assoc->thread = thread;
+ assoc->key = key;
- *thread = new_thread;
- _pthread_threads_count++;
+ /*
+ * Register assoc with key
+ */
+ if ((result = pthread_mutex_lock (&(key->threadsLock))) !=
+ 0)
+ {
+ goto FAIL2;
+ }
- return 0;
-}
+ assoc->nextThread = (ThreadKeyAssoc *) key->threads;
+ key->threads = (void *) assoc;
-int
-_pthread_delete_thread(_pthread_t * thread)
-{
- /* We don't check that the thread has been properly cleaned up, so
- it had better be done already. */
+ pthread_mutex_unlock (&(key->threadsLock));
+
+ if (thread != NULL)
+ {
+ /*
+ * Register assoc with thread
+ */
+ assoc->nextKey = (ThreadKeyAssoc *) thread->keys;
+ thread->keys = (void *) assoc;
+ }
+
+ *assocP = assoc;
+
+ return (result);
+
+ /*
+ * -------------
+ * Failure Code
+ * -------------
+ */
+FAIL2:
+ pthread_mutex_destroy (&(assoc->lock));
+
+FAIL1:
+ free (assoc);
- /* Release any keys */
+FAIL0:
- _pthread_destructor_run_all();
+ return (result);
- /* Remove the thread entry if necessary. */
+} /* tkAssocCreate */
- if (thread != NULL
- && thread->ptstatus == _PTHREAD_EXITED)
+
+void
+_pthread_tkAssocDestroy (ThreadKeyAssoc * assoc)
+ /*
+ * -------------------------------------------------------------------
+ * This routine releases all resources for the given ThreadKeyAssoc
+ * once it is no longer being referenced
+ * ie) both the key and thread have stopped referencing it.
+ *
+ * Parameters:
+ * assoc
+ * an instance of ThreadKeyAssoc.
+ * Returns:
+ * N/A
+ * -------------------------------------------------------------------
+ */
+{
+
+ if ((assoc != NULL) &&
+ (assoc->key == NULL && assoc->thread == NULL))
{
- pthread_attr_destroy(&(thread->attr));
- thread->win32handle = (HANDLE) NULL;
- thread->ptstatus = _PTHREAD_REUSE;
- _pthread_reuse[++_pthread_reuse_top] = thread;
- _pthread_threads_count--;
+ pthread_mutex_destroy (&(assoc->lock));
- return 0;
+ free (assoc);
}
- return EINVAL;
-}
+} /* tkAssocDestroy */
+
+
+void
+_pthread_callUserDestroyRoutines (pthread_t thread)
+ /*
+ * -------------------------------------------------------------------
+ * DOCPRIVATE
+ *
+ * This the routine runs through all thread keys and calls
+ * the destroy routines on the user's data for the current thread.
+ * It simulates the behaviour of POSIX Threads.
+ *
+ * PARAMETERS
+ * thread
+ * an instance of pthread_t
+ *
+ * RETURNS
+ * N/A
+ * -------------------------------------------------------------------
+ */
+{
+ ThreadKeyAssoc **nextP;
+ ThreadKeyAssoc *assoc;
+
+ if (thread != NULL)
+ {
+ /*
+ * Run through all Thread<-->Key associations
+ * for the current thread.
+ * If the pthread_key_t still exits (ie the assoc->key
+ * is not NULL) then call the user's TSD destroy routine.
+ * Notes:
+ * If assoc->key is NULL, then the user previously called
+ * PThreadKeyDestroy. The association is now only referenced
+ * by the current thread and must be released; otherwise
+ * the assoc will be destroyed when the key is destroyed.
+ */
+ nextP = (ThreadKeyAssoc **) & (thread->keys);
+ assoc = *nextP;
+
+ while (assoc != NULL)
+ {
+
+ if (pthread_mutex_lock (&(assoc->lock)) == 0)
+ {
+ pthread_key_t k;
+ if ((k = assoc->key) != NULL)
+ {
+ /*
+ * Key still active; pthread_key_delete
+ * will block on this same mutex before
+ * it can release actual key; therefore,
+ * key is valid and we can call the destroy
+ * routine;
+ */
+ void *value = NULL;
+
+ value = pthread_getspecific (k);
+ if (value != NULL && k->destructor != NULL)
+ {
+
+ __try
+ {
+ /*
+ * Run the caller's cleanup routine.
+ */
+ (*(k->destructor)) (value);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ /*
+ * A system unexpected exception had occurred
+ * running the user's destructor.
+ * We get control back within this block.
+ */
+ }
+ }
+ }
+
+ /*
+ * mark assoc->thread as NULL to indicate the
+ * thread no longer references this association
+ */
+ assoc->thread = NULL;
+
+ /*
+ * Remove association from the pthread_t chain
+ */
+ *nextP = assoc->nextKey;
+
+ pthread_mutex_unlock (&(assoc->lock));
+
+ _pthread_tkAssocDestroy (assoc);
+
+ assoc = *nextP;
+ }
+ }
+ }
+
+} /* callUserDestroyRoutines */
+
+/* </JEB> */
+