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
41typedef enum {
42 Unlocked, /* Not locked. */
43 Locked, /* Locked but without waiters. */
44 Waiting, /* Locked, may have waiters. */
45} mutex_state_t;
46
47typedef enum {
48 Normal,
49 Errorcheck,
50 Recursive,
51} mutex_type_t;
52
53/* The heap-allocated part of a mutex. */
54typedef 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). */
66static BOOL
67is_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. */
79static WINPTHREADS_ATTRIBUTE((noinline)) mutex_impl_t *
80mutex_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. */
103static WINPTHREADS_INLINE mutex_impl_t *
104mutex_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. */
119static WINPTHREADS_INLINE int
120pthread_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
191int
192pthread_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`. */
198static 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
213int pthread_mutex_timedlock64(pthread_mutex_t *m, const struct _timespec64 *ts)
214{
215 return __pthread_mutex_timedlock (m, ts);
216}
217
218int 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
224int 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
250int 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
269int
270pthread_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
298int 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
312int pthread_mutexattr_init(pthread_mutexattr_t *a)
313{
314 *a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3);
315 return 0;
316}
317
318int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
319{
320 if (!a)
321 return EINVAL;
322
323 return 0;
324}
325
326int 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
336int 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
346int 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
355int 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
374int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type)
375{
376 *type = *a & (8 + 16);
377
378 return 0;
379}
380
381int 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
391int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio)
392{
393 *prio = *a / PTHREAD_PRIO_MULT;
394 return 0;
395}
396
397int 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}