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 | { |
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 | |