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)
|
void SCR_DrawGameClock(void)
|
||||||
{
|
{
|
||||||
float showtime;
|
float showtime;
|
||||||
int minuites;
|
int minutes;
|
||||||
int seconds;
|
int seconds;
|
||||||
char str[16];
|
char str[16];
|
||||||
int flags;
|
int flags;
|
||||||
|
@ -1380,17 +1380,17 @@ void SCR_DrawGameClock(void)
|
||||||
if (showtime < 0)
|
if (showtime < 0)
|
||||||
{
|
{
|
||||||
showtime *= -1;
|
showtime *= -1;
|
||||||
minuites = showtime/60;
|
minutes = showtime/60;
|
||||||
seconds = (int)showtime - (minuites*60);
|
seconds = (int)showtime - (minutes*60);
|
||||||
minuites *= -1;
|
minutes *= -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
minuites = showtime/60;
|
minutes = showtime/60;
|
||||||
seconds = (int)showtime - (minuites*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);
|
SCR_StringXY(str, show_gameclock_x.value, show_gameclock_y.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -621,7 +621,7 @@ void Sys_WaitOnThread(void *thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mutex calls */
|
/* Mutex calls */
|
||||||
void *Sys_CreateMutex()
|
void *Sys_CreateMutex(void)
|
||||||
{
|
{
|
||||||
pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
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);
|
pthread_mutex_destroy(mutex);
|
||||||
free(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
|
#endif
|
|
@ -440,9 +440,17 @@ void Sys_LowFPPrecision (void)
|
||||||
void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize) { return NULL; }
|
void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize) { return NULL; }
|
||||||
void Sys_WaitOnThread(void *thread) {}
|
void Sys_WaitOnThread(void *thread) {}
|
||||||
/* Mutex calls */
|
/* Mutex calls */
|
||||||
void *Sys_CreateMutex() { return NULL; }
|
void *Sys_CreateMutex(void) { return NULL; }
|
||||||
qboolean Sys_TryLockMutex(void *mutex) { return FALSE; }
|
qboolean Sys_TryLockMutex(void *mutex) { return false; }
|
||||||
qboolean Sys_LockMutex(void *mutex) { return FALSE; }
|
qboolean Sys_LockMutex(void *mutex) { return false; }
|
||||||
qboolean Sys_UnlockMutex(void *mutex) { return FALSE; }
|
qboolean Sys_UnlockMutex(void *mutex) { return false; }
|
||||||
void Sys_DestroyMutex(void *mutex) {}
|
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
|
#endif
|
||||||
|
|
|
@ -366,7 +366,7 @@ void Sys_WaitOnThread(void *thread)
|
||||||
|
|
||||||
/* Mutex calls */
|
/* Mutex calls */
|
||||||
// SDL mutexes don't have try-locks for mutexes in the spec so we stick with 1-value semaphores
|
// 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);
|
return (void *)SDL_CreateSemaphore(1);
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,78 @@ qboolean Sys_UnlockMutex(void *mutex)
|
||||||
|
|
||||||
void Sys_DestroyMutex(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
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1403,7 +1403,7 @@ void Sys_WaitOnThread(void *thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mutex calls */
|
/* Mutex calls */
|
||||||
void *Sys_CreateMutex()
|
void *Sys_CreateMutex(void)
|
||||||
{
|
{
|
||||||
return (void *)CreateMutex(NULL, 0, NULL);
|
return (void *)CreateMutex(NULL, 0, NULL);
|
||||||
}
|
}
|
||||||
|
@ -1427,4 +1427,154 @@ void Sys_DestroyMutex(void *mutex)
|
||||||
{
|
{
|
||||||
CloseHandle(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
|
|
@ -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_CreateThread(int (*func)(void *), void *args, int stacksize);
|
||||||
void Sys_WaitOnThread(void *thread);
|
void Sys_WaitOnThread(void *thread);
|
||||||
|
|
||||||
void *Sys_CreateMutex();
|
void *Sys_CreateMutex(void);
|
||||||
qboolean Sys_TryLockMutex(void *mutex);
|
qboolean Sys_TryLockMutex(void *mutex);
|
||||||
qboolean Sys_LockMutex(void *mutex);
|
qboolean Sys_LockMutex(void *mutex);
|
||||||
qboolean Sys_UnlockMutex(void *mutex);
|
qboolean Sys_UnlockMutex(void *mutex);
|
||||||
void Sys_DestroyMutex(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
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
@ -6349,7 +6349,7 @@ typedef struct sqlserver_s
|
||||||
void *thread; // worker thread for server
|
void *thread; // worker thread for server
|
||||||
MYSQL *mysql; // mysql server
|
MYSQL *mysql; // mysql server
|
||||||
volatile qboolean active; // set to false to kill thread
|
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
|
void *resultlock; // mutex for queue read/write
|
||||||
int querynum; // next reference number for queries
|
int querynum; // next reference number for queries
|
||||||
queryrequest_t *requests; // query requests queue
|
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)
|
void SQL_PushRequest(sqlserver_t *server, queryrequest_t *qreq)
|
||||||
{
|
{
|
||||||
Sys_LockMutex(server->requestlock);
|
Sys_LockConditional(server->requestcondv);
|
||||||
qreq->next = NULL;
|
qreq->next = NULL;
|
||||||
if (!server->requestslast)
|
if (!server->requestslast)
|
||||||
server->requests = server->requestslast = qreq;
|
server->requests = server->requestslast = qreq;
|
||||||
else
|
else
|
||||||
server->requestslast = server->requestslast->next = qreq;
|
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;
|
queryrequest_t *qreq;
|
||||||
Sys_LockMutex(server->requestlock);
|
if (lock)
|
||||||
|
Sys_LockConditional(server->requestcondv);
|
||||||
qreq = server->requests;
|
qreq = server->requests;
|
||||||
if (qreq)
|
if (qreq)
|
||||||
{
|
{
|
||||||
|
@ -6410,7 +6411,7 @@ queryrequest_t *SQL_PullRequest(sqlserver_t *server)
|
||||||
if (!server->requests)
|
if (!server->requests)
|
||||||
server->requestslast = NULL;
|
server->requestslast = NULL;
|
||||||
}
|
}
|
||||||
Sys_UnlockMutex(server->requestlock);
|
Sys_UnlockConditional(server->requestcondv);
|
||||||
|
|
||||||
return qreq;
|
return qreq;
|
||||||
}
|
}
|
||||||
|
@ -6424,6 +6425,7 @@ int sql_serverworker(void *sref)
|
||||||
char *error = NULL;
|
char *error = NULL;
|
||||||
my_bool reconnect = 1;
|
my_bool reconnect = 1;
|
||||||
int tinit;
|
int tinit;
|
||||||
|
qboolean needlock = false;
|
||||||
|
|
||||||
if (tinit = mysql_thread_init())
|
if (tinit = mysql_thread_init())
|
||||||
error = "MYSQL thread init failed";
|
error = "MYSQL thread init failed";
|
||||||
|
@ -6439,9 +6441,9 @@ int sql_serverworker(void *sref)
|
||||||
|
|
||||||
while (server->active)
|
while (server->active)
|
||||||
{
|
{
|
||||||
// TODO: replace this with a conditional
|
Sys_LockConditional(server->requestcondv);
|
||||||
if (!server->requests)
|
Sys_ConditionWait(server->requestcondv);
|
||||||
continue;
|
needlock = false; // so we don't try to relock first round
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
@ -6453,9 +6455,13 @@ int sql_serverworker(void *sref)
|
||||||
int columns = -1;
|
int columns = -1;
|
||||||
int qesize = 0;
|
int qesize = 0;
|
||||||
|
|
||||||
if (!(qreq = SQL_PullRequest(server)))
|
if (!(qreq = SQL_PullRequest(server, needlock)))
|
||||||
break;
|
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
|
// perform the query and fill out the result structure
|
||||||
if (mysql_query(server->mysql, qreq->query))
|
if (mysql_query(server->mysql, qreq->query))
|
||||||
qerror = mysql_error(server->mysql);
|
qerror = mysql_error(server->mysql);
|
||||||
|
@ -6501,7 +6507,6 @@ int sql_serverworker(void *sref)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server->mysql)
|
if (server->mysql)
|
||||||
|
@ -6560,13 +6565,13 @@ void PF_sqlconnect (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||||
sqlservers[serverref] = server;
|
sqlservers[serverref] = server;
|
||||||
|
|
||||||
server->active = true;
|
server->active = true;
|
||||||
server->requestlock = Sys_CreateMutex();
|
server->requestcondv = Sys_CreateConditional();
|
||||||
server->resultlock = Sys_CreateMutex();
|
server->resultlock = Sys_CreateMutex();
|
||||||
|
|
||||||
if (!server->requestlock || !server->resultlock)
|
if (!server->requestcondv || !server->resultlock)
|
||||||
{
|
{
|
||||||
if (server->requestlock)
|
if (server->requestcondv)
|
||||||
Sys_DestroyMutex(server->requestlock);
|
Sys_DestroyConditional(server->requestcondv);
|
||||||
if (server->resultlock)
|
if (server->resultlock)
|
||||||
Sys_DestroyMutex(server->resultlock);
|
Sys_DestroyMutex(server->resultlock);
|
||||||
Z_Free(server);
|
Z_Free(server);
|
||||||
|
@ -6597,7 +6602,8 @@ void PF_sqldisconnect (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||||
|
|
||||||
sqlservers[serverref]->active = false;
|
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)
|
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);
|
Q_strncpy(qreq->query, querystr, qsize);
|
||||||
|
|
||||||
SQL_PushRequest(sqlservers[serverref], qreq);
|
SQL_PushRequest(sqlservers[serverref], qreq);
|
||||||
|
Sys_ConditionSignal(sqlservers[serverref]->requestcondv);
|
||||||
// TODO: conditional trip here
|
|
||||||
|
|
||||||
G_FLOAT(OFS_RETURN) = querynum;
|
G_FLOAT(OFS_RETURN) = querynum;
|
||||||
}
|
}
|
||||||
|
@ -6892,7 +6897,7 @@ void SQL_DeInit()
|
||||||
Sys_WaitOnThread(server->thread);
|
Sys_WaitOnThread(server->thread);
|
||||||
|
|
||||||
// server resource deallocation (TODO: should this be done in the thread itself?)
|
// server resource deallocation (TODO: should this be done in the thread itself?)
|
||||||
Sys_DestroyMutex(server->requestlock);
|
Sys_DestroyConditional(server->requestcondv);
|
||||||
Sys_DestroyMutex(server->resultlock);
|
Sys_DestroyMutex(server->resultlock);
|
||||||
|
|
||||||
qreq = server->requests;
|
qreq = server->requests;
|
||||||
|
|
|
@ -901,7 +901,7 @@ void Sys_WaitOnThread(void *thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mutex calls */
|
/* Mutex calls */
|
||||||
void *Sys_CreateMutex()
|
void *Sys_CreateMutex(void)
|
||||||
{
|
{
|
||||||
pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
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);
|
pthread_mutex_destroy(mutex);
|
||||||
free(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
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1274,7 +1274,7 @@ void Sys_WaitOnThread(void *thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mutex calls */
|
/* Mutex calls */
|
||||||
void *Sys_CreateMutex()
|
void *Sys_CreateMutex(void)
|
||||||
{
|
{
|
||||||
return (void *)CreateMutex(NULL, 0, NULL);
|
return (void *)CreateMutex(NULL, 0, NULL);
|
||||||
}
|
}
|
||||||
|
@ -1298,6 +1298,156 @@ void Sys_DestroyMutex(void *mutex)
|
||||||
{
|
{
|
||||||
CloseHandle(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
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue