master
1/*
2 Copyright (c) 2011-2016 mingw-w64 project
3
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files (the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 DEALINGS IN THE SOFTWARE.
21*/
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <assert.h>
28#include <malloc.h>
29#include <stdio.h>
30
31#define WIN32_LEAN_AND_MEAN
32#include <windows.h>
33
34#define WINPTHREAD_RWLOCK_DECL WINPTHREAD_API
35
36/* public header files */
37#include "pthread.h"
38/* internal header files */
39#include "misc.h"
40#include "rwlock.h"
41#include "thread.h"
42
43static pthread_spinlock_t rwl_global = PTHREAD_SPINLOCK_INITIALIZER;
44
45static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw);
46
47static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_unref(volatile pthread_rwlock_t *rwl, int res)
48{
49 pthread_spin_lock(&rwl_global);
50 assert((((rwlock_t *)*rwl)->valid == LIFE_RWLOCK) && (((rwlock_t *)*rwl)->busy > 0));
51 ((rwlock_t *)*rwl)->busy--;
52 pthread_spin_unlock(&rwl_global);
53 return res;
54}
55
56static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref(pthread_rwlock_t *rwl, int f )
57{
58 int r = 0;
59 if (STATIC_RWL_INITIALIZER(*rwl)) {
60 r = rwlock_static_init(rwl);
61 if (r != 0 && r != EBUSY)
62 return r;
63 }
64 pthread_spin_lock(&rwl_global);
65
66 if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL;
67 else {
68 ((rwlock_t *)*rwl)->busy ++;
69 }
70
71 pthread_spin_unlock(&rwl_global);
72
73 return r;
74}
75
76static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_unlock(pthread_rwlock_t *rwl )
77{
78 int r = 0;
79
80 pthread_spin_lock(&rwl_global);
81
82 if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL;
83 else if (STATIC_RWL_INITIALIZER(*rwl)) r= EPERM;
84 else {
85 ((rwlock_t *)*rwl)->busy ++;
86 }
87
88 pthread_spin_unlock(&rwl_global);
89
90 return r;
91}
92
93static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_destroy(pthread_rwlock_t *rwl, pthread_rwlock_t *rDestroy )
94{
95 int r = 0;
96
97 *rDestroy = (pthread_rwlock_t)NULL;
98 pthread_spin_lock(&rwl_global);
99
100 if (!rwl || !*rwl) r = EINVAL;
101 else {
102 rwlock_t *r_ = (rwlock_t *)*rwl;
103 if (STATIC_RWL_INITIALIZER(*rwl)) *rwl = (pthread_rwlock_t)NULL;
104 else if (r_->valid != LIFE_RWLOCK) r = EINVAL;
105 else if (r_->busy) r = EBUSY;
106 else {
107 *rDestroy = *rwl;
108 *rwl = (pthread_rwlock_t)NULL;
109 }
110 }
111
112 pthread_spin_unlock(&rwl_global);
113 return r;
114}
115
116static int rwlock_gain_both_locks(rwlock_t *rwlock)
117{
118 int ret;
119 ret = pthread_mutex_lock(&rwlock->mex);
120 if (ret != 0)
121 return ret;
122 ret = pthread_mutex_lock(&rwlock->mcomplete);
123 if (ret != 0)
124 pthread_mutex_unlock(&rwlock->mex);
125 return ret;
126}
127
128static int rwlock_free_both_locks(rwlock_t *rwlock, int last_fail)
129{
130 int ret, ret2;
131 ret = pthread_mutex_unlock(&rwlock->mcomplete);
132 ret2 = pthread_mutex_unlock(&rwlock->mex);
133 if (last_fail && ret2 != 0)
134 ret = ret2;
135 else if (!last_fail && !ret)
136 ret = ret2;
137 return ret;
138}
139
140static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER;
141
142static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw)
143{
144 int r;
145 pthread_spin_lock(&cond_locked);
146 if (*rw != PTHREAD_RWLOCK_INITIALIZER)
147 {
148 pthread_spin_unlock(&cond_locked);
149 return EINVAL;
150 }
151 r = pthread_rwlock_init (rw, NULL);
152 pthread_spin_unlock(&cond_locked);
153
154 return r;
155}
156
157int pthread_rwlock_init (pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr)
158{
159 rwlock_t *rwlock;
160 int r;
161
162 if(!rwlock_)
163 return EINVAL;
164 *rwlock_ = (pthread_rwlock_t)NULL;
165 if ((rwlock = calloc(1, sizeof(*rwlock))) == NULL)
166 return ENOMEM;
167 rwlock->valid = DEAD_RWLOCK;
168
169 rwlock->nex_count = rwlock->nsh_count = rwlock->ncomplete = 0;
170 if ((r = pthread_mutex_init (&rwlock->mex, NULL)) != 0)
171 {
172 free(rwlock);
173 return r;
174 }
175 if ((r = pthread_mutex_init (&rwlock->mcomplete, NULL)) != 0)
176 {
177 pthread_mutex_destroy(&rwlock->mex);
178 free(rwlock);
179 return r;
180 }
181 if ((r = pthread_cond_init (&rwlock->ccomplete, NULL)) != 0)
182 {
183 pthread_mutex_destroy(&rwlock->mex);
184 pthread_mutex_destroy (&rwlock->mcomplete);
185 free(rwlock);
186 return r;
187 }
188 rwlock->valid = LIFE_RWLOCK;
189 *rwlock_ = (pthread_rwlock_t)rwlock;
190 return r;
191}
192
193int pthread_rwlock_destroy (pthread_rwlock_t *rwlock_)
194{
195 rwlock_t *rwlock;
196 pthread_rwlock_t rDestroy;
197 int r, r2;
198
199 pthread_spin_lock(&cond_locked);
200 r = rwl_ref_destroy(rwlock_,&rDestroy);
201 pthread_spin_unlock(&cond_locked);
202
203 if(r) return r;
204 if(!rDestroy) return 0; /* destroyed a (still) static initialized rwl */
205
206 rwlock = (rwlock_t *)rDestroy;
207 r = rwlock_gain_both_locks (rwlock);
208 if (r != 0)
209 {
210 *rwlock_ = rDestroy;
211 return r;
212 }
213 if (rwlock->nsh_count > rwlock->ncomplete || rwlock->nex_count > 0)
214 {
215 *rwlock_ = rDestroy;
216 r = rwlock_free_both_locks(rwlock, 1);
217 if (!r)
218 r = EBUSY;
219 return r;
220 }
221 rwlock->valid = DEAD_RWLOCK;
222 r = rwlock_free_both_locks(rwlock, 0);
223 if (r != 0) { *rwlock_ = rDestroy; return r; }
224
225 r = pthread_cond_destroy(&rwlock->ccomplete);
226 r2 = pthread_mutex_destroy(&rwlock->mex);
227 if (!r) r = r2;
228 r2 = pthread_mutex_destroy(&rwlock->mcomplete);
229 if (!r) r = r2;
230 rwlock->valid = DEAD_RWLOCK;
231 free((void *)rDestroy);
232 return 0;
233}
234
235int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock_)
236{
237 rwlock_t *rwlock;
238 int ret;
239
240 /* pthread_testcancel(); */
241
242 ret = rwl_ref(rwlock_,0);
243 if(ret != 0) return ret;
244
245 rwlock = (rwlock_t *)*rwlock_;
246
247 ret = pthread_mutex_lock(&rwlock->mex);
248 if (ret != 0) return rwl_unref(rwlock_, ret);
249 InterlockedIncrement((long*)&rwlock->nsh_count);
250 if (rwlock->nsh_count == INT_MAX)
251 {
252 ret = pthread_mutex_lock(&rwlock->mcomplete);
253 if (ret != 0)
254 {
255 pthread_mutex_unlock(&rwlock->mex);
256 return rwl_unref(rwlock_,ret);
257 }
258 rwlock->nsh_count -= rwlock->ncomplete;
259 rwlock->ncomplete = 0;
260 ret = rwlock_free_both_locks(rwlock, 0);
261 return rwl_unref(rwlock_, ret);
262 }
263 ret = pthread_mutex_unlock(&rwlock->mex);
264 return rwl_unref(rwlock_, ret);
265}
266
267/* Internal version which always uses `struct _timespec64`. */
268static int __pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock_, const struct _timespec64 *ts)
269{
270 rwlock_t *rwlock;
271 int ret;
272
273 /* pthread_testcancel(); */
274
275 ret = rwl_ref(rwlock_,0);
276 if(ret != 0) return ret;
277
278 rwlock = (rwlock_t *)*rwlock_;
279 if ((ret = pthread_mutex_timedlock64 (&rwlock->mex, ts)) != 0)
280 return rwl_unref(rwlock_, ret);
281 InterlockedIncrement(&rwlock->nsh_count);
282 if (rwlock->nsh_count == INT_MAX)
283 {
284 ret = pthread_mutex_timedlock64(&rwlock->mcomplete, ts);
285 if (ret != 0)
286 {
287 if (ret == ETIMEDOUT)
288 InterlockedIncrement(&rwlock->ncomplete);
289 pthread_mutex_unlock(&rwlock->mex);
290 return rwl_unref(rwlock_, ret);
291 }
292 rwlock->nsh_count -= rwlock->ncomplete;
293 rwlock->ncomplete = 0;
294 ret = rwlock_free_both_locks(rwlock, 0);
295 return rwl_unref(rwlock_, ret);
296 }
297 ret = pthread_mutex_unlock(&rwlock->mex);
298 return rwl_unref(rwlock_, ret);
299}
300
301int pthread_rwlock_timedrdlock64(pthread_rwlock_t *l, const struct _timespec64 *ts)
302{
303 return __pthread_rwlock_timedrdlock (l, ts);
304}
305
306int pthread_rwlock_timedrdlock32(pthread_rwlock_t *l, const struct _timespec32 *ts)
307{
308 struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec};
309 return __pthread_rwlock_timedrdlock (l, &ts64);
310}
311
312int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock_)
313{
314 rwlock_t *rwlock;
315 int ret;
316
317 ret = rwl_ref(rwlock_,RWL_TRY);
318 if(ret != 0) return ret;
319
320 rwlock = (rwlock_t *)*rwlock_;
321 ret = pthread_mutex_trylock(&rwlock->mex);
322 if (ret != 0)
323 return rwl_unref(rwlock_, ret);
324 InterlockedIncrement(&rwlock->nsh_count);
325 if (rwlock->nsh_count == INT_MAX)
326 {
327 ret = pthread_mutex_lock(&rwlock->mcomplete);
328 if (ret != 0)
329 {
330 pthread_mutex_unlock(&rwlock->mex);
331 return rwl_unref(rwlock_, ret);
332 }
333 rwlock->nsh_count -= rwlock->ncomplete;
334 rwlock->ncomplete = 0;
335 ret = rwlock_free_both_locks(rwlock, 0);
336 return rwl_unref(rwlock_, ret);
337 }
338 ret = pthread_mutex_unlock(&rwlock->mex);
339 return rwl_unref(rwlock_,ret);
340}
341
342int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock_)
343{
344 rwlock_t *rwlock;
345 int ret;
346
347 ret = rwl_ref(rwlock_,RWL_TRY);
348 if(ret != 0) return ret;
349
350 rwlock = (rwlock_t *)*rwlock_;
351 ret = pthread_mutex_trylock (&rwlock->mex);
352 if (ret != 0)
353 return rwl_unref(rwlock_, ret);
354 ret = pthread_mutex_trylock(&rwlock->mcomplete);
355 if (ret != 0)
356 {
357 int r1 = pthread_mutex_unlock(&rwlock->mex);
358 if (r1 != 0)
359 ret = r1;
360 return rwl_unref(rwlock_, ret);
361 }
362 if (rwlock->nex_count != 0)
363 return rwl_unref(rwlock_, EBUSY);
364 if (rwlock->ncomplete > 0)
365 {
366 rwlock->nsh_count -= rwlock->ncomplete;
367 rwlock->ncomplete = 0;
368 }
369 if (rwlock->nsh_count > 0)
370 {
371 ret = rwlock_free_both_locks(rwlock, 0);
372 if (!ret)
373 ret = EBUSY;
374 return rwl_unref(rwlock_, ret);
375 }
376 rwlock->nex_count = 1;
377 return rwl_unref(rwlock_, 0);
378}
379
380int pthread_rwlock_unlock (pthread_rwlock_t *rwlock_)
381{
382 rwlock_t *rwlock;
383 int ret;
384
385 ret = rwl_ref_unlock(rwlock_);
386 if(ret != 0) return ret;
387
388 rwlock = (rwlock_t *)*rwlock_;
389 if (rwlock->nex_count == 0)
390 {
391 ret = pthread_mutex_lock(&rwlock->mcomplete);
392 if (!ret)
393 {
394 int r1;
395 InterlockedIncrement(&rwlock->ncomplete);
396 if (rwlock->ncomplete == 0)
397 ret = pthread_cond_signal(&rwlock->ccomplete);
398 r1 = pthread_mutex_unlock(&rwlock->mcomplete);
399 if (!ret)
400 ret = r1;
401 }
402 }
403 else
404 {
405 InterlockedDecrement(&rwlock->nex_count);
406 ret = rwlock_free_both_locks(rwlock, 0);
407 }
408 return rwl_unref(rwlock_, ret);
409}
410
411static void st_cancelwrite (void *arg)
412{
413 rwlock_t *rwlock = (rwlock_t *)arg;
414
415 rwlock->nsh_count = - rwlock->ncomplete;
416 rwlock->ncomplete = 0;
417 rwlock_free_both_locks(rwlock, 0);
418}
419
420int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock_)
421{
422 rwlock_t *rwlock;
423 int ret;
424
425 /* pthread_testcancel(); */
426 ret = rwl_ref(rwlock_,0);
427 if(ret != 0) return ret;
428
429 rwlock = (rwlock_t *)*rwlock_;
430 ret = rwlock_gain_both_locks(rwlock);
431 if (ret != 0)
432 return rwl_unref(rwlock_,ret);
433
434 if (rwlock->nex_count == 0)
435 {
436 if (rwlock->ncomplete > 0)
437 {
438 rwlock->nsh_count -= rwlock->ncomplete;
439 rwlock->ncomplete = 0;
440 }
441 if (rwlock->nsh_count > 0)
442 {
443 rwlock->ncomplete = -rwlock->nsh_count;
444 pthread_cleanup_push(st_cancelwrite, (void *) rwlock);
445 do {
446 ret = pthread_cond_wait(&rwlock->ccomplete, &rwlock->mcomplete);
447 } while (!ret && rwlock->ncomplete < 0);
448
449 pthread_cleanup_pop(!ret ? 0 : 1);
450 if (!ret)
451 rwlock->nsh_count = 0;
452 }
453 }
454 if(!ret)
455 InterlockedIncrement((long*)&rwlock->nex_count);
456 return rwl_unref(rwlock_,ret);
457}
458
459/* Internal version which always uses `struct _timespec64`. */
460static int __pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock_, const struct _timespec64 *ts)
461{
462 int ret;
463 rwlock_t *rwlock;
464
465 /* pthread_testcancel(); */
466 if (!rwlock_ || !ts)
467 return EINVAL;
468 if ((ret = rwl_ref(rwlock_,0)) != 0)
469 return ret;
470 rwlock = (rwlock_t *)*rwlock_;
471
472 ret = pthread_mutex_timedlock64(&rwlock->mex, ts);
473 if (ret != 0)
474 return rwl_unref(rwlock_,ret);
475 ret = pthread_mutex_timedlock64(&rwlock->mcomplete, ts);
476 if (ret != 0)
477 {
478 pthread_mutex_unlock(&rwlock->mex);
479 return rwl_unref(rwlock_,ret);
480 }
481 if (rwlock->nex_count == 0)
482 {
483 if (rwlock->ncomplete > 0)
484 {
485 rwlock->nsh_count -= rwlock->ncomplete;
486 rwlock->ncomplete = 0;
487 }
488 if (rwlock->nsh_count > 0)
489 {
490 rwlock->ncomplete = -rwlock->nsh_count;
491 pthread_cleanup_push(st_cancelwrite, (void *) rwlock);
492 do {
493 ret = pthread_cond_timedwait64(&rwlock->ccomplete, &rwlock->mcomplete, ts);
494 } while (rwlock->ncomplete < 0 && !ret);
495 pthread_cleanup_pop(!ret ? 0 : 1);
496
497 if (!ret)
498 rwlock->nsh_count = 0;
499 }
500 }
501 if(!ret)
502 InterlockedIncrement((long*)&rwlock->nex_count);
503 return rwl_unref(rwlock_,ret);
504}
505
506int pthread_rwlock_timedwrlock64(pthread_rwlock_t *rwlock, const struct _timespec64 *ts)
507{
508 return __pthread_rwlock_timedwrlock (rwlock, ts);
509}
510
511int pthread_rwlock_timedwrlock32(pthread_rwlock_t *rwlock, const struct _timespec32 *ts)
512{
513 struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec};
514 return __pthread_rwlock_timedwrlock (rwlock, &ts64);
515}
516
517int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a)
518{
519 if (!a)
520 return EINVAL;
521 return 0;
522}
523
524int pthread_rwlockattr_init(pthread_rwlockattr_t *a)
525{
526 if (!a)
527 return EINVAL;
528 *a = PTHREAD_PROCESS_PRIVATE;
529 return 0;
530}
531
532int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s)
533{
534 if (!a || !s)
535 return EINVAL;
536 *s = *a;
537 return 0;
538}
539
540int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s)
541{
542 if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
543 return EINVAL;
544 *a = s;
545 return 0;
546}