| /* |
| Copyright (c) 2011, 2014 mingw-w64 project |
| Copyright (c) 2015 Intel Corporation |
| |
| Permission is hereby granted, free of charge, to any person obtaining a |
| copy of this software and associated documentation files (the "Software"), |
| to deal in the Software without restriction, including without limitation |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| and/or sell copies of the Software, and to permit persons to whom the |
| Software is furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include <windows.h> |
| #include <stdio.h> |
| #include <malloc.h> |
| #include <stdbool.h> |
| #include "pthread.h" |
| #include "misc.h" |
| |
| typedef enum { |
| Unlocked, /* Not locked. */ |
| Locked, /* Locked but without waiters. */ |
| Waiting, /* Locked, may have waiters. */ |
| } mutex_state_t; |
| |
| typedef enum { |
| Normal, |
| Errorcheck, |
| Recursive, |
| } mutex_type_t; |
| |
| /* The heap-allocated part of a mutex. */ |
| typedef struct { |
| mutex_state_t state; |
| mutex_type_t type; |
| HANDLE event; /* Auto-reset event, or NULL if not yet allocated. */ |
| unsigned rec_lock; /* For recursive mutexes, the number of times the |
| mutex has been locked in excess by the same thread. */ |
| volatile DWORD owner; /* For recursive and error-checking mutexes, the |
| ID of the owning thread if the mutex is locked. */ |
| } mutex_impl_t; |
| |
| /* Whether a mutex is still a static initializer (not a pointer to |
| a mutex_impl_t). */ |
| static bool |
| is_static_initializer(pthread_mutex_t m) |
| { |
| /* Treat 0 as a static initializer as well (for normal mutexes), |
| to tolerate sloppy code in libgomp. (We should rather fix that code!) */ |
| intptr_t v = (intptr_t)m; |
| return v >= -3 && v <= 0; |
| /* Should be simple: |
| return (uintptr_t)m >= (uintptr_t)-3; */ |
| } |
| |
| /* Create and return the implementation part of a mutex from a static |
| initialiser. Return NULL on out-of-memory error. */ |
| static WINPTHREADS_ATTRIBUTE((noinline)) mutex_impl_t * |
| mutex_impl_init(pthread_mutex_t *m, mutex_impl_t *mi) |
| { |
| mutex_impl_t *new_mi = malloc(sizeof(mutex_impl_t)); |
| if (new_mi == NULL) |
| return NULL; |
| new_mi->state = Unlocked; |
| new_mi->type = (mi == (void *)PTHREAD_RECURSIVE_MUTEX_INITIALIZER ? Recursive |
| : mi == (void *)PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ? Errorcheck |
| : Normal); |
| new_mi->event = NULL; |
| new_mi->rec_lock = 0; |
| new_mi->owner = (DWORD)-1; |
| if (__sync_bool_compare_and_swap(m, (pthread_mutex_t)mi, (pthread_mutex_t)new_mi)) { |
| return new_mi; |
| } else { |
| /* Someone created the struct before us. */ |
| free(new_mi); |
| return (mutex_impl_t *)*m; |
| } |
| } |
| |
| #define likely(cond) __builtin_expect((cond) != 0, 1) |
| #define unlikely(cond) __builtin_expect((cond) != 0, 0) |
| |
| /* Return the implementation part of a mutex, creating it if necessary. |
| Return NULL on out-of-memory error. */ |
| static inline mutex_impl_t * |
| mutex_impl(pthread_mutex_t *m) |
| { |
| mutex_impl_t *mi = (mutex_impl_t *)*m; |
| if (is_static_initializer((pthread_mutex_t)mi)) { |
| return mutex_impl_init(m, mi); |
| } else { |
| /* mi cannot be null here; avoid a test in the fast path. */ |
| if (mi == NULL) |
| __builtin_unreachable(); |
| return mi; |
| } |
| } |
| |
| /* Lock a mutex. Give up after 'timeout' ms (with ETIMEDOUT), |
| or never if timeout=INFINITE. */ |
| static inline int |
| pthread_mutex_lock_intern (pthread_mutex_t *m, DWORD timeout) |
| { |
| mutex_impl_t *mi = mutex_impl(m); |
| if (mi == NULL) |
| return ENOMEM; |
| |
| mutex_state_t old_state = __sync_lock_test_and_set(&mi->state, Locked); |
| if (unlikely(old_state != Unlocked)) { |
| /* The mutex is already locked. */ |
| |
| if (mi->type != Normal) { |
| /* Recursive or Errorcheck */ |
| if (mi->owner == GetCurrentThreadId()) { |
| /* FIXME: A recursive mutex should not need two atomic ops when locking |
| recursively. We could rewrite by doing compare-and-swap instead of |
| test-and-set the first time, but it would lead to more code |
| duplication and add a conditional branch to the critical path. */ |
| __sync_bool_compare_and_swap(&mi->state, Locked, old_state); |
| if (mi->type == Recursive) { |
| mi->rec_lock++; |
| return 0; |
| } else { |
| /* type == Errorcheck */ |
| return EDEADLK; |
| } |
| } |
| } |
| |
| /* Make sure there is an event object on which to wait. */ |
| if (mi->event == NULL) { |
| /* Make an auto-reset event object. */ |
| HANDLE ev = CreateEvent(NULL, false, false, NULL); |
| if (ev == NULL) { |
| switch (GetLastError()) { |
| case ERROR_ACCESS_DENIED: |
| return EPERM; |
| default: |
| return ENOMEM; /* Probably accurate enough. */ |
| } |
| } |
| if (!__sync_bool_compare_and_swap(&mi->event, NULL, ev)) { |
| /* Someone created the event before us. */ |
| CloseHandle(ev); |
| } |
| } |
| |
| /* At this point, mi->event is non-NULL. */ |
| |
| while (__sync_lock_test_and_set(&mi->state, Waiting) != Unlocked) { |
| /* For timed locking attempts, it is possible (although unlikely) |
| that we are woken up but someone else grabs the lock before us, |
| and we have to go back to sleep again. In that case, the total |
| wait may be longer than expected. */ |
| |
| unsigned r = _pthread_wait_for_single_object(mi->event, timeout); |
| switch (r) { |
| case WAIT_TIMEOUT: |
| return ETIMEDOUT; |
| case WAIT_OBJECT_0: |
| break; |
| default: |
| return EINVAL; |
| } |
| } |
| } |
| |
| if (mi->type != Normal) |
| mi->owner = GetCurrentThreadId(); |
| |
| return 0; |
| } |
| |
| int |
| pthread_mutex_lock (pthread_mutex_t *m) |
| { |
| return pthread_mutex_lock_intern (m, INFINITE); |
| } |
| |
| int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts) |
| { |
| unsigned long long patience; |
| if (ts != NULL) { |
| unsigned long long end = _pthread_time_in_ms_from_timespec(ts); |
| unsigned long long now = _pthread_time_in_ms(); |
| patience = end > now ? end - now : 0; |
| if (patience > 0xffffffff) |
| patience = INFINITE; |
| } else { |
| patience = INFINITE; |
| } |
| return pthread_mutex_lock_intern(m, patience); |
| } |
| |
| int pthread_mutex_unlock(pthread_mutex_t *m) |
| { |
| /* Here m might an initialiser of an error-checking or recursive mutex, in |
| which case the behaviour is well-defined, so we can't skip this check. */ |
| mutex_impl_t *mi = mutex_impl(m); |
| if (mi == NULL) |
| return ENOMEM; |
| |
| if (unlikely(mi->type != Normal)) { |
| if (mi->state == Unlocked) |
| return EINVAL; |
| if (mi->owner != GetCurrentThreadId()) |
| return EPERM; |
| if (mi->rec_lock > 0) { |
| mi->rec_lock--; |
| return 0; |
| } |
| mi->owner = (DWORD)-1; |
| } |
| if (unlikely(__sync_lock_test_and_set(&mi->state, Unlocked) == Waiting)) { |
| if (!SetEvent(mi->event)) |
| return EPERM; |
| } |
| return 0; |
| } |
| |
| int pthread_mutex_trylock(pthread_mutex_t *m) |
| { |
| mutex_impl_t *mi = mutex_impl(m); |
| if (mi == NULL) |
| return ENOMEM; |
| |
| if (__sync_bool_compare_and_swap(&mi->state, Unlocked, Locked)) { |
| if (mi->type != Normal) |
| mi->owner = GetCurrentThreadId(); |
| return 0; |
| } else { |
| if (mi->type == Recursive && mi->owner == GetCurrentThreadId()) { |
| mi->rec_lock++; |
| return 0; |
| } |
| return EBUSY; |
| } |
| } |
| |
| int |
| pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a) |
| { |
| pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER; |
| if (a != NULL) { |
| int pshared; |
| if (pthread_mutexattr_getpshared(a, &pshared) == 0 |
| && pshared == PTHREAD_PROCESS_SHARED) |
| return ENOSYS; |
| |
| int type; |
| if (pthread_mutexattr_gettype(a, &type) == 0) { |
| switch (type) { |
| case PTHREAD_MUTEX_ERRORCHECK: |
| init = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER; |
| break; |
| case PTHREAD_MUTEX_RECURSIVE: |
| init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; |
| break; |
| default: |
| init = PTHREAD_MUTEX_INITIALIZER; |
| break; |
| } |
| } |
| } |
| *m = init; |
| return 0; |
| } |
| |
| int pthread_mutex_destroy (pthread_mutex_t *m) |
| { |
| mutex_impl_t *mi = (mutex_impl_t *)*m; |
| if (!is_static_initializer((pthread_mutex_t)mi)) { |
| if (mi->event != NULL) |
| CloseHandle(mi->event); |
| free(mi); |
| /* Sabotage attempts to re-use the mutex before initialising it again. */ |
| *m = (pthread_mutex_t)NULL; |
| } |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_init(pthread_mutexattr_t *a) |
| { |
| *a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3); |
| return 0; |
| } |
| |
| int pthread_mutexattr_destroy(pthread_mutexattr_t *a) |
| { |
| if (!a) |
| return EINVAL; |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type) |
| { |
| if (!a || !type) |
| return EINVAL; |
| |
| *type = *a & 3; |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) |
| { |
| if (!a || (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE && type != PTHREAD_MUTEX_ERRORCHECK)) |
| return EINVAL; |
| *a &= ~3; |
| *a |= type; |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type) |
| { |
| if (!a || !type) |
| return EINVAL; |
| *type = (*a & 4 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE); |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type) |
| { |
| int r = 0; |
| if (!a || (type != PTHREAD_PROCESS_SHARED |
| && type != PTHREAD_PROCESS_PRIVATE)) |
| return EINVAL; |
| if (type == PTHREAD_PROCESS_SHARED) |
| { |
| type = PTHREAD_PROCESS_PRIVATE; |
| r = ENOSYS; |
| } |
| type = (type == PTHREAD_PROCESS_SHARED ? 4 : 0); |
| |
| *a &= ~4; |
| *a |= type; |
| |
| return r; |
| } |
| |
| int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type) |
| { |
| *type = *a & (8 + 16); |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type) |
| { |
| if ((type & (8 + 16)) != 8 + 16) return EINVAL; |
| |
| *a &= ~(8 + 16); |
| *a |= type; |
| |
| return 0; |
| } |
| |
| int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio) |
| { |
| *prio = *a / PTHREAD_PRIO_MULT; |
| return 0; |
| } |
| |
| int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio) |
| { |
| *a &= (PTHREAD_PRIO_MULT - 1); |
| *a += prio * PTHREAD_PRIO_MULT; |
| |
| return 0; |
| } |