diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 02a7acf59..8e3eac4b5 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -1360,7 +1360,7 @@ void SCR_DrawClock(void) void SCR_DrawGameClock(void) { float showtime; - int minuites; + int minutes; int seconds; char str[16]; int flags; @@ -1380,17 +1380,17 @@ void SCR_DrawGameClock(void) if (showtime < 0) { showtime *= -1; - minuites = showtime/60; - seconds = (int)showtime - (minuites*60); - minuites *= -1; + minutes = showtime/60; + seconds = (int)showtime - (minutes*60); + minutes *= -1; } else { - minuites = showtime/60; - seconds = (int)showtime - (minuites*60); + minutes = showtime/60; + seconds = (int)showtime - (minutes*60); } - sprintf(str, " %02i:%02i", minuites, seconds); + sprintf(str, " %02i:%02i", minutes, seconds); SCR_StringXY(str, show_gameclock_x.value, show_gameclock_y.value); } diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 5c2a54c8e..8e064a7d4 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -621,7 +621,7 @@ void Sys_WaitOnThread(void *thread) } /* Mutex calls */ -void *Sys_CreateMutex() +void *Sys_CreateMutex(void) { pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); @@ -650,4 +650,84 @@ void Sys_DestroyMutex(void *mutex) pthread_mutex_destroy(mutex); free(mutex); } + +/* Conditional wait calls */ +typedef struct condvar_s +{ + pthread_mutex_t *mutex; + pthread_cond_t *cond; +} condvar_t; + +void *Sys_CreateConditional(void) +{ + condvar_t *condv; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + condv = (condvar_t *)malloc(sizeof(condvar_t)); + if (!condv) + return NULL; + + mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + if (!mutex) + return NULL; + + cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); + if (!cond) + return NULL; + + if (!pthread_mutex_init(mutex, NULL)) + { + if (!pthread_cond_init(cond, NULL)) + { + condv->cond = cond; + condv->mutex = mutex; + + return (void *)condv; + } + else + pthread_mutex_destroy(mutex); + } + + free(cond); + free(mutex); + free(condv); + return NULL; +} + +qboolean Sys_LockConditional(void *condv) +{ + return !pthread_mutex_lock(((condvar_t *)condv)->mutex); +} + +qboolean Sys_UnlockConditional(void *condv) +{ + return !pthread_mutex_unlock(((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionWait(void *condv) +{ + return !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionSignal(void *condv) +{ + return !pthread_cond_signal(((condvar_t *)condv)->cond); +} + +qboolean Sys_ConditionBroadcast(void *condv) +{ + return !pthread_cond_broadcast(((condvar_t *)condv)->cond); +} + +void Sys_DestroyConditional(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + pthread_cond_destroy(cv->cond); + pthread_mutex_destroy(cv->mutex); + free(cv->cond); + free(cv->mutex); + free(cv); +} #endif \ No newline at end of file diff --git a/engine/client/sys_morphos.c b/engine/client/sys_morphos.c index 879b4d1d7..63a75fbe8 100755 --- a/engine/client/sys_morphos.c +++ b/engine/client/sys_morphos.c @@ -440,9 +440,17 @@ void Sys_LowFPPrecision (void) void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize) { return NULL; } void Sys_WaitOnThread(void *thread) {} /* Mutex calls */ -void *Sys_CreateMutex() { return NULL; } -qboolean Sys_TryLockMutex(void *mutex) { return FALSE; } -qboolean Sys_LockMutex(void *mutex) { return FALSE; } -qboolean Sys_UnlockMutex(void *mutex) { return FALSE; } +void *Sys_CreateMutex(void) { return NULL; } +qboolean Sys_TryLockMutex(void *mutex) { return false; } +qboolean Sys_LockMutex(void *mutex) { return false; } +qboolean Sys_UnlockMutex(void *mutex) { return false; } void Sys_DestroyMutex(void *mutex) {} +/* Conditional wait calls */ +void *Sys_CreateConditional(void) { return NULL; } +qboolean Sys_LockConditional(void *condv) { return false; } +qboolean Sys_UnlockConditional(void *condv) { return false; } +qboolean Sys_ConditionWait(void *condv) { return false; } +qboolean Sys_ConditionSignal(void *condv) { return false; } +qboolean Sys_ConditionBroadcast(void *condv) { return false; } +void Sys_DestroyConditional(void *condv) {} #endif diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index 7fd9b0f44..541f05efb 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -366,7 +366,7 @@ void Sys_WaitOnThread(void *thread) /* Mutex calls */ // SDL mutexes don't have try-locks for mutexes in the spec so we stick with 1-value semaphores -void *Sys_CreateMutex() +void *Sys_CreateMutex(void) { return (void *)SDL_CreateSemaphore(1); } @@ -388,7 +388,78 @@ qboolean Sys_UnlockMutex(void *mutex) void Sys_DestroyMutex(void *mutex) { - return SDL_DestroySemaphore(mutex); + SDL_DestroySemaphore(mutex); +} + +/* Conditional wait calls */ +typedef struct condvar_s +{ + SDL_mutex *mutex; + SDL_cond *cond; +} condvar_t; + +void *Sys_CreateConditional(void) +{ + condvar_t *condv; + SDL_mutex *mutex; + SDL_cond *cond; + + condv = (condvar_t *)malloc(sizeof(condvar_t)); + if (!condv) + return NULL; + + mutex = SDL_CreateMutex(); + cond = SDL_CreateCond(); + + if (mutex) + { + if (cond) + { + condv->cond = cond; + condv->mutex = mutex; + + return (void *)condv; + } + else + SDL_DestroyMutex(mutex); + } + + free(condv); + return NULL; +} + +qboolean Sys_LockConditional(void *condv) +{ + return !SDL_mutexP(((condvar_t *)condv)->mutex); +} + +qboolean Sys_UnlockConditional(void *condv) +{ + return !SDL_mutexV(((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionWait(void *condv) +{ + return !SDL_CondWait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionSignal(void *condv) +{ + return !SDL_CondSignal(((condvar_t *)condv)->cond); +} + +qboolean Sys_ConditionBroadcast(void *condv) +{ + return !SDL_CondBroadcast(((condvar_t *)condv)->cond); +} + +void Sys_DestroyConditional(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + SDL_DestroyCond(cv->cond); + SDL_DestroyMutex(cv->mutex); + free(cv); } #endif diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index f8f2b4f95..c40354ae5 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -1403,7 +1403,7 @@ void Sys_WaitOnThread(void *thread) } /* Mutex calls */ -void *Sys_CreateMutex() +void *Sys_CreateMutex(void) { return (void *)CreateMutex(NULL, 0, NULL); } @@ -1427,4 +1427,154 @@ void Sys_DestroyMutex(void *mutex) { CloseHandle(mutex); } + +/* Conditional wait calls */ +/* +TODO: Windows Vista has condition variables as documented here: +http://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx +Note this uses Slim Reader/Writer locks (Vista+ exclusive) +or critical sections. + +The condition variable implementation is based on the libSDL implementation. +This code could probably be made more efficient with the use of events or +different mechanisms but for now the main concern is a correct and +complete solution. +*/ +typedef struct condvar_s +{ + int waiting; + int signals; + CRITICAL_SECTION countlock; + CRITICAL_SECTION mainlock; + HANDLE wait_sem; + HANDLE wait_done; +} condvar_t; + +void *Sys_CreateConditional(void) +{ + condvar_t *cv; + + cv = (condvar_t *)malloc(sizeof(condvar_t)); + if (!cv) + return NULL; + + cv->waiting = 0; + cv->signals = 0; + InitializeCriticalSection (&cv->mainlock); + InitializeCriticalSection (&cv->countlock); + cv->wait_sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); + cv->wait_done = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); + + if (cv->wait_sem && cv->wait_done) + return (void *)cv; + + // something failed so deallocate everything + if (cv->wait_done) + CloseHandle(cv->wait_done); + if (cv->wait_sem) + CloseHandle(cv->wait_sem); + DeleteCriticalSection(&cv->countlock); + DeleteCriticalSection(&cv->mainlock); + free(cv); + + return NULL; +} + +qboolean Sys_LockConditional(void *condv) +{ + EnterCriticalSection(&((condvar_t *)condv)->mainlock); + return true; +} + +qboolean Sys_UnlockConditional(void *condv) +{ + LeaveCriticalSection(&((condvar_t *)condv)->mainlock); + return true; +} + +qboolean Sys_ConditionWait(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + qboolean success; + + // increase count for non-signaled waiting threads + EnterCriticalSection(&cv->countlock); + cv->waiting++; + LeaveCriticalSection(&cv->countlock); + + LeaveCriticalSection(&cv->mainlock); // unlock as per condition variable definition + + // wait on a signal + success = (WaitForSingleObject(cv->wait_sem, INFINITE) != WAIT_FAILED); + + // update waiting count and alert signaling thread that we're done to avoid the deadlock condition + EnterCriticalSection(&cv->countlock); + if (cv->signals > 0) + { + ReleaseSemaphore(cv->wait_done, cv->signals, NULL); + cv->signals = 0; + } + cv->waiting--; + LeaveCriticalSection(&cv->countlock); + + EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition + + return success; +} + +qboolean Sys_ConditionSignal(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + // if there are non-signaled waiting threads, we signal one and wait on the response + EnterCriticalSection(&cv->countlock); + if (cv->waiting > cv->signals) + { + cv->signals++; + ReleaseSemaphore(cv->wait_sem, 1, NULL); + LeaveCriticalSection(&cv->countlock); + WaitForSingleObject(cv->wait_done, INFINITE); + } + else + LeaveCriticalSection(&cv->countlock); + + return true; +} + +qboolean Sys_ConditionBroadcast(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + // if there are non-signaled waiting threads, we signal all of them and wait on all the responses back + EnterCriticalSection(&cv->countlock); + if (cv->waiting > cv->signals) + { + int i, num_waiting; + + num_waiting = (cv->waiting - cv->signals); + cv->signals = cv->waiting; + + ReleaseSemaphore(cv->wait_sem, num_waiting, NULL); + LeaveCriticalSection(&cv->countlock); + // there's no call to wait for the same object multiple times so we need to loop through + // and burn up the semaphore count + for (i = 0; i < num_waiting; i++) + WaitForSingleObject(cv->wait_done, INFINITE); + } + else + LeaveCriticalSection(&cv->countlock); + + return true; +} + +void Sys_DestroyConditional(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + CloseHandle(cv->wait_done); + CloseHandle(cv->wait_sem); + DeleteCriticalSection(&cv->countlock); + DeleteCriticalSection(&cv->mainlock); + free(cv); +} #endif \ No newline at end of file diff --git a/engine/common/sys.h b/engine/common/sys.h index c3f2e78ee..fcb932e4b 100644 --- a/engine/common/sys.h +++ b/engine/common/sys.h @@ -91,11 +91,20 @@ qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refres void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize); void Sys_WaitOnThread(void *thread); -void *Sys_CreateMutex(); +void *Sys_CreateMutex(void); qboolean Sys_TryLockMutex(void *mutex); qboolean Sys_LockMutex(void *mutex); qboolean Sys_UnlockMutex(void *mutex); void Sys_DestroyMutex(void *mutex); + +/* Conditional wait calls */ +void *Sys_CreateConditional(void); +qboolean Sys_LockConditional(void *condv); +qboolean Sys_UnlockConditional(void *condv); +qboolean Sys_ConditionWait(void *condv); +qboolean Sys_ConditionSignal(void *condv); +qboolean Sys_ConditionBroadcast(void *condv); +void Sys_DestroyConditional(void *condv); #endif #ifdef _WIN32 diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 9d68b7386..ce8d01529 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -6349,7 +6349,7 @@ typedef struct sqlserver_s void *thread; // worker thread for server MYSQL *mysql; // mysql server volatile qboolean active; // set to false to kill thread - void *requestlock; // mutex for queue read/write + void *requestcondv; // lock and conditional variable for queue read/write void *resultlock; // mutex for queue read/write int querynum; // next reference number for queries queryrequest_t *requests; // query requests queue @@ -6390,19 +6390,20 @@ queryresult_t *SQL_PullResult(sqlserver_t *server) void SQL_PushRequest(sqlserver_t *server, queryrequest_t *qreq) { - Sys_LockMutex(server->requestlock); + Sys_LockConditional(server->requestcondv); qreq->next = NULL; if (!server->requestslast) server->requests = server->requestslast = qreq; else server->requestslast = server->requestslast->next = qreq; - Sys_UnlockMutex(server->requestlock); + Sys_UnlockConditional(server->requestcondv); } -queryrequest_t *SQL_PullRequest(sqlserver_t *server) +queryrequest_t *SQL_PullRequest(sqlserver_t *server, qboolean lock) { queryrequest_t *qreq; - Sys_LockMutex(server->requestlock); + if (lock) + Sys_LockConditional(server->requestcondv); qreq = server->requests; if (qreq) { @@ -6410,7 +6411,7 @@ queryrequest_t *SQL_PullRequest(sqlserver_t *server) if (!server->requests) server->requestslast = NULL; } - Sys_UnlockMutex(server->requestlock); + Sys_UnlockConditional(server->requestcondv); return qreq; } @@ -6424,6 +6425,7 @@ int sql_serverworker(void *sref) char *error = NULL; my_bool reconnect = 1; int tinit; + qboolean needlock = false; if (tinit = mysql_thread_init()) error = "MYSQL thread init failed"; @@ -6439,9 +6441,9 @@ int sql_serverworker(void *sref) while (server->active) { - // TODO: replace this with a conditional - if (!server->requests) - continue; + Sys_LockConditional(server->requestcondv); + Sys_ConditionWait(server->requestcondv); + needlock = false; // so we don't try to relock first round while (1) { @@ -6453,9 +6455,13 @@ int sql_serverworker(void *sref) int columns = -1; int qesize = 0; - if (!(qreq = SQL_PullRequest(server))) + if (!(qreq = SQL_PullRequest(server, needlock))) break; + // pullrequest makes sure our condition is unlocked but we'll need + // a lock next round + needlock = true; + // perform the query and fill out the result structure if (mysql_query(server->mysql, qreq->query)) qerror = mysql_error(server->mysql); @@ -6501,7 +6507,6 @@ int sql_serverworker(void *sref) break; } } - } if (server->mysql) @@ -6560,13 +6565,13 @@ void PF_sqlconnect (progfuncs_t *prinst, struct globalvars_s *pr_globals) sqlservers[serverref] = server; server->active = true; - server->requestlock = Sys_CreateMutex(); + server->requestcondv = Sys_CreateConditional(); server->resultlock = Sys_CreateMutex(); - if (!server->requestlock || !server->resultlock) + if (!server->requestcondv || !server->resultlock) { - if (server->requestlock) - Sys_DestroyMutex(server->requestlock); + if (server->requestcondv) + Sys_DestroyConditional(server->requestcondv); if (server->resultlock) Sys_DestroyMutex(server->resultlock); Z_Free(server); @@ -6597,7 +6602,8 @@ void PF_sqldisconnect (progfuncs_t *prinst, struct globalvars_s *pr_globals) sqlservers[serverref]->active = false; - // TODO: conditional broadcast here + // force the threads to reiterate requests and hopefully terminate + Sys_ConditionBroadcast(sqlservers[serverref]->requestcondv); } void PF_sqlopenquery (progfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -6624,8 +6630,7 @@ void PF_sqlopenquery (progfuncs_t *prinst, struct globalvars_s *pr_globals) Q_strncpy(qreq->query, querystr, qsize); SQL_PushRequest(sqlservers[serverref], qreq); - - // TODO: conditional trip here + Sys_ConditionSignal(sqlservers[serverref]->requestcondv); G_FLOAT(OFS_RETURN) = querynum; } @@ -6892,7 +6897,7 @@ void SQL_DeInit() Sys_WaitOnThread(server->thread); // server resource deallocation (TODO: should this be done in the thread itself?) - Sys_DestroyMutex(server->requestlock); + Sys_DestroyConditional(server->requestcondv); Sys_DestroyMutex(server->resultlock); qreq = server->requests; diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index 37c83bd09..300b57006 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -901,7 +901,7 @@ void Sys_WaitOnThread(void *thread) } /* Mutex calls */ -void *Sys_CreateMutex() +void *Sys_CreateMutex(void) { pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); @@ -930,5 +930,85 @@ void Sys_DestroyMutex(void *mutex) pthread_mutex_destroy(mutex); free(mutex); } + +/* Conditional wait calls */ +typedef struct condvar_s +{ + pthread_mutex_t *mutex; + pthread_cond_t *cond; +} condvar_t; + +void *Sys_CreateConditional(void) +{ + condvar_t *condv; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + condv = (condvar_t *)malloc(sizeof(condvar_t)); + if (!condv) + return NULL; + + mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + if (!mutex) + return NULL; + + cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); + if (!cond) + return NULL; + + if (!pthread_mutex_init(mutex, NULL)) + { + if (!pthread_cond_init(cond, NULL)) + { + condv->cond = cond; + condv->mutex = mutex; + + return (void *)condv; + } + else + pthread_mutex_destroy(mutex); + } + + free(cond); + free(mutex); + free(condv); + return NULL; +} + +qboolean Sys_LockConditional(void *condv) +{ + return !pthread_mutex_lock(((condvar_t *)condv)->mutex); +} + +qboolean Sys_UnlockConditional(void *condv) +{ + return !pthread_mutex_unlock(((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionWait(void *condv) +{ + return !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionSignal(void *condv) +{ + return !pthread_cond_signal(((condvar_t *)condv)->cond); +} + +qboolean Sys_ConditionBroadcast(void *condv) +{ + return !pthread_cond_broadcast(((condvar_t *)condv)->cond); +} + +void Sys_DestroyConditional(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + pthread_cond_destroy(cv->cond); + pthread_mutex_destroy(cv->mutex); + free(cv->cond); + free(cv->mutex); + free(cv); +} #endif diff --git a/engine/server/sv_sys_win.c b/engine/server/sv_sys_win.c index cd2dc0e29..6c23ccb72 100644 --- a/engine/server/sv_sys_win.c +++ b/engine/server/sv_sys_win.c @@ -1274,7 +1274,7 @@ void Sys_WaitOnThread(void *thread) } /* Mutex calls */ -void *Sys_CreateMutex() +void *Sys_CreateMutex(void) { return (void *)CreateMutex(NULL, 0, NULL); } @@ -1298,6 +1298,156 @@ void Sys_DestroyMutex(void *mutex) { CloseHandle(mutex); } + +/* Conditional wait calls */ +/* +TODO: Windows Vista has condition variables as documented here: +http://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx +Note this uses Slim Reader/Writer locks (Vista+ exclusive) +or critical sections. + +The condition variable implementation is based on the libSDL implementation. +This code could probably be made more efficient with the use of events or +different mechanisms but for now the main concern is a correct and +complete solution. +*/ +typedef struct condvar_s +{ + int waiting; + int signals; + CRITICAL_SECTION countlock; + CRITICAL_SECTION mainlock; + HANDLE wait_sem; + HANDLE wait_done; +} condvar_t; + +void *Sys_CreateConditional(void) +{ + condvar_t *cv; + + cv = (condvar_t *)malloc(sizeof(condvar_t)); + if (!cv) + return NULL; + + cv->waiting = 0; + cv->signals = 0; + InitializeCriticalSection (&cv->mainlock); + InitializeCriticalSection (&cv->countlock); + cv->wait_sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); + cv->wait_done = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); + + if (cv->wait_sem && cv->wait_done) + return (void *)cv; + + // something failed so deallocate everything + if (cv->wait_done) + CloseHandle(cv->wait_done); + if (cv->wait_sem) + CloseHandle(cv->wait_sem); + DeleteCriticalSection(&cv->countlock); + DeleteCriticalSection(&cv->mainlock); + free(cv); + + return NULL; +} + +qboolean Sys_LockConditional(void *condv) +{ + EnterCriticalSection(&((condvar_t *)condv)->mainlock); + return true; +} + +qboolean Sys_UnlockConditional(void *condv) +{ + LeaveCriticalSection(&((condvar_t *)condv)->mainlock); + return true; +} + +qboolean Sys_ConditionWait(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + qboolean success; + + // increase count for non-signaled waiting threads + EnterCriticalSection(&cv->countlock); + cv->waiting++; + LeaveCriticalSection(&cv->countlock); + + LeaveCriticalSection(&cv->mainlock); // unlock as per condition variable definition + + // wait on a signal + success = (WaitForSingleObject(cv->wait_sem, INFINITE) != WAIT_FAILED); + + // update waiting count and alert signaling thread that we're done to avoid the deadlock condition + EnterCriticalSection(&cv->countlock); + if (cv->signals > 0) + { + ReleaseSemaphore(cv->wait_done, cv->signals, NULL); + cv->signals = 0; + } + cv->waiting--; + LeaveCriticalSection(&cv->countlock); + + EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition + + return success; +} + +qboolean Sys_ConditionSignal(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + // if there are non-signaled waiting threads, we signal one and wait on the response + EnterCriticalSection(&cv->countlock); + if (cv->waiting > cv->signals) + { + cv->signals++; + ReleaseSemaphore(cv->wait_sem, 1, NULL); + LeaveCriticalSection(&cv->countlock); + WaitForSingleObject(cv->wait_done, INFINITE); + } + else + LeaveCriticalSection(&cv->countlock); + + return true; +} + +qboolean Sys_ConditionBroadcast(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + // if there are non-signaled waiting threads, we signal all of them and wait on all the responses back + EnterCriticalSection(&cv->countlock); + if (cv->waiting > cv->signals) + { + int i, num_waiting; + + num_waiting = (cv->waiting - cv->signals); + cv->signals = cv->waiting; + + ReleaseSemaphore(cv->wait_sem, num_waiting, NULL); + LeaveCriticalSection(&cv->countlock); + // there's no call to wait for the same object multiple times so we need to loop through + // and burn up the semaphore count + for (i = 0; i < num_waiting; i++) + WaitForSingleObject(cv->wait_done, INFINITE); + } + else + LeaveCriticalSection(&cv->countlock); + + return true; +} + +void Sys_DestroyConditional(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + CloseHandle(cv->wait_done); + CloseHandle(cv->wait_sem); + DeleteCriticalSection(&cv->countlock); + DeleteCriticalSection(&cv->mainlock); + free(cv); +} #endif #endif