| 1 | /* |
| 2 | Copyright (c) 2011, 2014 mingw-w64 project |
| 3 | Copyright (c) 2015 Intel Corporation |
| 4 | |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a |
| 6 | copy of this software and associated documentation files (the "Software"), |
| 7 | to deal in the Software without restriction, including without limitation |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 9 | and/or sell copies of the Software, and to permit persons to whom the |
| 10 | Software is furnished to do so, subject to the following conditions: |
| 11 | |
| 12 | The above copyright notice and this permission notice shall be included in |
| 13 | all copies or substantial portions of the Software. |
| 14 | |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| 21 | DEALINGS IN THE SOFTWARE. |
| 22 | */ |
| 23 | |
| 24 | #ifdef HAVE_CONFIG_H |
| 25 | #include "config.h" |
| 26 | #endif |
| 27 | |
| 28 | #include <malloc.h> |
| 29 | #include <stdio.h> |
| 30 | |
| 31 | #define WIN32_LEAN_AND_MEAN |
| 32 | #include <windows.h> |
| 33 | |
| 34 | #define WINPTHREAD_MUTEX_DECL WINPTHREAD_API |
| 35 | |
| 36 | /* public header files */ |
| 37 | #include "pthread.h" |
| 38 | /* internal header files */ |
| 39 | #include "misc.h" |
| 40 | |
| 41 | typedef enum { |
| 42 | Unlocked, /* Not locked. */ |
| 43 | Locked, /* Locked but without waiters. */ |
| 44 | Waiting, /* Locked, may have waiters. */ |
| 45 | } mutex_state_t; |
| 46 | |
| 47 | typedef enum { |
| 48 | Normal, |
| 49 | Errorcheck, |
| 50 | Recursive, |
| 51 | } mutex_type_t; |
| 52 | |
| 53 | /* The heap-allocated part of a mutex. */ |
| 54 | typedef struct { |
| 55 | mutex_state_t state; |
| 56 | mutex_type_t type; |
| 57 | HANDLE event; /* Auto-reset event, or NULL if not yet allocated. */ |
| 58 | unsigned rec_lock; /* For recursive mutexes, the number of times the |
| 59 | mutex has been locked in excess by the same thread. */ |
| 60 | volatile DWORD owner; /* For recursive and error-checking mutexes, the |
| 61 | ID of the owning thread if the mutex is locked. */ |
| 62 | } mutex_impl_t; |
| 63 | |
| 64 | /* Whether a mutex is still a static initializer (not a pointer to |
| 65 | a mutex_impl_t). */ |
| 66 | static BOOL |
| 67 | is_static_initializer(pthread_mutex_t m) |
| 68 | { |
| 69 | /* Treat 0 as a static initializer as well (for normal mutexes), |
| 70 | to tolerate sloppy code in libgomp. (We should rather fix that code!) */ |
| 71 | intptr_t v = (intptr_t)m; |
| 72 | return v >= -3 && v <= 0; |
| 73 | /* Should be simple: |
| 74 | return (uintptr_t)m >= (uintptr_t)-3; */ |
| 75 | } |
| 76 | |
| 77 | /* Create and return the implementation part of a mutex from a static |
| 78 | initialiser. Return NULL on out-of-memory error. */ |
| 79 | static WINPTHREADS_ATTRIBUTE((noinline)) mutex_impl_t * |
| 80 | mutex_impl_init(pthread_mutex_t *m, mutex_impl_t *mi) |
| 81 | { |
| 82 | mutex_impl_t *new_mi = malloc(sizeof(mutex_impl_t)); |
| 83 | if (new_mi == NULL) |
| 84 | return NULL; |
| 85 | new_mi->state = Unlocked; |
| 86 | new_mi->type = (mi == (void *)PTHREAD_RECURSIVE_MUTEX_INITIALIZER ? Recursive |
| 87 | : mi == (void *)PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ? Errorcheck |
| 88 | : Normal); |
| 89 | new_mi->event = NULL; |
| 90 | new_mi->rec_lock = 0; |
| 91 | new_mi->owner = (DWORD)-1; |
| 92 | if (InterlockedCompareExchangePointer((PVOID volatile *)m, new_mi, mi) == mi) { |
| 93 | return new_mi; |
| 94 | } else { |
| 95 | /* Someone created the struct before us. */ |
| 96 | free(new_mi); |
| 97 | return (mutex_impl_t *)*m; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | /* Return the implementation part of a mutex, creating it if necessary. |
| 102 | Return NULL on out-of-memory error. */ |
| 103 | static WINPTHREADS_INLINE mutex_impl_t * |
| 104 | mutex_impl(pthread_mutex_t *m) |
| 105 | { |
| 106 | mutex_impl_t *mi = (mutex_impl_t *)*m; |
| 107 | if (is_static_initializer((pthread_mutex_t)mi)) { |
| 108 | return mutex_impl_init(m, mi); |
| 109 | } else { |
| 110 | /* mi cannot be null here; avoid a test in the fast path. */ |
| 111 | if (mi == NULL) |
| 112 | UNREACHABLE(); |
| 113 | return mi; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /* Lock a mutex. Give up after 'timeout' ms (with ETIMEDOUT), |
| 118 | or never if timeout=INFINITE. */ |
| 119 | static WINPTHREADS_INLINE int |
| 120 | pthread_mutex_lock_intern (pthread_mutex_t *m, DWORD timeout) |
| 121 | { |
| 122 | mutex_impl_t *mi = mutex_impl(m); |
| 123 | if (mi == NULL) |
| 124 | return ENOMEM; |
| 125 | mutex_state_t old_state = InterlockedExchange((long *)&mi->state, Locked); |
| 126 | if (unlikely(old_state != Unlocked)) { |
| 127 | /* The mutex is already locked. */ |
| 128 | |
| 129 | if (mi->type != Normal) { |
| 130 | /* Recursive or Errorcheck */ |
| 131 | if (mi->owner == GetCurrentThreadId()) { |
| 132 | /* FIXME: A recursive mutex should not need two atomic ops when locking |
| 133 | recursively. We could rewrite by doing compare-and-swap instead of |
| 134 | test-and-set the first time, but it would lead to more code |
| 135 | duplication and add a conditional branch to the critical path. */ |
| 136 | InterlockedCompareExchange((long *)&mi->state, old_state, Locked); |
| 137 | if (mi->type == Recursive) { |
| 138 | mi->rec_lock++; |
| 139 | return 0; |
| 140 | } else { |
| 141 | /* type == Errorcheck */ |
| 142 | return EDEADLK; |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /* Make sure there is an event object on which to wait. */ |
| 148 | if (mi->event == NULL) { |
| 149 | /* Make an auto-reset event object. */ |
| 150 | HANDLE ev = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 151 | if (ev == NULL) { |
| 152 | switch (GetLastError()) { |
| 153 | case ERROR_ACCESS_DENIED: |
| 154 | return EPERM; |
| 155 | default: |
| 156 | return ENOMEM; /* Probably accurate enough. */ |
| 157 | } |
| 158 | } |
| 159 | if (InterlockedCompareExchangePointer(&mi->event, ev, NULL) != NULL) { |
| 160 | /* Someone created the event before us. */ |
| 161 | CloseHandle(ev); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | /* At this point, mi->event is non-NULL. */ |
| 166 | |
| 167 | while (InterlockedExchange((long *)&mi->state, Waiting) != Unlocked) { |
| 168 | /* For timed locking attempts, it is possible (although unlikely) |
| 169 | that we are woken up but someone else grabs the lock before us, |
| 170 | and we have to go back to sleep again. In that case, the total |
| 171 | wait may be longer than expected. */ |
| 172 | |
| 173 | unsigned r = _pthread_wait_for_single_object(mi->event, timeout); |
| 174 | switch (r) { |
| 175 | case WAIT_TIMEOUT: |
| 176 | return ETIMEDOUT; |
| 177 | case WAIT_OBJECT_0: |
| 178 | break; |
| 179 | default: |
| 180 | return EINVAL; |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | if (mi->type != Normal) |
| 186 | mi->owner = GetCurrentThreadId(); |
| 187 | |
| 188 | return 0; |
| 189 | } |
| 190 | |
| 191 | int |
| 192 | pthread_mutex_lock (pthread_mutex_t *m) |
| 193 | { |
| 194 | return pthread_mutex_lock_intern (m, INFINITE); |
| 195 | } |
| 196 | |
| 197 | /* Internal version which always uses `struct _timespec64`. */ |
| 198 | static int __pthread_mutex_timedlock(pthread_mutex_t *m, const struct _timespec64 *ts) |
| 199 | { |
| 200 | unsigned long long patience; |
| 201 | if (ts != NULL) { |
| 202 | unsigned long long end = _pthread_time_in_ms_from_timespec(ts); |
| 203 | unsigned long long now = _pthread_time_in_ms(); |
| 204 | patience = end > now ? end - now : 0; |
| 205 | if (patience > 0xffffffff) |
| 206 | patience = INFINITE; |
| 207 | } else { |
| 208 | patience = INFINITE; |
| 209 | } |
| 210 | return pthread_mutex_lock_intern(m, patience); |
| 211 | } |
| 212 | |
| 213 | int pthread_mutex_timedlock64(pthread_mutex_t *m, const struct _timespec64 *ts) |
| 214 | { |
| 215 | return __pthread_mutex_timedlock (m, ts); |
| 216 | } |
| 217 | |
| 218 | int pthread_mutex_timedlock32(pthread_mutex_t *m, const struct _timespec32 *ts) |
| 219 | { |
| 220 | struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec}; |
| 221 | return __pthread_mutex_timedlock (m, &ts64); |
| 222 | } |
| 223 | |
| 224 | int pthread_mutex_unlock(pthread_mutex_t *m) |
| 225 | { |
| 226 | /* Here m might an initialiser of an error-checking or recursive mutex, in |
| 227 | which case the behaviour is well-defined, so we can't skip this check. */ |
| 228 | mutex_impl_t *mi = mutex_impl(m); |
| 229 | if (mi == NULL) |
| 230 | return ENOMEM; |
| 231 | |
| 232 | if (unlikely(mi->type != Normal)) { |
| 233 | if (mi->state == Unlocked) |
| 234 | return EPERM; |
| 235 | if (mi->owner != GetCurrentThreadId()) |
| 236 | return EPERM; |
| 237 | if (mi->rec_lock > 0) { |
| 238 | mi->rec_lock--; |
| 239 | return 0; |
| 240 | } |
| 241 | mi->owner = (DWORD)-1; |
| 242 | } |
| 243 | if (unlikely(InterlockedExchange((long *)&mi->state, Unlocked) == Waiting)) { |
| 244 | if (!SetEvent(mi->event)) |
| 245 | return EPERM; |
| 246 | } |
| 247 | return 0; |
| 248 | } |
| 249 | |
| 250 | int pthread_mutex_trylock(pthread_mutex_t *m) |
| 251 | { |
| 252 | mutex_impl_t *mi = mutex_impl(m); |
| 253 | if (mi == NULL) |
| 254 | return ENOMEM; |
| 255 | |
| 256 | if (InterlockedCompareExchange((long *)&mi->state, Locked, Unlocked) == Unlocked) { |
| 257 | if (mi->type != Normal) |
| 258 | mi->owner = GetCurrentThreadId(); |
| 259 | return 0; |
| 260 | } else { |
| 261 | if (mi->type == Recursive && mi->owner == GetCurrentThreadId()) { |
| 262 | mi->rec_lock++; |
| 263 | return 0; |
| 264 | } |
| 265 | return EBUSY; |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | int |
| 270 | pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a) |
| 271 | { |
| 272 | pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER; |
| 273 | if (a != NULL) { |
| 274 | int pshared; |
| 275 | if (pthread_mutexattr_getpshared(a, &pshared) == 0 |
| 276 | && pshared == PTHREAD_PROCESS_SHARED) |
| 277 | return ENOSYS; |
| 278 | |
| 279 | int type; |
| 280 | if (pthread_mutexattr_gettype(a, &type) == 0) { |
| 281 | switch (type) { |
| 282 | case PTHREAD_MUTEX_ERRORCHECK: |
| 283 | init = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER; |
| 284 | break; |
| 285 | case PTHREAD_MUTEX_RECURSIVE: |
| 286 | init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; |
| 287 | break; |
| 288 | default: |
| 289 | init = PTHREAD_MUTEX_INITIALIZER; |
| 290 | break; |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | *m = init; |
| 295 | return 0; |
| 296 | } |
| 297 | |
| 298 | int pthread_mutex_destroy (pthread_mutex_t *m) |
| 299 | { |
| 300 | mutex_impl_t *mi = (mutex_impl_t *)*m; |
| 301 | if (!is_static_initializer((pthread_mutex_t)mi)) { |
| 302 | if (mi->event != NULL) |
| 303 | CloseHandle(mi->event); |
| 304 | free(mi); |
| 305 | /* Sabotage attempts to re-use the mutex before initialising it again. */ |
| 306 | *m = (pthread_mutex_t)NULL; |
| 307 | } |
| 308 | |
| 309 | return 0; |
| 310 | } |
| 311 | |
| 312 | int pthread_mutexattr_init(pthread_mutexattr_t *a) |
| 313 | { |
| 314 | *a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3); |
| 315 | return 0; |
| 316 | } |
| 317 | |
| 318 | int pthread_mutexattr_destroy(pthread_mutexattr_t *a) |
| 319 | { |
| 320 | if (!a) |
| 321 | return EINVAL; |
| 322 | |
| 323 | return 0; |
| 324 | } |
| 325 | |
| 326 | int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type) |
| 327 | { |
| 328 | if (!a || !type) |
| 329 | return EINVAL; |
| 330 | |
| 331 | *type = *a & 3; |
| 332 | |
| 333 | return 0; |
| 334 | } |
| 335 | |
| 336 | int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) |
| 337 | { |
| 338 | if (!a || (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE && type != PTHREAD_MUTEX_ERRORCHECK)) |
| 339 | return EINVAL; |
| 340 | *a &= ~3; |
| 341 | *a |= type; |
| 342 | |
| 343 | return 0; |
| 344 | } |
| 345 | |
| 346 | int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type) |
| 347 | { |
| 348 | if (!a || !type) |
| 349 | return EINVAL; |
| 350 | *type = (*a & 4 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE); |
| 351 | |
| 352 | return 0; |
| 353 | } |
| 354 | |
| 355 | int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type) |
| 356 | { |
| 357 | int r = 0; |
| 358 | if (!a || (type != PTHREAD_PROCESS_SHARED |
| 359 | 	&& type != PTHREAD_PROCESS_PRIVATE)) |
| 360 | return EINVAL; |
| 361 | if (type == PTHREAD_PROCESS_SHARED) |
| 362 | { |
| 363 | type = PTHREAD_PROCESS_PRIVATE; |
| 364 | r = ENOSYS; |
| 365 | } |
| 366 | type = (type == PTHREAD_PROCESS_SHARED ? 4 : 0); |
| 367 | |
| 368 | *a &= ~4; |
| 369 | *a |= type; |
| 370 | |
| 371 | return r; |
| 372 | } |
| 373 | |
| 374 | int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type) |
| 375 | { |
| 376 | *type = *a & (8 + 16); |
| 377 | |
| 378 | return 0; |
| 379 | } |
| 380 | |
| 381 | int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type) |
| 382 | { |
| 383 | if ((type & (8 + 16)) != 8 + 16) return EINVAL; |
| 384 | |
| 385 | *a &= ~(8 + 16); |
| 386 | *a |= type; |
| 387 | |
| 388 | return 0; |
| 389 | } |
| 390 | |
| 391 | int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio) |
| 392 | { |
| 393 | *prio = *a / PTHREAD_PRIO_MULT; |
| 394 | return 0; |
| 395 | } |
| 396 | |
| 397 | int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio) |
| 398 | { |
| 399 | *a &= (PTHREAD_PRIO_MULT - 1); |
| 400 | *a += prio * PTHREAD_PRIO_MULT; |
| 401 | |
| 402 | return 0; |
| 403 | } |