From a416ab17ecf9f2cb0f1e3f7bd645a8d1ce690ca2 Mon Sep 17 00:00:00 2001 From: rpj Date: Mon, 18 Feb 2002 03:16:52 +0000 Subject: Major reorganisation of source code; new routine and tests added. --- private.c | 980 +------------------------------------------------------------- 1 file changed, 12 insertions(+), 968 deletions(-) (limited to 'private.c') diff --git a/private.c b/private.c index e0ca43a..19a7aeb 100644 --- a/private.c +++ b/private.c @@ -36,974 +36,18 @@ */ #include "pthread.h" -#include "semaphore.h" #include "implement.h" -int -ptw32_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 - * ptw32_processInitialized to TRUE. - * - * RESULTS - * TRUE if successful, - * FALSE otherwise - * - * ------------------------------------------------------ - */ -{ - if (ptw32_processInitialized) { - /* - * ignore if already initialized. this is useful for - * programs that uses a non-dll pthread - * library. such programs must call ptw32_processInitialize() explicitely, - * since this initialization routine is automatically called only when - * the dll is loaded. - */ - return TRUE; - } - - ptw32_processInitialized = TRUE; - - /* - * Initialize Keys - */ - if ((pthread_key_create (&ptw32_selfThreadKey, NULL) != 0) || - (pthread_key_create (&ptw32_cleanupKey, NULL) != 0)) - { - - ptw32_processTerminate (); - } - - /* - * Set up the global test and init check locks. - */ - InitializeCriticalSection(&ptw32_mutex_test_init_lock); - InitializeCriticalSection(&ptw32_cond_test_init_lock); - InitializeCriticalSection(&ptw32_rwlock_test_init_lock); - InitializeCriticalSection(&ptw32_spinlock_test_init_lock); - - return (ptw32_processInitialized); - -} /* processInitialize */ - -void -ptw32_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 - * ptw32_processInitialized to FALSE - * - * RESULTS - * N/A - * - * ------------------------------------------------------ - */ -{ - if (ptw32_processInitialized) - { - - if (ptw32_selfThreadKey != NULL) - { - /* - * Release ptw32_selfThreadKey - */ - pthread_key_delete (ptw32_selfThreadKey); - - ptw32_selfThreadKey = NULL; - } - - if (ptw32_cleanupKey != NULL) - { - /* - * Release ptw32_cleanupKey - */ - pthread_key_delete (ptw32_cleanupKey); - - ptw32_cleanupKey = NULL; - } - - /* - * Destroy the global test and init check locks. - */ - DeleteCriticalSection(&ptw32_spinlock_test_init_lock); - DeleteCriticalSection(&ptw32_rwlock_test_init_lock); - DeleteCriticalSection(&ptw32_cond_test_init_lock); - DeleteCriticalSection(&ptw32_mutex_test_init_lock); - - ptw32_processInitialized = FALSE; - } - -} /* processTerminate */ - -#ifdef __CLEANUP_SEH - -static DWORD -ExceptionFilter (EXCEPTION_POINTERS * ep, DWORD * ei) -{ - switch (ep->ExceptionRecord->ExceptionCode) - { - case EXCEPTION_PTW32_SERVICES: - { - DWORD param; - DWORD numParams = ep->ExceptionRecord->NumberParameters; - - numParams = (numParams > 3) ? 3 : numParams; - - for (param = 0; param < numParams; param++) - { - ei[param] = ep->ExceptionRecord->ExceptionInformation[param]; - } - - return EXCEPTION_EXECUTE_HANDLER; - break; - } - default: - { - /* - * A system unexpected exception has occurred running the user's - * routine. We need to cleanup before letting the exception - * out of thread scope. - */ - pthread_t self = pthread_self(); - - (void) pthread_mutex_destroy(&self->cancelLock); - ptw32_callUserDestroyRoutines(self); - - return EXCEPTION_CONTINUE_SEARCH; - break; - } - } -} - -#elif defined(__CLEANUP_CXX) - -#if defined(_MSC_VER) -#include -static terminate_function ptw32_oldTerminate; -#else -#include -static terminate_handler ptw32_oldTerminate; -#endif - -#if 0 -#include -static pthread_mutex_t termLock = PTHREAD_MUTEX_INITIALIZER; -#endif - -void -ptw32_terminate () -{ - pthread_t self = pthread_self(); -#if 0 - FILE * fp; - pthread_mutex_lock(&termLock); - fp = fopen("pthread.log", "a"); - fprintf(fp, "Terminate\n"); - fclose(fp); - pthread_mutex_unlock(&termLock); -#endif - set_terminate(ptw32_oldTerminate); - (void) pthread_mutex_destroy(&self->cancelLock); - ptw32_callUserDestroyRoutines(self); - terminate(); -} - -#endif /* _MSC_VER */ - -#if ! defined (__MINGW32__) || defined (__MSVCRT__) -unsigned __stdcall -#else -void -#endif -ptw32_threadStart (void * vthreadParms) -{ - ThreadParms *threadParms = (ThreadParms *) vthreadParms; - pthread_t self; - void *(*start) (void *); - void *arg; - -#ifdef __CLEANUP_SEH - DWORD ei[] = {0,0,0}; -#endif - -#ifdef __CLEANUP_C - int setjmp_rc; -#endif - - void * status = (void *) 0; - - self = threadParms->tid; - start = threadParms->start; - arg = threadParms->arg; - - free (threadParms); - -#if defined (__MINGW32__) && ! defined (__MSVCRT__) - /* - * beginthread does not return the thread id and is running - * before it returns us the thread handle, and so we do it here. - */ - self->thread = GetCurrentThreadId (); - /* - * Here we're using cancelLock as a general-purpose lock - * to make the new thread wait until the creating thread - * has the new handle. - */ - if (pthread_mutex_lock(&self->cancelLock) == 0) - { - (void) pthread_mutex_unlock(&self->cancelLock); - } -#endif - - pthread_setspecific (ptw32_selfThreadKey, self); - - self->state = PThreadStateRunning; - -#ifdef __CLEANUP_SEH - - __try - { - /* - * Run the caller's routine; - */ - status = self->exitStatus = (*start) (arg); - -#ifdef _UWIN - if (--pthread_count <= 0) - exit(0); -#endif - - } - __except (ExceptionFilter(GetExceptionInformation(), ei)) - { - switch (ei[0]) - { - case PTW32_EPS_CANCEL: - status = PTHREAD_CANCELED; -#ifdef _UWIN - if (--pthread_count <= 0) - exit(0); -#endif - break; - case PTW32_EPS_EXIT: - status = self->exitStatus; - break; - default: - status = PTHREAD_CANCELED; - break; - } - } - -#else /* __CLEANUP_SEH */ - -#ifdef __CLEANUP_C - - setjmp_rc = setjmp( self->start_mark ); - - if( 0 == setjmp_rc ) { - - /* - * Run the caller's routine; - */ - status = self->exitStatus = (*start) (arg); - } - - else { - - switch (setjmp_rc) - { - case PTW32_EPS_CANCEL: - status = PTHREAD_CANCELED; - break; - case PTW32_EPS_EXIT: - status = self->exitStatus; - break; - default: - status = PTHREAD_CANCELED; - break; - } - } - -#else /* __CLEANUP_C */ - -#ifdef __CLEANUP_CXX - - ptw32_oldTerminate = set_terminate(&ptw32_terminate); - - try - { - /* - * Run the caller's routine in a nested try block so that we - * can run the user's terminate function, which may call - * pthread_exit() or be canceled. - */ - try - { - status = self->exitStatus = (*start) (arg); - } - catch (ptw32_exception &) - { - /* - * Pass these through to the outer block. - */ - throw; - } - catch(...) - { - /* - * We want to run the user's terminate function if supplied. - * That function may call pthread_exit() or be canceled, which will - * be handled by the outer try block. - * - * ptw32_terminate() will be called if there is no user - * supplied function. - */ - -#if defined(_MSC_VER) - terminate_function term_func = set_terminate(0); -#else - terminate_handler term_func = set_terminate(0); -#endif - - set_terminate(term_func); - - if (term_func != 0) { - term_func(); - } - - throw; - } - } - catch (ptw32_exception_cancel &) - { - /* - * Thread was canceled. - */ - status = self->exitStatus = PTHREAD_CANCELED; - } - catch (ptw32_exception_exit &) - { - /* - * Thread was exited via pthread_exit(). - */ - status = self->exitStatus; - } - catch (...) - { - /* - * A system unexpected exception has occurred running the user's - * terminate routine. We get control back within this block - cleanup - * and release the exception out of thread scope. - */ - status = self->exitStatus = PTHREAD_CANCELED; - (void) pthread_mutex_lock(&self->cancelLock); - self->state = PThreadStateException; - (void) pthread_mutex_unlock(&self->cancelLock); - (void) pthread_mutex_destroy(&self->cancelLock); - (void) set_terminate(ptw32_oldTerminate); - ptw32_callUserDestroyRoutines(self); - throw; - - /* - * Never reached. - */ - } - - (void) set_terminate(ptw32_oldTerminate); - -#else - -#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. - -#endif /* __CLEANUP_CXX */ -#endif /* __CLEANUP_C */ -#endif /* __CLEANUP_SEH */ - - (void) pthread_mutex_lock(&self->cancelLock); - self->state = PThreadStateLast; - (void) pthread_mutex_unlock(&self->cancelLock); - - - (void) pthread_mutex_destroy(&self->cancelLock); - -#if 1 - if (self->detachState == PTHREAD_CREATE_DETACHED) - { - /* - * We need to cleanup the pthread now in case we have - * been statically linked, in which case the cleanup - * in dllMain won't get done. Joinable threads will - * be cleaned up by pthread_join(). - * - * Note that implicitly created pthreads (those created - * for Win32 threads which have called pthreads routines) - * must be cleaned up explicitly by the application - * (by calling pthread_win32_thread_detach_np()) if - * this library has been statically linked. For the dll, - * dllMain will do the cleanup automatically. - */ - (void) pthread_win32_thread_detach_np (); - } - else - { - ptw32_callUserDestroyRoutines (self); - } -#else - ptw32_callUserDestroyRoutines (self); -#endif - -#if ! defined (__MINGW32__) || defined (__MSVCRT__) - _endthreadex ((unsigned) status); -#else - _endthread (); -#endif - - /* - * Never reached. - */ - -#if ! defined (__MINGW32__) || defined (__MSVCRT__) - return (unsigned) status; -#endif - -} /* ptw32_threadStart */ - -void -ptw32_threadDestroy (pthread_t thread) -{ - if (thread != NULL) - { - ptw32_callUserDestroyRoutines (thread); - - if (thread->cancelEvent != NULL) - { - CloseHandle (thread->cancelEvent); - } - -#if ! defined (__MINGW32__) || defined (__MSVCRT__) - /* See documentation for endthread vs endthreadex. */ - if( thread->threadH != 0 ) - { - CloseHandle( thread->threadH ); - } -#endif - - free (thread); - } - -} /* ptw32_threadDestroy */ - -int -ptw32_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 ptw32_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, - * ENOMEM - not enough memory to create assoc or other object - * EINVAL - an internal error occurred - * ENOSYS - an internal error occurred - * ------------------------------------------------------------------- - */ -{ - 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 (assoc == NULL) - { - result = ENOMEM; - goto FAIL0; - } - - /* - * Initialise only when used for the first time. - */ - assoc->lock = PTHREAD_MUTEX_INITIALIZER; - assoc->thread = thread; - assoc->key = key; - - /* - * Register assoc with key - */ - if ((result = pthread_mutex_lock (&(key->threadsLock))) != 0) - { - goto FAIL2; - } - - assoc->nextThread = (ThreadKeyAssoc *) key->threads; - key->threads = (void *) assoc; - - 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)); - free (assoc); - -FAIL0: - - return (result); - -} /* ptw32_tkAssocCreate */ - - -void -ptw32_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_mutex_destroy (&(assoc->lock)); - - free (assoc); - } - -} /* ptw32_tkAssocDestroy */ - - -void -ptw32_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) - { - -#ifdef __cplusplus - - try - { - /* - * Run the caller's cleanup routine. - */ - (*(k->destructor)) (value); - } - catch (...) - { - /* - * A system unexpected exception has occurred - * running the user's destructor. - * We get control back within this block in case - * the application has set up it's own terminate - * handler. Since we are leaving the thread we - * should not get any internal pthreads - * exceptions. - */ - terminate(); - } - -#else /* __cplusplus */ - - /* - * Run the caller's cleanup routine. - */ - (*(k->destructor)) (value); - -#endif /* __cplusplus */ - } - } - - /* - * 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)); - - ptw32_tkAssocDestroy (assoc); - - assoc = *nextP; - } - } - } - -} /* ptw32_callUserDestroyRoutines */ - - - -#ifdef NEED_FTIME - -/* - * time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds - */ -#define TIMESPEC_TO_FILETIME_OFFSET \ - ( ((LONGLONG) 27111902 << 32) + (LONGLONG) 3577643008 ) - -static INLINE void -timespec_to_filetime(const struct timespec *ts, FILETIME *ft) - /* - * ------------------------------------------------------------------- - * converts struct timespec - * where the time is expressed in seconds and nanoseconds from Jan 1, 1970. - * into FILETIME (as set by GetSystemTimeAsFileTime), where the time is - * expressed in 100 nanoseconds from Jan 1, 1601, - * ------------------------------------------------------------------- - */ -{ - *(LONGLONG *)ft = ts->tv_sec * 10000000 + (ts->tv_nsec + 50) / 100 + TIMESPEC_TO_FILETIME_OFFSET; -} - -static INLINE void -filetime_to_timespec(const FILETIME *ft, struct timespec *ts) - /* - * ------------------------------------------------------------------- - * converts FILETIME (as set by GetSystemTimeAsFileTime), where the time is - * expressed in 100 nanoseconds from Jan 1, 1601, - * into struct timespec - * where the time is expressed in seconds and nanoseconds from Jan 1, 1970. - * ------------------------------------------------------------------- - */ -{ - ts->tv_sec = (int)((*(LONGLONG *)ft - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); - ts->tv_nsec = (int)((*(LONGLONG *)ft - TIMESPEC_TO_FILETIME_OFFSET - ((LONGLONG)ts->tv_sec * (LONGLONG)10000000)) * 100); -} - -#endif /* NEED_FTIME */ - - -DWORD -ptw32_get_exception_services_code(void) -{ -#ifdef __CLEANUP_SEH - - return EXCEPTION_PTW32_SERVICES; - -#else - - return (DWORD) NULL; - -#endif -} - - -void -ptw32_throw(DWORD exception) -{ -#ifdef __CLEANUP_C - pthread_t self = pthread_self(); -#endif - - -#ifdef __CLEANUP_SEH - DWORD exceptionInformation[3]; -#endif - - if (exception != PTW32_EPS_CANCEL && - exception != PTW32_EPS_EXIT) - { - /* Should never enter here */ - exit(1); - } - -#ifdef __CLEANUP_SEH - - - exceptionInformation[0] = (DWORD) (exception); - exceptionInformation[1] = (DWORD) (0); - exceptionInformation[2] = (DWORD) (0); - - RaiseException ( - EXCEPTION_PTW32_SERVICES, - 0, - 3, - exceptionInformation); - -#else /* __CLEANUP_SEH */ - -#ifdef __CLEANUP_C - - ptw32_pop_cleanup_all( 1 ); - - longjmp( self->start_mark, exception ); - -#else /* __CLEANUP_C */ - -#ifdef __CLEANUP_CXX - - switch (exception) - { - case PTW32_EPS_CANCEL: - throw ptw32_exception_cancel(); - break; - case PTW32_EPS_EXIT: - throw ptw32_exception_exit(); - break; - } - -#else - -#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. - -#endif /* __CLEANUP_CXX */ - -#endif /* __CLEANUP_C */ - -#endif /* __CLEANUP_SEH */ - - /* Never reached */ -} - -void -ptw32_pop_cleanup_all(int execute) -{ - while( NULL != ptw32_pop_cleanup(execute) ) { - } -} - - -/* - * ptw32_InterlockedCompareExchange -- - * - * Needed because W95 doesn't support InterlockedCompareExchange. - * It is only used when running the dll on W95. Other versions of - * Windows use the Win32 supported version, which may be running on - * different processor types. - * - * This can't be inlined because we need to know it's address so that - * we can call it through a pointer. - */ -PTW32_INTERLOCKED_LONG WINAPI -ptw32_InterlockedCompareExchange(PTW32_INTERLOCKED_LPLONG location, - PTW32_INTERLOCKED_LONG value, - PTW32_INTERLOCKED_LONG comparand) -{ - PTW32_INTERLOCKED_LONG result; - -#if defined(_M_IX86) || defined(_X86_) - -#if defined(_MSC_VER) - - _asm { - PUSH ecx - PUSH edx - MOV ecx,dword ptr [location] - MOV edx,dword ptr [value] - MOV eax,dword ptr [comparand] - LOCK CMPXCHG dword ptr [ecx],edx ; if (EAX == [ECX]), - ; [ECX] = EDX - ; else - ; EAX = [ECX] - MOV dword ptr [result], eax - POP edx - POP ecx - } - -#elif defined(__GNUC__) - - __asm__ - ( - "lock\n\t" - "cmpxchgl %3,(%0)" /* if (EAX == [location]), */ - /* [location] = value */ - /* else */ - /* EAX = [location] */ - :"=r" (location), "=a" (result) - :"0" (location), "q" (value), "a" (comparand) - : "memory" ); - -#endif - -#else - - /* - * If execution gets to here then we should be running on a Win95 system - * but either running on something other than an X86 processor, or a - * compiler other than MSVC or GCC. Pthreads-win32 doesn't support that - * platform (yet). - */ - - result = 0; - -#endif - - return result; -} - - -/* - * ptw32_getprocessors() - * - * Get the number of CPUs available to the process. - * - * If the available number of CPUs is 1 then pthread_spin_lock() - * will block rather than spin if the lock is already owned. - * - * pthread_spin_init() calls this routine when initialising - * a spinlock. If the number of available processors changes - * (after a call to SetProcessAffinityMask()) then only - * newly initialised spinlocks will notice. - */ -int -ptw32_getprocessors(int * count) -{ - DWORD vProcessCPUs; - DWORD vSystemCPUs; - int result = 0; - - if (GetProcessAffinityMask(GetCurrentProcess(), - &vProcessCPUs, - &vSystemCPUs)) - { - DWORD bit; - int CPUs = 0; - - for (bit = 1; bit != 0; bit <<= 1) - { - if (vProcessCPUs & bit) - { - CPUs++; - } - } - *count = CPUs; - } - else - { - result = EAGAIN; - } - - return(result); -} +#include "ptw32_is_attr.c" +#include "ptw32_processInitialize.c" +#include "ptw32_processTerminate.c" +#include "ptw32_threadStart.c" +#include "ptw32_threadDestroy.c" +#include "ptw32_tkAssocCreate.c" +#include "ptw32_tkAssocDestroy.c" +#include "ptw32_callUserDestroyRoutines.c" +#include "ptw32_timespec.c" +#include "ptw32_throw.c" +#include "ptw32_InterlockedCompareExchange.c" +#include "ptw32_getprocessors.c" -- cgit v1.2.3