fix bugs in libretro's scond for win32, hopefully.
This commit is contained in:
parent
b4759f854c
commit
ae92918d27
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue