• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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