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 #include <windows.h>
24 #include <stdio.h>
25 #include <malloc.h>
26 #include "pthread.h"
27 #include "thread.h"
28 #include "ref.h"
29 #include "rwlock.h"
30 #include "misc.h"
31
32 static pthread_spinlock_t rwl_global = PTHREAD_SPINLOCK_INITIALIZER;
33
34 static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw);
35
rwl_unref(volatile pthread_rwlock_t * rwl,int res)36 static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_unref(volatile pthread_rwlock_t *rwl, int res)
37 {
38 pthread_spin_lock(&rwl_global);
39 #ifdef WINPTHREAD_DBG
40 assert((((rwlock_t *)*rwl)->valid == LIFE_RWLOCK) && (((rwlock_t *)*rwl)->busy > 0));
41 #endif
42 ((rwlock_t *)*rwl)->busy--;
43 pthread_spin_unlock(&rwl_global);
44 return res;
45 }
46
rwl_ref(pthread_rwlock_t * rwl,int f)47 static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref(pthread_rwlock_t *rwl, int f )
48 {
49 int r = 0;
50 INIT_RWLOCK(rwl);
51 pthread_spin_lock(&rwl_global);
52
53 if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL;
54 else {
55 ((rwlock_t *)*rwl)->busy ++;
56 }
57
58 pthread_spin_unlock(&rwl_global);
59
60 return r;
61 }
62
rwl_ref_unlock(pthread_rwlock_t * rwl)63 static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_unlock(pthread_rwlock_t *rwl )
64 {
65 int r = 0;
66
67 pthread_spin_lock(&rwl_global);
68
69 if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL;
70 else if (STATIC_RWL_INITIALIZER(*rwl)) r= EPERM;
71 else {
72 ((rwlock_t *)*rwl)->busy ++;
73 }
74
75 pthread_spin_unlock(&rwl_global);
76
77 return r;
78 }
79
rwl_ref_destroy(pthread_rwlock_t * rwl,pthread_rwlock_t * rDestroy)80 static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_destroy(pthread_rwlock_t *rwl, pthread_rwlock_t *rDestroy )
81 {
82 int r = 0;
83
84 *rDestroy = (pthread_rwlock_t)NULL;
85 pthread_spin_lock(&rwl_global);
86
87 if (!rwl || !*rwl) r = EINVAL;
88 else {
89 rwlock_t *r_ = (rwlock_t *)*rwl;
90 if (STATIC_RWL_INITIALIZER(*rwl)) *rwl = (pthread_rwlock_t)NULL;
91 else if (r_->valid != LIFE_RWLOCK) r = EINVAL;
92 else if (r_->busy) r = EBUSY;
93 else {
94 *rDestroy = *rwl;
95 *rwl = (pthread_rwlock_t)NULL;
96 }
97 }
98
99 pthread_spin_unlock(&rwl_global);
100 return r;
101 }
102
rwlock_gain_both_locks(rwlock_t * rwlock)103 static int rwlock_gain_both_locks(rwlock_t *rwlock)
104 {
105 int ret;
106 ret = pthread_mutex_lock(&rwlock->mex);
107 if (ret != 0)
108 return ret;
109 ret = pthread_mutex_lock(&rwlock->mcomplete);
110 if (ret != 0)
111 pthread_mutex_unlock(&rwlock->mex);
112 return ret;
113 }
114
rwlock_free_both_locks(rwlock_t * rwlock,int last_fail)115 static int rwlock_free_both_locks(rwlock_t *rwlock, int last_fail)
116 {
117 int ret, ret2;
118 ret = pthread_mutex_unlock(&rwlock->mcomplete);
119 ret2 = pthread_mutex_unlock(&rwlock->mex);
120 if (last_fail && ret2 != 0)
121 ret = ret2;
122 else if (!last_fail && !ret)
123 ret = ret2;
124 return ret;
125 }
126
127 #ifdef WINPTHREAD_DBG
128 static int print_state = 0;
rwl_print_set(int state)129 void rwl_print_set(int state)
130 {
131 print_state = state;
132 }
133
rwl_print(volatile pthread_rwlock_t * rwl,char * txt)134 void rwl_print(volatile pthread_rwlock_t *rwl, char *txt)
135 {
136 if (!print_state) return;
137 rwlock_t *r = (rwlock_t *)*rwl;
138 if (r == NULL) {
139 printf("RWL%p %d %s\n",(void *)*rwl,(int)GetCurrentThreadId(),txt);
140 } else {
141 printf("RWL%p %d V=%0X B=%d r=%ld w=%ld L=%p %s\n",
142 (void *)*rwl,
143 (int)GetCurrentThreadId(),
144 (int)r->valid,
145 (int)r->busy,
146 0L,0L,NULL,txt);
147 }
148 }
149 #endif
150
151 static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER;
152
rwlock_static_init(pthread_rwlock_t * rw)153 static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw)
154 {
155 int r;
156 pthread_spin_lock(&cond_locked);
157 if (*rw != PTHREAD_RWLOCK_INITIALIZER)
158 {
159 pthread_spin_unlock(&cond_locked);
160 return EINVAL;
161 }
162 r = pthread_rwlock_init (rw, NULL);
163 pthread_spin_unlock(&cond_locked);
164
165 return r;
166 }
167
pthread_rwlock_init(pthread_rwlock_t * rwlock_,const pthread_rwlockattr_t * attr)168 int pthread_rwlock_init (pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr)
169 {
170 rwlock_t *rwlock;
171 int r;
172
173 if(!rwlock_)
174 return EINVAL;
175 *rwlock_ = (pthread_rwlock_t)NULL;
176 if ((rwlock = calloc(1, sizeof(*rwlock))) == NULL)
177 return ENOMEM;
178 rwlock->valid = DEAD_RWLOCK;
179
180 rwlock->nex_count = rwlock->nsh_count = rwlock->ncomplete = 0;
181 if ((r = pthread_mutex_init (&rwlock->mex, NULL)) != 0)
182 {
183 free(rwlock);
184 return r;
185 }
186 if ((r = pthread_mutex_init (&rwlock->mcomplete, NULL)) != 0)
187 {
188 pthread_mutex_destroy(&rwlock->mex);
189 free(rwlock);
190 return r;
191 }
192 if ((r = pthread_cond_init (&rwlock->ccomplete, NULL)) != 0)
193 {
194 pthread_mutex_destroy(&rwlock->mex);
195 pthread_mutex_destroy (&rwlock->mcomplete);
196 free(rwlock);
197 return r;
198 }
199 rwlock->valid = LIFE_RWLOCK;
200 *rwlock_ = (pthread_rwlock_t)rwlock;
201 return r;
202 }
203
pthread_rwlock_destroy(pthread_rwlock_t * rwlock_)204 int pthread_rwlock_destroy (pthread_rwlock_t *rwlock_)
205 {
206 rwlock_t *rwlock;
207 pthread_rwlock_t rDestroy;
208 int r, r2;
209
210 pthread_spin_lock(&cond_locked);
211 r = rwl_ref_destroy(rwlock_,&rDestroy);
212 pthread_spin_unlock(&cond_locked);
213
214 if(r) return r;
215 if(!rDestroy) return 0; /* destroyed a (still) static initialized rwl */
216
217 rwlock = (rwlock_t *)rDestroy;
218 r = rwlock_gain_both_locks (rwlock);
219 if (r != 0)
220 {
221 *rwlock_ = rDestroy;
222 return r;
223 }
224 if (rwlock->nsh_count > rwlock->ncomplete || rwlock->nex_count > 0)
225 {
226 *rwlock_ = rDestroy;
227 r = rwlock_free_both_locks(rwlock, 1);
228 if (!r)
229 r = EBUSY;
230 return r;
231 }
232 rwlock->valid = DEAD_RWLOCK;
233 r = rwlock_free_both_locks(rwlock, 0);
234 if (r != 0) { *rwlock_ = rDestroy; return r; }
235
236 r = pthread_cond_destroy(&rwlock->ccomplete);
237 r2 = pthread_mutex_destroy(&rwlock->mex);
238 if (!r) r = r2;
239 r2 = pthread_mutex_destroy(&rwlock->mcomplete);
240 if (!r) r = r2;
241 rwlock->valid = DEAD_RWLOCK;
242 free((void *)rDestroy);
243 return 0;
244 }
245
pthread_rwlock_rdlock(pthread_rwlock_t * rwlock_)246 int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock_)
247 {
248 rwlock_t *rwlock;
249 int ret;
250
251 /* pthread_testcancel(); */
252
253 ret = rwl_ref(rwlock_,0);
254 if(ret != 0) return ret;
255
256 rwlock = (rwlock_t *)*rwlock_;
257
258 ret = pthread_mutex_lock(&rwlock->mex);
259 if (ret != 0) return rwl_unref(rwlock_, ret);
260 InterlockedIncrement((long*)&rwlock->nsh_count);
261 if (rwlock->nsh_count == INT_MAX)
262 {
263 ret = pthread_mutex_lock(&rwlock->mcomplete);
264 if (ret != 0)
265 {
266 pthread_mutex_unlock(&rwlock->mex);
267 return rwl_unref(rwlock_,ret);
268 }
269 rwlock->nsh_count -= rwlock->ncomplete;
270 rwlock->ncomplete = 0;
271 ret = rwlock_free_both_locks(rwlock, 0);
272 return rwl_unref(rwlock_, ret);
273 }
274 ret = pthread_mutex_unlock(&rwlock->mex);
275 return rwl_unref(rwlock_, ret);
276 }
277
pthread_rwlock_timedrdlock(pthread_rwlock_t * rwlock_,const struct timespec * ts)278 int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock_, const struct timespec *ts)
279 {
280 rwlock_t *rwlock;
281 int ret;
282
283 /* pthread_testcancel(); */
284
285 ret = rwl_ref(rwlock_,0);
286 if(ret != 0) return ret;
287
288 rwlock = (rwlock_t *)*rwlock_;
289 if ((ret = pthread_mutex_timedlock (&rwlock->mex, ts)) != 0)
290 return rwl_unref(rwlock_, ret);
291 InterlockedIncrement(&rwlock->nsh_count);
292 if (rwlock->nsh_count == INT_MAX)
293 {
294 ret = pthread_mutex_timedlock(&rwlock->mcomplete, ts);
295 if (ret != 0)
296 {
297 if (ret == ETIMEDOUT)
298 InterlockedIncrement(&rwlock->ncomplete);
299 pthread_mutex_unlock(&rwlock->mex);
300 return rwl_unref(rwlock_, ret);
301 }
302 rwlock->nsh_count -= rwlock->ncomplete;
303 rwlock->ncomplete = 0;
304 ret = rwlock_free_both_locks(rwlock, 0);
305 return rwl_unref(rwlock_, ret);
306 }
307 ret = pthread_mutex_unlock(&rwlock->mex);
308 return rwl_unref(rwlock_, ret);
309 }
310
pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock_)311 int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock_)
312 {
313 rwlock_t *rwlock;
314 int ret;
315
316 ret = rwl_ref(rwlock_,RWL_TRY);
317 if(ret != 0) return ret;
318
319 rwlock = (rwlock_t *)*rwlock_;
320 ret = pthread_mutex_trylock(&rwlock->mex);
321 if (ret != 0)
322 return rwl_unref(rwlock_, ret);
323 InterlockedIncrement(&rwlock->nsh_count);
324 if (rwlock->nsh_count == INT_MAX)
325 {
326 ret = pthread_mutex_lock(&rwlock->mcomplete);
327 if (ret != 0)
328 {
329 pthread_mutex_unlock(&rwlock->mex);
330 return rwl_unref(rwlock_, ret);
331 }
332 rwlock->nsh_count -= rwlock->ncomplete;
333 rwlock->ncomplete = 0;
334 ret = rwlock_free_both_locks(rwlock, 0);
335 return rwl_unref(rwlock_, ret);
336 }
337 ret = pthread_mutex_unlock(&rwlock->mex);
338 return rwl_unref(rwlock_,ret);
339 }
340
pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock_)341 int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock_)
342 {
343 rwlock_t *rwlock;
344 int ret;
345
346 ret = rwl_ref(rwlock_,RWL_TRY);
347 if(ret != 0) return ret;
348
349 rwlock = (rwlock_t *)*rwlock_;
350 ret = pthread_mutex_trylock (&rwlock->mex);
351 if (ret != 0)
352 return rwl_unref(rwlock_, ret);
353 ret = pthread_mutex_trylock(&rwlock->mcomplete);
354 if (ret != 0)
355 {
356 int r1 = pthread_mutex_unlock(&rwlock->mex);
357 if (r1 != 0)
358 ret = r1;
359 return rwl_unref(rwlock_, ret);
360 }
361 if (rwlock->nex_count != 0)
362 return rwl_unref(rwlock_, EBUSY);
363 if (rwlock->ncomplete > 0)
364 {
365 rwlock->nsh_count -= rwlock->ncomplete;
366 rwlock->ncomplete = 0;
367 }
368 if (rwlock->nsh_count > 0)
369 {
370 ret = rwlock_free_both_locks(rwlock, 0);
371 if (!ret)
372 ret = EBUSY;
373 return rwl_unref(rwlock_, ret);
374 }
375 rwlock->nex_count = 1;
376 return rwl_unref(rwlock_, 0);
377 }
378
pthread_rwlock_unlock(pthread_rwlock_t * rwlock_)379 int pthread_rwlock_unlock (pthread_rwlock_t *rwlock_)
380 {
381 rwlock_t *rwlock;
382 int ret;
383
384 ret = rwl_ref_unlock(rwlock_);
385 if(ret != 0) return ret;
386
387 rwlock = (rwlock_t *)*rwlock_;
388 if (rwlock->nex_count == 0)
389 {
390 ret = pthread_mutex_lock(&rwlock->mcomplete);
391 if (!ret)
392 {
393 int r1;
394 InterlockedIncrement(&rwlock->ncomplete);
395 if (rwlock->ncomplete == 0)
396 ret = pthread_cond_signal(&rwlock->ccomplete);
397 r1 = pthread_mutex_unlock(&rwlock->mcomplete);
398 if (!ret)
399 ret = r1;
400 }
401 }
402 else
403 {
404 InterlockedDecrement(&rwlock->nex_count);
405 ret = rwlock_free_both_locks(rwlock, 0);
406 }
407 return rwl_unref(rwlock_, ret);
408 }
409
st_cancelwrite(void * arg)410 static void st_cancelwrite (void *arg)
411 {
412 rwlock_t *rwlock = (rwlock_t *)arg;
413
414 rwlock->nsh_count = - rwlock->ncomplete;
415 rwlock->ncomplete = 0;
416 rwlock_free_both_locks(rwlock, 0);
417 }
418
pthread_rwlock_wrlock(pthread_rwlock_t * rwlock_)419 int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock_)
420 {
421 rwlock_t *rwlock;
422 int ret;
423
424 /* pthread_testcancel(); */
425 ret = rwl_ref(rwlock_,0);
426 if(ret != 0) return ret;
427
428 rwlock = (rwlock_t *)*rwlock_;
429 ret = rwlock_gain_both_locks(rwlock);
430 if (ret != 0)
431 return rwl_unref(rwlock_,ret);
432
433 if (rwlock->nex_count == 0)
434 {
435 if (rwlock->ncomplete > 0)
436 {
437 rwlock->nsh_count -= rwlock->ncomplete;
438 rwlock->ncomplete = 0;
439 }
440 if (rwlock->nsh_count > 0)
441 {
442 rwlock->ncomplete = -rwlock->nsh_count;
443 pthread_cleanup_push(st_cancelwrite, (void *) rwlock);
444 do {
445 ret = pthread_cond_wait(&rwlock->ccomplete, &rwlock->mcomplete);
446 } while (!ret && rwlock->ncomplete < 0);
447
448 pthread_cleanup_pop(!ret ? 0 : 1);
449 if (!ret)
450 rwlock->nsh_count = 0;
451 }
452 }
453 if(!ret)
454 InterlockedIncrement((long*)&rwlock->nex_count);
455 return rwl_unref(rwlock_,ret);
456 }
457
pthread_rwlock_timedwrlock(pthread_rwlock_t * rwlock_,const struct timespec * ts)458 int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock_, const struct timespec *ts)
459 {
460 int ret;
461 rwlock_t *rwlock;
462
463 /* pthread_testcancel(); */
464 if (!rwlock_ || !ts)
465 return EINVAL;
466 if ((ret = rwl_ref(rwlock_,0)) != 0)
467 return ret;
468 rwlock = (rwlock_t *)*rwlock_;
469
470 ret = pthread_mutex_timedlock(&rwlock->mex, ts);
471 if (ret != 0)
472 return rwl_unref(rwlock_,ret);
473 ret = pthread_mutex_timedlock (&rwlock->mcomplete, ts);
474 if (ret != 0)
475 {
476 pthread_mutex_unlock(&rwlock->mex);
477 return rwl_unref(rwlock_,ret);
478 }
479 if (rwlock->nex_count == 0)
480 {
481 if (rwlock->ncomplete > 0)
482 {
483 rwlock->nsh_count -= rwlock->ncomplete;
484 rwlock->ncomplete = 0;
485 }
486 if (rwlock->nsh_count > 0)
487 {
488 rwlock->ncomplete = -rwlock->nsh_count;
489 pthread_cleanup_push(st_cancelwrite, (void *) rwlock);
490 do {
491 ret = pthread_cond_timedwait(&rwlock->ccomplete, &rwlock->mcomplete, ts);
492 } while (rwlock->ncomplete < 0 && !ret);
493 pthread_cleanup_pop(!ret ? 0 : 1);
494
495 if (!ret)
496 rwlock->nsh_count = 0;
497 }
498 }
499 if(!ret)
500 InterlockedIncrement((long*)&rwlock->nex_count);
501 return rwl_unref(rwlock_,ret);
502 }
503
pthread_rwlockattr_destroy(pthread_rwlockattr_t * a)504 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a)
505 {
506 if (!a)
507 return EINVAL;
508 return 0;
509 }
510
pthread_rwlockattr_init(pthread_rwlockattr_t * a)511 int pthread_rwlockattr_init(pthread_rwlockattr_t *a)
512 {
513 if (!a)
514 return EINVAL;
515 *a = PTHREAD_PROCESS_PRIVATE;
516 return 0;
517 }
518
pthread_rwlockattr_getpshared(pthread_rwlockattr_t * a,int * s)519 int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s)
520 {
521 if (!a || !s)
522 return EINVAL;
523 *s = *a;
524 return 0;
525 }
526
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * a,int s)527 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s)
528 {
529 if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
530 return EINVAL;
531 *a = s;
532 return 0;
533 }
534