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