diff options
Diffstat (limited to 'mutex.c')
-rw-r--r-- | mutex.c | 508 |
1 files changed, 479 insertions, 29 deletions
@@ -10,85 +10,505 @@ #include "pthread.h" #include "implement.h" + +static int +_mutex_check_need_init(pthread_mutex_t *mutex) +{ + int result = 0; + + /* + * The following guarded test is specifically for statically + * initialised mutexes (via PTHREAD_MUTEX_INITIALIZER). + * + * Note that by not providing this synchronisation we risk + * introducing race conditions into applications which are + * correctly written. + * + * Approach + * -------- + * We know that static mutexes will not be PROCESS_SHARED + * so we can serialise access to internal state using + * Win32 Critical Sections rather than Win32 Mutexes. + * + * We still have a problem in that we would like a per-mutex + * lock, but attempting to create one will again lead + * to a race condition. We are forced to use a global + * lock in this instance. + * + * If using a single global lock slows applications down too much, + * multiple global locks could be created and hashed on some random + * value associated with each mutex, the pointer perhaps. At a guess, + * a good value for the optimal number of global locks might be + * the number of processors + 1. + * + * We need to maintain the following per-mutex state independently: + * - mutex staticinit (true iff static mutex and still + * needs to be initialised) + * - mutex valid (false iff mutex has been destroyed) + * + * For example, a mutex initialised by PTHREAD_MUTEX_INITIALIZER + * in this implementation will be valid but uninitialised until the thread + * attempts to lock it. It can also be destroyed (made invalid) before + * ever being locked. + */ + EnterCriticalSection(&_pthread_mutex_test_init_lock); + + /* + * We got here because staticinit tested true. + * Check staticinit again inside the critical section + * and only initialise if the mutex is valid (not been destroyed). + * If a static mutex has been destroyed, the application can + * re-initialise it only by calling pthread_mutex_init() + * explicitly. + */ + if (mutex->staticinit && mutex->valid) + { + result = pthread_mutex_init(mutex, NULL); + } + + LeaveCriticalSection(&_pthread_mutex_test_init_lock); + + return(result); +} + int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { + int result = 0; + if (mutex == NULL) { return EINVAL; } - /* Create a critical section. */ - InitializeCriticalSection(&mutex->cs); + mutex->mutex = 0; - /* Mark as valid. */ - mutex->valid = 1; + /* + * Assuming any race condition here is harmless. + */ + if (mutex->valid && !mutex->staticinit) + { + return EBUSY; + } - return 0; + if (attr != NULL + && *attr != NULL + && (*attr)->pshared == PTHREAD_PROCESS_SHARED + ) + { + /* + * Creating mutex that can be shared between + * processes. + */ +#if _POSIX_THREAD_PROCESS_SHARED + + /* + * Not implemented yet. + */ + +#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet. + + mutex->mutex = CreateMutex ( + NULL, + FALSE, + ????); + result = (mutex->mutex == 0) ? EAGAIN : 0; + +#else + + result = ENOSYS; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + } + else + { + if (_pthread_try_enter_critical_section != NULL + || (attr != NULL + && *attr != NULL + && (*attr)->forcecs == 1) + ) + { + /* + * Create a critical section. + */ + InitializeCriticalSection(&mutex->cs); + } + else + { + /* + * Create a mutex that can only be used within the + * current process + */ + mutex->mutex = CreateMutex (NULL, + FALSE, + NULL); + result = (mutex->mutex == 0) ? EAGAIN : 0; + } + } + + if (result == 0) + { + mutex->staticinit = 0; + + /* Mark as valid. */ + mutex->valid = 1; + } + + return(result); } int pthread_mutex_destroy(pthread_mutex_t *mutex) { + int result = 0; + if (mutex == NULL) { return EINVAL; } - DeleteCriticalSection(&mutex->cs); - - /* Mark as invalid. */ - mutex->valid = 0; + /* + * Check to see if we have something to delete. + */ + if (!mutex->staticinit) + { + if (mutex->mutex == 0) + { + DeleteCriticalSection(&mutex->cs); + } + else + { + result = (CloseHandle (mutex->mutex) ? 0 : EINVAL); + } + } - return 0; + if (result == 0) + { + mutex->mutex = 0; + + /* Mark as invalid. */ + mutex->valid = 0; + } + + return(result); } int -pthread_mutexattr_init(pthread_mutexattr_t *attr) +pthread_mutexattr_init (pthread_mutexattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a mutex attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * + * DESCRIPTION + * Initializes a mutex attributes object with default + * attributes. + * + * NOTES: + * 1) Used to define mutex types + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + pthread_mutexattr_t attr_result; + int result = 0; + + attr_result = calloc (1, sizeof (*attr_result)); + + result = (attr_result == NULL) + ? ENOMEM + : 0; + + *attr = attr_result; + + return (result); + +} /* pthread_mutexattr_init */ + + +int +pthread_mutexattr_destroy (pthread_mutexattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a mutex attributes object. The object can + * no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * + * DESCRIPTION + * Destroys a mutex attributes object. The object can + * no longer be used. + * + * NOTES: + * 1) Does not affect mutexes 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_mutexattr_destroy */ + + +int +pthread_mutexattr_setforcecs_np(pthread_mutexattr_t *attr, + int forcecs) { - if (attr == NULL) + if (attr == NULL || *attr == NULL) { /* This is disallowed. */ return EINVAL; } - /* None of the optional attributes are supported yet. */ + (*attr)->forcecs = forcecs; + return 0; } + int -pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, + int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether mutexes created with 'attr' can be + * shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_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 + * 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 retrieved attribute, + * EINVAL 'attr' is invalid, + * + * ------------------------------------------------------ + */ { - /* Nothing to do. */ - return 0; -} + int result; + + if ((attr != NULL && *attr != NULL) && + (pshared != NULL)) + { + *pshared = (*attr)->pshared; + result = 0; + } + else + { + *pshared = PTHREAD_PROCESS_PRIVATE; + result = EINVAL; + } + + return (result); + +} /* pthread_mutexattr_getpshared */ + + +int +pthread_mutexattr_setpshared (pthread_mutexattr_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_mutexattr_setpshared */ + int pthread_mutex_lock(pthread_mutex_t *mutex) { - if (!mutex->valid) + int result = 0; + + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check 'staticinit' + * again inside the guarded section of _mutex_check_need_init() + * to avoid race conditions. + */ + if (mutex->staticinit) { - pthread_mutex_init(mutex, NULL); + result = _mutex_check_need_init(mutex); } - EnterCriticalSection(&mutex->cs); - return 0; + + if (result == 0) + { + if (mutex->mutex == 0) + { + EnterCriticalSection(&mutex->cs); + } + else + { + result = (WaitForSingleObject(mutex->mutex, INFINITE) + == WAIT_OBJECT_0) + ? 0 + : EINVAL; + } + } + + return(result); } int pthread_mutex_unlock(pthread_mutex_t *mutex) { - if (!mutex->valid) + int result = 0; + + /* + * If the thread calling us holds the mutex then there is no + * race condition. If another thread holds the + * lock then we shouldn't be in here. + */ + if (!mutex->staticinit && mutex->valid) { - return EINVAL; + if (mutex->mutex == 0) + { + LeaveCriticalSection(&mutex->cs); + } + else + { + result = (ReleaseMutex (mutex->mutex) ? 0 : EINVAL); + } } - LeaveCriticalSection(&mutex->cs); - return 0; + else + { + result = EINVAL; + } + + return(result); } int pthread_mutex_trylock(pthread_mutex_t *mutex) { - if (_pthread_try_enter_critical_section == NULL) + int result = 0; + + if (mutex->mutex == 0 && _pthread_try_enter_critical_section == NULL) { /* TryEnterCriticalSection does not exist in the OS; return ENOSYS. */ return ENOSYS; @@ -99,10 +519,40 @@ pthread_mutex_trylock(pthread_mutex_t *mutex) return EINVAL; } - if (!mutex->valid) + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check 'staticinit' + * again inside the guarded section of _mutex_check_need_init() + * to avoid race conditions. + */ + if (mutex->staticinit) + { + result = _mutex_check_need_init(mutex); + } + + if (result == 0) { - pthread_mutex_init(mutex, NULL); + if (mutex->mutex == 0) + { + if ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) + { + result = EBUSY; + } + } + else + { + DWORD status; + + status = WaitForSingleObject (mutex->mutex, 0); + + if (status != WAIT_OBJECT_0) + { + result = ((status == WAIT_TIMEOUT) + ? EBUSY + : EINVAL); + } + } } - return ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) ? EBUSY : 0; + return(result); } |