Threadlib refactoring, phase 1

All constructs, save Mutexes, can carry names.  (Mutexes will carry them
once HMalloc doesn't require a Mutex.)

All constructs are now opaque to the game logic.  Some limited upcalls
will be added in phase 2 so that the game logic can initialize some
thread-local data in a backend-independent way.

DEBUG_TRACK_SEM is gone.  In its place are NAMED_SYNCHRO (which names
synchronization constructs) and TRACK_CONTENTION (which reports when
threads go to sleep because of synchronization constructs.)  Again,
Mutexes are immune.

A lot of conditionally compiled code in the threadlib was collated,
dramatically reducing the number of preprocessing directives.

The exposed (threadlib.h) and backend (sdlthreads.h) APIs match one
another more closely now, further reducing the level of preprocessor
work required.


git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1255 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
mcmartin
2003-09-21 09:21:23 +00:00
parent cd98bb8a75
commit eea57e89d5
18 changed files with 677 additions and 759 deletions
-2
View File
@@ -59,7 +59,6 @@ CHOICE_debug_OPTION_debug_TITLE="Debugging information"
CHOICE_debug_OPTION_debug_ACTION='debug_action'
debug_action() {
CFLAGS="$CFLAGS -g -O0 -W -Wall"
CFLAGS="$CFLAGS -DDEBUG_TRACK_SEM" # enable semaphore deugging
LDFLAGS="$LDFLAGS -O0"
DEBUG=1
}
@@ -67,7 +66,6 @@ CHOICE_debug_OPTION_strictdebug_TITLE="Debug info + strict compile checks"
CHOICE_debug_OPTION_strictdebug_ACTION='strictdebug_action'
strictdebug_action() {
CFLAGS="$CFLAGS -O1" # This is needed for -Wunitialized
CFLAGS="$CFLAGS -DDEBUG_TRACK_SEM" # enable semaphore debugging
CFLAGS="$CFLAGS -W -Wall \
-Wbad-function-cast -Wcast-qual -Wmissing-prototypes \
-Wstrict-prototypes -Wmissing-declarations \
+6 -6
View File
@@ -65,7 +65,7 @@ int clock_task_func(void* data)
* can be halted. (e.g. during battle
* or communications)
*/
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
SetSemaphore (GLOBAL (GameClock.clock_sem));
TimeIn = GetTimeCounter ();
if (GLOBAL (GameClock).tick_count <= 0
@@ -165,7 +165,7 @@ int clock_task_func(void* data)
LastTime += num_ticks;
}
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
ClearSemaphore (GLOBAL (GameClock.clock_sem));
SleepThreadUntil (TimeIn + ONE_SECOND / 120);
}
FinishTask (task);
@@ -215,7 +215,7 @@ SuspendGameClock (void)
LockMutex (clock_mutex);
if (GameClockRunning ())
{
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
SetSemaphore (GLOBAL (GameClock.clock_sem));
GLOBAL (GameClock.TimeCounter) = 0;
}
UnlockMutex (clock_mutex);
@@ -228,7 +228,7 @@ ResumeGameClock (void)
if (!GameClockRunning ())
{
GLOBAL (GameClock.TimeCounter) = GetTimeCounter ();
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
ClearSemaphore (GLOBAL (GameClock.clock_sem));
}
UnlockMutex (clock_mutex);
}
@@ -245,7 +245,7 @@ SetGameClockRate (COUNT seconds_per_day)
SIZE new_day_in_ticks, new_tick_count;
//if (GLOBAL (GameClock.clock_sem)) fprintf (stderr, "%u\n", GLOBAL (GameClock.clock_sem));
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
SetSemaphore (GLOBAL (GameClock.clock_sem));
new_day_in_ticks = (SIZE)(seconds_per_day * CLOCK_BASE_FRAMERATE);
if (GLOBAL (GameClock.day_in_ticks) == 0)
new_tick_count = new_day_in_ticks;
@@ -256,7 +256,7 @@ SetGameClockRate (COUNT seconds_per_day)
new_tick_count = 1;
GLOBAL (GameClock.day_in_ticks) = new_day_in_ticks;
GLOBAL (GameClock.tick_count) = new_tick_count;
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
ClearSemaphore (GLOBAL (GameClock.clock_sem));
}
BOOLEAN
+1 -1
View File
@@ -56,7 +56,7 @@ typedef struct
BYTE day_index, month_index;
COUNT year_index;
SIZE tick_count, day_in_ticks;
CrossThreadMutex clock_lock;
Semaphore clock_sem;
Task clock_task;
DWORD TimeCounter;
+2 -5
View File
@@ -270,12 +270,9 @@ InitGlobData (void)
GLOBAL (DisplayArray) = DisplayArray;
// The clock semaphore was initially initialized as '1'
// but it is always cleared before set, so it toggled between
// 2 and 1, which doesn't actually do anything. When this
// was transformed into a mutex, we lock it first to prevent
// double-unlock.
// 2 and 1, which doesn't actually do anything.
GLOBAL (GameClock.clock_lock) = CreateCrossThreadMutex("Clock");
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
GLOBAL (GameClock.clock_sem) = CreateSemaphore(0, "Clock");
}
int
@@ -42,6 +42,7 @@ enum
#define TFB_GFXFLAGS_SCALE_BIADAPT (1<<4)
#define TFB_GFXFLAGS_SCALE_BIADAPTADV (1<<5)
void TFB_PreInit (void);
int TFB_InitGraphics (int driver, int flags, int width, int height, int bpp);
void TFB_UninitGraphics (void);
void TFB_ProcessEvents (void);
+1 -6
View File
@@ -66,12 +66,7 @@ TFB_GL_InitGraphics (int driver, int flags, int width, int height, int bpp)
GraphicsDriver = driver;
fprintf (stderr, "Initializing SDL (OpenGL).\n");
if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
{
fprintf (stderr, "Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
fprintf (stderr, "Initializing SDL with OpenGL support.\n");
SDL_VideoDriverName (VideoName, sizeof (VideoName));
fprintf (stderr, "SDL driver used: %s\n", VideoName);
+1 -7
View File
@@ -46,13 +46,7 @@ TFB_Pure_InitGraphics (int driver, int flags, int width, int height, int bpp)
GraphicsDriver = driver;
fprintf (stderr, "Initializing SDL (pure).\n");
if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
{
fprintf (stderr, "Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
fprintf (stderr, "Initializing Pure-SDL graphics.).\n");
SDL_VideoDriverName (VideoName, sizeof (VideoName));
fprintf (stderr, "SDL driver used: %s\n", VideoName);
@@ -55,6 +55,17 @@ TFB_Abort (void)
abortFlag = TRUE;
}
void
TFB_PreInit (void)
{
fprintf (stderr, "Initializing base SDL functionality.\n");
if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
{
fprintf (stderr, "Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
}
int
TFB_InitGraphics (int driver, int flags, int width, int height, int bpp)
{
+66 -59
View File
@@ -22,10 +22,14 @@
#define THREADLIB SDL
/*
This is now a compile-time define
#define DEBUG_TRACK_SEM
*/
#define NAMED_SYNCHRO /* Should synchronizable objects have names? */
// #define TRACK_CONTENTION /* Should we report when a thread sleeps on synchronize? */
#ifdef TRACK_CONTENTION
# ifndef NAMED_SYNCHRO
# define NAMED_SYNCHRO
# endif
#endif /* TRACK_CONTENTION */
#ifdef DEBUG
# ifndef DEBUG_THREADS
@@ -74,80 +78,83 @@ void uninit_cond_bank (void);
typedef int (*ThreadFunction) (void *);
typedef struct Thread {
void *native;
#ifdef THREAD_NAMES
const char *name;
typedef void *Thread;
typedef void *Mutex;
typedef void *Semaphore;
typedef void *RecursiveMutex;
typedef void *CondVar;
#ifdef NAMED_SYNCHRO
/* Prototypes with the "name" field */
Thread CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
Semaphore CreateSemaphore_Core (DWORD initial, const char *name);
Mutex CreateMutex_Core (void);
RecursiveMutex CreateRecursiveMutex_Core (const char *name);
CondVar CreateCondVar_Core (const char *name);
/* Preprocessor directives to forward to the appropriate routines */
#define CreateThread(func, data, stackSize, name) \
CreateThread_Core ((func), (data), (stackSize), (name))
#define CreateSemaphore(initial, name) \
CreateSemaphore_Core ((initial), (name))
#define CreateMutex() \
CreateMutex_Core ()
#define CreateRecursiveMutex(name) \
CreateRecursiveMutex_Core((name))
#define CreateCondVar(name) \
CreateCondVar_Core ((name))
#else
/* Prototypes without the "name" field. */
Thread CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize);
Semaphore CreateSemaphore_Core (DWORD initial);
Mutex CreateMutex_Core (void);
RecursiveMutex CreateRecursiveMutex_Core (void);
CondVar CreateCondVar_Core (void);
/* Preprocessor directives to forward to the appropriate routines.
The "name" field is stripped away in preprocessing. */
#define CreateThread(func, data, stackSize, name) \
CreateThread_Core ((func), (data), (stackSize))
#define CreateSemaphore(initial, name) \
CreateSemaphore_Core ((initial))
#define CreateMutex() \
CreateMutex_Core ()
#define CreateRecursiveMutex(name) \
CreateRecursiveMutex_Core()
#define CreateCondVar(name) \
CreateCondVar_Core ()
#endif
#ifdef PROFILE_THREADS
int startTime;
#endif /* PROFILE_THREADS */
#ifdef THREAD_QUEUE
struct Thread *next;
#endif
} *Thread;
#ifdef THREAD_NAMES
Thread CreateThreadAux (ThreadFunction func, void *data,
SDWORD stackSize, const char *name);
# define CreateThread(func, data, stackSize, name) \
CreateThreadAux ((func), (data), (stackSize), (name))
#else /* !defined(THREAD_NAMES) */
Thread CreateThreadAux (ThreadFunction func, void *data,
SDWORD stackSize);
# define CreateThread(func, data, stackSize, name) \
CreateThreadAux ((func), (data), (stackSize))
#endif /* !defined(THREAD_NAMES) */
void SleepThread (TimePeriod timePeriod);
void SleepThreadUntil (TimeCount wakeTime);
void TaskSwitch (void);
void WaitThread (Thread thread, int *status);
typedef void *Semaphore;
#ifdef DEBUG_TRACK_SEM
Semaphore CreateSemaphoreAux (DWORD initial, const char *sem_name);
# define CreateSemaphore(initial,sem_name) \
CreateSemaphoreAux ((initial), (sem_name))
void ResetSemaphoreOwnerAux (Semaphore sem);
# define ResetSemaphoreOwner(sem_name) \
ResetSemaphoreOwnerAux (sem_name)
#else
Semaphore CreateSemaphoreAux (DWORD initial);
# define CreateSemaphore(initial,sem_name) \
CreateSemaphoreAux ((initial))
# define ResetSemaphoreOwner(sem_name)
#endif
DWORD SemaphoreValue (Semaphore sem);
void DestroySemaphore (Semaphore sem);
int SetSemaphore (Semaphore sem);
int TrySetSemaphore (Semaphore sem);
int TimeoutSetSemaphore (Semaphore sem, TimePeriod timeout);
void ClearSemaphore (Semaphore sem);
#ifdef PROFILE_THREADS
void PrintThreadsStats (void);
#endif /* PROFILE_THREADS */
typedef void *Mutex;
Mutex CreateMutex (void);
void DestroySemaphore (Semaphore sem);
void SetSemaphore (Semaphore sem);
void ClearSemaphore (Semaphore sem);
void DestroyMutex (Mutex sem);
int LockMutex (Mutex sem);
void LockMutex (Mutex sem);
void UnlockMutex (Mutex sem);
typedef void *RecursiveMutex;
RecursiveMutex CreateRecursiveMutex (const char *name);
void DestroyRecursiveMutex (RecursiveMutex m);
void LockRecursiveMutex (RecursiveMutex m);
void UnlockRecursiveMutex (RecursiveMutex m);
int GetRecursiveMutexDepth (RecursiveMutex m);
typedef void *CrossThreadMutex;
CrossThreadMutex CreateCrossThreadMutex (const char *name);
void DestroyCrossThreadMutex (CrossThreadMutex ctm);
int LockCrossThreadMutex (CrossThreadMutex ctm);
void UnlockCrossThreadMutex (CrossThreadMutex ctm);
typedef void *CondVar;
CondVar CreateCondVar (void);
void DestroyCondVar (CondVar);
void WaitCondVar (CondVar);
void WaitProtectedCondVar (CondVar, Mutex);
+1 -1
View File
@@ -45,7 +45,7 @@ init_cond_bank ()
bank_mutex = CreateMutex ();
for (i = 0; i < CONDVAR_BANK_SIZE; i++)
{
bank[i].var = CreateCondVar ();
bank[i].var = CreateCondVar ("FlushGraphics Bank");
bank[i].id = bank[i].used = 0;
bank[i].control = CreateMutex ();
}
+397 -93
View File
@@ -20,15 +20,55 @@
#include <stdlib.h>
#include "misc.h"
#include "sdlthreads.h"
#ifdef PROFILE_THREADS
#include <signal.h>
#include <unistd.h>
#endif
#if defined(PROFILE_THREADS) && !defined(WIN32)
#include <sys/time.h>
#include <sys/resource.h>
#endif
typedef struct _thread {
void *native;
#ifdef NAMED_SYNCHRO
const char *name;
#endif
#ifdef PROFILE_THREADS
void
SDLWrapper_PrintThreadStats (SDL_Thread *thread) {
int startTime;
#endif /* PROFILE_THREADS */
struct _thread *next;
} *TrueThread;
static volatile TrueThread threadQueue = NULL;
static SDL_mutex *threadQueueMutex;
struct ThreadStartInfo
{
ThreadFunction func;
void *data;
SDL_sem *sem;
TrueThread thread;
};
#ifdef PROFILE_THREADS
static void
SigUSR1Handler (int signr) {
if (getpgrp () != getpid ())
{
// Only act for the main process
return;
}
PrintThreadsStats ();
// It's not a good idea in general to do many things in a signal
// handler, (and especially the locking) but I guess it will
// have to do for now (and it's only for debugging).
(void) signr; /* Satisfying compiler (unused parameter) */
}
static void
LocalStats (SDL_Thread *thread) {
#if defined (WIN32) || !defined(SDL_PTHREADS)
fprintf (stderr, "Thread ID %u\n", SDL_GetThreadID (thread));
#else /* !defined (WIN32) && defined(SDL_PTHREADS) */
@@ -45,74 +85,327 @@ SDLWrapper_PrintThreadStats (SDL_Thread *thread) {
seconds / 60, seconds % 60);
#endif /* defined (WIN32) && defined(SDL_PTHREADS) */
}
#endif
void
SDLWrapper_SleepThread (TimeCount sleepTime)
PrintThreadsStats_SDL (void)
{
TrueThread ptr;
int now;
now = GetTimeCounter ();
SDL_mutexP (threadQueueMutex);
fprintf(stderr, "--- Active threads ---\n");
for (ptr = threadQueue; ptr != NULL; ptr = ptr->next) {
fprintf (stderr, "Thread named '%s'.\n", ptr->name);
fprintf (stderr, "Started %d.%d minutes ago.\n",
(now - ptr->startTime) / 60000,
((now - ptr->startTime) / 1000) % 60);
LocalStats (ptr->native);
if (ptr->next != NULL)
fprintf(stderr, "\n");
}
SDL_mutexV (threadQueueMutex);
fprintf(stderr, "----------------------\n");
fflush (stderr);
}
#endif /* PROFILE_THREADS */
void
InitThreadSystem_SDL (void)
{
threadQueueMutex = SDL_CreateMutex ();
#ifdef PROFILE_THREADS
signal(SIGUSR1, SigUSR1Handler);
#endif
init_cond_bank ();
}
void
UnInitThreadSystem_SDL (void)
{
uninit_cond_bank ();
#ifdef PROFILE_THREADS
signal(SIGUSR1, SIG_DFL);
#endif
SDL_DestroyMutex (threadQueueMutex);
}
static void
QueueThread (TrueThread thread)
{
SDL_mutexP (threadQueueMutex);
thread->next = threadQueue;
threadQueue = thread;
SDL_mutexV (threadQueueMutex);
}
static void
UnQueueThread (TrueThread thread)
{
volatile TrueThread *ptr;
ptr = &threadQueue;
SDL_mutexP (threadQueueMutex);
while (*ptr != thread)
{
#ifdef DEBUG_THREADS
if (*ptr == NULL)
{
// Should not happen.
fprintf (stderr, "Error: Trying to remove non-present thread "
"from thread queue.\n");
fflush (stderr);
abort();
}
#endif /* DEBUG_THREADS */
ptr = &(*ptr)->next;
}
*ptr = (*ptr)->next;
SDL_mutexV (threadQueueMutex);
}
static TrueThread
FindThreadInfo (Uint32 threadID)
{
TrueThread ptr;
ptr = threadQueue;
SDL_mutexP (threadQueueMutex);
while (ptr)
{
if (SDL_GetThreadID (ptr->native) == threadID)
{
SDL_mutexV (threadQueueMutex);
return ptr;
}
ptr = ptr->next;
}
SDL_mutexV (threadQueueMutex);
return NULL;
}
#ifdef NAMED_SYNCHRO
static const char *
MyThreadName (void)
{
TrueThread t = FindThreadInfo (SDL_ThreadID ());
return t ? t->name : "Unknown (probably renderer)";
}
#endif
static int
ThreadHelper (void *startInfo) {
ThreadFunction func;
void *data;
SDL_sem *sem;
TrueThread thread;
int result;
func = ((struct ThreadStartInfo *) startInfo)->func;
data = ((struct ThreadStartInfo *) startInfo)->data;
sem = ((struct ThreadStartInfo *) startInfo)->sem;
// Wait until the Thread structure is available.
SDL_SemWait (sem);
SDL_DestroySemaphore (sem);
thread = ((struct ThreadStartInfo *) startInfo)->thread;
HFree (startInfo);
result = (*func) (data);
#ifdef DEBUG_THREADS
fprintf (stderr, "Thread '%s' done (returned %d).\n",
thread->name, result);
fflush (stderr);
#endif
UnQueueThread (thread);
HFree (thread);
return result;
}
Thread
CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize
#ifdef NAMED_SYNCHRO
, const char *name
#endif
)
{
TrueThread thread;
struct ThreadStartInfo *startInfo;
thread = (struct _thread *) HMalloc (sizeof *thread);
#ifdef NAMED_SYNCHRO
thread->name = name;
#endif
#ifdef PROFILE_THREADS
thread->startTime = GetTimeCounter ();
#endif
startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo));
startInfo->func = func;
startInfo->data = data;
startInfo->sem = SDL_CreateSemaphore (0);
startInfo->thread = thread;
thread->native = SDL_CreateThread (ThreadHelper, (void *) startInfo);
if (!(thread->native))
{
HFree (startInfo);
HFree (thread);
return NULL;
}
// The responsibility to free 'startInfo' and 'thread' is now by the new
// thread.
QueueThread (thread);
#ifdef DEBUG_THREADS
fprintf (stderr, "Thread '%s' created.\n", ThreadName (thread));
fflush (stderr);
#endif
// Signal to the new thread that the thread structure is ready
// and it can begin to use it.
SDL_SemPost (startInfo->sem);
(void) stackSize; /* Satisfying compiler (unused parameter) */
return thread;
}
void
SleepThread_SDL (TimeCount sleepTime)
{
SDL_Delay (sleepTime * 1000 / ONE_SECOND);
}
void
SDLWrapper_SleepThreadUntil (TimeCount wakeTime) {
SleepThreadUntil_SDL (TimeCount wakeTime) {
TimeCount now;
now = GetTimeCounter ();
if (wakeTime <= now)
SDLWrapper_TaskSwitch ();
TaskSwitch_SDL ();
else
SDL_Delay ((wakeTime - now) * 1000 / ONE_SECOND);
}
int
SDLWrapper_TimeoutSetSemaphore (Semaphore sem, TimePeriod timeout) {
return SDL_SemWaitTimeout (sem, timeout * 1000 / ONE_SECOND);
}
void
SDLWrapper_TaskSwitch (void) {
TaskSwitch_SDL (void) {
SDL_Delay (1);
}
void
SDLWrapper_WaitCondVar (CondVar cv) {
int result;
Mutex temp = CreateMutex ();
LockMutex (temp);
result = SDL_CondWait (cv, temp);
UnlockMutex (temp);
DestroyMutex (temp);
if (result != 0) {
fprintf (stderr, "Error result from SDL_CondWait: %d\n", result);
WaitThread_SDL (Thread thread, int *status) {
SDL_WaitThread (((TrueThread)thread)->native, status);
}
/* These are the SDL implementations of the UQM synchronization objects. */
/* TODO: Remove the names of the following data types when compiling
* under release mode. */
/* Mutexes. */
/* TODO. The w_memlib uses Mutexes right now, so we can't use HMalloc
* or HFree. */
/* Semaphores. */
typedef struct _sem {
SDL_sem *sem;
#ifdef NAMED_SYNCHRO
const char *name;
#endif
} Sem;
Semaphore
CreateSemaphore_SDL (DWORD initial
#ifdef NAMED_SYNCHRO
, const char *name
#endif
)
{
Sem *sem = (Sem *) HMalloc (sizeof (struct _sem));
#ifdef NAMED_SYNCHRO
sem->name = name;
#endif
sem->sem = SDL_CreateSemaphore (initial);
return sem;
}
void
DestroySemaphore_SDL (Semaphore s)
{
Sem *sem = (Sem *)s;
SDL_DestroySemaphore (sem->sem);
HFree (sem);
}
void
SetSemaphore_SDL (Semaphore s)
{
Sem *sem = (Sem *)s;
#ifdef TRACK_CONTENTION
BOOLEAN contention = !(SDL_SemValue (sem->sem));
if (contention)
{
fprintf (stderr, "Thread '%s' goes to sleep, waiting on semaphore '%s'\n", MyThreadName (), sem->name);
}
#endif
while (SDL_SemWait (sem->sem) == -1)
{
TaskSwitch_SDL ();
}
#ifdef TRACK_CONTENTION
if (contention)
{
fprintf (stderr, "Thread '%s' awakens, released from semaphore '%s'\n", MyThreadName (), sem->name);
}
#endif
}
void
ClearSemaphore_SDL (Semaphore s)
{
Sem *sem = (Sem *)s;
while (SDL_SemPost (sem->sem) == -1)
{
TaskSwitch_SDL ();
}
}
/* Code for recursive mutexes. Adapted from mixSDL code, which was adapted from the
original DCQ code. */
/* TODO: Make these be forwarded calls instead of just implementations of threadlib.h functions. */
/* TODO: Remove the names when compiling under release mode. */
/* Recursive mutexes. Adapted from mixSDL code, which was adapted from
the original DCQ code. */
typedef struct _recm {
SDL_mutex *mutex;
Uint32 thread_id;
Uint32 locks;
#ifdef NAMED_SYNCHRO
const char *name;
#endif
} RecM;
RecursiveMutex
CreateRecursiveMutex (const char *name)
#ifdef NAMED_SYNCHRO
CreateRecursiveMutex_SDL (const char *name)
#else
CreateRecursiveMutex_SDL (void)
#endif
{
RecM *mtx = (RecM *) HMalloc (sizeof (struct _recm));
mtx->thread_id = 0;
mtx->mutex = SDL_CreateMutex ();
#ifdef NAMED_SYNCHRO
mtx->name = name;
#endif
mtx->locks = 0;
return (RecursiveMutex) mtx;
}
void
DestroyRecursiveMutex (RecursiveMutex val)
DestroyRecursiveMutex_SDL (RecursiveMutex val)
{
RecM *mtx = (RecM *)val;
SDL_DestroyMutex (mtx->mutex);
@@ -120,27 +413,35 @@ DestroyRecursiveMutex (RecursiveMutex val)
}
void
LockRecursiveMutex (RecursiveMutex val)
LockRecursiveMutex_SDL (RecursiveMutex val)
{
RecM *mtx = (RecM *)val;
Uint32 thread_id = SDL_ThreadID();
if (mtx->thread_id != thread_id)
{
#ifdef TRACK_CONTENTION
if (mtx->thread_id)
{
fprintf (stderr, "Thread '%s' blocking on '%s'\n", MyThreadName (), mtx->name);
}
#endif
while (SDL_mutexP (mtx->mutex))
TaskSwitch ();
TaskSwitch_SDL ();
mtx->thread_id = thread_id;
}
mtx->locks++;
}
void
UnlockRecursiveMutex (RecursiveMutex val)
UnlockRecursiveMutex_SDL (RecursiveMutex val)
{
RecM *mtx = (RecM *)val;
Uint32 thread_id = SDL_ThreadID();
if (mtx->thread_id != thread_id)
{
fprintf (stderr, "%8x attempted to unlock %s when it didn't hold it\n", thread_id, mtx->name);
#ifdef NAMED_SYNCHRO
fprintf (stderr, "'%s' attempted to unlock %s when it didn't hold it\n", MyThreadName (), mtx->name);
#endif
}
else
{
@@ -154,85 +455,88 @@ UnlockRecursiveMutex (RecursiveMutex val)
}
int
GetRecursiveMutexDepth (RecursiveMutex val)
GetRecursiveMutexDepth_SDL (RecursiveMutex val)
{
RecM *mtx = (RecM *)val;
return mtx->locks;
}
/* Code for cross-thread mutexes. The prototypes for these functions are in threadlib.h. */
typedef struct _ctm {
typedef struct _cond {
SDL_cond *cond;
SDL_mutex *mutex;
SDL_cond *cond;
#ifdef NAMED_SYNCHRO
const char *name;
Uint32 locker;
} _NativeCTM;
#endif
} cvar;
CrossThreadMutex
CreateCrossThreadMutex (const char *name)
CondVar
#ifdef NAMED_SYNCHRO
CreateCondVar_SDL (const char *name)
#else
CreateCondVar_SDL (void)
#endif
{
_NativeCTM *result = HMalloc (sizeof (_NativeCTM));
result->mutex = SDL_CreateMutex ();
result->cond = SDL_CreateCond ();
result->name = name;
result->locker = 0;
return (CrossThreadMutex)result;
cvar *cv = (cvar *) HMalloc (sizeof (cvar));
cv->cond = SDL_CreateCond ();
cv->mutex = SDL_CreateMutex ();
#ifdef NAMED_SYNCHRO
cv->name = name;
#endif
return cv;
}
void
DestroyCrossThreadMutex (CrossThreadMutex val)
DestroyCondVar_SDL (CondVar c)
{
_NativeCTM *ctm = (_NativeCTM *)val;
if (ctm)
{
SDL_DestroyMutex (ctm->mutex);
SDL_DestroyCond (ctm->cond);
HFree (ctm);
}
}
int
LockCrossThreadMutex (CrossThreadMutex val)
{
_NativeCTM *ctm = (_NativeCTM *)val;
if (SDL_mutexP (ctm->mutex))
{
fprintf (stderr, "LockCrossThreadMutex failed to lock internal mutex in %s!\n", ctm->name);
return -1;
}
while (ctm->locker)
{
// fprintf (stderr, "Thread %8x goes to sleep, waiting on %s\n", SDL_ThreadID (), ctm->name);
SDL_CondWait (ctm->cond, ctm->mutex);
// fprintf (stderr, "Thread %8x awakens, acquires %s.\n", SDL_ThreadID (), ctm->name);
}
ctm->locker = SDL_ThreadID ();
SDL_mutexV (ctm->mutex);
return 0; /* success */
cvar *cv = (cvar *) c;
SDL_DestroyCond (cv->cond);
SDL_DestroyMutex (cv->mutex);
HFree (cv);
}
void
UnlockCrossThreadMutex (CrossThreadMutex val)
WaitCondVar_SDL (CondVar c)
{
_NativeCTM *ctm = (_NativeCTM *)val;
if (SDL_mutexP (ctm->mutex))
cvar *cv = (cvar *) c;
SDL_mutexP (cv->mutex);
#ifdef TRACK_CONTENTION
fprintf (stderr, "Thread '%s' waiting for signal from '%s'\n", MyThreadName (), cv->name);
#endif
while (SDL_CondWait (cv->cond, cv->mutex) != 0)
{
fprintf (stderr, "UnlockCrossThreadMutex failed to lock internal mutex in %s!\n", ctm->name);
return;
TaskSwitch_SDL ();
}
if (ctm->locker)
{
if (ctm->locker != SDL_ThreadID ())
{
fprintf (stderr, "Cross-thread unlock on %s.\n", ctm->name);
}
ctm->locker = 0;
SDL_CondSignal (ctm->cond);
}
else
{
fprintf (stderr, "Double unlock attempt on %s ignored.\n", ctm->name);
}
SDL_mutexV (ctm->mutex);
#ifdef TRACK_CONTENTION
fprintf (stderr, "Thread '%s' received signal from '%s', awakening.\n", MyThreadName (), cv->name);
#endif
SDL_mutexV (cv->mutex);
}
void
WaitProtectedCondVar_SDL (CondVar c, Mutex m)
{
cvar *cv = (cvar *) c;
#ifdef TRACK_CONTENTION
fprintf (stderr, "Thread '%s' waiting for signal from '%s'\n", MyThreadName (), cv->name);
#endif
if (SDL_CondWait (cv->cond, m) != 0) {
TaskSwitch_SDL ();
}
#ifdef TRACK_CONTENTION
fprintf (stderr, "Thread '%s' received signal from '%s', awakening.\n", MyThreadName (), cv->name);
#endif
}
void
SignalCondVar_SDL (CondVar c)
{
cvar *cv = (cvar *) c;
SDL_CondSignal (cv->cond);
}
void
BroadcastCondVar_SDL (CondVar c)
{
cvar *cv = (cvar *) c;
SDL_CondBroadcast (cv->cond);
}
+64 -58
View File
@@ -25,51 +25,8 @@
#include "libs/threadlib.h"
#include "libs/timelib.h"
typedef SDL_Thread *NativeThread;
typedef int (*NativeThreadFunction) (void *);
#define NativeInitThreadSystem()
#define NativeUnInitThreadSystem()
#define NativeCreateThread(func, data, stackSize) \
SDL_CreateThread ((func), (data))
extern void SDLWrapper_SleepThread (TimeCount sleepTime);
#define NativeSleepThread(sleepTime) \
SDLWrapper_SleepThread ((sleepTime))
extern void SDLWrapper_SleepThreadUntil (TimeCount wakeTime);
#define NativeSleepThreadUntil(wakeTime) \
SDLWrapper_SleepThreadUntil ((wakeTime))
extern void SDLWrapper_TaskSwitch (void);
#define NativeTaskSwitch() \
SDLWrapper_TaskSwitch()
#define NativeWaitThread(thread, status) \
SDL_WaitThread ((thread), (status))
#define NativeGetThreadID(thread) SDL_GetThreadID ((thread))
#define NativeThreadID() SDL_ThreadID ()
#ifdef PROFILE_THREADS
extern void SDLWrapper_PrintThreadStats (SDL_Thread *thread);
#define NativePrintThreadStats(thread) \
SDLWrapper_PrintThreadStats ((thread))
#endif
#define NativeThreadOk(thread) \
((thread) != NULL)
typedef SDL_sem *NativeSemaphore;
#define NativeCreateSemaphore(initial) \
SDL_CreateSemaphore ((initial))
#define NativeDestroySemaphore(sem) \
SDL_DestroySemaphore ((sem))
#define NativeSetSemaphore(sem) \
SDL_SemWait ((sem))
#define NativeTrySetSemaphore(sem) \
SDL_SemTryWait ((sem))
#define NativeSemValue(sem) \
SDL_SemValue ((sem))
#define NATIVE_MUTEX_TIMEOUT SDL_MUTEX_TIMEDOUT
extern int SDLWrapper_TimeoutSetSemaphore (Semaphore sem,
TimePeriod timeperiod);
#define NativeTimeoutSetSemaphore(sem, timeperiod) \
SDLWrapper_TimeoutSetSemaphore ((sem), timeperiod)
#define NativeClearSemaphore(sem) \
SDL_SemPost ((sem))
typedef SDL_mutex *NativeMutex;
#define NativeCreateMutex() \
@@ -81,23 +38,72 @@ typedef SDL_mutex *NativeMutex;
#define NativeUnlockMutex(mutex) \
SDL_mutexV ((mutex))
typedef SDL_cond *NativeCondVar;
#define NativeCreateCondVar() \
SDL_CreateCond ()
#define NativeDestroyCondVar(condvar) \
SDL_DestroyCond ((condvar))
extern void SDLWrapper_WaitCondVar (CondVar candvar);
#define NativeWaitCondVar(condvar) \
SDLWrapper_WaitCondVar ((condvar))
#define NativeWaitProtectedCondVar(condvar, mutex) \
SDL_CondWait ((condvar), (mutex))
#define NativeSignalCondVar(condvar) \
SDL_CondSignal ((condvar))
#define NativeBroadcastCondVar(condvar) \
SDL_CondBroadcast ((condvar))
#define NativeCurrentThreadID() \
SDL_ThreadID ()
void InitThreadSystem_SDL (void);
void UnInitThreadSystem_SDL (void);
#ifdef NAMED_SYNCHRO
/* Prototypes with the "name" field */
Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
Semaphore CreateSemaphore_SDL (DWORD initial, const char *name);
RecursiveMutex CreateRecursiveMutex_SDL (const char *name);
CondVar CreateCondVar_SDL (const char *name);
#else
/* Prototypes without the "name" field. */
Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize);
Semaphore CreateSemaphore_SDL (DWORD initial);
RecursiveMutex CreateRecursiveMutex_SDL (void);
CondVar CreateCondVar_SDL (void);
#endif
void SleepThread_SDL (TimeCount sleepTime);
void SleepThreadUntil_SDL (TimeCount wakeTime);
void TaskSwitch_SDL (void);
void WaitThread_SDL (Thread thread, int *status);
void DestroySemaphore_SDL (Semaphore sem);
void SetSemaphore_SDL (Semaphore sem);
void ClearSemaphore_SDL (Semaphore sem);
void DestroyCondVar_SDL (CondVar c);
void WaitCondVar_SDL (CondVar c);
void WaitProtectedCondVar_SDL (CondVar c, Mutex m);
void SignalCondVar_SDL (CondVar c);
void BroadcastCondVar_SDL (CondVar c);
void DestroyRecursiveMutex_SDL (RecursiveMutex m);
void LockRecursiveMutex_SDL (RecursiveMutex m);
void UnlockRecursiveMutex_SDL (RecursiveMutex m);
int GetRecursiveMutexDepth_SDL (RecursiveMutex m);
#define NativeInitThreadSystem InitThreadSystem_SDL
#define NativeUnInitThreadSystem UnInitThreadSystem_SDL
#define NativeCreateThread CreateThread_SDL
#define NativeSleepThread SleepThread_SDL
#define NativeSleepThreadUntil SleepThreadUntil_SDL
#define NativeTaskSwitch TaskSwitch_SDL
#define NativeWaitThread WaitThread_SDL
#define NativeCreateSemaphore CreateSemaphore_SDL
#define NativeDestroySemaphore DestroySemaphore_SDL
#define NativeSetSemaphore SetSemaphore_SDL
#define NativeClearSemaphore ClearSemaphore_SDL
#define NativeCreateCondVar CreateCondVar_SDL
#define NativeDestroyCondVar DestroyCondVar_SDL
#define NativeWaitCondVar WaitCondVar_SDL
#define NativeWaitProtectedCondVar WaitProtectedCondVar_SDL
#define NativeSignalCondVar SignalCondVar_SDL
#define NativeBroadcastCondVar BroadcastCondVar_SDL
#define NativeCreateRecursiveMutex CreateRecursiveMutex_SDL
#define NativeDestroyRecursiveMutex DestroyRecursiveMutex_SDL
#define NativeLockRecursiveMutex LockRecursiveMutex_SDL
#define NativeUnlockRecursiveMutex UnlockRecursiveMutex_SDL
#define NativeGetRecursiveMutexDepth GetRecursiveMutexDepth_SDL
#endif /* _SDLTHREAD_H */
+112 -508
View File
@@ -22,249 +22,90 @@
#include "libs/timelib.h"
#include "libs/misc.h"
#include "thrcommon.h"
#ifdef PROFILE_THREADS
#include <signal.h>
#include <unistd.h>
#endif
#ifdef DEBUG_TRACK_SEM
#include <string.h>
// The semaphore tracker looks for possible semaphore issues.
// It will report semaphores cleared by threads other than what set them
// and when the semaphore value is larger than 1
#define NUM_SEMAPHORES 50
// Set the timeout to 60 seconds
#define SEM_TIMEOUT 6000
#undef DEBUG_SEM_DEADLOCK
typedef struct {
Semaphore Sem;
Uint32 Thread;
char Name[20];
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
char ThreadName[20];
#endif
} MonitorSem;
Semaphore SemMutex;
static MonitorSem SemMon[NUM_SEMAPHORES];
static UWORD numSems = 0;
#endif
#ifdef THREAD_QUEUE
static volatile Thread threadQueue = NULL;
static Semaphore threadQueueSemaphore;
#endif
struct ThreadStartInfo
{
ThreadFunction func;
void *data;
Semaphore sem;
Thread thread;
};
#ifdef PROFILE_THREADS
static void
SigUSR1Handler (int signr) {
if (getpgrp () != getpid ())
{
// Only act for the main process
return;
}
PrintThreadsStats ();
// It's not a good idea in general to do many things in a signal
// handler, (and especially the locking) but I guess it will
// have to do for now (and it's only for debugging).
(void) signr; /* Satisfying compiler (unused parameter) */
}
#endif
void
InitThreadSystem (void)
{
#ifdef THREAD_QUEUE
threadQueueSemaphore = CreateSemaphore (1, "ThreadQueue");
#endif /* THREAD_QUEUE */
#ifdef PROFILE_THREADS
signal(SIGUSR1, SigUSR1Handler);
#endif
NativeInitThreadSystem ();
init_cond_bank ();
}
void
UnInitThreadSystem (void)
{
uninit_cond_bank ();
NativeUnInitThreadSystem ();
#ifdef PROFILE_THREADS
signal(SIGUSR1, SIG_DFL);
#endif
#ifdef THREAD_QUEUE
DestroySemaphore (threadQueueSemaphore);
#endif /* THREAD_QUEUE */
}
#ifdef THREAD_QUEUE
static void
QueueThread (Thread thread)
{
SetSemaphore (threadQueueSemaphore);
thread->next = threadQueue;
threadQueue = thread;
ClearSemaphore (threadQueueSemaphore);
}
static void
UnQueueThread (Thread thread)
{
volatile Thread *ptr;
ptr = &threadQueue;
SetSemaphore (threadQueueSemaphore);
while (*ptr != thread)
{
#ifdef DEBUG_THREADS
if (*ptr == NULL)
{
// Should not happen.
fprintf (stderr, "Error: Trying to remove non-present thread "
"from thread queue.\n");
fflush (stderr);
abort();
}
#endif /* DEBUG_THREADS */
ptr = &(*ptr)->next;
}
*ptr = (*ptr)->next;
ClearSemaphore (threadQueueSemaphore);
}
#endif /* THREAD_QUEUE */
#if defined (DEBUG_TRACK_SEM) && defined (THREAD_QUEUE) \
&& defined (THREAD_NAMES)
static char *ThreadNameNative (Uint32 native)
{
volatile Thread ptr;
if (threadQueueSemaphore)
NativeSetSemaphore (threadQueueSemaphore);
ptr = threadQueue;
while (ptr && NativeGetThreadID (ptr->native) != native)
{
ptr = ptr->next;
}
if(threadQueueSemaphore)
NativeClearSemaphore (threadQueueSemaphore);
if (ptr)
return ((char *)ptr->name);
else
return (NULL);
}
#endif /* DEBUG_TRACK_SEM */
#ifdef DEBUG_THREADS
static const char *
ThreadName(Thread thread) {
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
return thread->name;
#else
return "<<UNNAMED>>";
#endif /* !defined (THREAD_QUEUE) || !defined (THREAD_NAMES) */
}
#endif
static int
ThreadHelper (void *startInfo) {
ThreadFunction func;
void *data;
Semaphore sem;
Thread thread;
int result;
func = ((struct ThreadStartInfo *) startInfo)->func;
data = ((struct ThreadStartInfo *) startInfo)->data;
sem = ((struct ThreadStartInfo *) startInfo)->sem;
// Wait until the Thread structure is available.
while (SetSemaphore (sem) == -1)
;
DestroySemaphore (sem);
thread = ((struct ThreadStartInfo *) startInfo)->thread;
HFree (startInfo);
result = (*(NativeThreadFunction) func) (data);
#ifdef DEBUG_THREADS
fprintf (stderr, "Thread '%s' done (returned %d).\n",
ThreadName (thread), result);
fflush (stderr);
#endif
#ifdef THREAD_QUEUE
UnQueueThread (thread);
#endif /* THREAD_QUEUE */
HFree (thread);
return result;
}
/* The Create routines look different based on whether NAMED_SYNCHRO
is defined or not. */
#ifdef NAMED_SYNCHRO
Thread
CreateThreadAux (ThreadFunction func, void *data, SDWORD stackSize
#ifdef THREAD_NAMES
, const char *name
#endif
)
CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name)
{
Thread thread;
struct ThreadStartInfo *startInfo;
thread = (struct Thread *) HMalloc (sizeof *thread);
#ifdef THREAD_NAMES
thread->name = name;
#endif
#ifdef PROFILE_THREADS
thread->startTime = GetTimeCounter ();
#endif
startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo));
startInfo->func = func;
startInfo->data = data;
startInfo->sem = CreateSemaphore (0, "StartThread");
startInfo->thread = thread;
thread->native = NativeCreateThread (ThreadHelper, (void *) startInfo,
stackSize ? stackSize + 32 : 0);
if (!NativeThreadOk (thread->native))
{
HFree (startInfo);
HFree (thread);
return NULL;
}
// The responsibility to free 'startInfo' and 'thread' is now by the new
// thread.
#ifdef THREAD_QUEUE
QueueThread (thread);
#endif /* THREAD_QUEUE */
#ifdef DEBUG_THREADS
fprintf (stderr, "Thread '%s' created.\n", ThreadName (thread));
fflush (stderr);
#endif
// Signal to the new thread that the thread structure is ready
// and it can begin to use it.
ClearSemaphore (startInfo->sem);
(void) stackSize; /* Satisfying compiler (unused parameter) */
return thread;
return NativeCreateThread (func, data, stackSize, name);
}
Mutex
CreateMutex_Core (void)
{
return (Mutex) NativeCreateMutex ();
}
Semaphore
CreateSemaphore_Core (DWORD initial, const char *name)
{
return NativeCreateSemaphore (initial, name);
}
RecursiveMutex
CreateRecursiveMutex_Core (const char *name)
{
return NativeCreateRecursiveMutex (name);
}
CondVar
CreateCondVar_Core (const char *name)
{
return NativeCreateCondVar (name);
}
#else
/* These are the versions of Create* without the names. */
Thread
CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize)
{
return NativeCreateThread (func, data, stackSize);
}
Mutex
CreateMutex_Core (void)
{
return (Mutex) NativeCreateMutex ();
}
Semaphore
CreateSemaphore_Core (DWORD initial)
{
return NativeCreateSemaphore (initial);
}
RecursiveMutex
CreateRecursiveMutex_Core (void)
{
return NativeCreateRecursiveMutex ();
}
CondVar
CreateCondVar_Core (void)
{
return NativeCreateCondVar ();
}
#endif
void
WaitThread (Thread thread, int *status)
{
NativeWaitThread (thread->native, status);
NativeWaitThread (thread, status);
}
void
@@ -285,292 +126,19 @@ TaskSwitch (void)
NativeTaskSwitch ();
}
#ifdef PROFILE_THREADS
// PROFILE_THREADS implies THREAD_QUEUES
void
PrintThreadsStats (void)
{
Thread ptr;
int now;
now = GetTimeCounter ();
SetSemaphore (threadQueueSemaphore);
fprintf(stderr, "--- Active threads ---\n");
for (ptr = threadQueue; ptr != NULL; ptr = ptr->next) {
#ifndef THREAD_NAMES
fprintf (stderr, "(Thread name not available).\n");
#else
fprintf (stderr, "Thread named '%s'.\n", ptr->name);
#endif
fprintf (stderr, "Started %d.%d minutes ago.\n",
(now - ptr->startTime) / 60000,
((now - ptr->startTime) / 1000) % 60);
NativePrintThreadStats (ptr->native);
if (ptr->next != NULL)
fprintf(stderr, "\n");
}
ClearSemaphore (threadQueueSemaphore);
fprintf(stderr, "----------------------\n");
fflush (stderr);
}
#endif /* PROFILE_THREADS */
Semaphore
CreateSemaphoreAux (DWORD initial
#ifdef DEBUG_TRACK_SEM
, const char *sem_name
#endif
)
{
Semaphore sem = (Semaphore)NativeCreateSemaphore (initial);
#ifdef DEBUG_TRACK_SEM
int pos;
if (SemMutex == 0)
SemMutex = NativeCreateSemaphore (0);
else
NativeSetSemaphore (SemMutex);
for (pos = 0; pos < numSems; pos++)
if (SemMon[pos].Sem == 0)
break;
if (pos == numSems)
{
numSems++;
if (numSems == NUM_SEMAPHORES)
{
fprintf(stderr, "Error: We ran out of semaphores. aborting!\n");
NativeClearSemaphore (SemMutex);
return (0);
}
}
SemMon[pos].Sem = sem;
SemMon[pos].Thread = 0;
strncpy(SemMon[pos].Name, sem_name, 20);
SemMon[pos].Name[19] = 0;
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
// fprintf (stderr, "Created Semaphore # %d: %s in thread '%s'\n",
// numSems, SemMon[pos].Name, ThreadNameNative (NativeThreadID ()));
#else
// fprintf (stderr, "Created Semaphore # %d: %s\n",
// numSems, SemMon[pos].Name);
#endif
NativeClearSemaphore (SemMutex);
#endif
return (sem);
}
void
DestroySemaphore (Semaphore sem)
{
#ifdef DEBUG_TRACK_SEM
int i;
NativeSetSemaphore (SemMutex);
for (i = 0; i <numSems; i++)
if (SemMon[i].Sem == sem)
{
SemMon[i].Sem = 0;
break;
}
NativeClearSemaphore (SemMutex);
#endif
NativeDestroySemaphore ((NativeSemaphore) sem);
}
#ifdef DEBUG_TRACK_SEM
static void debug_set_sem (Semaphore sem
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
, char *name
#endif
)
{
int i;
for (i = 0; i < numSems; i++)
{
if (SemMon[i].Sem == sem)
{
SemMon[i].Thread = NativeThreadID ();
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
if (name != NULL)
{
strncpy(SemMon[i].ThreadName,name,20);
SemMon[i].ThreadName[19] = 0;
}
#endif
break;
}
}
}
#endif
int
SetSemaphore (Semaphore sem)
{
int i;
#if defined (DEBUG_TRACK_SEM) && defined (THREAD_QUEUE) \
&& defined (THREAD_NAMES)
char *name = ThreadNameNative (NativeThreadID ());
#endif
#if defined (DEBUG_TRACK_SEM) && defined (DEBUG_SEM_DEADLOCK)
do
{
i = NativeTimeoutSetSemaphore ((NativeSemaphore) sem, SEM_TIMEOUT);
if (i == NATIVE_MUTEX_TIMEOUT)
{
int j = -1;
for (j = 0; j < numSems; j++)
{
if (SemMon[j].Sem == sem)
break;
}
if (j != -1)
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
fprintf (stderr, "Failed to acquire '%s' semaphore in thread '%s'.\n\tIt is held by thread '%s'. Retrying\n",
SemMon[j].Name,
name,
SemMon[j].ThreadName);
#else
fprintf (stderr, "Failed to acquire '%s' semaphore. Retrying\n",
SemMon[j].Name);
#endif /* THREAD_NAMES */
}
} while (i == NATIVE_MUTEX_TIMEOUT);
#else
i = NativeSetSemaphore ((NativeSemaphore) sem);
#endif /* DEBUG_SEM_DEADLOCK */
#ifdef DEBUG_TRACK_SEM
if (i != 0)
fprintf(stderr, "WARNING: SetSemaphore did not return 0, this could be bad!\n");
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
debug_set_sem (sem, name);
#else
debug_set_sem (sem);
#endif /* THREAD_NAMES */
#endif /* DEBUG_TRACK_SEM */
return i;
}
int
TrySetSemaphore (Semaphore sem)
{
int i;
#if defined (DEBUG_TRACK_SEM) && defined (THREAD_QUEUE) \
&& defined (THREAD_NAMES)
char *name = ThreadNameNative (NativeThreadID ());
#endif
i = NativeTrySetSemaphore ((NativeSemaphore) sem);
#ifdef DEBUG_TRACK_SEM
if (i == 0)
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
debug_set_sem (sem, name);
#else
debug_set_sem (sem);
#endif /* THREAD_NAMES */
#endif /* DEBUG_TRACK_SEM */
return (i);
}
int
TimeoutSetSemaphore (Semaphore sem, TimePeriod timeout)
{
int i;
#if defined (DEBUG_TRACK_SEM) && defined (THREAD_QUEUE) \
&& defined (THREAD_NAMES)
char *name = ThreadNameNative (NativeThreadID ());
#endif
i = NativeTimeoutSetSemaphore ((NativeSemaphore) sem, timeout);
#ifdef DEBUG_TRACK_SEM
if (i == 0)
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
debug_set_sem (sem, name);
#else
debug_set_sem (sem);
#endif /* THREAD_NAMES */
#endif /* DEBUG_TRACK_SEM */
return (i);
}
DWORD
SemaphoreValue (Semaphore sem)
{
return NativeSemValue (sem);
}
#ifdef DEBUG_TRACK_SEM
// Use this function to prevent messages when it is known that
// a semaphore will be cleared by a different thread than set it
void
ResetSemaphoreOwnerAux (Semaphore sem)
{
int i;
for (i = 0; i < numSems; i++)
if (SemMon[i].Sem == sem)
{
SemMon[i].Thread = 0;
break;
}
}
#endif
void
ClearSemaphore (Semaphore sem)
{
#ifdef DEBUG_TRACK_SEM
int i;
Uint32 semval = NativeSemValue (sem);
char *sem_name = NULL;
for (i = 0; i < numSems; i++)
if (SemMon[i].Sem == sem)
{
sem_name = SemMon[i].Name;
if (SemMon[i].Thread && SemMon[i].Thread != NativeThreadID ())
#if defined (THREAD_QUEUE) && defined (THREAD_NAMES)
{
if (ThreadNameNative (SemMon[i].Thread) == NULL)
fprintf( stderr, "Freeing %s Semaphore in '%s' set by defunct thread '%s'!\n",
sem_name,
ThreadNameNative (NativeThreadID ()),
SemMon[i].ThreadName);
else
fprintf( stderr, "Freeing %s Semaphore in '%s' set by thread '%s'!\n",
sem_name,
ThreadNameNative (NativeThreadID ()),
ThreadNameNative (SemMon[i].Thread));
}
SemMon[i].ThreadName[0] = 0;
#else
fprintf (stderr, "Freeing %s Semaphore that was set by a different thread\n",
sem_name);
#endif
SemMon[i].Thread = 0;
break;
}
if (semval != 0)
fprintf (stderr, "Incrementing semaphore '%s' (newvalue=%d)\n", sem_name, semval + 1);
#endif
NativeClearSemaphore ((NativeSemaphore) sem);
}
Mutex
CreateMutex ()
{
return (Mutex) NativeCreateMutex ();
}
void
DestroyMutex (Mutex sem)
{
NativeDestroyMutex ((NativeMutex) sem);
}
int
void
LockMutex (Mutex sem)
{
return NativeLockMutex ((NativeMutex) sem);
while (NativeLockMutex ((NativeMutex) sem) == -1)
{
TaskSwitch ();
}
}
void
@@ -579,40 +147,52 @@ UnlockMutex (Mutex sem)
NativeUnlockMutex ((NativeMutex) sem);
}
CondVar
CreateCondVar ()
void
DestroySemaphore (Semaphore sem)
{
return NativeCreateCondVar ();
NativeDestroySemaphore (sem);
}
void
SetSemaphore (Semaphore sem)
{
NativeSetSemaphore (sem);
}
void
ClearSemaphore (Semaphore sem)
{
NativeClearSemaphore (sem);
}
void
DestroyCondVar (CondVar cv)
{
NativeDestroyCondVar ((CondVar)cv);
NativeDestroyCondVar (cv);
}
void
WaitCondVar (CondVar cv)
{
NativeWaitCondVar ((NativeCondVar)cv);
NativeWaitCondVar (cv);
}
void
WaitProtectedCondVar (CondVar cv, Mutex m)
{
NativeWaitProtectedCondVar ((NativeCondVar)cv, (NativeMutex)m);
NativeWaitProtectedCondVar (cv, m);
}
void
SignalCondVar (CondVar cv)
{
NativeSignalCondVar ((NativeCondVar)cv);
NativeSignalCondVar (cv);
}
void
BroadcastCondVar (CondVar cv)
{
NativeBroadcastCondVar ((NativeCondVar)cv);
NativeBroadcastCondVar (cv);
}
DWORD
@@ -620,3 +200,27 @@ CurrentThreadID ()
{
return (DWORD)NativeThreadID ();
}
void
DestroyRecursiveMutex (RecursiveMutex mutex)
{
NativeDestroyRecursiveMutex (mutex);
}
void
LockRecursiveMutex (RecursiveMutex mutex)
{
NativeLockRecursiveMutex (mutex);
}
void
UnlockRecursiveMutex (RecursiveMutex mutex)
{
NativeUnlockRecursiveMutex (mutex);
}
int
GetRecursiveMutexDepth (RecursiveMutex mutex)
{
return NativeGetRecursiveMutexDepth (mutex);
}
+1 -1
View File
@@ -128,7 +128,7 @@ _init_video_file(PVOID pStr)
vid->w = vid->decoder->w;
vid->h = vid->decoder->h;
vid->guard = CreateMutex ();
vid->frame_lock = CreateCondVar ();
vid->frame_lock = CreateCondVar ("frame lock");
return (VIDEO_REF) vid;
}
-1
View File
@@ -308,7 +308,6 @@ TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y)
if (!vid->play_task)
{
vid->playing = false;
//UnlockCrossThreadMutex (vid->frame_lock);
ClearSemaphore (vp_interthread_lock);
TFB_StopVideo (VidRef);
+3 -3
View File
@@ -96,7 +96,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc)
uio_Stream *fp;
DECODE_REF fh;
COUNT num_links;
CrossThreadMutex clock_lock;
Semaphore clock_sem;
Task clock_task;
QUEUE event_q, encounter_q, avail_q, npc_q, player_q;
STAR_DESC SD;
@@ -126,7 +126,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc)
ReinitQueue (&GLOBAL (npc_built_ship_q));
ReinitQueue (&GLOBAL (built_ship_q));
clock_lock = GLOBAL (GameClock.clock_lock);
clock_sem = GLOBAL (GameClock.clock_sem);
clock_task = GLOBAL (GameClock.clock_task);
event_q = GLOBAL (GameClock.event_q);
encounter_q = GLOBAL (encounter_q);
@@ -141,7 +141,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc)
NextActivity = GLOBAL (CurrentActivity);
GLOBAL (CurrentActivity) = Activity;
GLOBAL (GameClock.clock_lock) = clock_lock;
GLOBAL (GameClock.clock_sem) = clock_sem;
GLOBAL (GameClock.clock_task) = clock_task;
GLOBAL (GameClock.event_q) = event_q;
GLOBAL (encounter_q) = encounter_q;
+2 -2
View File
@@ -46,7 +46,7 @@ int arilou_gate_task(void* data)
counter = GET_GAME_STATE (ARILOU_SPACE_COUNTER);
while (!Task_ReadState (task, TASK_EXIT))
{
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
SetSemaphore (GLOBAL (GameClock.clock_sem));
if (GET_GAME_STATE (ARILOU_SPACE) == OPENING)
{
@@ -63,7 +63,7 @@ int arilou_gate_task(void* data)
SET_GAME_STATE (ARILOU_SPACE_COUNTER, counter);
UnlockMutex (GraphicsLock);
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
ClearSemaphore (GLOBAL (GameClock.clock_sem));
SleepThreadUntil (TimeIn + BATTLE_FRAME_RATE);
TimeIn = GetTimeCounter ();
}
+8 -6
View File
@@ -134,12 +134,14 @@ main (int argc, char *argv[])
contentDir = "content";
#endif
/* InitThreadSystem should come before anything else.
* The memory system uses semaphores.
* Everything else uses the memory system.
*/
InitThreadSystem ();
/* mem_init () uses mutexes. Mutex creation cannot use
the memory system until the memory system is rewritten
to rely on a thread-safe allocator.
*/
TFB_PreInit ();
mem_init ();
InitThreadSystem ();
addons = HMalloc(1 * sizeof (const char *));
addons[0] = NULL;
@@ -349,7 +351,7 @@ main (int argc, char *argv[])
InitTaskSystem ();
GraphicsLock = CreateMutex (/*"Graphics"*/);
RenderingCond = CreateCondVar ();
RenderingCond = CreateCondVar ("DCQ empty");
TFB_InitGraphics (gfxdriver, gfxflags, width, height, bpp);
init_communication ();