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
58namespace {
59 struct waiter {
60 struct waiter *prev;
61 struct waiter *next;
62 HANDLE semaphore;
63 bool chosen_by_signal;
64 };
65}
66
67/*****************************************************************************/
68
69struct 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); }
77private:
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
84struct 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
97private:
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
105namespace
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
244extern "C" {
245
246/*****************************************************************************/
247
248struct dng_pthread_attr_impl
249 {
250 size_t stacksize;
251 };
252
253/*****************************************************************************/
254
255int 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
272int 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
286int 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
298int 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
310int 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
360int 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
393int 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
449dng_pthread_t dng_pthread_self()
450{
451 return (dng_pthread_t)::GetCurrentThreadId();
452}
453
454/*****************************************************************************/
455
456void 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
473int 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
491int 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
506int 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
524int 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
539int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t* mutexattr)
540{
541 return 0;
542}
543
544/*****************************************************************************/
545
546int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t* mutexattr, int type)
547{
548 return 0;
549}
550
551/*****************************************************************************/
552
553int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex)
554{
555 ValidateMutex(mutex);
556 (*mutex)->Lock();
557 return 0;
558}
559
560/*****************************************************************************/
561
562int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex)
563{
564 ValidateMutex(mutex);
565 (*mutex)->Unlock();
566 return 0;
567}
568
569/*****************************************************************************/
570
571static 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
653int 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
662int 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
683int 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
715int 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
744int 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
768int 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
782int 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
791int 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
800void *dng_pthread_getspecific(dng_pthread_key_t key)
801{
802 return ::TlsGetValue((DWORD)key);
803}
804
805/*****************************************************************************/
806
807namespace {
808 struct rw_waiter {
809 struct rw_waiter *prev;
810 struct rw_waiter *next;
811 HANDLE semaphore;
812 bool is_writer;
813 };
814}
815
816struct 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
861int 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
876int 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
902int 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
949int 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
968int 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
989int 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
1029int 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
1079void dng_pthread_disassociate()
1080{
1081 FreeThreadSemaphore();
1082}
1083
1084void dng_pthread_terminate()
1085 {
1086 finalize_thread_TLS();
1087 }
1088
1089/*****************************************************************************/
1090
1091} // extern "C"
1092
1093/*****************************************************************************/
1094
1095#endif
1096
1097/*****************************************************************************/
1098
1099int dng_pthread_now (struct timespec *now)
1100 {
1101
1102 if (now == NULL)
1103 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 struct timeval tv;
1124
1125 if (gettimeofday (&tv, NULL) != 0)
1126 return errno;
1127
1128 now->tv_sec = tv.tv_sec;
1129 now->tv_nsec = tv.tv_usec * 1000;
1130
1131 #endif
1132
1133 return 0;
1134
1135 }
1136
1137/*****************************************************************************/
1138
1139#endif // qDNGThreadSafe
1140
1141/*****************************************************************************/
1142