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' CHOICE_debug_OPTION_debug_ACTION='debug_action'
debug_action() { debug_action() {
CFLAGS="$CFLAGS -g -O0 -W -Wall" CFLAGS="$CFLAGS -g -O0 -W -Wall"
CFLAGS="$CFLAGS -DDEBUG_TRACK_SEM" # enable semaphore deugging
LDFLAGS="$LDFLAGS -O0" LDFLAGS="$LDFLAGS -O0"
DEBUG=1 DEBUG=1
} }
@@ -67,7 +66,6 @@ CHOICE_debug_OPTION_strictdebug_TITLE="Debug info + strict compile checks"
CHOICE_debug_OPTION_strictdebug_ACTION='strictdebug_action' CHOICE_debug_OPTION_strictdebug_ACTION='strictdebug_action'
strictdebug_action() { strictdebug_action() {
CFLAGS="$CFLAGS -O1" # This is needed for -Wunitialized CFLAGS="$CFLAGS -O1" # This is needed for -Wunitialized
CFLAGS="$CFLAGS -DDEBUG_TRACK_SEM" # enable semaphore debugging
CFLAGS="$CFLAGS -W -Wall \ CFLAGS="$CFLAGS -W -Wall \
-Wbad-function-cast -Wcast-qual -Wmissing-prototypes \ -Wbad-function-cast -Wcast-qual -Wmissing-prototypes \
-Wstrict-prototypes -Wmissing-declarations \ -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 * can be halted. (e.g. during battle
* or communications) * or communications)
*/ */
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); SetSemaphore (GLOBAL (GameClock.clock_sem));
TimeIn = GetTimeCounter (); TimeIn = GetTimeCounter ();
if (GLOBAL (GameClock).tick_count <= 0 if (GLOBAL (GameClock).tick_count <= 0
@@ -165,7 +165,7 @@ int clock_task_func(void* data)
LastTime += num_ticks; LastTime += num_ticks;
} }
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); ClearSemaphore (GLOBAL (GameClock.clock_sem));
SleepThreadUntil (TimeIn + ONE_SECOND / 120); SleepThreadUntil (TimeIn + ONE_SECOND / 120);
} }
FinishTask (task); FinishTask (task);
@@ -215,7 +215,7 @@ SuspendGameClock (void)
LockMutex (clock_mutex); LockMutex (clock_mutex);
if (GameClockRunning ()) if (GameClockRunning ())
{ {
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); SetSemaphore (GLOBAL (GameClock.clock_sem));
GLOBAL (GameClock.TimeCounter) = 0; GLOBAL (GameClock.TimeCounter) = 0;
} }
UnlockMutex (clock_mutex); UnlockMutex (clock_mutex);
@@ -228,7 +228,7 @@ ResumeGameClock (void)
if (!GameClockRunning ()) if (!GameClockRunning ())
{ {
GLOBAL (GameClock.TimeCounter) = GetTimeCounter (); GLOBAL (GameClock.TimeCounter) = GetTimeCounter ();
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); ClearSemaphore (GLOBAL (GameClock.clock_sem));
} }
UnlockMutex (clock_mutex); UnlockMutex (clock_mutex);
} }
@@ -245,7 +245,7 @@ SetGameClockRate (COUNT seconds_per_day)
SIZE new_day_in_ticks, new_tick_count; SIZE new_day_in_ticks, new_tick_count;
//if (GLOBAL (GameClock.clock_sem)) fprintf (stderr, "%u\n", GLOBAL (GameClock.clock_sem)); //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); new_day_in_ticks = (SIZE)(seconds_per_day * CLOCK_BASE_FRAMERATE);
if (GLOBAL (GameClock.day_in_ticks) == 0) if (GLOBAL (GameClock.day_in_ticks) == 0)
new_tick_count = new_day_in_ticks; new_tick_count = new_day_in_ticks;
@@ -256,7 +256,7 @@ SetGameClockRate (COUNT seconds_per_day)
new_tick_count = 1; new_tick_count = 1;
GLOBAL (GameClock.day_in_ticks) = new_day_in_ticks; GLOBAL (GameClock.day_in_ticks) = new_day_in_ticks;
GLOBAL (GameClock.tick_count) = new_tick_count; GLOBAL (GameClock.tick_count) = new_tick_count;
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); ClearSemaphore (GLOBAL (GameClock.clock_sem));
} }
BOOLEAN BOOLEAN
+1 -1
View File
@@ -56,7 +56,7 @@ typedef struct
BYTE day_index, month_index; BYTE day_index, month_index;
COUNT year_index; COUNT year_index;
SIZE tick_count, day_in_ticks; SIZE tick_count, day_in_ticks;
CrossThreadMutex clock_lock; Semaphore clock_sem;
Task clock_task; Task clock_task;
DWORD TimeCounter; DWORD TimeCounter;
+2 -5
View File
@@ -270,12 +270,9 @@ InitGlobData (void)
GLOBAL (DisplayArray) = DisplayArray; GLOBAL (DisplayArray) = DisplayArray;
// The clock semaphore was initially initialized as '1' // The clock semaphore was initially initialized as '1'
// but it is always cleared before set, so it toggled between // but it is always cleared before set, so it toggled between
// 2 and 1, which doesn't actually do anything. When this // 2 and 1, which doesn't actually do anything.
// was transformed into a mutex, we lock it first to prevent
// double-unlock.
GLOBAL (GameClock.clock_lock) = CreateCrossThreadMutex("Clock"); GLOBAL (GameClock.clock_sem) = CreateSemaphore(0, "Clock");
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock));
} }
int int
@@ -42,6 +42,7 @@ enum
#define TFB_GFXFLAGS_SCALE_BIADAPT (1<<4) #define TFB_GFXFLAGS_SCALE_BIADAPT (1<<4)
#define TFB_GFXFLAGS_SCALE_BIADAPTADV (1<<5) #define TFB_GFXFLAGS_SCALE_BIADAPTADV (1<<5)
void TFB_PreInit (void);
int TFB_InitGraphics (int driver, int flags, int width, int height, int bpp); int TFB_InitGraphics (int driver, int flags, int width, int height, int bpp);
void TFB_UninitGraphics (void); void TFB_UninitGraphics (void);
void TFB_ProcessEvents (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; GraphicsDriver = driver;
fprintf (stderr, "Initializing SDL (OpenGL).\n"); fprintf (stderr, "Initializing SDL with OpenGL support.\n");
if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
{
fprintf (stderr, "Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
SDL_VideoDriverName (VideoName, sizeof (VideoName)); SDL_VideoDriverName (VideoName, sizeof (VideoName));
fprintf (stderr, "SDL driver used: %s\n", 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; GraphicsDriver = driver;
fprintf (stderr, "Initializing SDL (pure).\n"); fprintf (stderr, "Initializing Pure-SDL graphics.).\n");
if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
{
fprintf (stderr, "Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
SDL_VideoDriverName (VideoName, sizeof (VideoName)); SDL_VideoDriverName (VideoName, sizeof (VideoName));
fprintf (stderr, "SDL driver used: %s\n", VideoName); fprintf (stderr, "SDL driver used: %s\n", VideoName);
@@ -55,6 +55,17 @@ TFB_Abort (void)
abortFlag = TRUE; 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 int
TFB_InitGraphics (int driver, int flags, int width, int height, int bpp) TFB_InitGraphics (int driver, int flags, int width, int height, int bpp)
{ {
+65 -58
View File
@@ -22,10 +22,14 @@
#define THREADLIB SDL #define THREADLIB SDL
/* #define NAMED_SYNCHRO /* Should synchronizable objects have names? */
This is now a compile-time define // #define TRACK_CONTENTION /* Should we report when a thread sleeps on synchronize? */
#define DEBUG_TRACK_SEM
*/ #ifdef TRACK_CONTENTION
# ifndef NAMED_SYNCHRO
# define NAMED_SYNCHRO
# endif
#endif /* TRACK_CONTENTION */
#ifdef DEBUG #ifdef DEBUG
# ifndef DEBUG_THREADS # ifndef DEBUG_THREADS
@@ -74,80 +78,83 @@ void uninit_cond_bank (void);
typedef int (*ThreadFunction) (void *); typedef int (*ThreadFunction) (void *);
typedef struct Thread { typedef void *Thread;
void *native; typedef void *Mutex;
#ifdef THREAD_NAMES typedef void *Semaphore;
const char *name; typedef void *RecursiveMutex;
#endif typedef void *CondVar;
#ifdef PROFILE_THREADS
int startTime; #ifdef NAMED_SYNCHRO
#endif /* PROFILE_THREADS */ /* Prototypes with the "name" field */
#ifdef THREAD_QUEUE
struct Thread *next; Thread CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
#endif Semaphore CreateSemaphore_Core (DWORD initial, const char *name);
} *Thread; Mutex CreateMutex_Core (void);
RecursiveMutex CreateRecursiveMutex_Core (const char *name);
CondVar CreateCondVar_Core (const char *name);
/* Preprocessor directives to forward to the appropriate routines */
#ifdef THREAD_NAMES
Thread CreateThreadAux (ThreadFunction func, void *data,
SDWORD stackSize, const char *name);
#define CreateThread(func, data, stackSize, name) \ #define CreateThread(func, data, stackSize, name) \
CreateThreadAux ((func), (data), (stackSize), (name)) CreateThread_Core ((func), (data), (stackSize), (name))
#else /* !defined(THREAD_NAMES) */ #define CreateSemaphore(initial, name) \
Thread CreateThreadAux (ThreadFunction func, void *data, CreateSemaphore_Core ((initial), (name))
SDWORD stackSize); #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) \ #define CreateThread(func, data, stackSize, name) \
CreateThreadAux ((func), (data), (stackSize)) CreateThread_Core ((func), (data), (stackSize))
#endif /* !defined(THREAD_NAMES) */ #define CreateSemaphore(initial, name) \
CreateSemaphore_Core ((initial))
#define CreateMutex() \
CreateMutex_Core ()
#define CreateRecursiveMutex(name) \
CreateRecursiveMutex_Core()
#define CreateCondVar(name) \
CreateCondVar_Core ()
#endif
void SleepThread (TimePeriod timePeriod); void SleepThread (TimePeriod timePeriod);
void SleepThreadUntil (TimeCount wakeTime); void SleepThreadUntil (TimeCount wakeTime);
void TaskSwitch (void); void TaskSwitch (void);
void WaitThread (Thread thread, int *status); 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 #ifdef PROFILE_THREADS
void PrintThreadsStats (void); void PrintThreadsStats (void);
#endif /* PROFILE_THREADS */ #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); void DestroyMutex (Mutex sem);
int LockMutex (Mutex sem); void LockMutex (Mutex sem);
void UnlockMutex (Mutex sem); void UnlockMutex (Mutex sem);
typedef void *RecursiveMutex;
RecursiveMutex CreateRecursiveMutex (const char *name);
void DestroyRecursiveMutex (RecursiveMutex m); void DestroyRecursiveMutex (RecursiveMutex m);
void LockRecursiveMutex (RecursiveMutex m); void LockRecursiveMutex (RecursiveMutex m);
void UnlockRecursiveMutex (RecursiveMutex m); void UnlockRecursiveMutex (RecursiveMutex m);
int GetRecursiveMutexDepth (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 DestroyCondVar (CondVar);
void WaitCondVar (CondVar); void WaitCondVar (CondVar);
void WaitProtectedCondVar (CondVar, Mutex); void WaitProtectedCondVar (CondVar, Mutex);
+1 -1
View File
@@ -45,7 +45,7 @@ init_cond_bank ()
bank_mutex = CreateMutex (); bank_mutex = CreateMutex ();
for (i = 0; i < CONDVAR_BANK_SIZE; i++) 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].id = bank[i].used = 0;
bank[i].control = CreateMutex (); bank[i].control = CreateMutex ();
} }
+393 -89
View File
@@ -20,15 +20,55 @@
#include <stdlib.h> #include <stdlib.h>
#include "misc.h" #include "misc.h"
#include "sdlthreads.h" #include "sdlthreads.h"
#ifdef PROFILE_THREADS
#include <signal.h>
#include <unistd.h>
#endif
#if defined(PROFILE_THREADS) && !defined(WIN32) #if defined(PROFILE_THREADS) && !defined(WIN32)
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#endif #endif
typedef struct _thread {
void *native;
#ifdef NAMED_SYNCHRO
const char *name;
#endif
#ifdef PROFILE_THREADS #ifdef PROFILE_THREADS
void int startTime;
SDLWrapper_PrintThreadStats (SDL_Thread *thread) { #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) #if defined (WIN32) || !defined(SDL_PTHREADS)
fprintf (stderr, "Thread ID %u\n", SDL_GetThreadID (thread)); fprintf (stderr, "Thread ID %u\n", SDL_GetThreadID (thread));
#else /* !defined (WIN32) && defined(SDL_PTHREADS) */ #else /* !defined (WIN32) && defined(SDL_PTHREADS) */
@@ -45,74 +85,327 @@ SDLWrapper_PrintThreadStats (SDL_Thread *thread) {
seconds / 60, seconds % 60); seconds / 60, seconds % 60);
#endif /* defined (WIN32) && defined(SDL_PTHREADS) */ #endif /* defined (WIN32) && defined(SDL_PTHREADS) */
} }
#endif
void 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); SDL_Delay (sleepTime * 1000 / ONE_SECOND);
} }
void void
SDLWrapper_SleepThreadUntil (TimeCount wakeTime) { SleepThreadUntil_SDL (TimeCount wakeTime) {
TimeCount now; TimeCount now;
now = GetTimeCounter (); now = GetTimeCounter ();
if (wakeTime <= now) if (wakeTime <= now)
SDLWrapper_TaskSwitch (); TaskSwitch_SDL ();
else else
SDL_Delay ((wakeTime - now) * 1000 / ONE_SECOND); SDL_Delay ((wakeTime - now) * 1000 / ONE_SECOND);
} }
int
SDLWrapper_TimeoutSetSemaphore (Semaphore sem, TimePeriod timeout) {
return SDL_SemWaitTimeout (sem, timeout * 1000 / ONE_SECOND);
}
void void
SDLWrapper_TaskSwitch (void) { TaskSwitch_SDL (void) {
SDL_Delay (1); SDL_Delay (1);
} }
void void
SDLWrapper_WaitCondVar (CondVar cv) { WaitThread_SDL (Thread thread, int *status) {
int result; SDL_WaitThread (((TrueThread)thread)->native, status);
Mutex temp = CreateMutex (); }
LockMutex (temp);
result = SDL_CondWait (cv, temp); /* These are the SDL implementations of the UQM synchronization objects. */
UnlockMutex (temp); /* TODO: Remove the names of the following data types when compiling
DestroyMutex (temp); * under release mode. */
if (result != 0) {
fprintf (stderr, "Error result from SDL_CondWait: %d\n", result); /* 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 /* Recursive mutexes. Adapted from mixSDL code, which was adapted from
original DCQ code. */ 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. */
typedef struct _recm { typedef struct _recm {
SDL_mutex *mutex; SDL_mutex *mutex;
Uint32 thread_id; Uint32 thread_id;
Uint32 locks; Uint32 locks;
#ifdef NAMED_SYNCHRO
const char *name; const char *name;
#endif
} RecM; } RecM;
RecursiveMutex 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)); RecM *mtx = (RecM *) HMalloc (sizeof (struct _recm));
mtx->thread_id = 0; mtx->thread_id = 0;
mtx->mutex = SDL_CreateMutex (); mtx->mutex = SDL_CreateMutex ();
#ifdef NAMED_SYNCHRO
mtx->name = name; mtx->name = name;
#endif
mtx->locks = 0; mtx->locks = 0;
return (RecursiveMutex) mtx; return (RecursiveMutex) mtx;
} }
void void
DestroyRecursiveMutex (RecursiveMutex val) DestroyRecursiveMutex_SDL (RecursiveMutex val)
{ {
RecM *mtx = (RecM *)val; RecM *mtx = (RecM *)val;
SDL_DestroyMutex (mtx->mutex); SDL_DestroyMutex (mtx->mutex);
@@ -120,27 +413,35 @@ DestroyRecursiveMutex (RecursiveMutex val)
} }
void void
LockRecursiveMutex (RecursiveMutex val) LockRecursiveMutex_SDL (RecursiveMutex val)
{ {
RecM *mtx = (RecM *)val; RecM *mtx = (RecM *)val;
Uint32 thread_id = SDL_ThreadID(); Uint32 thread_id = SDL_ThreadID();
if (mtx->thread_id != thread_id) 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)) while (SDL_mutexP (mtx->mutex))
TaskSwitch (); TaskSwitch_SDL ();
mtx->thread_id = thread_id; mtx->thread_id = thread_id;
} }
mtx->locks++; mtx->locks++;
} }
void void
UnlockRecursiveMutex (RecursiveMutex val) UnlockRecursiveMutex_SDL (RecursiveMutex val)
{ {
RecM *mtx = (RecM *)val; RecM *mtx = (RecM *)val;
Uint32 thread_id = SDL_ThreadID(); Uint32 thread_id = SDL_ThreadID();
if (mtx->thread_id != thread_id) 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 else
{ {
@@ -154,85 +455,88 @@ UnlockRecursiveMutex (RecursiveMutex val)
} }
int int
GetRecursiveMutexDepth (RecursiveMutex val) GetRecursiveMutexDepth_SDL (RecursiveMutex val)
{ {
RecM *mtx = (RecM *)val; RecM *mtx = (RecM *)val;
return mtx->locks; return mtx->locks;
} }
/* Code for cross-thread mutexes. The prototypes for these functions are in threadlib.h. */ typedef struct _cond {
typedef struct _ctm {
SDL_mutex *mutex;
SDL_cond *cond; SDL_cond *cond;
SDL_mutex *mutex;
#ifdef NAMED_SYNCHRO
const char *name; const char *name;
Uint32 locker; #endif
} _NativeCTM; } cvar;
CrossThreadMutex CondVar
CreateCrossThreadMutex (const char *name) #ifdef NAMED_SYNCHRO
CreateCondVar_SDL (const char *name)
#else
CreateCondVar_SDL (void)
#endif
{ {
_NativeCTM *result = HMalloc (sizeof (_NativeCTM)); cvar *cv = (cvar *) HMalloc (sizeof (cvar));
result->mutex = SDL_CreateMutex (); cv->cond = SDL_CreateCond ();
result->cond = SDL_CreateCond (); cv->mutex = SDL_CreateMutex ();
result->name = name; #ifdef NAMED_SYNCHRO
result->locker = 0; cv->name = name;
return (CrossThreadMutex)result; #endif
return cv;
} }
void void
DestroyCrossThreadMutex (CrossThreadMutex val) DestroyCondVar_SDL (CondVar c)
{ {
_NativeCTM *ctm = (_NativeCTM *)val; cvar *cv = (cvar *) c;
if (ctm) SDL_DestroyCond (cv->cond);
{ SDL_DestroyMutex (cv->mutex);
SDL_DestroyMutex (ctm->mutex); HFree (cv);
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 */
} }
void void
UnlockCrossThreadMutex (CrossThreadMutex val) WaitCondVar_SDL (CondVar c)
{ {
_NativeCTM *ctm = (_NativeCTM *)val; cvar *cv = (cvar *) c;
if (SDL_mutexP (ctm->mutex)) 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); TaskSwitch_SDL ();
return;
} }
if (ctm->locker) #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)
{ {
if (ctm->locker != SDL_ThreadID ()) 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)
{ {
fprintf (stderr, "Cross-thread unlock on %s.\n", ctm->name); cvar *cv = (cvar *) c;
SDL_CondSignal (cv->cond);
} }
ctm->locker = 0;
SDL_CondSignal (ctm->cond); void
} BroadcastCondVar_SDL (CondVar c)
else
{ {
fprintf (stderr, "Double unlock attempt on %s ignored.\n", ctm->name); cvar *cv = (cvar *) c;
} SDL_CondBroadcast (cv->cond);
SDL_mutexV (ctm->mutex);
} }
+64 -58
View File
@@ -25,51 +25,8 @@
#include "libs/threadlib.h" #include "libs/threadlib.h"
#include "libs/timelib.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 NativeGetThreadID(thread) SDL_GetThreadID ((thread))
#define NativeThreadID() SDL_ThreadID () #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; typedef SDL_mutex *NativeMutex;
#define NativeCreateMutex() \ #define NativeCreateMutex() \
@@ -81,23 +38,72 @@ typedef SDL_mutex *NativeMutex;
#define NativeUnlockMutex(mutex) \ #define NativeUnlockMutex(mutex) \
SDL_mutexV ((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() \ #define NativeCurrentThreadID() \
SDL_ThreadID () 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 */ #endif /* _SDLTHREAD_H */
+107 -503
View File
@@ -22,249 +22,90 @@
#include "libs/timelib.h" #include "libs/timelib.h"
#include "libs/misc.h" #include "libs/misc.h"
#include "thrcommon.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 void
InitThreadSystem (void) InitThreadSystem (void)
{ {
#ifdef THREAD_QUEUE
threadQueueSemaphore = CreateSemaphore (1, "ThreadQueue");
#endif /* THREAD_QUEUE */
#ifdef PROFILE_THREADS
signal(SIGUSR1, SigUSR1Handler);
#endif
NativeInitThreadSystem (); NativeInitThreadSystem ();
init_cond_bank ();
} }
void void
UnInitThreadSystem (void) UnInitThreadSystem (void)
{ {
uninit_cond_bank ();
NativeUnInitThreadSystem (); NativeUnInitThreadSystem ();
#ifdef PROFILE_THREADS
signal(SIGUSR1, SIG_DFL);
#endif
#ifdef THREAD_QUEUE
DestroySemaphore (threadQueueSemaphore);
#endif /* THREAD_QUEUE */
} }
#ifdef THREAD_QUEUE /* The Create routines look different based on whether NAMED_SYNCHRO
static void is defined or not. */
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;
}
#ifdef NAMED_SYNCHRO
Thread Thread
CreateThreadAux (ThreadFunction func, void *data, SDWORD stackSize CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name)
#ifdef THREAD_NAMES
, const char *name
#endif
)
{ {
Thread thread; return NativeCreateThread (func, data, stackSize, name);
struct ThreadStartInfo *startInfo; }
thread = (struct Thread *) HMalloc (sizeof *thread); Mutex
#ifdef THREAD_NAMES CreateMutex_Core (void)
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); return (Mutex) NativeCreateMutex ();
HFree (thread);
return NULL;
} }
// The responsibility to free 'startInfo' and 'thread' is now by the new
// thread.
#ifdef THREAD_QUEUE Semaphore
QueueThread (thread); CreateSemaphore_Core (DWORD initial, const char *name)
#endif /* THREAD_QUEUE */ {
return NativeCreateSemaphore (initial, name);
}
#ifdef DEBUG_THREADS RecursiveMutex
fprintf (stderr, "Thread '%s' created.\n", ThreadName (thread)); CreateRecursiveMutex_Core (const char *name)
fflush (stderr); {
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 #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;
}
void void
WaitThread (Thread thread, int *status) WaitThread (Thread thread, int *status)
{ {
NativeWaitThread (thread->native, status); NativeWaitThread (thread, status);
} }
void void
@@ -285,292 +126,19 @@ TaskSwitch (void)
NativeTaskSwitch (); 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 void
DestroyMutex (Mutex sem) DestroyMutex (Mutex sem)
{ {
NativeDestroyMutex ((NativeMutex) sem); NativeDestroyMutex ((NativeMutex) sem);
} }
int void
LockMutex (Mutex sem) LockMutex (Mutex sem)
{ {
return NativeLockMutex ((NativeMutex) sem); while (NativeLockMutex ((NativeMutex) sem) == -1)
{
TaskSwitch ();
}
} }
void void
@@ -579,40 +147,52 @@ UnlockMutex (Mutex sem)
NativeUnlockMutex ((NativeMutex) sem); NativeUnlockMutex ((NativeMutex) sem);
} }
CondVar void
CreateCondVar () DestroySemaphore (Semaphore sem)
{ {
return NativeCreateCondVar (); NativeDestroySemaphore (sem);
}
void
SetSemaphore (Semaphore sem)
{
NativeSetSemaphore (sem);
}
void
ClearSemaphore (Semaphore sem)
{
NativeClearSemaphore (sem);
} }
void void
DestroyCondVar (CondVar cv) DestroyCondVar (CondVar cv)
{ {
NativeDestroyCondVar ((CondVar)cv); NativeDestroyCondVar (cv);
} }
void void
WaitCondVar (CondVar cv) WaitCondVar (CondVar cv)
{ {
NativeWaitCondVar ((NativeCondVar)cv); NativeWaitCondVar (cv);
} }
void void
WaitProtectedCondVar (CondVar cv, Mutex m) WaitProtectedCondVar (CondVar cv, Mutex m)
{ {
NativeWaitProtectedCondVar ((NativeCondVar)cv, (NativeMutex)m); NativeWaitProtectedCondVar (cv, m);
} }
void void
SignalCondVar (CondVar cv) SignalCondVar (CondVar cv)
{ {
NativeSignalCondVar ((NativeCondVar)cv); NativeSignalCondVar (cv);
} }
void void
BroadcastCondVar (CondVar cv) BroadcastCondVar (CondVar cv)
{ {
NativeBroadcastCondVar ((NativeCondVar)cv); NativeBroadcastCondVar (cv);
} }
DWORD DWORD
@@ -620,3 +200,27 @@ CurrentThreadID ()
{ {
return (DWORD)NativeThreadID (); 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->w = vid->decoder->w;
vid->h = vid->decoder->h; vid->h = vid->decoder->h;
vid->guard = CreateMutex (); vid->guard = CreateMutex ();
vid->frame_lock = CreateCondVar (); vid->frame_lock = CreateCondVar ("frame lock");
return (VIDEO_REF) vid; return (VIDEO_REF) vid;
} }
-1
View File
@@ -308,7 +308,6 @@ TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y)
if (!vid->play_task) if (!vid->play_task)
{ {
vid->playing = false; vid->playing = false;
//UnlockCrossThreadMutex (vid->frame_lock);
ClearSemaphore (vp_interthread_lock); ClearSemaphore (vp_interthread_lock);
TFB_StopVideo (VidRef); TFB_StopVideo (VidRef);
+3 -3
View File
@@ -96,7 +96,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc)
uio_Stream *fp; uio_Stream *fp;
DECODE_REF fh; DECODE_REF fh;
COUNT num_links; COUNT num_links;
CrossThreadMutex clock_lock; Semaphore clock_sem;
Task clock_task; Task clock_task;
QUEUE event_q, encounter_q, avail_q, npc_q, player_q; QUEUE event_q, encounter_q, avail_q, npc_q, player_q;
STAR_DESC SD; STAR_DESC SD;
@@ -126,7 +126,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc)
ReinitQueue (&GLOBAL (npc_built_ship_q)); ReinitQueue (&GLOBAL (npc_built_ship_q));
ReinitQueue (&GLOBAL (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); clock_task = GLOBAL (GameClock.clock_task);
event_q = GLOBAL (GameClock.event_q); event_q = GLOBAL (GameClock.event_q);
encounter_q = GLOBAL (encounter_q); encounter_q = GLOBAL (encounter_q);
@@ -141,7 +141,7 @@ LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc)
NextActivity = GLOBAL (CurrentActivity); NextActivity = GLOBAL (CurrentActivity);
GLOBAL (CurrentActivity) = Activity; GLOBAL (CurrentActivity) = Activity;
GLOBAL (GameClock.clock_lock) = clock_lock; GLOBAL (GameClock.clock_sem) = clock_sem;
GLOBAL (GameClock.clock_task) = clock_task; GLOBAL (GameClock.clock_task) = clock_task;
GLOBAL (GameClock.event_q) = event_q; GLOBAL (GameClock.event_q) = event_q;
GLOBAL (encounter_q) = encounter_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); counter = GET_GAME_STATE (ARILOU_SPACE_COUNTER);
while (!Task_ReadState (task, TASK_EXIT)) while (!Task_ReadState (task, TASK_EXIT))
{ {
LockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); SetSemaphore (GLOBAL (GameClock.clock_sem));
if (GET_GAME_STATE (ARILOU_SPACE) == OPENING) if (GET_GAME_STATE (ARILOU_SPACE) == OPENING)
{ {
@@ -63,7 +63,7 @@ int arilou_gate_task(void* data)
SET_GAME_STATE (ARILOU_SPACE_COUNTER, counter); SET_GAME_STATE (ARILOU_SPACE_COUNTER, counter);
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
UnlockCrossThreadMutex (GLOBAL (GameClock.clock_lock)); ClearSemaphore (GLOBAL (GameClock.clock_sem));
SleepThreadUntil (TimeIn + BATTLE_FRAME_RATE); SleepThreadUntil (TimeIn + BATTLE_FRAME_RATE);
TimeIn = GetTimeCounter (); TimeIn = GetTimeCounter ();
} }
+7 -5
View File
@@ -134,12 +134,14 @@ main (int argc, char *argv[])
contentDir = "content"; contentDir = "content";
#endif #endif
/* InitThreadSystem should come before anything else.
* The memory system uses semaphores. /* mem_init () uses mutexes. Mutex creation cannot use
* Everything else uses the memory system. the memory system until the memory system is rewritten
to rely on a thread-safe allocator.
*/ */
InitThreadSystem (); TFB_PreInit ();
mem_init (); mem_init ();
InitThreadSystem ();
addons = HMalloc(1 * sizeof (const char *)); addons = HMalloc(1 * sizeof (const char *));
addons[0] = NULL; addons[0] = NULL;
@@ -349,7 +351,7 @@ main (int argc, char *argv[])
InitTaskSystem (); InitTaskSystem ();
GraphicsLock = CreateMutex (/*"Graphics"*/); GraphicsLock = CreateMutex (/*"Graphics"*/);
RenderingCond = CreateCondVar (); RenderingCond = CreateCondVar ("DCQ empty");
TFB_InitGraphics (gfxdriver, gfxflags, width, height, bpp); TFB_InitGraphics (gfxdriver, gfxflags, width, height, bpp);
init_communication (); init_communication ();