fix bugs in libretro's scond for win32, hopefully.

This commit is contained in:
zeromus 2016-08-23 21:12:49 +00:00
parent b4759f854c
commit ae92918d27
1 changed files with 26 additions and 14 deletions

View File

@ -80,10 +80,9 @@ struct slock
struct scond
{
#ifdef USE_WIN32_THREADS
/* this might could be done with a semaphore? I'm not sure. */
HANDLE event;
int waiters;
bool waiting_ack;
volatile int waiters;
volatile bool waiting_ack;
HANDLE ack;
#else
pthread_cond_t cond;
@ -314,9 +313,12 @@ scond_t *scond_new(void)
return NULL;
#ifdef USE_WIN32_THREADS
cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
cond->waiters = 0;
/* this is very complex because recreating condition variable semantics with win32 parts is not easy (or maybe it is and I just havent seen how) */
/* the main problem is that a condition variable can be used to wake up a thread, but only if the thread is already waiting. */
/* whereas a win32 event will 'wake up' a thread in advance (the event will be set in advance, so a 'waiter' wont even have to wait on it) */
cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
cond->ack = CreateEvent(NULL, FALSE, FALSE, NULL);
cond->waiters = 0;
cond->waiting_ack = false;
event_created = !!cond->event;
#else
@ -365,20 +367,31 @@ void scond_wait(scond_t *cond, slock_t *lock)
#ifdef USE_WIN32_THREADS
/* remember: we currently have mutex so this will be safe */
cond->waiters++;
if(cond->waiting_ack)
WaitForSingleObject(cond->ack,INFINITE);
ReleaseMutex(lock->lock);
/* wait for a signaller */
WaitForSingleObject(cond->event, INFINITE);
/* the algorithm hinges on this uncontrolled variable access. It's too hard to explain why it's safe. (..erm.. I hope it is) */
cond->waiting_ack = false;
/* the algorithm hinges on this doing this stuff outside of the mutex */
/* suppose several people signal right now. Actually, only one of them can. He'll be waiting on an ack signal! *inside the mutex* */
/* we need to clear waiting_ack before we release him, otherwise it might race to set it to true and beat us */
/* also: suppose several people are waiting right now (in the above wait on `event`). */
/* well, only one of them is going to get freed by a signal; it must have been us */
/* notice that both of the waits for ack are inside the mutex; this guarantees only one of them can be waiting at a time */
/* that's essential for making this safe */
//if(cond->waiting_ack)
{
cond->waiting_ack = false;
SetEvent(cond->ack);
}
/* reacquire mutex and finish up */
WaitForSingleObject(lock->lock, INFINITE);
cond->waiters--;
/* only when the waiter is COMPLETELY FINISHED do we ack a signaller */
SetEvent(cond->ack);
#else
pthread_cond_wait(&cond->cond, &lock->lock);
#endif
@ -419,15 +432,14 @@ void scond_signal(scond_t *cond)
/* OK, someone is waiting for a signal */
/* if we're waiting for an ack, we can't proceed until we receive an ack (signifies cond->event is freed up) */
/* if we're waiting for an ack, we can't proceed until we receive an ack (signifies that the event is freed up from the waiter destined to be waked by it) */
if(cond->waiting_ack)
WaitForSingleObject(cond->ack,INFINITE);
/* so someone set the ack event; a waiter is proceeding. we can wait for another ack now... */
/* before any further waits or signals, we'll need to wait for a waiter to wake up */
cond->waiting_ack = true;
/* ...and set an event to wake up a waiter so he can actually set that ack... */
/* but definitely not right now, since we still have the mutex. So it may take a while */
/* the main wakeup event. the winning waiter definitely won't wake up this moment since we're in a mutex. */
SetEvent(cond->event);
#else