Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (rthreads.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #ifdef __unix__ | |
24 | #ifndef __sun__ | |
25 | #define _POSIX_C_SOURCE 199309 | |
26 | #endif | |
27 | #endif | |
28 | ||
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | ||
32 | #include <boolean.h> | |
33 | #include <rthreads/rthreads.h> | |
34 | ||
35 | /* with RETRO_WIN32_USE_PTHREADS, pthreads can be used even on win32. Maybe only supported in MSVC>=2005 */ | |
36 | ||
37 | #if defined(_WIN32) && !defined(RETRO_WIN32_USE_PTHREADS) | |
38 | #define USE_WIN32_THREADS | |
39 | #ifdef _XBOX | |
40 | #include <xtl.h> | |
41 | #else | |
42 | #define WIN32_LEAN_AND_MEAN | |
43 | #ifndef _WIN32_WINNT | |
44 | #define _WIN32_WINNT 0x0500 /*_WIN32_WINNT_WIN2K */ | |
45 | #endif | |
46 | #include <windows.h> | |
47 | #include <mmsystem.h> | |
48 | #endif | |
49 | #elif defined(GEKKO) | |
50 | #include <ogc/lwp_watchdog.h> | |
51 | #include "gx_pthread.h" | |
52 | #elif defined(_3DS) | |
53 | #include "ctr_pthread.h" | |
54 | #else | |
55 | #include <pthread.h> | |
56 | #include <time.h> | |
57 | #endif | |
58 | ||
59 | #if defined(VITA) || defined(BSD) || defined(ORBIS) || defined(__mips__) || defined(_3DS) | |
60 | #include <sys/time.h> | |
61 | #endif | |
62 | ||
63 | #ifdef __MACH__ | |
64 | #include <mach/clock.h> | |
65 | #include <mach/mach.h> | |
66 | #endif | |
67 | ||
68 | struct thread_data | |
69 | { | |
70 | void (*func)(void*); | |
71 | void *userdata; | |
72 | }; | |
73 | ||
74 | struct sthread | |
75 | { | |
76 | #ifdef USE_WIN32_THREADS | |
77 | HANDLE thread; | |
78 | DWORD id; | |
79 | #else | |
80 | pthread_t id; | |
81 | #endif | |
82 | }; | |
83 | ||
84 | struct slock | |
85 | { | |
86 | #ifdef USE_WIN32_THREADS | |
87 | CRITICAL_SECTION lock; | |
88 | #else | |
89 | pthread_mutex_t lock; | |
90 | #endif | |
91 | }; | |
92 | ||
93 | #ifdef USE_WIN32_THREADS | |
94 | /* The syntax we'll use is mind-bending unless we use a struct. Plus, we might want to store more info later */ | |
95 | /* This will be used as a linked list immplementing a queue of waiting threads */ | |
96 | struct queue_entry | |
97 | { | |
98 | struct queue_entry *next; | |
99 | }; | |
100 | #endif | |
101 | ||
102 | struct scond | |
103 | { | |
104 | #ifdef USE_WIN32_THREADS | |
105 | /* With this implementation of scond, we don't have any way of waking | |
106 | * (or even identifying) specific threads | |
107 | * But we need to wake them in the order indicated by the queue. | |
108 | * This potato token will get get passed around every waiter. | |
109 | * The bearer can test whether he's next, and hold onto the potato if he is. | |
110 | * When he's done he can then put it back into play to progress | |
111 | * the queue further */ | |
112 | HANDLE hot_potato; | |
113 | ||
114 | /* The primary signalled event. Hot potatoes are passed until this is set. */ | |
115 | HANDLE event; | |
116 | ||
117 | /* the head of the queue; NULL if queue is empty */ | |
118 | struct queue_entry *head; | |
119 | ||
120 | /* equivalent to the queue length */ | |
121 | int waiters; | |
122 | ||
123 | /* how many waiters in the queue have been conceptually wakened by signals | |
124 | * (even if we haven't managed to actually wake them yet) */ | |
125 | int wakens; | |
126 | ||
127 | /* used to control access to this scond, in case the user fails */ | |
128 | CRITICAL_SECTION cs; | |
129 | ||
130 | #else | |
131 | pthread_cond_t cond; | |
132 | #endif | |
133 | }; | |
134 | ||
135 | #ifdef USE_WIN32_THREADS | |
136 | static DWORD CALLBACK thread_wrap(void *data_) | |
137 | #else | |
138 | static void *thread_wrap(void *data_) | |
139 | #endif | |
140 | { | |
141 | struct thread_data *data = (struct thread_data*)data_; | |
142 | if (!data) | |
143 | return 0; | |
144 | data->func(data->userdata); | |
145 | free(data); | |
146 | return 0; | |
147 | } | |
148 | ||
149 | /** | |
150 | * sthread_create: | |
151 | * @start_routine : thread entry callback function | |
152 | * @userdata : pointer to userdata that will be made | |
153 | * available in thread entry callback function | |
154 | * | |
155 | * Create a new thread. | |
156 | * | |
157 | * Returns: pointer to new thread if successful, otherwise NULL. | |
158 | */ | |
159 | sthread_t *sthread_create(void (*thread_func)(void*), void *userdata) | |
160 | { | |
161 | return sthread_create_with_priority(thread_func, userdata, 0); | |
162 | } | |
163 | ||
164 | /* TODO/FIXME - this needs to be implemented for Switch/3DS */ | |
165 | #if !defined(SWITCH) && !defined(USE_WIN32_THREADS) && !defined(_3DS) && !defined(GEKKO) && !defined(__HAIKU__) && !defined(EMSCRIPTEN) | |
166 | #define HAVE_THREAD_ATTR | |
167 | #endif | |
168 | ||
169 | /** | |
170 | * sthread_create_with_priority: | |
171 | * @start_routine : thread entry callback function | |
172 | * @userdata : pointer to userdata that will be made | |
173 | * available in thread entry callback function | |
174 | * @thread_priority : thread priority hint value from [1-100] | |
175 | * | |
176 | * Create a new thread. It is possible for the caller to give a hint | |
177 | * for the thread's priority from [1-100]. Any passed in @thread_priority | |
178 | * values that are outside of this range will cause sthread_create() to | |
179 | * create a new thread using the operating system's default thread | |
180 | * priority. | |
181 | * | |
182 | * Returns: pointer to new thread if successful, otherwise NULL. | |
183 | */ | |
184 | sthread_t *sthread_create_with_priority(void (*thread_func)(void*), void *userdata, int thread_priority) | |
185 | { | |
186 | #ifdef HAVE_THREAD_ATTR | |
187 | pthread_attr_t thread_attr; | |
188 | bool thread_attr_needed = false; | |
189 | #endif | |
190 | bool thread_created = false; | |
191 | struct thread_data *data = NULL; | |
192 | sthread_t *thread = (sthread_t*)malloc(sizeof(*thread)); | |
193 | ||
194 | if (!thread) | |
195 | return NULL; | |
196 | ||
197 | if (!(data = (struct thread_data*)malloc(sizeof(*data)))) | |
198 | { | |
199 | free(thread); | |
200 | return NULL; | |
201 | } | |
202 | ||
203 | data->func = thread_func; | |
204 | data->userdata = userdata; | |
205 | ||
206 | thread->id = 0; | |
207 | #ifdef USE_WIN32_THREADS | |
208 | thread->thread = CreateThread(NULL, 0, thread_wrap, | |
209 | data, 0, &thread->id); | |
210 | thread_created = !!thread->thread; | |
211 | #else | |
212 | #ifdef HAVE_THREAD_ATTR | |
213 | pthread_attr_init(&thread_attr); | |
214 | ||
215 | if ((thread_priority >= 1) && (thread_priority <= 100)) | |
216 | { | |
217 | struct sched_param sp; | |
218 | memset(&sp, 0, sizeof(struct sched_param)); | |
219 | sp.sched_priority = thread_priority; | |
220 | pthread_attr_setschedpolicy(&thread_attr, SCHED_RR); | |
221 | pthread_attr_setschedparam(&thread_attr, &sp); | |
222 | ||
223 | thread_attr_needed = true; | |
224 | } | |
225 | ||
226 | #if defined(VITA) | |
227 | pthread_attr_setstacksize(&thread_attr , 0x10000 ); | |
228 | thread_attr_needed = true; | |
229 | #elif defined(__APPLE__) | |
230 | /* Default stack size on Apple is 512Kb; | |
231 | * for PS2 disc scanning and other reasons, we'd like 2MB. */ | |
232 | pthread_attr_setstacksize(&thread_attr , 0x200000 ); | |
233 | thread_attr_needed = true; | |
234 | #endif | |
235 | ||
236 | if (thread_attr_needed) | |
237 | thread_created = pthread_create(&thread->id, &thread_attr, thread_wrap, data) == 0; | |
238 | else | |
239 | thread_created = pthread_create(&thread->id, NULL, thread_wrap, data) == 0; | |
240 | ||
241 | pthread_attr_destroy(&thread_attr); | |
242 | #else | |
243 | thread_created = pthread_create(&thread->id, NULL, thread_wrap, data) == 0; | |
244 | #endif | |
245 | ||
246 | #endif | |
247 | ||
248 | if (thread_created) | |
249 | return thread; | |
250 | free(data); | |
251 | free(thread); | |
252 | return NULL; | |
253 | } | |
254 | ||
255 | /** | |
256 | * sthread_detach: | |
257 | * @thread : pointer to thread object | |
258 | * | |
259 | * Detach a thread. When a detached thread terminates, its | |
260 | * resources are automatically released back to the system | |
261 | * without the need for another thread to join with the | |
262 | * terminated thread. | |
263 | * | |
264 | * Returns: 0 on success, otherwise it returns a non-zero error number. | |
265 | */ | |
266 | int sthread_detach(sthread_t *thread) | |
267 | { | |
268 | #ifdef USE_WIN32_THREADS | |
269 | CloseHandle(thread->thread); | |
270 | free(thread); | |
271 | return 0; | |
272 | #else | |
273 | int ret = pthread_detach(thread->id); | |
274 | free(thread); | |
275 | return ret; | |
276 | #endif | |
277 | } | |
278 | ||
279 | /** | |
280 | * sthread_join: | |
281 | * @thread : pointer to thread object | |
282 | * | |
283 | * Join with a terminated thread. Waits for the thread specified by | |
284 | * @thread to terminate. If that thread has already terminated, then | |
285 | * it will return immediately. The thread specified by @thread must | |
286 | * be joinable. | |
287 | * | |
288 | * Returns: 0 on success, otherwise it returns a non-zero error number. | |
289 | */ | |
290 | void sthread_join(sthread_t *thread) | |
291 | { | |
292 | if (!thread) | |
293 | return; | |
294 | #ifdef USE_WIN32_THREADS | |
295 | WaitForSingleObject(thread->thread, INFINITE); | |
296 | CloseHandle(thread->thread); | |
297 | #else | |
298 | pthread_join(thread->id, NULL); | |
299 | #endif | |
300 | free(thread); | |
301 | } | |
302 | ||
303 | #if !defined(GEKKO) | |
304 | /** | |
305 | * sthread_isself: | |
306 | * @thread : pointer to thread object | |
307 | * | |
308 | * Returns: true (1) if calling thread is the specified thread | |
309 | */ | |
310 | bool sthread_isself(sthread_t *thread) | |
311 | { | |
312 | #ifdef USE_WIN32_THREADS | |
313 | return thread ? GetCurrentThreadId() == thread->id : false; | |
314 | #else | |
315 | return thread ? pthread_equal(pthread_self(), thread->id) : false; | |
316 | #endif | |
317 | } | |
318 | #endif | |
319 | ||
320 | /** | |
321 | * slock_new: | |
322 | * | |
323 | * Create and initialize a new mutex. Must be manually | |
324 | * freed. | |
325 | * | |
326 | * Returns: pointer to a new mutex if successful, otherwise NULL. | |
327 | **/ | |
328 | slock_t *slock_new(void) | |
329 | { | |
330 | slock_t *lock = (slock_t*)calloc(1, sizeof(*lock)); | |
331 | if (!lock) | |
332 | return NULL; | |
333 | #ifdef USE_WIN32_THREADS | |
334 | InitializeCriticalSection(&lock->lock); | |
335 | #else | |
336 | if (pthread_mutex_init(&lock->lock, NULL) != 0) | |
337 | { | |
338 | free(lock); | |
339 | return NULL; | |
340 | } | |
341 | #endif | |
342 | return lock; | |
343 | } | |
344 | ||
345 | /** | |
346 | * slock_free: | |
347 | * @lock : pointer to mutex object | |
348 | * | |
349 | * Frees a mutex. | |
350 | **/ | |
351 | void slock_free(slock_t *lock) | |
352 | { | |
353 | if (!lock) | |
354 | return; | |
355 | ||
356 | #ifdef USE_WIN32_THREADS | |
357 | DeleteCriticalSection(&lock->lock); | |
358 | #else | |
359 | pthread_mutex_destroy(&lock->lock); | |
360 | #endif | |
361 | free(lock); | |
362 | } | |
363 | ||
364 | /** | |
365 | * slock_lock: | |
366 | * @lock : pointer to mutex object | |
367 | * | |
368 | * Locks a mutex. If a mutex is already locked by | |
369 | * another thread, the calling thread shall block until | |
370 | * the mutex becomes available. | |
371 | **/ | |
372 | void slock_lock(slock_t *lock) | |
373 | { | |
374 | if (!lock) | |
375 | return; | |
376 | #ifdef USE_WIN32_THREADS | |
377 | EnterCriticalSection(&lock->lock); | |
378 | #else | |
379 | pthread_mutex_lock(&lock->lock); | |
380 | #endif | |
381 | } | |
382 | ||
383 | /** | |
384 | * slock_try_lock: | |
385 | * @lock : pointer to mutex object | |
386 | * | |
387 | * Attempts to lock a mutex. If a mutex is already locked by | |
388 | * another thread, return false. If the lock is acquired, return true. | |
389 | **/ | |
390 | bool slock_try_lock(slock_t *lock) | |
391 | { | |
392 | #ifdef USE_WIN32_THREADS | |
393 | return lock && TryEnterCriticalSection(&lock->lock); | |
394 | #else | |
395 | return lock && (pthread_mutex_trylock(&lock->lock) == 0); | |
396 | #endif | |
397 | } | |
398 | ||
399 | /** | |
400 | * slock_unlock: | |
401 | * @lock : pointer to mutex object | |
402 | * | |
403 | * Unlocks a mutex. | |
404 | **/ | |
405 | void slock_unlock(slock_t *lock) | |
406 | { | |
407 | if (!lock) | |
408 | return; | |
409 | #ifdef USE_WIN32_THREADS | |
410 | LeaveCriticalSection(&lock->lock); | |
411 | #else | |
412 | pthread_mutex_unlock(&lock->lock); | |
413 | #endif | |
414 | } | |
415 | ||
416 | /** | |
417 | * scond_new: | |
418 | * | |
419 | * Creates and initializes a condition variable. Must | |
420 | * be manually freed. | |
421 | * | |
422 | * Returns: pointer to new condition variable on success, | |
423 | * otherwise NULL. | |
424 | **/ | |
425 | scond_t *scond_new(void) | |
426 | { | |
427 | scond_t *cond = (scond_t*)calloc(1, sizeof(*cond)); | |
428 | ||
429 | if (!cond) | |
430 | return NULL; | |
431 | ||
432 | #ifdef USE_WIN32_THREADS | |
433 | /* This is very complex because recreating condition variable semantics | |
434 | * with Win32 parts is not easy. | |
435 | * | |
436 | * The main problem is that a condition variable can't be used to | |
437 | * "pre-wake" a thread (it will get wakened only after it's waited). | |
438 | * | |
439 | * Whereas a win32 event can pre-wake a thread (the event will be set | |
440 | * in advance, so a 'waiter' won't even have to wait on it). | |
441 | * | |
442 | * Keep in mind a condition variable can apparently pre-wake a thread, | |
443 | * insofar as spurious wakeups are always possible, | |
444 | * but nobody will be expecting this and it does not need to be simulated. | |
445 | * | |
446 | * Moreover, we won't be doing this, because it counts as a spurious wakeup | |
447 | * -- someone else with a genuine claim must get wakened, in any case. | |
448 | * | |
449 | * Therefore we choose to wake only one of the correct waiting threads. | |
450 | * So at the very least, we need to do something clever. But there's | |
451 | * bigger problems. | |
452 | * We don't even have a straightforward way in win32 to satisfy | |
453 | * pthread_cond_wait's atomicity requirement. The bulk of this | |
454 | * algorithm is solving that. | |
455 | * | |
456 | * Note: We might could simplify this using vista+ condition variables, | |
457 | * but we wanted an XP compatible solution. */ | |
458 | if (!(cond->event = CreateEvent(NULL, FALSE, FALSE, NULL))) | |
459 | goto error; | |
460 | if (!(cond->hot_potato = CreateEvent(NULL, FALSE, FALSE, NULL))) | |
461 | { | |
462 | CloseHandle(cond->event); | |
463 | goto error; | |
464 | } | |
465 | ||
466 | InitializeCriticalSection(&cond->cs); | |
467 | #else | |
468 | if (pthread_cond_init(&cond->cond, NULL) != 0) | |
469 | goto error; | |
470 | #endif | |
471 | ||
472 | return cond; | |
473 | ||
474 | error: | |
475 | free(cond); | |
476 | return NULL; | |
477 | } | |
478 | ||
479 | /** | |
480 | * scond_free: | |
481 | * @cond : pointer to condition variable object | |
482 | * | |
483 | * Frees a condition variable. | |
484 | **/ | |
485 | void scond_free(scond_t *cond) | |
486 | { | |
487 | if (!cond) | |
488 | return; | |
489 | ||
490 | #ifdef USE_WIN32_THREADS | |
491 | CloseHandle(cond->event); | |
492 | CloseHandle(cond->hot_potato); | |
493 | DeleteCriticalSection(&cond->cs); | |
494 | #else | |
495 | pthread_cond_destroy(&cond->cond); | |
496 | #endif | |
497 | free(cond); | |
498 | } | |
499 | ||
500 | #ifdef USE_WIN32_THREADS | |
501 | static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds) | |
502 | { | |
503 | struct queue_entry myentry; | |
504 | struct queue_entry **ptr; | |
505 | ||
506 | #if _WIN32_WINNT >= 0x0500 || defined(_XBOX) | |
507 | static LARGE_INTEGER performanceCounterFrequency; | |
508 | LARGE_INTEGER tsBegin; | |
509 | static bool first_init = true; | |
510 | #else | |
511 | static bool beginPeriod = false; | |
512 | DWORD tsBegin; | |
513 | #endif | |
514 | DWORD waitResult; | |
515 | DWORD dwFinalTimeout = dwMilliseconds; /* Careful! in case we begin in the head, | |
516 | we don't do the hot potato stuff, | |
517 | so this timeout needs presetting. */ | |
518 | ||
519 | /* Reminder: `lock` is held before this is called. */ | |
520 | /* however, someone else may have called scond_signal without the lock. soo... */ | |
521 | EnterCriticalSection(&cond->cs); | |
522 | ||
523 | /* since this library is meant for realtime game software | |
524 | * I have no problem setting this to 1 and forgetting about it. */ | |
525 | #if _WIN32_WINNT >= 0x0500 || defined(_XBOX) | |
526 | if (first_init) | |
527 | { | |
528 | performanceCounterFrequency.QuadPart = 0; | |
529 | first_init = false; | |
530 | } | |
531 | ||
532 | if (performanceCounterFrequency.QuadPart == 0) | |
533 | QueryPerformanceFrequency(&performanceCounterFrequency); | |
534 | #else | |
535 | if (!beginPeriod) | |
536 | { | |
537 | beginPeriod = true; | |
538 | timeBeginPeriod(1); | |
539 | } | |
540 | #endif | |
541 | ||
542 | /* Now we can take a good timestamp for use in faking the timeout ourselves. */ | |
543 | /* But don't bother unless we need to (to save a little time) */ | |
544 | if (dwMilliseconds != INFINITE) | |
545 | #if _WIN32_WINNT >= 0x0500 || defined(_XBOX) | |
546 | QueryPerformanceCounter(&tsBegin); | |
547 | #else | |
548 | tsBegin = timeGetTime(); | |
549 | #endif | |
550 | ||
551 | /* add ourselves to a queue of waiting threads */ | |
552 | ptr = &cond->head; | |
553 | ||
554 | /* walk to the end of the linked list */ | |
555 | while (*ptr) | |
556 | ptr = &((*ptr)->next); | |
557 | ||
558 | *ptr = &myentry; | |
559 | myentry.next = NULL; | |
560 | ||
561 | cond->waiters++; | |
562 | ||
563 | /* now the conceptual lock release and condition block are supposed to be atomic. | |
564 | * we can't do that in Windows, but we can simulate the effects by using | |
565 | * the queue, by the following analysis: | |
566 | * What happens if they aren't atomic? | |
567 | * | |
568 | * 1. a signaller can rush in and signal, expecting a waiter to get it; | |
569 | * but the waiter wouldn't, because he isn't blocked yet. | |
570 | * Solution: Win32 events make this easy. The event will sit there enabled | |
571 | * | |
572 | * 2. a signaller can rush in and signal, and then turn right around and wait. | |
573 | * Solution: the signaller will get queued behind the waiter, who's | |
574 | * enqueued before he releases the mutex. */ | |
575 | ||
576 | /* It's my turn if I'm the head of the queue. | |
577 | * Check to see if it's my turn. */ | |
578 | while (cond->head != &myentry) | |
579 | { | |
580 | /* It isn't my turn: */ | |
581 | DWORD timeout = INFINITE; | |
582 | ||
583 | /* As long as someone is even going to be able to wake up | |
584 | * when they receive the potato, keep it going round. */ | |
585 | if (cond->wakens > 0) | |
586 | SetEvent(cond->hot_potato); | |
587 | ||
588 | /* Assess the remaining timeout time */ | |
589 | if (dwMilliseconds != INFINITE) | |
590 | { | |
591 | #if _WIN32_WINNT >= 0x0500 || defined(_XBOX) | |
592 | LARGE_INTEGER now; | |
593 | LONGLONG elapsed; | |
594 | ||
595 | QueryPerformanceCounter(&now); | |
596 | elapsed = now.QuadPart - tsBegin.QuadPart; | |
597 | elapsed *= 1000; | |
598 | elapsed /= performanceCounterFrequency.QuadPart; | |
599 | #else | |
600 | DWORD now = timeGetTime(); | |
601 | DWORD elapsed = now - tsBegin; | |
602 | #endif | |
603 | ||
604 | /* Try one last time with a zero timeout (keeps the code simpler) */ | |
605 | if (elapsed > dwMilliseconds) | |
606 | elapsed = dwMilliseconds; | |
607 | ||
608 | timeout = dwMilliseconds - elapsed; | |
609 | } | |
610 | ||
611 | /* Let someone else go */ | |
612 | LeaveCriticalSection(&lock->lock); | |
613 | LeaveCriticalSection(&cond->cs); | |
614 | ||
615 | /* Wait a while to catch the hot potato.. | |
616 | * someone else should get a chance to go */ | |
617 | /* After all, it isn't my turn (and it must be someone else's) */ | |
618 | Sleep(0); | |
619 | waitResult = WaitForSingleObject(cond->hot_potato, timeout); | |
620 | ||
621 | /* I should come out of here with the main lock taken */ | |
622 | EnterCriticalSection(&lock->lock); | |
623 | EnterCriticalSection(&cond->cs); | |
624 | ||
625 | if (waitResult == WAIT_TIMEOUT) | |
626 | { | |
627 | /* Out of time! Now, let's think about this. I do have the potato now-- | |
628 | * maybe it's my turn, and I have the event? | |
629 | * If that's the case, I could proceed right now without aborting | |
630 | * due to timeout. | |
631 | * | |
632 | * However.. I DID wait a real long time. The caller was willing | |
633 | * to wait that long. | |
634 | * | |
635 | * I choose to give him one last chance with a zero timeout | |
636 | * in the next step | |
637 | */ | |
638 | if (cond->head == &myentry) | |
639 | { | |
640 | dwFinalTimeout = 0; | |
641 | break; | |
642 | } | |
643 | else | |
644 | { | |
645 | /* It's not our turn and we're out of time. Give up. | |
646 | * Remove ourself from the queue and bail. */ | |
647 | struct queue_entry *curr = cond->head; | |
648 | ||
649 | while (curr->next != &myentry) | |
650 | curr = curr->next; | |
651 | curr->next = myentry.next; | |
652 | cond->waiters--; | |
653 | LeaveCriticalSection(&cond->cs); | |
654 | return false; | |
655 | } | |
656 | } | |
657 | ||
658 | } | |
659 | ||
660 | /* It's my turn now -- and I hold the potato */ | |
661 | ||
662 | /* I still have the main lock, in any case */ | |
663 | /* I need to release it so that someone can set the event */ | |
664 | LeaveCriticalSection(&lock->lock); | |
665 | LeaveCriticalSection(&cond->cs); | |
666 | ||
667 | /* Wait for someone to actually signal this condition */ | |
668 | /* We're the only waiter waiting on the event right now -- everyone else | |
669 | * is waiting on something different */ | |
670 | waitResult = WaitForSingleObject(cond->event, dwFinalTimeout); | |
671 | ||
672 | /* Take the main lock so we can do work. Nobody else waits on this lock | |
673 | * for very long, so even though it's GO TIME we won't have to wait long */ | |
674 | EnterCriticalSection(&lock->lock); | |
675 | EnterCriticalSection(&cond->cs); | |
676 | ||
677 | /* Remove ourselves from the queue */ | |
678 | cond->head = myentry.next; | |
679 | cond->waiters--; | |
680 | ||
681 | if (waitResult == WAIT_TIMEOUT) | |
682 | { | |
683 | /* Oops! ran out of time in the final wait. Just bail. */ | |
684 | LeaveCriticalSection(&cond->cs); | |
685 | return false; | |
686 | } | |
687 | ||
688 | /* If any other wakenings are pending, go ahead and set it up */ | |
689 | /* There may actually be no waiters. That's OK. The first waiter will come in, | |
690 | * find it's his turn, and immediately get the signaled event */ | |
691 | cond->wakens--; | |
692 | if (cond->wakens > 0) | |
693 | { | |
694 | SetEvent(cond->event); | |
695 | ||
696 | /* Progress the queue: Put the hot potato back into play. It'll be | |
697 | * tossed around until next in line gets it */ | |
698 | SetEvent(cond->hot_potato); | |
699 | } | |
700 | ||
701 | LeaveCriticalSection(&cond->cs); | |
702 | return true; | |
703 | } | |
704 | #endif | |
705 | ||
706 | /** | |
707 | * scond_wait: | |
708 | * @cond : pointer to condition variable object | |
709 | * @lock : pointer to mutex object | |
710 | * | |
711 | * Block on a condition variable (i.e. wait on a condition). | |
712 | **/ | |
713 | void scond_wait(scond_t *cond, slock_t *lock) | |
714 | { | |
715 | #ifdef USE_WIN32_THREADS | |
716 | _scond_wait_win32(cond, lock, INFINITE); | |
717 | #else | |
718 | pthread_cond_wait(&cond->cond, &lock->lock); | |
719 | #endif | |
720 | } | |
721 | ||
722 | /** | |
723 | * scond_broadcast: | |
724 | * @cond : pointer to condition variable object | |
725 | * | |
726 | * Broadcast a condition. Unblocks all threads currently blocked | |
727 | * on the specified condition variable @cond. | |
728 | **/ | |
729 | int scond_broadcast(scond_t *cond) | |
730 | { | |
731 | #ifdef USE_WIN32_THREADS | |
732 | /* Remember, we currently have mutex */ | |
733 | if (cond->waiters != 0) | |
734 | { | |
735 | /* Awaken everything which is currently queued up */ | |
736 | if (cond->wakens == 0) | |
737 | SetEvent(cond->event); | |
738 | cond->wakens = cond->waiters; | |
739 | ||
740 | /* Since there is now at least one pending waken, the potato must be in play */ | |
741 | SetEvent(cond->hot_potato); | |
742 | } | |
743 | return 0; | |
744 | #else | |
745 | return pthread_cond_broadcast(&cond->cond); | |
746 | #endif | |
747 | } | |
748 | ||
749 | /** | |
750 | * scond_signal: | |
751 | * @cond : pointer to condition variable object | |
752 | * | |
753 | * Signal a condition. Unblocks at least one of the threads currently blocked | |
754 | * on the specified condition variable @cond. | |
755 | **/ | |
756 | void scond_signal(scond_t *cond) | |
757 | { | |
758 | #ifdef USE_WIN32_THREADS | |
759 | ||
760 | /* Unfortunately, pthread_cond_signal does not require that the | |
761 | * lock be held in advance */ | |
762 | /* To avoid stomping on the condvar from other threads, we need | |
763 | * to control access to it with this */ | |
764 | EnterCriticalSection(&cond->cs); | |
765 | ||
766 | /* remember: we currently have mutex */ | |
767 | if (cond->waiters == 0) | |
768 | { | |
769 | LeaveCriticalSection(&cond->cs); | |
770 | return; | |
771 | } | |
772 | ||
773 | /* wake up the next thing in the queue */ | |
774 | if (cond->wakens == 0) | |
775 | SetEvent(cond->event); | |
776 | ||
777 | cond->wakens++; | |
778 | ||
779 | /* The data structure is done being modified.. I think we can leave the CS now. | |
780 | * This would prevent some other thread from receiving the hot potato and then | |
781 | * immediately stalling for the critical section. | |
782 | * But remember, we were trying to replicate a semantic where this entire | |
783 | * scond_signal call was controlled (by the user) by a lock. | |
784 | * So in case there's trouble with this, we can move it after SetEvent() */ | |
785 | LeaveCriticalSection(&cond->cs); | |
786 | ||
787 | /* Since there is now at least one pending waken, the potato must be in play */ | |
788 | SetEvent(cond->hot_potato); | |
789 | ||
790 | #else | |
791 | pthread_cond_signal(&cond->cond); | |
792 | #endif | |
793 | } | |
794 | ||
795 | /** | |
796 | * scond_wait_timeout: | |
797 | * @cond : pointer to condition variable object | |
798 | * @lock : pointer to mutex object | |
799 | * @timeout_us : timeout (in microseconds) | |
800 | * | |
801 | * Try to block on a condition variable (i.e. wait on a condition) until | |
802 | * @timeout_us elapses. | |
803 | * | |
804 | * Returns: false (0) if timeout elapses before condition variable is | |
805 | * signaled or broadcast, otherwise true (1). | |
806 | **/ | |
807 | bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us) | |
808 | { | |
809 | #ifdef USE_WIN32_THREADS | |
810 | /* How to convert a microsecond (us) timeout to millisecond (ms)? | |
811 | * | |
812 | * Someone asking for a 0 timeout clearly wants immediate timeout. | |
813 | * Someone asking for a 1 timeout clearly wants an actual timeout | |
814 | * of the minimum length */ | |
815 | /* The implementation of a 0 timeout here with pthreads is sketchy. | |
816 | * It isn't clear what happens if pthread_cond_timedwait is called with NOW. | |
817 | * Moreover, it is possible that this thread gets pre-empted after the | |
818 | * clock_gettime but before the pthread_cond_timedwait. | |
819 | * In order to help smoke out problems caused by this strange usage, | |
820 | * let's treat a 0 timeout as always timing out. | |
821 | */ | |
822 | if (timeout_us == 0) | |
823 | return false; | |
824 | else if (timeout_us < 1000) | |
825 | return _scond_wait_win32(cond, lock, 1); | |
826 | /* Someone asking for 1000 or 1001 timeout shouldn't | |
827 | * accidentally get 2ms. */ | |
828 | return _scond_wait_win32(cond, lock, timeout_us / 1000); | |
829 | #else | |
830 | int64_t seconds, remainder; | |
831 | struct timespec now; | |
832 | #ifdef __MACH__ | |
833 | /* OSX doesn't have clock_gettime. */ | |
834 | clock_serv_t cclock; | |
835 | mach_timespec_t mts; | |
836 | host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); | |
837 | clock_get_time(cclock, &mts); | |
838 | mach_port_deallocate(mach_task_self(), cclock); | |
839 | now.tv_sec = mts.tv_sec; | |
840 | now.tv_nsec = mts.tv_nsec; | |
841 | #elif !defined(__PSL1GHT__) && defined(__PS3__) | |
842 | sys_time_sec_t s; | |
843 | sys_time_nsec_t n; | |
844 | sys_time_get_current_time(&s, &n); | |
845 | now.tv_sec = s; | |
846 | now.tv_nsec = n; | |
847 | #elif defined(PS2) | |
848 | int tickms = ps2_clock(); | |
849 | now.tv_sec = tickms / 1000; | |
850 | now.tv_nsec = tickms * 1000; | |
851 | #elif !defined(DINGUX_BETA) && (defined(__mips__) || defined(VITA) || defined(_3DS)) | |
852 | struct timeval tm; | |
853 | gettimeofday(&tm, NULL); | |
854 | now.tv_sec = tm.tv_sec; | |
855 | now.tv_nsec = tm.tv_usec * 1000; | |
856 | #elif defined(RETRO_WIN32_USE_PTHREADS) | |
857 | _ftime64_s(&now); | |
858 | #elif defined(GEKKO) | |
859 | /* Avoid gettimeofday due to it being reported to be broken */ | |
860 | const uint64_t tickms = gettime() / TB_TIMER_CLOCK; | |
861 | now.tv_sec = tickms / 1000; | |
862 | now.tv_nsec = tickms * 1000; | |
863 | #else | |
864 | clock_gettime(CLOCK_REALTIME, &now); | |
865 | #endif | |
866 | ||
867 | seconds = timeout_us / INT64_C(1000000); | |
868 | remainder = timeout_us % INT64_C(1000000); | |
869 | ||
870 | now.tv_sec += seconds; | |
871 | now.tv_nsec += remainder * INT64_C(1000); | |
872 | ||
873 | if (now.tv_nsec > 1000000000) | |
874 | { | |
875 | now.tv_nsec -= 1000000000; | |
876 | now.tv_sec += 1; | |
877 | } | |
878 | ||
879 | return (pthread_cond_timedwait(&cond->cond, &lock->lock, &now) == 0); | |
880 | #endif | |
881 | } | |
882 | ||
883 | #ifdef HAVE_THREAD_STORAGE | |
884 | bool sthread_tls_create(sthread_tls_t *tls) | |
885 | { | |
886 | #ifdef USE_WIN32_THREADS | |
887 | return (*tls = TlsAlloc()) != TLS_OUT_OF_INDEXES; | |
888 | #else | |
889 | return pthread_key_create((pthread_key_t*)tls, NULL) == 0; | |
890 | #endif | |
891 | } | |
892 | ||
893 | bool sthread_tls_delete(sthread_tls_t *tls) | |
894 | { | |
895 | #ifdef USE_WIN32_THREADS | |
896 | return TlsFree(*tls) != 0; | |
897 | #else | |
898 | return pthread_key_delete(*tls) == 0; | |
899 | #endif | |
900 | } | |
901 | ||
902 | void *sthread_tls_get(sthread_tls_t *tls) | |
903 | { | |
904 | #ifdef USE_WIN32_THREADS | |
905 | return TlsGetValue(*tls); | |
906 | #else | |
907 | return pthread_getspecific(*tls); | |
908 | #endif | |
909 | } | |
910 | ||
911 | bool sthread_tls_set(sthread_tls_t *tls, const void *data) | |
912 | { | |
913 | #ifdef USE_WIN32_THREADS | |
914 | return TlsSetValue(*tls, (void*)data) != 0; | |
915 | #else | |
916 | return pthread_setspecific(*tls, data) == 0; | |
917 | #endif | |
918 | } | |
919 | #endif | |
920 | ||
921 | uintptr_t sthread_get_thread_id(sthread_t *thread) | |
922 | { | |
923 | if (thread) | |
924 | return (uintptr_t)thread->id; | |
925 | return 0; | |
926 | } | |
927 | ||
928 | uintptr_t sthread_get_current_thread_id(void) | |
929 | { | |
930 | #ifdef USE_WIN32_THREADS | |
931 | return (uintptr_t)GetCurrentThreadId(); | |
932 | #else | |
933 | return (uintptr_t)pthread_self(); | |
934 | #endif | |
935 | } |