mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 05:41:52 +00:00
added conditional variables for win/linux/sdl, switched sql thread worker to conditional to avoid busy looping, corrected a spikeism
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2991 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
5d64f13a18
commit
a631205fe7
9 changed files with 590 additions and 37 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue