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}