Coverage Report

Created: 2023-03-26 07:33

/src/gnutls/gl/glthread/lock.c
Line
Count
Source (jump to first uncovered line)
1
/* Locking in multithreaded situations.
2
   Copyright (C) 2005-2023 Free Software Foundation, Inc.
3
4
   This file is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU Lesser General Public License as
6
   published by the Free Software Foundation; either version 2.1 of the
7
   License, or (at your option) any later version.
8
9
   This file is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
   GNU Lesser General Public License for more details.
13
14
   You should have received a copy of the GNU Lesser General Public License
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
18
   Based on GCC's gthr-posix.h, gthr-posix95.h.  */
19
20
#include <config.h>
21
22
#include "glthread/lock.h"
23
24
/* ========================================================================= */
25
26
#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
27
28
/* -------------------------- gl_lock_t datatype -------------------------- */
29
30
int
31
glthread_lock_init (gl_lock_t *lock)
32
{
33
  if (mtx_init (&lock->mutex, mtx_plain) != thrd_success)
34
    return ENOMEM;
35
  lock->init_needed = 0;
36
  return 0;
37
}
38
39
int
40
glthread_lock_lock (gl_lock_t *lock)
41
{
42
  if (lock->init_needed)
43
    call_once (&lock->init_once, lock->init_func);
44
  if (mtx_lock (&lock->mutex) != thrd_success)
45
    return EAGAIN;
46
  return 0;
47
}
48
49
int
50
glthread_lock_unlock (gl_lock_t *lock)
51
{
52
  if (lock->init_needed)
53
    call_once (&lock->init_once, lock->init_func);
54
  if (mtx_unlock (&lock->mutex) != thrd_success)
55
    return EINVAL;
56
  return 0;
57
}
58
59
int
60
glthread_lock_destroy (gl_lock_t *lock)
61
{
62
  if (lock->init_needed)
63
    call_once (&lock->init_once, lock->init_func);
64
  mtx_destroy (&lock->mutex);
65
  return 0;
66
}
67
68
/* ------------------------- gl_rwlock_t datatype ------------------------- */
69
70
int
71
glthread_rwlock_init (gl_rwlock_t *lock)
72
{
73
  if (mtx_init (&lock->lock, mtx_plain) != thrd_success
74
      || cnd_init (&lock->waiting_readers) != thrd_success
75
      || cnd_init (&lock->waiting_writers) != thrd_success)
76
    return ENOMEM;
77
  lock->waiting_writers_count = 0;
78
  lock->runcount = 0;
79
  lock->init_needed = 0;
80
  return 0;
81
}
82
83
int
84
glthread_rwlock_rdlock (gl_rwlock_t *lock)
85
{
86
  if (lock->init_needed)
87
    call_once (&lock->init_once, lock->init_func);
88
  if (mtx_lock (&lock->lock) != thrd_success)
89
    return EAGAIN;
90
  /* Test whether only readers are currently running, and whether the runcount
91
     field will not overflow, and whether no writer is waiting.  The latter
92
     condition is because POSIX recommends that "write locks shall take
93
     precedence over read locks", to avoid "writer starvation".  */
94
  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
95
    {
96
      /* This thread has to wait for a while.  Enqueue it among the
97
         waiting_readers.  */
98
      if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success)
99
        {
100
          mtx_unlock (&lock->lock);
101
          return EINVAL;
102
        }
103
    }
104
  lock->runcount++;
105
  if (mtx_unlock (&lock->lock) != thrd_success)
106
    return EINVAL;
107
  return 0;
108
}
109
110
int
111
glthread_rwlock_wrlock (gl_rwlock_t *lock)
112
{
113
  if (lock->init_needed)
114
    call_once (&lock->init_once, lock->init_func);
115
  if (mtx_lock (&lock->lock) != thrd_success)
116
    return EAGAIN;
117
  /* Test whether no readers or writers are currently running.  */
118
  while (!(lock->runcount == 0))
119
    {
120
      /* This thread has to wait for a while.  Enqueue it among the
121
         waiting_writers.  */
122
      lock->waiting_writers_count++;
123
      if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success)
124
        {
125
          lock->waiting_writers_count--;
126
          mtx_unlock (&lock->lock);
127
          return EINVAL;
128
        }
129
      lock->waiting_writers_count--;
130
    }
131
  lock->runcount--; /* runcount becomes -1 */
132
  if (mtx_unlock (&lock->lock) != thrd_success)
133
    return EINVAL;
134
  return 0;
135
}
136
137
int
138
glthread_rwlock_unlock (gl_rwlock_t *lock)
139
{
140
  if (lock->init_needed)
141
    call_once (&lock->init_once, lock->init_func);
142
  if (mtx_lock (&lock->lock) != thrd_success)
143
    return EAGAIN;
144
  if (lock->runcount < 0)
145
    {
146
      /* Drop a writer lock.  */
147
      if (!(lock->runcount == -1))
148
        {
149
          mtx_unlock (&lock->lock);
150
          return EINVAL;
151
        }
152
      lock->runcount = 0;
153
    }
154
  else
155
    {
156
      /* Drop a reader lock.  */
157
      if (!(lock->runcount > 0))
158
        {
159
          mtx_unlock (&lock->lock);
160
          return EINVAL;
161
        }
162
      lock->runcount--;
163
    }
164
  if (lock->runcount == 0)
165
    {
166
      /* POSIX recommends that "write locks shall take precedence over read
167
         locks", to avoid "writer starvation".  */
168
      if (lock->waiting_writers_count > 0)
169
        {
170
          /* Wake up one of the waiting writers.  */
171
          if (cnd_signal (&lock->waiting_writers) != thrd_success)
172
            {
173
              mtx_unlock (&lock->lock);
174
              return EINVAL;
175
            }
176
        }
177
      else
178
        {
179
          /* Wake up all waiting readers.  */
180
          if (cnd_broadcast (&lock->waiting_readers) != thrd_success)
181
            {
182
              mtx_unlock (&lock->lock);
183
              return EINVAL;
184
            }
185
        }
186
    }
187
  if (mtx_unlock (&lock->lock) != thrd_success)
188
    return EINVAL;
189
  return 0;
190
}
191
192
int
193
glthread_rwlock_destroy (gl_rwlock_t *lock)
194
{
195
  if (lock->init_needed)
196
    call_once (&lock->init_once, lock->init_func);
197
  mtx_destroy (&lock->lock);
198
  cnd_destroy (&lock->waiting_readers);
199
  cnd_destroy (&lock->waiting_writers);
200
  return 0;
201
}
202
203
/* --------------------- gl_recursive_lock_t datatype --------------------- */
204
205
int
206
glthread_recursive_lock_init (gl_recursive_lock_t *lock)
207
{
208
  if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success)
209
    return ENOMEM;
210
  lock->init_needed = 0;
211
  return 0;
212
}
213
214
int
215
glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
216
{
217
  if (lock->init_needed)
218
    call_once (&lock->init_once, lock->init_func);
219
  if (mtx_lock (&lock->mutex) != thrd_success)
220
    return EAGAIN;
221
  return 0;
222
}
223
224
int
225
glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
226
{
227
  if (lock->init_needed)
228
    call_once (&lock->init_once, lock->init_func);
229
  if (mtx_unlock (&lock->mutex) != thrd_success)
230
    return EINVAL;
231
  return 0;
232
}
233
234
int
235
glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
236
{
237
  if (lock->init_needed)
238
    call_once (&lock->init_once, lock->init_func);
239
  mtx_destroy (&lock->mutex);
240
  return 0;
241
}
242
243
/* -------------------------- gl_once_t datatype -------------------------- */
244
245
#endif
246
247
/* ========================================================================= */
248
249
#if USE_POSIX_THREADS
250
251
/* -------------------------- gl_lock_t datatype -------------------------- */
252
253
/* ------------------------- gl_rwlock_t datatype ------------------------- */
254
255
# if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
256
257
#  if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
258
259
#   if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
260
     /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
261
262
int
263
glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
264
0
{
265
0
  pthread_rwlockattr_t attributes;
266
0
  int err;
267
268
0
  err = pthread_rwlockattr_init (&attributes);
269
0
  if (err != 0)
270
0
    return err;
271
  /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
272
     causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
273
     do this; see
274
     http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
275
0
  err = pthread_rwlockattr_setkind_np (&attributes,
276
0
                                       PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
277
0
  if (err == 0)
278
0
    err = pthread_rwlock_init(lock, &attributes);
279
  /* pthread_rwlockattr_destroy always returns 0.  It cannot influence the
280
     return value.  */
281
0
  pthread_rwlockattr_destroy (&attributes);
282
0
  return err;
283
0
}
284
285
#   endif
286
#  else
287
288
int
289
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
290
{
291
  int err;
292
293
  err = pthread_rwlock_init (&lock->rwlock, NULL);
294
  if (err != 0)
295
    return err;
296
  lock->initialized = 1;
297
  return 0;
298
}
299
300
int
301
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
302
{
303
  if (!lock->initialized)
304
    {
305
      int err;
306
307
      err = pthread_mutex_lock (&lock->guard);
308
      if (err != 0)
309
        return err;
310
      if (!lock->initialized)
311
        {
312
          err = glthread_rwlock_init_multithreaded (lock);
313
          if (err != 0)
314
            {
315
              pthread_mutex_unlock (&lock->guard);
316
              return err;
317
            }
318
        }
319
      err = pthread_mutex_unlock (&lock->guard);
320
      if (err != 0)
321
        return err;
322
    }
323
  return pthread_rwlock_rdlock (&lock->rwlock);
324
}
325
326
int
327
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
328
{
329
  if (!lock->initialized)
330
    {
331
      int err;
332
333
      err = pthread_mutex_lock (&lock->guard);
334
      if (err != 0)
335
        return err;
336
      if (!lock->initialized)
337
        {
338
          err = glthread_rwlock_init_multithreaded (lock);
339
          if (err != 0)
340
            {
341
              pthread_mutex_unlock (&lock->guard);
342
              return err;
343
            }
344
        }
345
      err = pthread_mutex_unlock (&lock->guard);
346
      if (err != 0)
347
        return err;
348
    }
349
  return pthread_rwlock_wrlock (&lock->rwlock);
350
}
351
352
int
353
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
354
{
355
  if (!lock->initialized)
356
    return EINVAL;
357
  return pthread_rwlock_unlock (&lock->rwlock);
358
}
359
360
int
361
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
362
{
363
  int err;
364
365
  if (!lock->initialized)
366
    return EINVAL;
367
  err = pthread_rwlock_destroy (&lock->rwlock);
368
  if (err != 0)
369
    return err;
370
  lock->initialized = 0;
371
  return 0;
372
}
373
374
#  endif
375
376
# else
377
378
int
379
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
380
{
381
  int err;
382
383
  err = pthread_mutex_init (&lock->lock, NULL);
384
  if (err != 0)
385
    return err;
386
  err = pthread_cond_init (&lock->waiting_readers, NULL);
387
  if (err != 0)
388
    return err;
389
  err = pthread_cond_init (&lock->waiting_writers, NULL);
390
  if (err != 0)
391
    return err;
392
  lock->waiting_writers_count = 0;
393
  lock->runcount = 0;
394
  return 0;
395
}
396
397
int
398
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
399
{
400
  int err;
401
402
  err = pthread_mutex_lock (&lock->lock);
403
  if (err != 0)
404
    return err;
405
  /* Test whether only readers are currently running, and whether the runcount
406
     field will not overflow, and whether no writer is waiting.  The latter
407
     condition is because POSIX recommends that "write locks shall take
408
     precedence over read locks", to avoid "writer starvation".  */
409
  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
410
    {
411
      /* This thread has to wait for a while.  Enqueue it among the
412
         waiting_readers.  */
413
      err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
414
      if (err != 0)
415
        {
416
          pthread_mutex_unlock (&lock->lock);
417
          return err;
418
        }
419
    }
420
  lock->runcount++;
421
  return pthread_mutex_unlock (&lock->lock);
422
}
423
424
int
425
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
426
{
427
  int err;
428
429
  err = pthread_mutex_lock (&lock->lock);
430
  if (err != 0)
431
    return err;
432
  /* Test whether no readers or writers are currently running.  */
433
  while (!(lock->runcount == 0))
434
    {
435
      /* This thread has to wait for a while.  Enqueue it among the
436
         waiting_writers.  */
437
      lock->waiting_writers_count++;
438
      err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
439
      if (err != 0)
440
        {
441
          lock->waiting_writers_count--;
442
          pthread_mutex_unlock (&lock->lock);
443
          return err;
444
        }
445
      lock->waiting_writers_count--;
446
    }
447
  lock->runcount--; /* runcount becomes -1 */
448
  return pthread_mutex_unlock (&lock->lock);
449
}
450
451
int
452
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
453
{
454
  int err;
455
456
  err = pthread_mutex_lock (&lock->lock);
457
  if (err != 0)
458
    return err;
459
  if (lock->runcount < 0)
460
    {
461
      /* Drop a writer lock.  */
462
      if (!(lock->runcount == -1))
463
        {
464
          pthread_mutex_unlock (&lock->lock);
465
          return EINVAL;
466
        }
467
      lock->runcount = 0;
468
    }
469
  else
470
    {
471
      /* Drop a reader lock.  */
472
      if (!(lock->runcount > 0))
473
        {
474
          pthread_mutex_unlock (&lock->lock);
475
          return EINVAL;
476
        }
477
      lock->runcount--;
478
    }
479
  if (lock->runcount == 0)
480
    {
481
      /* POSIX recommends that "write locks shall take precedence over read
482
         locks", to avoid "writer starvation".  */
483
      if (lock->waiting_writers_count > 0)
484
        {
485
          /* Wake up one of the waiting writers.  */
486
          err = pthread_cond_signal (&lock->waiting_writers);
487
          if (err != 0)
488
            {
489
              pthread_mutex_unlock (&lock->lock);
490
              return err;
491
            }
492
        }
493
      else
494
        {
495
          /* Wake up all waiting readers.  */
496
          err = pthread_cond_broadcast (&lock->waiting_readers);
497
          if (err != 0)
498
            {
499
              pthread_mutex_unlock (&lock->lock);
500
              return err;
501
            }
502
        }
503
    }
504
  return pthread_mutex_unlock (&lock->lock);
505
}
506
507
int
508
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
509
{
510
  int err;
511
512
  err = pthread_mutex_destroy (&lock->lock);
513
  if (err != 0)
514
    return err;
515
  err = pthread_cond_destroy (&lock->waiting_readers);
516
  if (err != 0)
517
    return err;
518
  err = pthread_cond_destroy (&lock->waiting_writers);
519
  if (err != 0)
520
    return err;
521
  return 0;
522
}
523
524
# endif
525
526
/* --------------------- gl_recursive_lock_t datatype --------------------- */
527
528
# if HAVE_PTHREAD_MUTEX_RECURSIVE
529
530
#  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
531
532
int
533
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
534
0
{
535
0
  pthread_mutexattr_t attributes;
536
0
  int err;
537
538
0
  err = pthread_mutexattr_init (&attributes);
539
0
  if (err != 0)
540
0
    return err;
541
0
  err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
542
0
  if (err != 0)
543
0
    {
544
0
      pthread_mutexattr_destroy (&attributes);
545
0
      return err;
546
0
    }
547
0
  err = pthread_mutex_init (lock, &attributes);
548
0
  if (err != 0)
549
0
    {
550
0
      pthread_mutexattr_destroy (&attributes);
551
0
      return err;
552
0
    }
553
0
  err = pthread_mutexattr_destroy (&attributes);
554
0
  if (err != 0)
555
0
    return err;
556
0
  return 0;
557
0
}
558
559
#  else
560
561
int
562
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
563
{
564
  pthread_mutexattr_t attributes;
565
  int err;
566
567
  err = pthread_mutexattr_init (&attributes);
568
  if (err != 0)
569
    return err;
570
  err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
571
  if (err != 0)
572
    {
573
      pthread_mutexattr_destroy (&attributes);
574
      return err;
575
    }
576
  err = pthread_mutex_init (&lock->recmutex, &attributes);
577
  if (err != 0)
578
    {
579
      pthread_mutexattr_destroy (&attributes);
580
      return err;
581
    }
582
  err = pthread_mutexattr_destroy (&attributes);
583
  if (err != 0)
584
    return err;
585
  lock->initialized = 1;
586
  return 0;
587
}
588
589
int
590
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
591
{
592
  if (!lock->initialized)
593
    {
594
      int err;
595
596
      err = pthread_mutex_lock (&lock->guard);
597
      if (err != 0)
598
        return err;
599
      if (!lock->initialized)
600
        {
601
          err = glthread_recursive_lock_init_multithreaded (lock);
602
          if (err != 0)
603
            {
604
              pthread_mutex_unlock (&lock->guard);
605
              return err;
606
            }
607
        }
608
      err = pthread_mutex_unlock (&lock->guard);
609
      if (err != 0)
610
        return err;
611
    }
612
  return pthread_mutex_lock (&lock->recmutex);
613
}
614
615
int
616
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
617
{
618
  if (!lock->initialized)
619
    return EINVAL;
620
  return pthread_mutex_unlock (&lock->recmutex);
621
}
622
623
int
624
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
625
{
626
  int err;
627
628
  if (!lock->initialized)
629
    return EINVAL;
630
  err = pthread_mutex_destroy (&lock->recmutex);
631
  if (err != 0)
632
    return err;
633
  lock->initialized = 0;
634
  return 0;
635
}
636
637
#  endif
638
639
# else
640
641
int
642
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
643
{
644
  int err;
645
646
  err = pthread_mutex_init (&lock->mutex, NULL);
647
  if (err != 0)
648
    return err;
649
  lock->owner = (pthread_t) 0;
650
  lock->depth = 0;
651
  return 0;
652
}
653
654
int
655
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
656
{
657
  pthread_t self = pthread_self ();
658
  if (lock->owner != self)
659
    {
660
      int err;
661
662
      err = pthread_mutex_lock (&lock->mutex);
663
      if (err != 0)
664
        return err;
665
      lock->owner = self;
666
    }
667
  if (++(lock->depth) == 0) /* wraparound? */
668
    {
669
      lock->depth--;
670
      return EAGAIN;
671
    }
672
  return 0;
673
}
674
675
int
676
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
677
{
678
  if (lock->owner != pthread_self ())
679
    return EPERM;
680
  if (lock->depth == 0)
681
    return EINVAL;
682
  if (--(lock->depth) == 0)
683
    {
684
      lock->owner = (pthread_t) 0;
685
      return pthread_mutex_unlock (&lock->mutex);
686
    }
687
  else
688
    return 0;
689
}
690
691
int
692
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
693
{
694
  if (lock->owner != (pthread_t) 0)
695
    return EBUSY;
696
  return pthread_mutex_destroy (&lock->mutex);
697
}
698
699
# endif
700
701
/* -------------------------- gl_once_t datatype -------------------------- */
702
703
static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
704
705
int
706
glthread_once_singlethreaded (pthread_once_t *once_control)
707
0
{
708
  /* We don't know whether pthread_once_t is an integer type, a floating-point
709
     type, a pointer type, or a structure type.  */
710
0
  char *firstbyte = (char *)once_control;
711
0
  if (*firstbyte == *(const char *)&fresh_once)
712
0
    {
713
      /* First time use of once_control.  Invert the first byte.  */
714
0
      *firstbyte = ~ *(const char *)&fresh_once;
715
0
      return 1;
716
0
    }
717
0
  else
718
0
    return 0;
719
0
}
720
721
# if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)
722
723
int
724
glthread_once_multithreaded (pthread_once_t *once_control,
725
                             void (*init_function) (void))
726
{
727
  int err = pthread_once (once_control, init_function);
728
  if (err == ENOSYS)
729
    {
730
      /* This happens on FreeBSD 11: The pthread_once function in libc returns
731
         ENOSYS.  */
732
      if (glthread_once_singlethreaded (once_control))
733
        init_function ();
734
      return 0;
735
    }
736
  return err;
737
}
738
739
# endif
740
741
#endif
742
743
/* ========================================================================= */
744
745
#if USE_WINDOWS_THREADS
746
747
#endif
748
749
/* ========================================================================= */