diff --git a/sc2/ChangeLog b/sc2/ChangeLog index 4cc46ea00..b5c2c4a14 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,5 @@ Changes towards version 0.2: +- Fixed deadlock races in new FlushGraphics method -PhracturedBlue - FlushGraphics now waits and notifies on a per-thread level - No longer using SHGetFolderPath on Windows - SvdB - Key repeat is now enabled when typing text, from slayne diff --git a/sc2/src/sc2code/libs/graphics/sdl/3do_funcs.c b/sc2/src/sc2code/libs/graphics/sdl/3do_funcs.c index 1eb604889..2343f0f84 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/3do_funcs.c +++ b/sc2/src/sc2code/libs/graphics/sdl/3do_funcs.c @@ -80,6 +80,8 @@ FlushGraphics (void) TFB_BatchReset (); DrawCommand.Type = TFB_DRAWCOMMANDTYPE_FLUSHGRAPHICS; DrawCommand.image = 0; + // We need to lock the mutex before quein the DC to prevent races + LockSignalMutex (); TFB_EnqueueDrawCommand(&DrawCommand); WaitForSignal (); } diff --git a/sc2/src/sc2code/libs/threadlib.h b/sc2/src/sc2code/libs/threadlib.h index 78c699a5c..19d665ff0 100644 --- a/sc2/src/sc2code/libs/threadlib.h +++ b/sc2/src/sc2code/libs/threadlib.h @@ -104,11 +104,16 @@ typedef void *Semaphore; extern Semaphore CreateSemaphoreAux (DWORD initial, char *sem_name); # define CreateSemaphore(initial,sem_name) \ CreateSemaphoreAux ((initial), (sem_name)) +extern void ResetSemaphoreOwnerAux (Semaphore sem); +# define ResetSemaphoreOwner(sem_name) \ + ResetSemaphoreOwnerAux (sem_name) #else extern Semaphore CreateSemaphoreAux (DWORD initial); # define CreateSemaphore(initial,sem_name) \ CreateSemaphoreAux ((initial)) +# define ResetSemaphoreOwner(sem_name) #endif +extern DWORD SemaphoreValue (Semaphore sem); extern void DestroySemaphore (Semaphore sem); extern int SetSemaphore (Semaphore sem); extern int TrySetSemaphore (Semaphore sem); @@ -133,6 +138,7 @@ extern void BroadcastCondVar (CondVar); extern DWORD CurrentThreadID (void); +extern void LockSignalMutex(void); extern void WaitForSignal (void); extern void SignalThread (DWORD); diff --git a/sc2/src/sc2code/libs/threads/condbank.c b/sc2/src/sc2code/libs/threads/condbank.c index a3bbb1523..692703fe5 100644 --- a/sc2/src/sc2code/libs/threads/condbank.c +++ b/sc2/src/sc2code/libs/threads/condbank.c @@ -29,10 +29,11 @@ #define CONDVAR_BANK_SIZE 10 -static Semaphore bank_sem; +static Mutex bank_mutex; +static int wait = 0, signal = 0; static struct { - CondVar var; + Semaphore var; DWORD id; int used; } bank[CONDVAR_BANK_SIZE]; @@ -41,10 +42,12 @@ void init_cond_bank () { int i; - bank_sem = CreateSemaphore (1, "CondVar Bank Semaphore"); + bank_mutex = CreateMutex (); for (i = 0; i < CONDVAR_BANK_SIZE; i++) { - bank[i].var = CreateCondVar (); + char str[20]; + sprintf (str, "bank sem %d", i); + bank[i].var = CreateSemaphore (0, str); bank[i].id = bank[i].used = 0; } } @@ -57,16 +60,20 @@ uninit_cond_bank () { DestroyCondVar (bank[i].var); } - DestroySemaphore (bank_sem); + DestroyMutex (bank_mutex); } +void +LockSignalMutex () +{ + LockMutex (bank_mutex); +} void WaitForSignal () { int i; int index = -1; DWORD me = CurrentThreadID (); - SetSemaphore (bank_sem); for (i = 0; i < CONDVAR_BANK_SIZE; i++) { if (!bank[i].used) @@ -79,15 +86,19 @@ WaitForSignal () { /* The bank is full! */ fprintf(stderr, "Condvar bank is full, %ul is waiting on DCQ.", me); - ClearSemaphore (bank_sem); + UnlockMutex (bank_mutex); WaitCondVar (RenderingCond); } else { bank[i].used = 1; bank[i].id = me; - ClearSemaphore (bank_sem); - WaitCondVar (bank[i].var); + // Initialize the Semaphore to a value of '0' in case it isn't already + while (SemaphoreValue (bank[i].var)) + SetSemaphore (bank[i].var); + UnlockMutex (bank_mutex); + // Block on Semaphore until it is cleared by the Signal + SetSemaphore (bank[i].var); } } @@ -95,16 +106,19 @@ void SignalThread (DWORD id) { int i; - SetSemaphore (bank_sem); + LockMutex (bank_mutex); for (i = 0; i < CONDVAR_BANK_SIZE; i++) { if (bank[i].used && bank[i].id == id) { bank[i].id = bank[i].used = 0; - SignalCondVar (bank[i].var); + ResetSemaphoreOwner (bank[i].var); + ClearSemaphore (bank[i].var); break; } } - ClearSemaphore (bank_sem); + if (i == CONDVAR_BANK_SIZE) + fprintf (stderr, "Warning: Couldn't find thread to signal!\n"); + UnlockMutex (bank_mutex); } diff --git a/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h b/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h index 68501b6e6..03c2c4a0b 100644 --- a/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h +++ b/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h @@ -87,6 +87,7 @@ typedef SDL_cond *NativeCondVar; SDL_CreateCond () #define NativeDestroyCondVar(condvar) \ SDL_DestroyCond ((condvar)) +extern void SDLWrapper_WaitCondVar (CondVar candvar); #define NativeWaitCondVar(condvar) \ SDLWrapper_WaitCondVar ((condvar)) #define NativeSignalCondVar(condvar) \ diff --git a/sc2/src/sc2code/libs/threads/thrcommon.c b/sc2/src/sc2code/libs/threads/thrcommon.c index 5d96d39ad..1500e55f8 100644 --- a/sc2/src/sc2code/libs/threads/thrcommon.c +++ b/sc2/src/sc2code/libs/threads/thrcommon.c @@ -518,6 +518,27 @@ TimeoutSetSemaphore (Semaphore sem, TimePeriod timeout) return (i); } +DWORD +SemaphoreValue (Semaphore sem) +{ + return NativeSemValue (sem); +} + +#ifdef DEBUG_TRACK_SEM +// Use this function to prevent messages when it is known that +// a semaphore will be cleared by a different thread than set it +void +ResetSemaphoreOwnerAux (Semaphore sem) +{ + int i; + for (i = 0; i < numSems; i++) + if (SemMon[i].Sem == sem) + { + SemMon[i].Thread = 0; + break; + } +} +#endif void ClearSemaphore (Semaphore sem) {