Coverage Report

Created: 2025-07-12 06:56

/src/dng_sdk/source/dng_pthread.cpp
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************/
2
// Copyright 2002-2008 Adobe Systems Incorporated
3
// All Rights Reserved.
4
//
5
// NOTICE:  Adobe permits you to use, modify, and distribute this file in
6
// accordance with the terms of the Adobe license agreement accompanying it.
7
/*****************************************************************************/
8
9
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pthread.cpp#2 $ */ 
10
/* $DateTime: 2012/07/31 22:04:34 $ */
11
/* $Change: 840853 $ */
12
/* $Author: tknoll $ */
13
14
#include "dng_pthread.h"
15
16
/*****************************************************************************/
17
18
#if qDNGThreadSafe
19
20
/*****************************************************************************/
21
22
#include "dng_assertions.h"
23
24
/*****************************************************************************/
25
26
#if qWinOS
27
28
#pragma warning(disable : 4786)
29
30
// Nothing in this file requires Unicode,
31
// However, CreateSemaphore has a path parameter
32
// (which is NULL always in this code) and thus
33
// does not work on Win98 if UNICODE is defined.
34
// So we force it off here.
35
36
#undef UNICODE
37
#undef _UNICODE
38
39
#include <windows.h>
40
#include <process.h>
41
#include <errno.h>
42
#include <memory>
43
#include <new>
44
#include <map>
45
46
#else
47
48
#include <sys/time.h>
49
50
#endif
51
52
/*****************************************************************************/
53
54
#if qWinOS
55
56
/*****************************************************************************/
57
58
namespace {
59
  struct waiter {
60
    struct waiter *prev;
61
    struct waiter *next;
62
    HANDLE semaphore;
63
    bool chosen_by_signal;
64
  };
65
}
66
67
/*****************************************************************************/
68
69
struct dng_pthread_mutex_impl
70
{
71
  CRITICAL_SECTION lock;
72
73
  dng_pthread_mutex_impl()  { ::InitializeCriticalSection(&lock); }
74
  ~dng_pthread_mutex_impl() { ::DeleteCriticalSection(&lock); }
75
  void Lock()          { ::EnterCriticalSection(&lock); }
76
  void Unlock()        { ::LeaveCriticalSection(&lock); }
77
private:
78
  dng_pthread_mutex_impl &operator=(const dng_pthread_mutex_impl &);
79
  dng_pthread_mutex_impl(const dng_pthread_mutex_impl &) { }
80
};
81
82
/*****************************************************************************/
83
84
struct dng_pthread_cond_impl
85
{
86
  dng_pthread_mutex_impl lock;    // Mutual exclusion on next two variables
87
  waiter *head_waiter;      // List of threads waiting on this condition
88
  waiter *tail_waiter;      // Used to get FIFO, rather than LIFO, behavior for pthread_cond_signal 
89
  unsigned int broadcast_generation;  // Used as sort of a separator on broadcasts
90
                    // saves having to walk the waiters list setting
91
                    // each one's "chosen_by_signal" flag while the condition is locked
92
93
  dng_pthread_cond_impl() : head_waiter(NULL), tail_waiter(NULL), broadcast_generation(0) { }
94
  ~dng_pthread_cond_impl() { } ;
95
96
// Non copyable
97
private:
98
  dng_pthread_cond_impl &operator=(const dng_pthread_cond_impl &);
99
  dng_pthread_cond_impl(const dng_pthread_cond_impl &) { }
100
101
};
102
103
/*****************************************************************************/
104
105
namespace
106
{
107
108
  struct ScopedLock
109
  {
110
    dng_pthread_mutex_impl *mutex;
111
112
    ScopedLock(dng_pthread_mutex_impl *arg) : mutex(arg)
113
    {
114
      mutex->Lock();
115
    }
116
    ScopedLock(dng_pthread_mutex_impl &arg) : mutex(&arg)
117
    {
118
      mutex->Lock();
119
    }
120
    ~ScopedLock()
121
    {
122
      mutex->Unlock();
123
    }
124
  private:
125
    ScopedLock &operator=(const ScopedLock &);
126
    ScopedLock(const ScopedLock &) { }
127
  };
128
129
  dng_pthread_mutex_impl validationLock;
130
131
  void ValidateMutex(dng_pthread_mutex_t *mutex)
132
  {
133
    if (*mutex != DNG_PTHREAD_MUTEX_INITIALIZER)
134
      return;
135
136
    ScopedLock lock(validationLock);
137
138
    if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER)
139
      dng_pthread_mutex_init(mutex, NULL);
140
  }
141
142
  void ValidateCond(dng_pthread_cond_t *cond)
143
  {
144
    if (*cond != DNG_PTHREAD_COND_INITIALIZER)
145
      return;
146
147
    ScopedLock lock(validationLock);
148
149
    if (*cond == DNG_PTHREAD_COND_INITIALIZER)
150
      dng_pthread_cond_init(cond, NULL);
151
  }
152
153
  DWORD thread_wait_sema_TLS_index;
154
  bool thread_wait_sema_inited = false;
155
  dng_pthread_once_t once_thread_TLS = DNG_PTHREAD_ONCE_INIT;
156
157
  void init_thread_TLS()
158
  {
159
    thread_wait_sema_TLS_index = ::TlsAlloc();
160
    thread_wait_sema_inited = true;
161
  }
162
163
  void finalize_thread_TLS()
164
  {
165
    if (thread_wait_sema_inited)
166
      {
167
      ::TlsFree(thread_wait_sema_TLS_index);
168
      thread_wait_sema_inited = false;
169
      }
170
  }
171
172
  dng_pthread_mutex_impl primaryHandleMapLock;
173
174
  typedef std::map<DWORD, std::pair<HANDLE, void **> > ThreadMapType;
175
176
  // A map to make sure handles are freed and to allow returning a pointer sized result
177
  // even on 64-bit Windows.
178
  ThreadMapType primaryHandleMap;
179
180
  HANDLE GetThreadSemaphore()
181
  {
182
    dng_pthread_once(&once_thread_TLS, init_thread_TLS);
183
184
    HANDLE semaphore = ::TlsGetValue(thread_wait_sema_TLS_index);
185
    if (semaphore == NULL)
186
    {
187
      semaphore = ::CreateSemaphore(NULL, 0, 1, NULL);
188
      ::TlsSetValue(thread_wait_sema_TLS_index, semaphore);
189
    }
190
191
    return semaphore;
192
  }
193
194
  void FreeThreadSemaphore()
195
  {
196
    if (thread_wait_sema_inited)
197
    {
198
      HANDLE semaphore = (HANDLE)::TlsGetValue(thread_wait_sema_TLS_index);
199
200
      if (semaphore != NULL)
201
      {
202
        ::TlsSetValue(thread_wait_sema_TLS_index, NULL);
203
        ::CloseHandle(semaphore);
204
      }
205
    }
206
  }
207
208
  struct trampoline_args
209
  {
210
    void *(*func)(void *);
211
    void *arg;
212
  };
213
214
  // This trampoline takes care of the return type being different
215
  // between pthreads thread funcs and Windows C lib thread funcs
216
  unsigned __stdcall trampoline(void *arg_arg)
217
  {
218
    trampoline_args *args_ptr = (trampoline_args *)arg_arg;
219
    trampoline_args args = *args_ptr;
220
221
    delete args_ptr;
222
223
    GetThreadSemaphore();
224
    
225
    void *result = args.func(args.arg);
226
227
    {
228
      ScopedLock lockMap(primaryHandleMapLock);
229
230
      ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self());
231
      if (iter != primaryHandleMap.end())
232
        *iter->second.second = result;
233
    }
234
235
    FreeThreadSemaphore();
236
237
    return S_OK;
238
  }
239
240
}
241
242
/*****************************************************************************/
243
244
extern "C" {
245
246
/*****************************************************************************/
247
248
struct dng_pthread_attr_impl
249
  {
250
  size_t stacksize;
251
  };
252
253
/*****************************************************************************/
254
255
int dng_pthread_attr_init(pthread_attr_t *attr)
256
  {
257
  dng_pthread_attr_impl *newAttrs;
258
259
  newAttrs = new (std::nothrow) dng_pthread_attr_impl;
260
  if (newAttrs == NULL)
261
    return -1; // ENOMEM;
262
263
  newAttrs->stacksize = 0;
264
265
  *attr = newAttrs;
266
267
  return 0;
268
  }
269
270
/*****************************************************************************/
271
272
int dng_pthread_attr_destroy(pthread_attr_t *attr)
273
  {
274
  if (*attr == NULL)
275
    return -1; // EINVAL
276
277
  delete *attr;
278
279
  *attr = NULL;
280
281
  return 0;
282
  }
283
284
/*****************************************************************************/
285
286
int dng_pthread_attr_setstacksize(dng_pthread_attr_t *attr, size_t stacksize)
287
  {
288
  if (attr == NULL || (*attr) == NULL)
289
    return -1; // EINVAL
290
291
  (*attr)->stacksize = stacksize;
292
293
  return 0;
294
  }
295
296
/*****************************************************************************/
297
298
int dng_pthread_attr_getstacksize(const dng_pthread_attr_t *attr, size_t *stacksize)
299
  {
300
  if (attr == NULL || (*attr) == NULL || stacksize == NULL)
301
    return -1; // EINVAL
302
303
  *stacksize = (*attr)->stacksize;
304
305
  return 0;
306
  }
307
308
/*****************************************************************************/
309
310
int dng_pthread_create(dng_pthread_t *thread, const pthread_attr_t *attrs, void * (*func)(void *), void *arg)
311
{
312
  try
313
  {
314
    uintptr_t result;
315
    unsigned threadID;
316
    std::auto_ptr<trampoline_args> args(new (std::nothrow) trampoline_args);
317
    std::auto_ptr<void *> resultHolder(new (std::nothrow) (void *));
318
319
    if (args.get() == NULL || resultHolder.get () == NULL)
320
      return -1; // ENOMEM
321
322
    args->func = func;
323
    args->arg = arg;
324
325
    size_t stacksize = 0;
326
327
    if (attrs != NULL)
328
      dng_pthread_attr_getstacksize (attrs, &stacksize);
329
330
    {
331
      ScopedLock lockMap(primaryHandleMapLock);
332
333
      result = _beginthreadex(NULL, (unsigned)stacksize, trampoline, args.get(), 0, &threadID);
334
      if (result == NULL)
335
        return -1; // ENOMEM
336
      args.release();
337
338
      std::pair<DWORD, std::pair<HANDLE, void **> > newMapEntry(threadID,
339
                                   std::pair<HANDLE, void **>((HANDLE)result, resultHolder.get ()));
340
      std::pair<ThreadMapType::iterator, bool> insertion = primaryHandleMap.insert(newMapEntry);
341
342
      // If there is a handle open on the thread, its ID should not be reused so assert that an insertion was made.
343
      DNG_ASSERT(insertion.second, "pthread emulation logic error");
344
    }
345
346
347
    resultHolder.release ();
348
349
    *thread = (dng_pthread_t)threadID;
350
    return 0;
351
  }
352
  catch (const std::bad_alloc &)
353
  {
354
    return -1;
355
  }
356
}
357
358
/*****************************************************************************/
359
360
int dng_pthread_detach(dng_pthread_t thread)
361
{
362
  HANDLE primaryHandle;
363
  void **resultHolder = NULL;
364
365
  {
366
    ScopedLock lockMap(primaryHandleMapLock);
367
368
    ThreadMapType::iterator iter = primaryHandleMap.find(thread);
369
    if (iter == primaryHandleMap.end())
370
      return -1;
371
372
    primaryHandle = iter->second.first;
373
374
    // A join is waiting on the thread.
375
    if (primaryHandle == NULL)
376
      return -1;
377
378
    resultHolder = iter->second.second;
379
380
    primaryHandleMap.erase(iter);
381
  }
382
383
  delete resultHolder;
384
385
  if (!::CloseHandle(primaryHandle))
386
    return -1;
387
388
  return 0;
389
}
390
391
/*****************************************************************************/
392
393
int dng_pthread_join(dng_pthread_t thread, void **result)
394
{
395
  bool found = false;
396
  HANDLE primaryHandle = NULL;
397
  void **resultHolder = NULL;
398
399
  ThreadMapType::iterator iter;
400
401
  {
402
    ScopedLock lockMap(primaryHandleMapLock);
403
404
    iter = primaryHandleMap.find(thread);
405
    found = iter != primaryHandleMap.end();
406
    if (found)
407
    {
408
      primaryHandle = iter->second.first;
409
      resultHolder = iter->second.second;
410
411
      // Set HANDLE to NULL to force any later join or detach to fail.
412
      iter->second.first = NULL;
413
    }
414
  }
415
416
  // This case can happens when joining a thread not created with pthread_create,
417
  // which is a bad idea, but it gets mapped to doing the join, but always returns NULL.
418
  if (!found)
419
    primaryHandle = ::OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION, FALSE, thread);
420
421
  if (primaryHandle == NULL)
422
    return -1;
423
424
  DWORD err;
425
  if (::WaitForSingleObject(primaryHandle, INFINITE) != WAIT_OBJECT_0)
426
  {
427
    err = ::GetLastError();
428
    return -1;
429
  }
430
431
  {
432
    ScopedLock lockMap(primaryHandleMapLock);
433
434
    if (iter != primaryHandleMap.end())
435
      primaryHandleMap.erase(iter);
436
  }
437
438
  ::CloseHandle(primaryHandle);
439
  if (result != NULL && resultHolder != NULL)
440
    *result = *resultHolder;
441
442
  delete resultHolder;
443
444
  return 0;
445
}
446
447
/*****************************************************************************/
448
449
dng_pthread_t dng_pthread_self()
450
{
451
  return (dng_pthread_t)::GetCurrentThreadId();
452
}
453
454
/*****************************************************************************/
455
456
void dng_pthread_exit(void *result)
457
{
458
  {
459
    ScopedLock lockMap(primaryHandleMapLock);
460
461
    ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self());
462
    if (iter != primaryHandleMap.end())
463
      *iter->second.second = result;
464
  }
465
466
  FreeThreadSemaphore();
467
468
  _endthreadex(S_OK);
469
}
470
471
/*****************************************************************************/
472
473
int dng_pthread_mutex_init(dng_pthread_mutex_t *mutex, void * /* attrs */)
474
{
475
  dng_pthread_mutex_t result;
476
  try {
477
    result = new(dng_pthread_mutex_impl);
478
  } catch (const std::bad_alloc &)
479
  {
480
    return -1;
481
  }
482
483
  if (result == NULL)
484
    return -1;
485
  *mutex = result;
486
  return 0;
487
}
488
489
/*****************************************************************************/
490
491
int dng_pthread_mutex_destroy(dng_pthread_mutex_t *mutex)
492
{
493
  if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER)
494
  {
495
    *mutex = NULL;
496
    return 0;
497
  }
498
499
  delete *mutex;
500
  *mutex = NULL;
501
  return 0;
502
}
503
504
/*****************************************************************************/
505
506
int dng_pthread_cond_init(dng_pthread_cond_t *cond, void * /* attrs */)
507
{
508
  dng_pthread_cond_t result;
509
  try {
510
    result = new(dng_pthread_cond_impl);
511
  } catch (const std::bad_alloc &)
512
  {
513
    return -1;
514
  }
515
516
  if (result == NULL)
517
    return -1;
518
  *cond = result;
519
  return 0;
520
}
521
522
/*****************************************************************************/
523
524
int dng_pthread_cond_destroy(dng_pthread_cond_t *cond)
525
{
526
  if (*cond == DNG_PTHREAD_COND_INITIALIZER)
527
  {
528
    *cond = NULL;
529
    return 0;
530
  }
531
532
  delete *cond;
533
  *cond = NULL;
534
  return 0;
535
}
536
537
/*****************************************************************************/
538
539
int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t* mutexattr)
540
{
541
  return 0;
542
}
543
544
/*****************************************************************************/
545
546
int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t* mutexattr, int type)
547
{
548
  return 0;
549
}
550
551
/*****************************************************************************/
552
553
int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex)
554
{
555
  ValidateMutex(mutex);
556
  (*mutex)->Lock();
557
  return 0;
558
}
559
560
/*****************************************************************************/
561
562
int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex)
563
{
564
  ValidateMutex(mutex);
565
  (*mutex)->Unlock();
566
  return 0;
567
}
568
569
/*****************************************************************************/
570
571
static int cond_wait_internal(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, int timeout_milliseconds)
572
{
573
  dng_pthread_cond_impl &real_cond = **cond;
574
  dng_pthread_mutex_impl &real_mutex = **mutex;
575
576
  waiter this_wait;
577
  HANDLE semaphore = GetThreadSemaphore();
578
  int my_generation; // The broadcast generation this waiter is in
579
580
  {
581
    this_wait.next = NULL;
582
    this_wait.semaphore = semaphore;
583
    this_wait.chosen_by_signal = 0;
584
585
    ScopedLock lock1(real_cond.lock);
586
587
    // Add this waiter to the end of the list.
588
    this_wait.prev = real_cond.tail_waiter;
589
    if (real_cond.tail_waiter != NULL)
590
      real_cond.tail_waiter->next = &this_wait;
591
    real_cond.tail_waiter = &this_wait;
592
593
    // If the list was empty, set the head of the list to this waiter.
594
    if (real_cond.head_waiter == NULL)
595
      real_cond.head_waiter = &this_wait;
596
597
    // Note which broadcast generation this waiter belongs to.
598
    my_generation = real_cond.broadcast_generation;
599
  }
600
601
  real_mutex.Unlock();
602
603
  DWORD result = ::WaitForSingleObject(semaphore, timeout_milliseconds);
604
605
  if (result == WAIT_TIMEOUT)
606
  {
607
    // If the wait timed out, this thread is likely still on the waiters list
608
    // of the condition. However, there is a race in that the thread may have been
609
    // signaled or broadcast between when WaitForSingleObject decided
610
    // we had timed out and this code running.
611
612
    bool mustConsumeSemaphore = false;
613
    {
614
      ScopedLock lock2(real_cond.lock);
615
616
      bool chosen_by_signal = this_wait.chosen_by_signal;
617
      bool chosen_by_broadcast = my_generation != real_cond.broadcast_generation;
618
619
      if (chosen_by_signal || chosen_by_broadcast)
620
        mustConsumeSemaphore = true;
621
      else
622
      {
623
        // Still on waiters list. Remove this waiter from list.
624
        if (this_wait.next != NULL)
625
          this_wait.next->prev = this_wait.prev;
626
        else
627
          real_cond.tail_waiter = this_wait.prev;
628
629
        if (this_wait.prev != NULL)
630
          this_wait.prev->next = this_wait.next;
631
        else
632
          real_cond.head_waiter = this_wait.next;
633
      }
634
    }
635
636
    if (mustConsumeSemaphore)
637
    {
638
      ::WaitForSingleObject(semaphore, INFINITE);
639
      result = WAIT_OBJECT_0;
640
    }
641
  }
642
  else
643
    DNG_ASSERT (result == WAIT_OBJECT_0, "pthread emulation logic error");
644
645
  // reacquire the mutex
646
  real_mutex.Lock();
647
648
  return (result == WAIT_TIMEOUT) ? DNG_ETIMEDOUT : 0;
649
}
650
651
/*****************************************************************************/
652
653
int dng_pthread_cond_wait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex)
654
{
655
  ValidateCond(cond);
656
657
  return cond_wait_internal(cond, mutex, INFINITE); 
658
}
659
660
/*****************************************************************************/
661
662
int dng_pthread_cond_timedwait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, struct dng_timespec *latest_time)
663
{
664
  ValidateCond(cond);
665
  
666
  struct dng_timespec sys_timespec;
667
  
668
  dng_pthread_now (&sys_timespec);
669
670
  __int64 sys_time  = (__int64)sys_timespec.tv_sec * 1000000000 + sys_timespec.tv_nsec;
671
  __int64 lock_time = (__int64)latest_time->tv_sec * 1000000000 + latest_time->tv_nsec;
672
673
  int wait_millisecs = (int)((lock_time - sys_time + 500000) / 1000000);
674
  
675
  if (wait_millisecs < 0)
676
    wait_millisecs = 0;
677
678
  return cond_wait_internal(cond, mutex, wait_millisecs); 
679
}
680
681
/*****************************************************************************/
682
683
int dng_pthread_cond_signal(dng_pthread_cond_t *cond)
684
{
685
  ValidateCond(cond);
686
687
  waiter *first;
688
  dng_pthread_cond_impl &real_cond = **cond;
689
690
  {
691
    ScopedLock lock(real_cond.lock);
692
693
    first = real_cond.head_waiter;
694
    if (first != NULL)
695
    {
696
      if (first->next != NULL)
697
        first->next->prev = NULL;
698
      else
699
        real_cond.tail_waiter = NULL; // Or first->prev, which is always NULL in this case
700
701
      first->chosen_by_signal = true;
702
703
      real_cond.head_waiter = first->next;
704
    }
705
  }
706
707
  if (first != NULL)
708
    ::ReleaseSemaphore(first->semaphore, 1, NULL);
709
710
  return 0;
711
}
712
713
/*****************************************************************************/
714
715
int dng_pthread_cond_broadcast(dng_pthread_cond_t *cond)
716
{
717
  ValidateCond(cond);
718
719
  waiter *first;
720
  dng_pthread_cond_impl &real_cond = **cond;
721
722
  {
723
    ScopedLock lock(real_cond.lock);
724
725
    first = real_cond.head_waiter;
726
    real_cond.head_waiter = NULL;
727
    real_cond.tail_waiter = NULL;
728
729
    real_cond.broadcast_generation++;
730
  }
731
732
  while (first != NULL)
733
  {
734
    waiter *next = first->next;
735
    ::ReleaseSemaphore(first->semaphore, 1, NULL);
736
    first = next;
737
  }
738
739
  return 0;
740
}
741
742
/*****************************************************************************/
743
744
int dng_pthread_once(dng_pthread_once_t *once, void (*init_func)())
745
{
746
  if (once == NULL || init_func == NULL)
747
    return EINVAL;
748
749
  if (once->inited)
750
    return 0;
751
752
  if (::InterlockedIncrement(&once->semaphore) == 0)
753
  {
754
    init_func();
755
    once->inited = 1;
756
  }
757
  else
758
  {
759
    while (!once->inited)
760
      Sleep(0);
761
  }
762
763
  return 0;
764
}
765
766
/*****************************************************************************/
767
768
int dng_pthread_key_create(dng_pthread_key_t * key, void (*destructor) (void *))
769
{
770
  if (destructor != NULL)
771
    return -1;
772
773
  DWORD result = ::TlsAlloc();
774
  if (result == TLS_OUT_OF_INDEXES)
775
    return -1;
776
  *key = (unsigned long)result;
777
  return 0;
778
}
779
780
/*****************************************************************************/
781
782
int dng_pthread_key_delete(dng_pthread_key_t key)
783
{
784
  if (::TlsFree((DWORD)key))
785
    return 0;
786
  return -1;
787
}
788
789
/*****************************************************************************/
790
791
int dng_pthread_setspecific(dng_pthread_key_t key, const void *value)
792
{
793
  if (::TlsSetValue((DWORD)key, const_cast<void *>(value)))
794
    return 0;
795
  return -1;
796
}
797
798
/*****************************************************************************/
799
800
void *dng_pthread_getspecific(dng_pthread_key_t key)
801
{
802
  return ::TlsGetValue((DWORD)key);
803
}
804
805
/*****************************************************************************/
806
807
namespace {
808
  struct rw_waiter {
809
    struct rw_waiter *prev;
810
    struct rw_waiter *next;
811
    HANDLE semaphore;
812
    bool is_writer;
813
  };
814
}
815
816
struct dng_pthread_rwlock_impl
817
{
818
  dng_pthread_mutex_impl mutex;
819
  
820
  rw_waiter *head_waiter;
821
  rw_waiter *tail_waiter;
822
  
823
  unsigned long readers_active;
824
  unsigned long writers_waiting;
825
  bool writer_active;
826
827
  dng_pthread_cond_impl read_wait;
828
  dng_pthread_cond_impl write_wait;
829
830
  dng_pthread_rwlock_impl ()
831
    : mutex ()
832
    , head_waiter (NULL)
833
    , tail_waiter (NULL)
834
    , readers_active (0)
835
    , writers_waiting (0)
836
    , read_wait ()
837
    , write_wait ()
838
    , writer_active (false)
839
  {
840
  }
841
842
  ~dng_pthread_rwlock_impl ()
843
  {
844
  }
845
846
  void WakeHeadWaiter ()
847
  {
848
    HANDLE semaphore = head_waiter->semaphore;
849
850
    head_waiter = head_waiter->next;
851
    if (head_waiter == NULL)
852
      tail_waiter = NULL;
853
854
    ::ReleaseSemaphore(semaphore, 1, NULL);
855
  }
856
857
};
858
859
/*****************************************************************************/
860
861
int dng_pthread_rwlock_init(dng_pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attrs)
862
{
863
  dng_pthread_rwlock_impl *newRWLock;
864
865
  newRWLock = new (std::nothrow) dng_pthread_rwlock_impl;
866
  if (newRWLock == NULL)
867
    return -1; // ENOMEM;
868
869
  *rwlock = newRWLock;
870
871
  return 0;
872
}
873
874
/*****************************************************************************/
875
876
int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t *rwlock)
877
{
878
  dng_pthread_rwlock_impl &real_rwlock = **rwlock;
879
880
  {
881
    ScopedLock lock (real_rwlock.mutex);
882
883
    if (real_rwlock.head_waiter != NULL ||
884
      real_rwlock.readers_active != 0 ||
885
      real_rwlock.writers_waiting != 0 ||
886
      real_rwlock.writer_active)
887
      return -1; // EBUSY
888
  }
889
890
  delete *rwlock;
891
  *rwlock = NULL;
892
  return 0;
893
}
894
895
/*****************************************************************************/
896
897
#define CHECK_RWLOCK_STATE(real_rwlock) \
898
  DNG_ASSERT (!real_rwlock.writer_active || real_rwlock.readers_active == 0, "dng_pthread_rwlock_t logic error")
899
900
/*****************************************************************************/
901
902
int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t *rwlock)
903
{
904
  dng_pthread_rwlock_impl &real_rwlock = **rwlock;
905
906
  struct rw_waiter this_wait;
907
  bool doWait = false;;
908
  int result = 0;
909
  HANDLE semaphore=NULL;
910
911
    {
912
913
    ScopedLock lock (real_rwlock.mutex);
914
915
    CHECK_RWLOCK_STATE (real_rwlock);
916
917
    if (real_rwlock.writers_waiting > 0 || real_rwlock.writer_active)
918
    {
919
      semaphore = GetThreadSemaphore();
920
921
      this_wait.next = NULL;
922
      this_wait.semaphore = semaphore;
923
      this_wait.is_writer = false;
924
925
      // Add this waiter to the end of the list.
926
      this_wait.prev = real_rwlock.tail_waiter;
927
      if (real_rwlock.tail_waiter != NULL)
928
        real_rwlock.tail_waiter->next = &this_wait;
929
      real_rwlock.tail_waiter = &this_wait;
930
931
      // If the list was empty, set the head of the list to this waiter.
932
      if (real_rwlock.head_waiter == NULL)
933
        real_rwlock.head_waiter = &this_wait;
934
935
      doWait = true;
936
    }
937
    else
938
      real_rwlock.readers_active++;
939
  }
940
941
  if (result == 0 && doWait)
942
    result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1;
943
944
  return result;
945
}
946
947
/*****************************************************************************/
948
949
int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t *rwlock)
950
{
951
  dng_pthread_rwlock_impl &real_rwlock = **rwlock;
952
953
  ScopedLock lock (real_rwlock.mutex);
954
955
  CHECK_RWLOCK_STATE (real_rwlock);
956
957
  if (real_rwlock.writers_waiting == 0 && !real_rwlock.writer_active)
958
  {
959
    real_rwlock.readers_active++;
960
    return 0;
961
  }
962
963
  return -1;
964
}
965
966
/*****************************************************************************/
967
968
int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t *rwlock)
969
{
970
  dng_pthread_rwlock_impl &real_rwlock = **rwlock;
971
972
  ScopedLock lock (real_rwlock.mutex);
973
974
  CHECK_RWLOCK_STATE (real_rwlock);
975
976
  if (real_rwlock.readers_active == 0 &&
977
    real_rwlock.writers_waiting == 0 &&
978
    !real_rwlock.writer_active)
979
    {
980
    real_rwlock.writer_active = true;
981
    return 0;
982
    }
983
984
  return -1;
985
}
986
987
/*****************************************************************************/
988
989
int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t *rwlock)
990
  {
991
  dng_pthread_rwlock_impl &real_rwlock = **rwlock;
992
993
  int result = 0;
994
995
  ScopedLock lock (real_rwlock.mutex);
996
997
  CHECK_RWLOCK_STATE (real_rwlock);
998
999
  if (real_rwlock.readers_active > 0)
1000
    --real_rwlock.readers_active;
1001
  else
1002
    real_rwlock.writer_active = false;
1003
1004
  while (real_rwlock.head_waiter != NULL)
1005
  {
1006
    if (real_rwlock.head_waiter->is_writer)
1007
    {
1008
      if (real_rwlock.readers_active == 0)
1009
      {
1010
          real_rwlock.writers_waiting--;
1011
          real_rwlock.writer_active = true;
1012
          real_rwlock.WakeHeadWaiter ();
1013
      }
1014
1015
      break;
1016
    }
1017
    else
1018
    {
1019
      ++real_rwlock.readers_active;
1020
      real_rwlock.WakeHeadWaiter ();
1021
    }
1022
  }
1023
1024
  return result;
1025
  }
1026
1027
/*****************************************************************************/
1028
1029
int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t *rwlock)
1030
  {
1031
  dng_pthread_rwlock_impl &real_rwlock = **rwlock;
1032
1033
  int result = 0;
1034
  struct rw_waiter this_wait;
1035
  HANDLE semaphore=NULL;
1036
  bool doWait = false;
1037
1038
  {
1039
    ScopedLock lock (real_rwlock.mutex);
1040
1041
    CHECK_RWLOCK_STATE (real_rwlock);
1042
1043
    if (real_rwlock.readers_active ||
1044
      real_rwlock.writers_waiting ||
1045
      real_rwlock.writer_active)
1046
      {
1047
      semaphore = GetThreadSemaphore();
1048
1049
      this_wait.next = NULL;
1050
      this_wait.semaphore = semaphore;
1051
      this_wait.is_writer = true;
1052
1053
      // Add this waiter to the end of the list.
1054
      this_wait.prev = real_rwlock.tail_waiter;
1055
      if (real_rwlock.tail_waiter != NULL)
1056
        real_rwlock.tail_waiter->next = &this_wait;
1057
      real_rwlock.tail_waiter = &this_wait;
1058
1059
      // If the list was empty, set the head of the list to this waiter.
1060
      if (real_rwlock.head_waiter == NULL)
1061
        real_rwlock.head_waiter = &this_wait;
1062
1063
      real_rwlock.writers_waiting++;
1064
1065
      doWait = true;
1066
    }
1067
    else
1068
      real_rwlock.writer_active = true;
1069
  }
1070
1071
  if (result == 0 && doWait)
1072
    result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1;
1073
1074
  return result;
1075
  }
1076
1077
/*****************************************************************************/
1078
1079
void dng_pthread_disassociate()
1080
{
1081
  FreeThreadSemaphore();
1082
}
1083
1084
void dng_pthread_terminate()
1085
  {
1086
  finalize_thread_TLS();
1087
  }
1088
1089
/*****************************************************************************/
1090
1091
} // extern "C"
1092
1093
/*****************************************************************************/
1094
1095
#endif
1096
1097
/*****************************************************************************/
1098
1099
int dng_pthread_now (struct timespec *now)
1100
0
  {
1101
  
1102
0
  if (now == NULL)
1103
0
    return -1; // EINVAL
1104
    
1105
  #if qWinOS
1106
1107
  FILETIME ft;
1108
  ::GetSystemTimeAsFileTime(&ft);
1109
1110
  __int64 sys_time = ((__int64)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
1111
1112
  #define SecsFrom1601To1970 11644473600
1113
  
1114
  sys_time -= SecsFrom1601To1970 * 10000000LL;
1115
  
1116
  sys_time *= 100;  // Convert from 100ns to 1ns units
1117
1118
  now->tv_sec  = (long)(sys_time / 1000000000);
1119
  now->tv_nsec = (long)(sys_time % 1000000000);
1120
  
1121
  #else
1122
  
1123
0
  struct timeval tv;
1124
1125
0
  if (gettimeofday (&tv, NULL) != 0)
1126
0
    return errno;
1127
1128
0
  now->tv_sec  = tv.tv_sec;
1129
0
  now->tv_nsec = tv.tv_usec * 1000;
1130
1131
0
  #endif
1132
1133
0
  return 0;
1134
  
1135
0
  }
1136
1137
/*****************************************************************************/
1138
1139
#endif // qDNGThreadSafe
1140
1141
/*****************************************************************************/