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:
TimeServ 2008-06-01 05:42:23 +00:00
parent 5d64f13a18
commit a631205fe7
9 changed files with 590 additions and 37 deletions

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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