~ubuntu-branches/ubuntu/vivid/gzip/vivid

« back to all changes in this revision

Viewing changes to lib/glthread/lock.c

  • Committer: Steve Langasek
  • Date: 2012-06-29 02:07:40 UTC
  • mfrom: (4.1.9 sid)
  • Revision ID: steve.langasek@canonical.com-20120629020740-qqikrblzana08v2y
Merge version 1.5-1.1 from Debian

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Locking in multithreaded situations.
 
2
   Copyright (C) 2005-2012 Free Software Foundation, Inc.
 
3
 
 
4
   This program is free software; you can redistribute it and/or modify
 
5
   it under the terms of the GNU General Public License as published by
 
6
   the Free Software Foundation; either version 3, or (at your option)
 
7
   any later version.
 
8
 
 
9
   This program 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 General Public License for more details.
 
13
 
 
14
   You should have received a copy of the GNU General Public License
 
15
   along with this program; if not, see <http://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, gthr-solaris.h,
 
19
   gthr-win32.h.  */
 
20
 
 
21
#include <config.h>
 
22
 
 
23
#include "glthread/lock.h"
 
24
 
 
25
/* ========================================================================= */
 
26
 
 
27
#if USE_POSIX_THREADS
 
28
 
 
29
/* -------------------------- gl_lock_t datatype -------------------------- */
 
30
 
 
31
/* ------------------------- gl_rwlock_t datatype ------------------------- */
 
32
 
 
33
# if HAVE_PTHREAD_RWLOCK
 
34
 
 
35
#  if !defined PTHREAD_RWLOCK_INITIALIZER
 
36
 
 
37
int
 
38
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
 
39
{
 
40
  int err;
 
41
 
 
42
  err = pthread_rwlock_init (&lock->rwlock, NULL);
 
43
  if (err != 0)
 
44
    return err;
 
45
  lock->initialized = 1;
 
46
  return 0;
 
47
}
 
48
 
 
49
int
 
50
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
 
51
{
 
52
  if (!lock->initialized)
 
53
    {
 
54
      int err;
 
55
 
 
56
      err = pthread_mutex_lock (&lock->guard);
 
57
      if (err != 0)
 
58
        return err;
 
59
      if (!lock->initialized)
 
60
        {
 
61
          err = glthread_rwlock_init_multithreaded (lock);
 
62
          if (err != 0)
 
63
            {
 
64
              pthread_mutex_unlock (&lock->guard);
 
65
              return err;
 
66
            }
 
67
        }
 
68
      err = pthread_mutex_unlock (&lock->guard);
 
69
      if (err != 0)
 
70
        return err;
 
71
    }
 
72
  return pthread_rwlock_rdlock (&lock->rwlock);
 
73
}
 
74
 
 
75
int
 
76
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
 
77
{
 
78
  if (!lock->initialized)
 
79
    {
 
80
      int err;
 
81
 
 
82
      err = pthread_mutex_lock (&lock->guard);
 
83
      if (err != 0)
 
84
        return err;
 
85
      if (!lock->initialized)
 
86
        {
 
87
          err = glthread_rwlock_init_multithreaded (lock);
 
88
          if (err != 0)
 
89
            {
 
90
              pthread_mutex_unlock (&lock->guard);
 
91
              return err;
 
92
            }
 
93
        }
 
94
      err = pthread_mutex_unlock (&lock->guard);
 
95
      if (err != 0)
 
96
        return err;
 
97
    }
 
98
  return pthread_rwlock_wrlock (&lock->rwlock);
 
99
}
 
100
 
 
101
int
 
102
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
 
103
{
 
104
  if (!lock->initialized)
 
105
    return EINVAL;
 
106
  return pthread_rwlock_unlock (&lock->rwlock);
 
107
}
 
108
 
 
109
int
 
110
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
 
111
{
 
112
  int err;
 
113
 
 
114
  if (!lock->initialized)
 
115
    return EINVAL;
 
116
  err = pthread_rwlock_destroy (&lock->rwlock);
 
117
  if (err != 0)
 
118
    return err;
 
119
  lock->initialized = 0;
 
120
  return 0;
 
121
}
 
122
 
 
123
#  endif
 
124
 
 
125
# else
 
126
 
 
127
int
 
128
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
 
129
{
 
130
  int err;
 
131
 
 
132
  err = pthread_mutex_init (&lock->lock, NULL);
 
133
  if (err != 0)
 
134
    return err;
 
135
  err = pthread_cond_init (&lock->waiting_readers, NULL);
 
136
  if (err != 0)
 
137
    return err;
 
138
  err = pthread_cond_init (&lock->waiting_writers, NULL);
 
139
  if (err != 0)
 
140
    return err;
 
141
  lock->waiting_writers_count = 0;
 
142
  lock->runcount = 0;
 
143
  return 0;
 
144
}
 
145
 
 
146
int
 
147
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
 
148
{
 
149
  int err;
 
150
 
 
151
  err = pthread_mutex_lock (&lock->lock);
 
152
  if (err != 0)
 
153
    return err;
 
154
  /* Test whether only readers are currently running, and whether the runcount
 
155
     field will not overflow.  */
 
156
  /* POSIX says: "It is implementation-defined whether the calling thread
 
157
     acquires the lock when a writer does not hold the lock and there are
 
158
     writers blocked on the lock."  Let's say, no: give the writers a higher
 
159
     priority.  */
 
160
  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
 
161
    {
 
162
      /* This thread has to wait for a while.  Enqueue it among the
 
163
         waiting_readers.  */
 
164
      err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
 
165
      if (err != 0)
 
166
        {
 
167
          pthread_mutex_unlock (&lock->lock);
 
168
          return err;
 
169
        }
 
170
    }
 
171
  lock->runcount++;
 
172
  return pthread_mutex_unlock (&lock->lock);
 
173
}
 
174
 
 
175
int
 
176
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
 
177
{
 
178
  int err;
 
179
 
 
180
  err = pthread_mutex_lock (&lock->lock);
 
181
  if (err != 0)
 
182
    return err;
 
183
  /* Test whether no readers or writers are currently running.  */
 
184
  while (!(lock->runcount == 0))
 
185
    {
 
186
      /* This thread has to wait for a while.  Enqueue it among the
 
187
         waiting_writers.  */
 
188
      lock->waiting_writers_count++;
 
189
      err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
 
190
      if (err != 0)
 
191
        {
 
192
          lock->waiting_writers_count--;
 
193
          pthread_mutex_unlock (&lock->lock);
 
194
          return err;
 
195
        }
 
196
      lock->waiting_writers_count--;
 
197
    }
 
198
  lock->runcount--; /* runcount becomes -1 */
 
199
  return pthread_mutex_unlock (&lock->lock);
 
200
}
 
201
 
 
202
int
 
203
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
 
204
{
 
205
  int err;
 
206
 
 
207
  err = pthread_mutex_lock (&lock->lock);
 
208
  if (err != 0)
 
209
    return err;
 
210
  if (lock->runcount < 0)
 
211
    {
 
212
      /* Drop a writer lock.  */
 
213
      if (!(lock->runcount == -1))
 
214
        {
 
215
          pthread_mutex_unlock (&lock->lock);
 
216
          return EINVAL;
 
217
        }
 
218
      lock->runcount = 0;
 
219
    }
 
220
  else
 
221
    {
 
222
      /* Drop a reader lock.  */
 
223
      if (!(lock->runcount > 0))
 
224
        {
 
225
          pthread_mutex_unlock (&lock->lock);
 
226
          return EINVAL;
 
227
        }
 
228
      lock->runcount--;
 
229
    }
 
230
  if (lock->runcount == 0)
 
231
    {
 
232
      /* POSIX recommends that "write locks shall take precedence over read
 
233
         locks", to avoid "writer starvation".  */
 
234
      if (lock->waiting_writers_count > 0)
 
235
        {
 
236
          /* Wake up one of the waiting writers.  */
 
237
          err = pthread_cond_signal (&lock->waiting_writers);
 
238
          if (err != 0)
 
239
            {
 
240
              pthread_mutex_unlock (&lock->lock);
 
241
              return err;
 
242
            }
 
243
        }
 
244
      else
 
245
        {
 
246
          /* Wake up all waiting readers.  */
 
247
          err = pthread_cond_broadcast (&lock->waiting_readers);
 
248
          if (err != 0)
 
249
            {
 
250
              pthread_mutex_unlock (&lock->lock);
 
251
              return err;
 
252
            }
 
253
        }
 
254
    }
 
255
  return pthread_mutex_unlock (&lock->lock);
 
256
}
 
257
 
 
258
int
 
259
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
 
260
{
 
261
  int err;
 
262
 
 
263
  err = pthread_mutex_destroy (&lock->lock);
 
264
  if (err != 0)
 
265
    return err;
 
266
  err = pthread_cond_destroy (&lock->waiting_readers);
 
267
  if (err != 0)
 
268
    return err;
 
269
  err = pthread_cond_destroy (&lock->waiting_writers);
 
270
  if (err != 0)
 
271
    return err;
 
272
  return 0;
 
273
}
 
274
 
 
275
# endif
 
276
 
 
277
/* --------------------- gl_recursive_lock_t datatype --------------------- */
 
278
 
 
279
# if HAVE_PTHREAD_MUTEX_RECURSIVE
 
280
 
 
281
#  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
 
282
 
 
283
int
 
284
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
 
285
{
 
286
  pthread_mutexattr_t attributes;
 
287
  int err;
 
288
 
 
289
  err = pthread_mutexattr_init (&attributes);
 
290
  if (err != 0)
 
291
    return err;
 
292
  err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
 
293
  if (err != 0)
 
294
    {
 
295
      pthread_mutexattr_destroy (&attributes);
 
296
      return err;
 
297
    }
 
298
  err = pthread_mutex_init (lock, &attributes);
 
299
  if (err != 0)
 
300
    {
 
301
      pthread_mutexattr_destroy (&attributes);
 
302
      return err;
 
303
    }
 
304
  err = pthread_mutexattr_destroy (&attributes);
 
305
  if (err != 0)
 
306
    return err;
 
307
  return 0;
 
308
}
 
309
 
 
310
#  else
 
311
 
 
312
int
 
313
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
 
314
{
 
315
  pthread_mutexattr_t attributes;
 
316
  int err;
 
317
 
 
318
  err = pthread_mutexattr_init (&attributes);
 
319
  if (err != 0)
 
320
    return err;
 
321
  err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
 
322
  if (err != 0)
 
323
    {
 
324
      pthread_mutexattr_destroy (&attributes);
 
325
      return err;
 
326
    }
 
327
  err = pthread_mutex_init (&lock->recmutex, &attributes);
 
328
  if (err != 0)
 
329
    {
 
330
      pthread_mutexattr_destroy (&attributes);
 
331
      return err;
 
332
    }
 
333
  err = pthread_mutexattr_destroy (&attributes);
 
334
  if (err != 0)
 
335
    return err;
 
336
  lock->initialized = 1;
 
337
  return 0;
 
338
}
 
339
 
 
340
int
 
341
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
 
342
{
 
343
  if (!lock->initialized)
 
344
    {
 
345
      int err;
 
346
 
 
347
      err = pthread_mutex_lock (&lock->guard);
 
348
      if (err != 0)
 
349
        return err;
 
350
      if (!lock->initialized)
 
351
        {
 
352
          err = glthread_recursive_lock_init_multithreaded (lock);
 
353
          if (err != 0)
 
354
            {
 
355
              pthread_mutex_unlock (&lock->guard);
 
356
              return err;
 
357
            }
 
358
        }
 
359
      err = pthread_mutex_unlock (&lock->guard);
 
360
      if (err != 0)
 
361
        return err;
 
362
    }
 
363
  return pthread_mutex_lock (&lock->recmutex);
 
364
}
 
365
 
 
366
int
 
367
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
 
368
{
 
369
  if (!lock->initialized)
 
370
    return EINVAL;
 
371
  return pthread_mutex_unlock (&lock->recmutex);
 
372
}
 
373
 
 
374
int
 
375
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
 
376
{
 
377
  int err;
 
378
 
 
379
  if (!lock->initialized)
 
380
    return EINVAL;
 
381
  err = pthread_mutex_destroy (&lock->recmutex);
 
382
  if (err != 0)
 
383
    return err;
 
384
  lock->initialized = 0;
 
385
  return 0;
 
386
}
 
387
 
 
388
#  endif
 
389
 
 
390
# else
 
391
 
 
392
int
 
393
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
 
394
{
 
395
  int err;
 
396
 
 
397
  err = pthread_mutex_init (&lock->mutex, NULL);
 
398
  if (err != 0)
 
399
    return err;
 
400
  lock->owner = (pthread_t) 0;
 
401
  lock->depth = 0;
 
402
  return 0;
 
403
}
 
404
 
 
405
int
 
406
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
 
407
{
 
408
  pthread_t self = pthread_self ();
 
409
  if (lock->owner != self)
 
410
    {
 
411
      int err;
 
412
 
 
413
      err = pthread_mutex_lock (&lock->mutex);
 
414
      if (err != 0)
 
415
        return err;
 
416
      lock->owner = self;
 
417
    }
 
418
  if (++(lock->depth) == 0) /* wraparound? */
 
419
    {
 
420
      lock->depth--;
 
421
      return EAGAIN;
 
422
    }
 
423
  return 0;
 
424
}
 
425
 
 
426
int
 
427
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
 
428
{
 
429
  if (lock->owner != pthread_self ())
 
430
    return EPERM;
 
431
  if (lock->depth == 0)
 
432
    return EINVAL;
 
433
  if (--(lock->depth) == 0)
 
434
    {
 
435
      lock->owner = (pthread_t) 0;
 
436
      return pthread_mutex_unlock (&lock->mutex);
 
437
    }
 
438
  else
 
439
    return 0;
 
440
}
 
441
 
 
442
int
 
443
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
 
444
{
 
445
  if (lock->owner != (pthread_t) 0)
 
446
    return EBUSY;
 
447
  return pthread_mutex_destroy (&lock->mutex);
 
448
}
 
449
 
 
450
# endif
 
451
 
 
452
/* -------------------------- gl_once_t datatype -------------------------- */
 
453
 
 
454
static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
 
455
 
 
456
int
 
457
glthread_once_singlethreaded (pthread_once_t *once_control)
 
458
{
 
459
  /* We don't know whether pthread_once_t is an integer type, a floating-point
 
460
     type, a pointer type, or a structure type.  */
 
461
  char *firstbyte = (char *)once_control;
 
462
  if (*firstbyte == *(const char *)&fresh_once)
 
463
    {
 
464
      /* First time use of once_control.  Invert the first byte.  */
 
465
      *firstbyte = ~ *(const char *)&fresh_once;
 
466
      return 1;
 
467
    }
 
468
  else
 
469
    return 0;
 
470
}
 
471
 
 
472
#endif
 
473
 
 
474
/* ========================================================================= */
 
475
 
 
476
#if USE_PTH_THREADS
 
477
 
 
478
/* Use the GNU Pth threads library.  */
 
479
 
 
480
/* -------------------------- gl_lock_t datatype -------------------------- */
 
481
 
 
482
/* ------------------------- gl_rwlock_t datatype ------------------------- */
 
483
 
 
484
/* --------------------- gl_recursive_lock_t datatype --------------------- */
 
485
 
 
486
/* -------------------------- gl_once_t datatype -------------------------- */
 
487
 
 
488
static void
 
489
glthread_once_call (void *arg)
 
490
{
 
491
  void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
 
492
  void (*initfunction) (void) = *gl_once_temp_addr;
 
493
  initfunction ();
 
494
}
 
495
 
 
496
int
 
497
glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
 
498
{
 
499
  void (*temp) (void) = initfunction;
 
500
  return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
 
501
}
 
502
 
 
503
int
 
504
glthread_once_singlethreaded (pth_once_t *once_control)
 
505
{
 
506
  /* We know that pth_once_t is an integer type.  */
 
507
  if (*once_control == PTH_ONCE_INIT)
 
508
    {
 
509
      /* First time use of once_control.  Invert the marker.  */
 
510
      *once_control = ~ PTH_ONCE_INIT;
 
511
      return 1;
 
512
    }
 
513
  else
 
514
    return 0;
 
515
}
 
516
 
 
517
#endif
 
518
 
 
519
/* ========================================================================= */
 
520
 
 
521
#if USE_SOLARIS_THREADS
 
522
 
 
523
/* Use the old Solaris threads library.  */
 
524
 
 
525
/* -------------------------- gl_lock_t datatype -------------------------- */
 
526
 
 
527
/* ------------------------- gl_rwlock_t datatype ------------------------- */
 
528
 
 
529
/* --------------------- gl_recursive_lock_t datatype --------------------- */
 
530
 
 
531
int
 
532
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
 
533
{
 
534
  int err;
 
535
 
 
536
  err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
 
537
  if (err != 0)
 
538
    return err;
 
539
  lock->owner = (thread_t) 0;
 
540
  lock->depth = 0;
 
541
  return 0;
 
542
}
 
543
 
 
544
int
 
545
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
 
546
{
 
547
  thread_t self = thr_self ();
 
548
  if (lock->owner != self)
 
549
    {
 
550
      int err;
 
551
 
 
552
      err = mutex_lock (&lock->mutex);
 
553
      if (err != 0)
 
554
        return err;
 
555
      lock->owner = self;
 
556
    }
 
557
  if (++(lock->depth) == 0) /* wraparound? */
 
558
    {
 
559
      lock->depth--;
 
560
      return EAGAIN;
 
561
    }
 
562
  return 0;
 
563
}
 
564
 
 
565
int
 
566
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
 
567
{
 
568
  if (lock->owner != thr_self ())
 
569
    return EPERM;
 
570
  if (lock->depth == 0)
 
571
    return EINVAL;
 
572
  if (--(lock->depth) == 0)
 
573
    {
 
574
      lock->owner = (thread_t) 0;
 
575
      return mutex_unlock (&lock->mutex);
 
576
    }
 
577
  else
 
578
    return 0;
 
579
}
 
580
 
 
581
int
 
582
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
 
583
{
 
584
  if (lock->owner != (thread_t) 0)
 
585
    return EBUSY;
 
586
  return mutex_destroy (&lock->mutex);
 
587
}
 
588
 
 
589
/* -------------------------- gl_once_t datatype -------------------------- */
 
590
 
 
591
int
 
592
glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
 
593
{
 
594
  if (!once_control->inited)
 
595
    {
 
596
      int err;
 
597
 
 
598
      /* Use the mutex to guarantee that if another thread is already calling
 
599
         the initfunction, this thread waits until it's finished.  */
 
600
      err = mutex_lock (&once_control->mutex);
 
601
      if (err != 0)
 
602
        return err;
 
603
      if (!once_control->inited)
 
604
        {
 
605
          once_control->inited = 1;
 
606
          initfunction ();
 
607
        }
 
608
      return mutex_unlock (&once_control->mutex);
 
609
    }
 
610
  else
 
611
    return 0;
 
612
}
 
613
 
 
614
int
 
615
glthread_once_singlethreaded (gl_once_t *once_control)
 
616
{
 
617
  /* We know that gl_once_t contains an integer type.  */
 
618
  if (!once_control->inited)
 
619
    {
 
620
      /* First time use of once_control.  Invert the marker.  */
 
621
      once_control->inited = ~ 0;
 
622
      return 1;
 
623
    }
 
624
  else
 
625
    return 0;
 
626
}
 
627
 
 
628
#endif
 
629
 
 
630
/* ========================================================================= */
 
631
 
 
632
#if USE_WINDOWS_THREADS
 
633
 
 
634
/* -------------------------- gl_lock_t datatype -------------------------- */
 
635
 
 
636
void
 
637
glthread_lock_init_func (gl_lock_t *lock)
 
638
{
 
639
  InitializeCriticalSection (&lock->lock);
 
640
  lock->guard.done = 1;
 
641
}
 
642
 
 
643
int
 
644
glthread_lock_lock_func (gl_lock_t *lock)
 
645
{
 
646
  if (!lock->guard.done)
 
647
    {
 
648
      if (InterlockedIncrement (&lock->guard.started) == 0)
 
649
        /* This thread is the first one to need this lock.  Initialize it.  */
 
650
        glthread_lock_init (lock);
 
651
      else
 
652
        /* Yield the CPU while waiting for another thread to finish
 
653
           initializing this lock.  */
 
654
        while (!lock->guard.done)
 
655
          Sleep (0);
 
656
    }
 
657
  EnterCriticalSection (&lock->lock);
 
658
  return 0;
 
659
}
 
660
 
 
661
int
 
662
glthread_lock_unlock_func (gl_lock_t *lock)
 
663
{
 
664
  if (!lock->guard.done)
 
665
    return EINVAL;
 
666
  LeaveCriticalSection (&lock->lock);
 
667
  return 0;
 
668
}
 
669
 
 
670
int
 
671
glthread_lock_destroy_func (gl_lock_t *lock)
 
672
{
 
673
  if (!lock->guard.done)
 
674
    return EINVAL;
 
675
  DeleteCriticalSection (&lock->lock);
 
676
  lock->guard.done = 0;
 
677
  return 0;
 
678
}
 
679
 
 
680
/* ------------------------- gl_rwlock_t datatype ------------------------- */
 
681
 
 
682
/* In this file, the waitqueues are implemented as circular arrays.  */
 
683
#define gl_waitqueue_t gl_carray_waitqueue_t
 
684
 
 
685
static inline void
 
686
gl_waitqueue_init (gl_waitqueue_t *wq)
 
687
{
 
688
  wq->array = NULL;
 
689
  wq->count = 0;
 
690
  wq->alloc = 0;
 
691
  wq->offset = 0;
 
692
}
 
693
 
 
694
/* Enqueues the current thread, represented by an event, in a wait queue.
 
695
   Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
 
696
static HANDLE
 
697
gl_waitqueue_add (gl_waitqueue_t *wq)
 
698
{
 
699
  HANDLE event;
 
700
  unsigned int index;
 
701
 
 
702
  if (wq->count == wq->alloc)
 
703
    {
 
704
      unsigned int new_alloc = 2 * wq->alloc + 1;
 
705
      HANDLE *new_array =
 
706
        (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
 
707
      if (new_array == NULL)
 
708
        /* No more memory.  */
 
709
        return INVALID_HANDLE_VALUE;
 
710
      /* Now is a good opportunity to rotate the array so that its contents
 
711
         starts at offset 0.  */
 
712
      if (wq->offset > 0)
 
713
        {
 
714
          unsigned int old_count = wq->count;
 
715
          unsigned int old_alloc = wq->alloc;
 
716
          unsigned int old_offset = wq->offset;
 
717
          unsigned int i;
 
718
          if (old_offset + old_count > old_alloc)
 
719
            {
 
720
              unsigned int limit = old_offset + old_count - old_alloc;
 
721
              for (i = 0; i < limit; i++)
 
722
                new_array[old_alloc + i] = new_array[i];
 
723
            }
 
724
          for (i = 0; i < old_count; i++)
 
725
            new_array[i] = new_array[old_offset + i];
 
726
          wq->offset = 0;
 
727
        }
 
728
      wq->array = new_array;
 
729
      wq->alloc = new_alloc;
 
730
    }
 
731
  /* Whether the created event is a manual-reset one or an auto-reset one,
 
732
     does not matter, since we will wait on it only once.  */
 
733
  event = CreateEvent (NULL, TRUE, FALSE, NULL);
 
734
  if (event == INVALID_HANDLE_VALUE)
 
735
    /* No way to allocate an event.  */
 
736
    return INVALID_HANDLE_VALUE;
 
737
  index = wq->offset + wq->count;
 
738
  if (index >= wq->alloc)
 
739
    index -= wq->alloc;
 
740
  wq->array[index] = event;
 
741
  wq->count++;
 
742
  return event;
 
743
}
 
744
 
 
745
/* Notifies the first thread from a wait queue and dequeues it.  */
 
746
static inline void
 
747
gl_waitqueue_notify_first (gl_waitqueue_t *wq)
 
748
{
 
749
  SetEvent (wq->array[wq->offset + 0]);
 
750
  wq->offset++;
 
751
  wq->count--;
 
752
  if (wq->count == 0 || wq->offset == wq->alloc)
 
753
    wq->offset = 0;
 
754
}
 
755
 
 
756
/* Notifies all threads from a wait queue and dequeues them all.  */
 
757
static inline void
 
758
gl_waitqueue_notify_all (gl_waitqueue_t *wq)
 
759
{
 
760
  unsigned int i;
 
761
 
 
762
  for (i = 0; i < wq->count; i++)
 
763
    {
 
764
      unsigned int index = wq->offset + i;
 
765
      if (index >= wq->alloc)
 
766
        index -= wq->alloc;
 
767
      SetEvent (wq->array[index]);
 
768
    }
 
769
  wq->count = 0;
 
770
  wq->offset = 0;
 
771
}
 
772
 
 
773
void
 
774
glthread_rwlock_init_func (gl_rwlock_t *lock)
 
775
{
 
776
  InitializeCriticalSection (&lock->lock);
 
777
  gl_waitqueue_init (&lock->waiting_readers);
 
778
  gl_waitqueue_init (&lock->waiting_writers);
 
779
  lock->runcount = 0;
 
780
  lock->guard.done = 1;
 
781
}
 
782
 
 
783
int
 
784
glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
 
785
{
 
786
  if (!lock->guard.done)
 
787
    {
 
788
      if (InterlockedIncrement (&lock->guard.started) == 0)
 
789
        /* This thread is the first one to need this lock.  Initialize it.  */
 
790
        glthread_rwlock_init (lock);
 
791
      else
 
792
        /* Yield the CPU while waiting for another thread to finish
 
793
           initializing this lock.  */
 
794
        while (!lock->guard.done)
 
795
          Sleep (0);
 
796
    }
 
797
  EnterCriticalSection (&lock->lock);
 
798
  /* Test whether only readers are currently running, and whether the runcount
 
799
     field will not overflow.  */
 
800
  if (!(lock->runcount + 1 > 0))
 
801
    {
 
802
      /* This thread has to wait for a while.  Enqueue it among the
 
803
         waiting_readers.  */
 
804
      HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
 
805
      if (event != INVALID_HANDLE_VALUE)
 
806
        {
 
807
          DWORD result;
 
808
          LeaveCriticalSection (&lock->lock);
 
809
          /* Wait until another thread signals this event.  */
 
810
          result = WaitForSingleObject (event, INFINITE);
 
811
          if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
 
812
            abort ();
 
813
          CloseHandle (event);
 
814
          /* The thread which signalled the event already did the bookkeeping:
 
815
             removed us from the waiting_readers, incremented lock->runcount.  */
 
816
          if (!(lock->runcount > 0))
 
817
            abort ();
 
818
          return 0;
 
819
        }
 
820
      else
 
821
        {
 
822
          /* Allocation failure.  Weird.  */
 
823
          do
 
824
            {
 
825
              LeaveCriticalSection (&lock->lock);
 
826
              Sleep (1);
 
827
              EnterCriticalSection (&lock->lock);
 
828
            }
 
829
          while (!(lock->runcount + 1 > 0));
 
830
        }
 
831
    }
 
832
  lock->runcount++;
 
833
  LeaveCriticalSection (&lock->lock);
 
834
  return 0;
 
835
}
 
836
 
 
837
int
 
838
glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
 
839
{
 
840
  if (!lock->guard.done)
 
841
    {
 
842
      if (InterlockedIncrement (&lock->guard.started) == 0)
 
843
        /* This thread is the first one to need this lock.  Initialize it.  */
 
844
        glthread_rwlock_init (lock);
 
845
      else
 
846
        /* Yield the CPU while waiting for another thread to finish
 
847
           initializing this lock.  */
 
848
        while (!lock->guard.done)
 
849
          Sleep (0);
 
850
    }
 
851
  EnterCriticalSection (&lock->lock);
 
852
  /* Test whether no readers or writers are currently running.  */
 
853
  if (!(lock->runcount == 0))
 
854
    {
 
855
      /* This thread has to wait for a while.  Enqueue it among the
 
856
         waiting_writers.  */
 
857
      HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
 
858
      if (event != INVALID_HANDLE_VALUE)
 
859
        {
 
860
          DWORD result;
 
861
          LeaveCriticalSection (&lock->lock);
 
862
          /* Wait until another thread signals this event.  */
 
863
          result = WaitForSingleObject (event, INFINITE);
 
864
          if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
 
865
            abort ();
 
866
          CloseHandle (event);
 
867
          /* The thread which signalled the event already did the bookkeeping:
 
868
             removed us from the waiting_writers, set lock->runcount = -1.  */
 
869
          if (!(lock->runcount == -1))
 
870
            abort ();
 
871
          return 0;
 
872
        }
 
873
      else
 
874
        {
 
875
          /* Allocation failure.  Weird.  */
 
876
          do
 
877
            {
 
878
              LeaveCriticalSection (&lock->lock);
 
879
              Sleep (1);
 
880
              EnterCriticalSection (&lock->lock);
 
881
            }
 
882
          while (!(lock->runcount == 0));
 
883
        }
 
884
    }
 
885
  lock->runcount--; /* runcount becomes -1 */
 
886
  LeaveCriticalSection (&lock->lock);
 
887
  return 0;
 
888
}
 
889
 
 
890
int
 
891
glthread_rwlock_unlock_func (gl_rwlock_t *lock)
 
892
{
 
893
  if (!lock->guard.done)
 
894
    return EINVAL;
 
895
  EnterCriticalSection (&lock->lock);
 
896
  if (lock->runcount < 0)
 
897
    {
 
898
      /* Drop a writer lock.  */
 
899
      if (!(lock->runcount == -1))
 
900
        abort ();
 
901
      lock->runcount = 0;
 
902
    }
 
903
  else
 
904
    {
 
905
      /* Drop a reader lock.  */
 
906
      if (!(lock->runcount > 0))
 
907
        {
 
908
          LeaveCriticalSection (&lock->lock);
 
909
          return EPERM;
 
910
        }
 
911
      lock->runcount--;
 
912
    }
 
913
  if (lock->runcount == 0)
 
914
    {
 
915
      /* POSIX recommends that "write locks shall take precedence over read
 
916
         locks", to avoid "writer starvation".  */
 
917
      if (lock->waiting_writers.count > 0)
 
918
        {
 
919
          /* Wake up one of the waiting writers.  */
 
920
          lock->runcount--;
 
921
          gl_waitqueue_notify_first (&lock->waiting_writers);
 
922
        }
 
923
      else
 
924
        {
 
925
          /* Wake up all waiting readers.  */
 
926
          lock->runcount += lock->waiting_readers.count;
 
927
          gl_waitqueue_notify_all (&lock->waiting_readers);
 
928
        }
 
929
    }
 
930
  LeaveCriticalSection (&lock->lock);
 
931
  return 0;
 
932
}
 
933
 
 
934
int
 
935
glthread_rwlock_destroy_func (gl_rwlock_t *lock)
 
936
{
 
937
  if (!lock->guard.done)
 
938
    return EINVAL;
 
939
  if (lock->runcount != 0)
 
940
    return EBUSY;
 
941
  DeleteCriticalSection (&lock->lock);
 
942
  if (lock->waiting_readers.array != NULL)
 
943
    free (lock->waiting_readers.array);
 
944
  if (lock->waiting_writers.array != NULL)
 
945
    free (lock->waiting_writers.array);
 
946
  lock->guard.done = 0;
 
947
  return 0;
 
948
}
 
949
 
 
950
/* --------------------- gl_recursive_lock_t datatype --------------------- */
 
951
 
 
952
void
 
953
glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
 
954
{
 
955
  lock->owner = 0;
 
956
  lock->depth = 0;
 
957
  InitializeCriticalSection (&lock->lock);
 
958
  lock->guard.done = 1;
 
959
}
 
960
 
 
961
int
 
962
glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
 
963
{
 
964
  if (!lock->guard.done)
 
965
    {
 
966
      if (InterlockedIncrement (&lock->guard.started) == 0)
 
967
        /* This thread is the first one to need this lock.  Initialize it.  */
 
968
        glthread_recursive_lock_init (lock);
 
969
      else
 
970
        /* Yield the CPU while waiting for another thread to finish
 
971
           initializing this lock.  */
 
972
        while (!lock->guard.done)
 
973
          Sleep (0);
 
974
    }
 
975
  {
 
976
    DWORD self = GetCurrentThreadId ();
 
977
    if (lock->owner != self)
 
978
      {
 
979
        EnterCriticalSection (&lock->lock);
 
980
        lock->owner = self;
 
981
      }
 
982
    if (++(lock->depth) == 0) /* wraparound? */
 
983
      {
 
984
        lock->depth--;
 
985
        return EAGAIN;
 
986
      }
 
987
  }
 
988
  return 0;
 
989
}
 
990
 
 
991
int
 
992
glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
 
993
{
 
994
  if (lock->owner != GetCurrentThreadId ())
 
995
    return EPERM;
 
996
  if (lock->depth == 0)
 
997
    return EINVAL;
 
998
  if (--(lock->depth) == 0)
 
999
    {
 
1000
      lock->owner = 0;
 
1001
      LeaveCriticalSection (&lock->lock);
 
1002
    }
 
1003
  return 0;
 
1004
}
 
1005
 
 
1006
int
 
1007
glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
 
1008
{
 
1009
  if (lock->owner != 0)
 
1010
    return EBUSY;
 
1011
  DeleteCriticalSection (&lock->lock);
 
1012
  lock->guard.done = 0;
 
1013
  return 0;
 
1014
}
 
1015
 
 
1016
/* -------------------------- gl_once_t datatype -------------------------- */
 
1017
 
 
1018
void
 
1019
glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
 
1020
{
 
1021
  if (once_control->inited <= 0)
 
1022
    {
 
1023
      if (InterlockedIncrement (&once_control->started) == 0)
 
1024
        {
 
1025
          /* This thread is the first one to come to this once_control.  */
 
1026
          InitializeCriticalSection (&once_control->lock);
 
1027
          EnterCriticalSection (&once_control->lock);
 
1028
          once_control->inited = 0;
 
1029
          initfunction ();
 
1030
          once_control->inited = 1;
 
1031
          LeaveCriticalSection (&once_control->lock);
 
1032
        }
 
1033
      else
 
1034
        {
 
1035
          /* Undo last operation.  */
 
1036
          InterlockedDecrement (&once_control->started);
 
1037
          /* Some other thread has already started the initialization.
 
1038
             Yield the CPU while waiting for the other thread to finish
 
1039
             initializing and taking the lock.  */
 
1040
          while (once_control->inited < 0)
 
1041
            Sleep (0);
 
1042
          if (once_control->inited <= 0)
 
1043
            {
 
1044
              /* Take the lock.  This blocks until the other thread has
 
1045
                 finished calling the initfunction.  */
 
1046
              EnterCriticalSection (&once_control->lock);
 
1047
              LeaveCriticalSection (&once_control->lock);
 
1048
              if (!(once_control->inited > 0))
 
1049
                abort ();
 
1050
            }
 
1051
        }
 
1052
    }
 
1053
}
 
1054
 
 
1055
#endif
 
1056
 
 
1057
/* ========================================================================= */