diff --git a/desmume/src/libretro-common/rthreads/rthreads.c b/desmume/src/libretro-common/rthreads/rthreads.c index c61ca1096..93f7425b3 100644 --- a/desmume/src/libretro-common/rthreads/rthreads.c +++ b/desmume/src/libretro-common/rthreads/rthreads.c @@ -37,6 +37,9 @@ #include #else #define WIN32_LEAN_AND_MEAN +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 /*_WIN32_WINNT_WIN2K */ +#endif #include #include #endif @@ -52,7 +55,7 @@ #include #endif -#if defined(VITA) +#if defined(VITA) || defined(BSD) #include #endif @@ -97,10 +100,13 @@ struct QueueEntry struct scond { #ifdef USE_WIN32_THREADS - /* With this implementation of scond, we don't have any way of waking (or even identifying) specific threads */ - /* But we need to wake them in the order indicated by the queue. */ - /* This potato token will get get passed around every waiter. The bearer can test whether he's next, and hold onto the potato if he is. */ - /* When he's done he can then put it back into play to progress the queue further */ + /* With this implementation of scond, we don't have any way of waking + * (or even identifying) specific threads + * But we need to wake them in the order indicated by the queue. + * This potato token will get get passed around every waiter. + * The bearer can test whether he's next, and hold onto the potato if he is. + * When he's done he can then put it back into play to progress + * the queue further */ HANDLE hot_potato; /* The primary signalled event. Hot potatoes are passed until this is set. */ @@ -112,7 +118,8 @@ struct scond /* equivalent to the queue length */ int waiters; - /* how many waiters in the queue have been conceptually wakened by signals (even if we haven't managed to actually wake them yet) */ + /* how many waiters in the queue have been conceptually wakened by signals + * (even if we haven't managed to actually wake them yet) */ int wakens; /* used to control access to this scond, in case the user fails */ @@ -152,7 +159,9 @@ sthread_t *sthread_create(void (*thread_func)(void*), void *userdata) bool thread_created = false; struct thread_data *data = NULL; sthread_t *thread = (sthread_t*)calloc(1, sizeof(*thread)); - +#if defined(_WIN32_WINNT) && _WIN32_WINNT <= 0x0410 + DWORD thread_id = 0; +#endif if (!thread) return NULL; @@ -164,7 +173,11 @@ sthread_t *sthread_create(void (*thread_func)(void*), void *userdata) data->userdata = userdata; #ifdef USE_WIN32_THREADS - thread->thread = CreateThread(NULL, 0, thread_wrap, data, 0, NULL); +#if defined(_WIN32_WINNT) && _WIN32_WINNT <= 0x0410 + thread->thread = CreateThread(NULL, 0, thread_wrap, data, 0, &thread_id); +#else + thread->thread = CreateThread(NULL, 0, thread_wrap, data, 0, NULL); +#endif thread_created = !!thread->thread; #else #if defined(VITA) @@ -224,6 +237,8 @@ int sthread_detach(sthread_t *thread) */ void sthread_join(sthread_t *thread) { + if (!thread) + return; #ifdef USE_WIN32_THREADS WaitForSingleObject(thread->thread, INFINITE); CloseHandle(thread->thread); @@ -356,16 +371,31 @@ scond_t *scond_new(void) #ifdef USE_WIN32_THREADS - /* This is very complex because recreating condition variable semantics with win32 parts is not easy */ - /* The main problem is that a condition variable can't be used to "pre-wake" a thread (it will get wakened only after it's waited) */ - /* whereas a win32 event can pre-wake a thread (the event will be set in advance, so a 'waiter' won't even have to wait on it) */ - /* Keep in mind a condition variable can apparently pre-wake a thread, insofar as spurious wakeups are always possible, */ - /* but nobody will be expecting this and it does not need to be simulated. */ - /* Moreover, we won't be doing this, because it counts as a spurious wakeup -- someone else with a genuine claim must get wakened, in any case. */ - /* Therefore we choose to wake only one of the correct waiting threads. */ - /* So at the very least, we need to do something clever. But there's bigger problems. */ - /* We don't even have a straightforward way in win32 to satisfy pthread_cond_wait's atomicity requirement. The bulk of this algorithm is solving that. */ - /* Note: We might could simplify this using vista+ condition variables, but we wanted an XP compatible solution. */ + /* This is very complex because recreating condition variable semantics + * with Win32 parts is not easy. + * + * The main problem is that a condition variable can't be used to + * "pre-wake" a thread (it will get wakened only after it's waited). + * + * Whereas a win32 event can pre-wake a thread (the event will be set + * in advance, so a 'waiter' won't even have to wait on it). + * + * Keep in mind a condition variable can apparently pre-wake a thread, + * insofar as spurious wakeups are always possible, + * but nobody will be expecting this and it does not need to be simulated. + * + * Moreover, we won't be doing this, because it counts as a spurious wakeup + * -- someone else with a genuine claim must get wakened, in any case. + * + * Therefore we choose to wake only one of the correct waiting threads. + * So at the very least, we need to do something clever. But there's + * bigger problems. + * We don't even have a straightforward way in win32 to satisfy + * pthread_cond_wait's atomicity requirement. The bulk of this + * algorithm is solving that. + * + * Note: We might could simplify this using vista+ condition variables, + * but we wanted an XP compatible solution. */ cond->event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!cond->event) goto error; cond->hot_potato = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -380,7 +410,7 @@ scond_t *scond_new(void) cond->head = NULL; #else - if(pthread_cond_init(&cond->cond, NULL) != 0) + if (pthread_cond_init(&cond->cond, NULL) != 0) goto error; #endif @@ -415,68 +445,114 @@ void scond_free(scond_t *cond) #ifdef USE_WIN32_THREADS static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds) { - static bool beginPeriod = false; - struct QueueEntry myentry; struct QueueEntry **ptr; + +#if _WIN32_WINNT >= 0x0500 + static LARGE_INTEGER performanceCounterFrequency; + LARGE_INTEGER tsBegin; + static bool first_init = true; +#else + static bool beginPeriod = false; DWORD tsBegin; - DWORD dwFinalTimeout = dwMilliseconds; /* Careful! in case we begin in the head, we don't do the hot potato stuff, so this timeout needs presetting */ +#endif + DWORD waitResult; + DWORD dwFinalTimeout = dwMilliseconds; /* Careful! in case we begin in the head, + we don't do the hot potato stuff, + so this timeout needs presetting. */ /* Reminder: `lock` is held before this is called. */ /* however, someone else may have called scond_signal without the lock. soo... */ EnterCriticalSection(&cond->cs); - /* since this library is meant for realtime game software I have no problem setting this to 1 and forgetting about it. */ - if(!beginPeriod) + /* since this library is meant for realtime game software + * I have no problem setting this to 1 and forgetting about it. */ +#if _WIN32_WINNT >= 0x0500 + if (first_init) + { + performanceCounterFrequency.QuadPart = 0; + first_init = false; + } + + if (performanceCounterFrequency.QuadPart == 0) + { + QueryPerformanceFrequency(&performanceCounterFrequency); + } +#else + if (!beginPeriod) { beginPeriod = true; timeBeginPeriod(1); } +#endif /* Now we can take a good timestamp for use in faking the timeout ourselves. */ /* But don't bother unless we need to (to save a little time) */ - if(dwMilliseconds != INFINITE) + if (dwMilliseconds != INFINITE) +#if _WIN32_WINNT >= 0x0500 + QueryPerformanceCounter(&tsBegin); +#else tsBegin = timeGetTime(); +#endif /* add ourselves to a queue of waiting threads */ ptr = &cond->head; - while(*ptr) /* walk to the end of the linked list */ + + /* walk to the end of the linked list */ + while (*ptr) ptr = &((*ptr)->next); + *ptr = &myentry; myentry.next = NULL; cond->waiters++; - /* now the conceptual lock release and condition block are supposed to be atomic. */ - /* we can't do that in windows, but we can simulate the effects by using the queue, by the following analysis: */ - /* What happens if they aren't atomic? */ - /* 1. a signaller can rush in and signal, expecting a waiter to get it; but the waiter wouldn't, because he isn't blocked yet */ - /* solution: win32 events make this easy. the event will sit there enabled */ - /* 2. a signaller can rush in and signal, and then turn right around and wait */ - /* solution: the signaller will get queued behind the waiter, who's enqueued before he releases the mutex */ + /* now the conceptual lock release and condition block are supposed to be atomic. + * we can't do that in Windows, but we can simulate the effects by using + * the queue, by the following analysis: + * What happens if they aren't atomic? + * + * 1. a signaller can rush in and signal, expecting a waiter to get it; + * but the waiter wouldn't, because he isn't blocked yet. + * Solution: Win32 events make this easy. The event will sit there enabled + * + * 2. a signaller can rush in and signal, and then turn right around and wait. + * Solution: the signaller will get queued behind the waiter, who's + * enqueued before he releases the mutex. */ - /* It's my turn if I'm the head of the queue. Check to see if it's my turn. */ + /* It's my turn if I'm the head of the queue. + * Check to see if it's my turn. */ while (cond->head != &myentry) { /* It isn't my turn: */ - DWORD timeout = INFINITE; - /* As long as someone is even going to be able to wake up when they receive the potato, keep it going round */ + /* As long as someone is even going to be able to wake up + * when they receive the potato, keep it going round. */ if (cond->wakens > 0) SetEvent(cond->hot_potato); /* Assess the remaining timeout time */ - if(dwMilliseconds != INFINITE) + if (dwMilliseconds != INFINITE) { - DWORD now = timeGetTime(); +#if _WIN32_WINNT >= 0x0500 + LARGE_INTEGER now; + LONGLONG elapsed; + + QueryPerformanceCounter(&now); + elapsed = now.QuadPart - tsBegin.QuadPart; + elapsed *= 1000; + elapsed /= performanceCounterFrequency.QuadPart; +#else + DWORD now = timeGetTime(); DWORD elapsed = now - tsBegin; - if(elapsed > dwMilliseconds) - { - /* Try one last time with a zero timeout (keeps the code simpler) */ +#endif + + /* Try one last time with a zero timeout (keeps the code simpler) */ + if (elapsed > dwMilliseconds) elapsed = dwMilliseconds; - } + timeout = dwMilliseconds - elapsed; } @@ -484,7 +560,8 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds LeaveCriticalSection(&lock->lock); LeaveCriticalSection(&cond->cs); - /* Wait a while to catch the hot potato.. someone else should get a chance to go */ + /* Wait a while to catch the hot potato.. + * someone else should get a chance to go */ /* After all, it isn't my turn (and it must be someone else's) */ Sleep(0); waitResult = WaitForSingleObject(cond->hot_potato, timeout); @@ -493,23 +570,32 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds EnterCriticalSection(&lock->lock); EnterCriticalSection(&cond->cs); - if(waitResult == WAIT_TIMEOUT) + if (waitResult == WAIT_TIMEOUT) { - /* Out of time! Now, let's think about this. I do have the potato now--maybe it's my turn, and I have the event? */ - /* If that's the case, I could proceed right now without aborting due to timeout. */ - /* However.. I DID wait a real long time. The caller was willing to wait that long. */ - /* I choose to give him one last chance with a zero timeout in the next step */ - if(cond->head == &myentry) + /* Out of time! Now, let's think about this. I do have the potato now-- + * maybe it's my turn, and I have the event? + * If that's the case, I could proceed right now without aborting + * due to timeout. + * + * However.. I DID wait a real long time. The caller was willing + * to wait that long. + * + * I choose to give him one last chance with a zero timeout + * in the next step + */ + if (cond->head == &myentry) { dwFinalTimeout = 0; break; } else { - /* It's not our turn and we're out of time. Give up. */ - /* Remove ourself from the queue and bail. */ + /* It's not our turn and we're out of time. Give up. + * Remove ourself from the queue and bail. */ struct QueueEntry* curr = cond->head; - while(curr->next != &myentry) curr = curr->next; + + while (curr->next != &myentry) + curr = curr->next; curr->next = myentry.next; cond->waiters--; LeaveCriticalSection(&cond->cs); @@ -527,10 +613,12 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds LeaveCriticalSection(&cond->cs); /* Wait for someone to actually signal this condition */ - /* We're the only waiter waiting on the event right now -- everyone else is waiting on something different */ + /* We're the only waiter waiting on the event right now -- everyone else + * is waiting on something different */ waitResult = WaitForSingleObject(cond->event, dwFinalTimeout); - /* Take the main lock so we can do work. Nobody else waits on this lock for very long, so even though it's GO TIME we won't have to wait long */ + /* Take the main lock so we can do work. Nobody else waits on this lock + * for very long, so even though it's GO TIME we won't have to wait long */ EnterCriticalSection(&lock->lock); EnterCriticalSection(&cond->cs); @@ -538,7 +626,7 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds cond->head = myentry.next; cond->waiters--; - if(waitResult == WAIT_TIMEOUT) + if (waitResult == WAIT_TIMEOUT) { /* Oops! ran out of time in the final wait. Just bail. */ LeaveCriticalSection(&cond->cs); @@ -546,13 +634,15 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds } /* If any other wakenings are pending, go ahead and set it up */ - /* There may actually be no waiters. That's OK. The first waiter will come in, find it's his turn, and immediately get the signaled event */ + /* There may actually be no waiters. That's OK. The first waiter will come in, + * find it's his turn, and immediately get the signaled event */ cond->wakens--; if (cond->wakens > 0) { SetEvent(cond->event); - /* Progress the queue: Put the hot potato back into play. It'll be tossed around until next in line gets it */ + /* Progress the queue: Put the hot potato back into play. It'll be + * tossed around until next in line gets it */ SetEvent(cond->hot_potato); } @@ -587,12 +677,13 @@ void scond_wait(scond_t *cond, slock_t *lock) int scond_broadcast(scond_t *cond) { #ifdef USE_WIN32_THREADS - /* remember: we currently have mutex */ - if(cond->waiters == 0) return 0; + if (cond->waiters == 0) + return 0; /* awaken everything which is currently queued up */ - if(cond->wakens == 0) SetEvent(cond->event); + if (cond->wakens == 0) + SetEvent(cond->event); cond->wakens = cond->waiters; /* Since there is now at least one pending waken, the potato must be in play */ @@ -615,27 +706,31 @@ void scond_signal(scond_t *cond) { #ifdef USE_WIN32_THREADS - /* Unfortunately, pthread_cond_signal does not require that the lock be held in advance */ - /* To avoid stomping on the condvar from other threads, we need to control access to it with this */ + /* Unfortunately, pthread_cond_signal does not require that the + * lock be held in advance */ + /* To avoid stomping on the condvar from other threads, we need + * to control access to it with this */ EnterCriticalSection(&cond->cs); /* remember: we currently have mutex */ - if(cond->waiters == 0) + if (cond->waiters == 0) { LeaveCriticalSection(&cond->cs); return; } /* wake up the next thing in the queue */ - if(cond->wakens == 0) SetEvent(cond->event); + if (cond->wakens == 0) + SetEvent(cond->event); + cond->wakens++; - /* The data structure is done being modified.. I think we can leave the CS now. */ - /* This would prevent some other thread from receiving the hot potato and then - /* immediately stalling for the critical section. */ - /* But remember, we were trying to replicate a semantic where this entire scond_signal call - /* was controlled (by the user) by a lock. */ - /* So in case there's trouble with this, we can move it after SetEvent() */ + /* The data structure is done being modified.. I think we can leave the CS now. + * This would prevent some other thread from receiving the hot potato and then + * immediately stalling for the critical section. + * But remember, we were trying to replicate a semantic where this entire + * scond_signal call was controlled (by the user) by a lock. + * So in case there's trouble with this, we can move it after SetEvent() */ LeaveCriticalSection(&cond->cs); /* Since there is now at least one pending waken, the potato must be in play */ @@ -661,19 +756,28 @@ void scond_signal(scond_t *cond) bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us) { #ifdef USE_WIN32_THREADS - /* How to convert a us timeout to ms? */ - /* Someone asking for a 0 timeout clearly wants immediate timeout. */ - /* Someone asking for a 1 timeout clearly wants an actual timeout of the minimum length */ - /* Someone asking for 1000 or 1001 timeout shouldn't accidentally get 2ms. */ + /* How to convert a microsecond (us) timeout to millisecond (ms)? + * + * Someone asking for a 0 timeout clearly wants immediate timeout. + * Someone asking for a 1 timeout clearly wants an actual timeout + * of the minimum length */ + + /* Someone asking for 1000 or 1001 timeout shouldn't + * accidentally get 2ms. */ DWORD dwMilliseconds = timeout_us/1000; - if(timeout_us == 0) { - /* The implementation of a 0 timeout here with pthreads is sketchy. */ - /* It isn't clear what happens if pthread_cond_timedwait is called with NOW. */ - /* Moreover, it is possible that this thread gets pre-empted after the clock_gettime but before the pthread_cond_timedwait. */ - /* In order to help smoke out problems caused by this strange usage, let's treat a 0 timeout as always timing out. */ + + /* The implementation of a 0 timeout here with pthreads is sketchy. + * It isn't clear what happens if pthread_cond_timedwait is called with NOW. + * Moreover, it is possible that this thread gets pre-empted after the + * clock_gettime but before the pthread_cond_timedwait. + * In order to help smoke out problems caused by this strange usage, + * let's treat a 0 timeout as always timing out. + */ + if (timeout_us == 0) return false; - } - else if(timeout_us < 1000) dwMilliseconds = 1; + else if (timeout_us < 1000) + dwMilliseconds = 1; + return _scond_wait_win32(cond,lock,dwMilliseconds); #else int ret;