From e94b959c9d2297484e1683b8a9e4bdd6006be28c Mon Sep 17 00:00:00 2001 From: mcmartin Date: Tue, 23 Sep 2003 16:33:50 +0000 Subject: [PATCH] Threadlib refactoring, phase 2. This is a near-total reorganization. Full details will appear in doc/devel/threads when that document is completed. git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1257 8092fc87-c524-0410-9efc-e669fe64eaf9 --- sc2/ChangeLog | 1 + sc2/doc/devel/threads | 33 ++++ sc2/src/msvc++/UrQuanMasters.dsp | 4 - sc2/src/sc2code/clock.c | 2 +- sc2/src/sc2code/comm.c | 4 +- sc2/src/sc2code/init.c | 2 +- sc2/src/sc2code/libs/graphics/drawcmd.h | 2 +- sc2/src/sc2code/libs/graphics/sdl/dcqueue.c | 5 +- .../sc2code/libs/graphics/sdl/sdl_common.c | 2 +- sc2/src/sc2code/libs/graphics/tfb_draw.c | 9 +- sc2/src/sc2code/libs/memory/w_memlib.c | 2 +- sc2/src/sc2code/libs/sound/mixsdl/mixer.c | 6 +- .../sc2code/libs/sound/mixsdl/sound_mixsdl.c | 4 +- .../sc2code/libs/sound/openal/sound_openal.c | 2 +- sc2/src/sc2code/libs/sound/trackplayer.c | 2 +- sc2/src/sc2code/libs/task/tasklib.c | 2 +- sc2/src/sc2code/libs/threadlib.h | 87 ++++---- sc2/src/sc2code/libs/threads/Makeinfo | 2 +- sc2/src/sc2code/libs/threads/condbank.c | 130 ------------ sc2/src/sc2code/libs/threads/sdl/sdlthreads.c | 185 +++++++++++++++--- sc2/src/sc2code/libs/threads/sdl/sdlthreads.h | 39 ++-- sc2/src/sc2code/libs/threads/thrcommon.c | 60 +++--- sc2/src/sc2code/libs/video/sdl/sdlvideo.c | 2 +- sc2/src/sc2code/libs/video/video.c | 4 +- sc2/src/sc2code/libs/video/vidplayer.c | 2 +- sc2/src/sc2code/sis.c | 2 +- sc2/src/starcon2.c | 4 +- 27 files changed, 309 insertions(+), 290 deletions(-) create mode 100644 sc2/doc/devel/threads delete mode 100644 sc2/src/sc2code/libs/threads/condbank.c diff --git a/sc2/ChangeLog b/sc2/ChangeLog index bede24951..28f74c971 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,5 @@ Changes towards version 0.4: +- Major refactoring of threadlib; see doc/devel/threads -Michael - Downgraded the GraphicsLock to an ordinary Mutex -Michael - Added movie player; only movies defined are intro and ending; only .duk decoder present (.duk audio decoder mostly derived diff --git a/sc2/doc/devel/threads b/sc2/doc/devel/threads new file mode 100644 index 000000000..972766211 --- /dev/null +++ b/sc2/doc/devel/threads @@ -0,0 +1,33 @@ +The UQM Threading and Synchronization library + +This document is woefully incomplete at the moment. It will be filled +in later. It's just that the ChangeLog refers to it so I want the +file to be here. --Michael + +#defines + +NAMED_SYNCHRO: When #defined, all synchronization objects are named. +This should be kept on all the time, at least until 1.0. + +TRACK_CONTENTION: implies NAMED_SYNCHRO. Spits out status messages +whenever a thread goes to sleep because of an object matching +TRACK_CONTENTION_CLASSES. + +TRACK_CONTENTION_CLASSES: This is an ORring of enums defined in +threadlib.h. + +Constructs + +- Task + +- Thread + +- ThreadLocal + +- Mutex + +- Semaphore + +- CondVar + +- RecursiveMutex diff --git a/sc2/src/msvc++/UrQuanMasters.dsp b/sc2/src/msvc++/UrQuanMasters.dsp index 4e544e55e..2a960720f 100644 --- a/sc2/src/msvc++/UrQuanMasters.dsp +++ b/sc2/src/msvc++/UrQuanMasters.dsp @@ -783,10 +783,6 @@ SOURCE=..\sc2code\libs\threads\thrcommon.c SOURCE=..\sc2code\libs\threads\thrcommon.h # End Source File -# Begin Source File - -SOURCE=..\sc2code\libs\threads\condbank.c -# End Source File # End Group # Begin Group "time" diff --git a/sc2/src/sc2code/clock.c b/sc2/src/sc2code/clock.c index 6bc787884..648610c87 100644 --- a/sc2/src/sc2code/clock.c +++ b/sc2/src/sc2code/clock.c @@ -177,7 +177,7 @@ InitGameClock (void) { if (!InitQueue (&GLOBAL (GameClock.event_q), NUM_EVENTS, sizeof (EVENT))) return (FALSE); - clock_mutex = CreateMutex (); + clock_mutex = CreateMutex ("Clock Mutex", SYNC_CLASS_TOPLEVEL); GLOBAL (GameClock.month_index) = 2; GLOBAL (GameClock.day_index) = 17; GLOBAL (GameClock.year_index) = START_YEAR; /* Feb 17, START_YEAR */ diff --git a/sc2/src/sc2code/comm.c b/sc2/src/sc2code/comm.c index 24d60b40a..641a2740a 100644 --- a/sc2/src/sc2code/comm.c +++ b/sc2/src/sc2code/comm.c @@ -488,7 +488,7 @@ init_xform_control (void) { XFormControl.XFormCurrent = XFormControl.XFormInsertPoint = 0; XFormControl.XFormsPending = FALSE; - XFormControl.XFormLock = CreateMutex (); + XFormControl.XFormLock = CreateMutex ("Transform Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); } void @@ -521,7 +521,7 @@ init_communication (void) { TFB_Canvas canvas; - subtitle_mutex = CreateMutex (); + subtitle_mutex = CreateMutex ("Subtitle Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); init_xform_control (); canvas = TFB_DrawCanvas_New_TrueColor (SIS_SCREEN_WIDTH, diff --git a/sc2/src/sc2code/init.c b/sc2/src/sc2code/init.c index 81b33c7b9..702d99647 100644 --- a/sc2/src/sc2code/init.c +++ b/sc2/src/sc2code/init.c @@ -272,7 +272,7 @@ InitGlobData (void) // but it is always cleared before set, so it toggled between // 2 and 1, which doesn't actually do anything. - GLOBAL (GameClock.clock_sem) = CreateSemaphore(0, "Clock"); + GLOBAL (GameClock.clock_sem) = CreateSemaphore(0, "Clock", SYNC_CLASS_TOPLEVEL); } int diff --git a/sc2/src/sc2code/libs/graphics/drawcmd.h b/sc2/src/sc2code/libs/graphics/drawcmd.h index 26de281a8..49f7466fd 100644 --- a/sc2/src/sc2code/libs/graphics/drawcmd.h +++ b/sc2/src/sc2code/libs/graphics/drawcmd.h @@ -109,7 +109,7 @@ typedef struct tfb_dc_delimg typedef struct tfb_dc_signal { - DWORD thread; + Semaphore sem; } TFB_DrawCommand_SendSignal; typedef struct tfb_drawcommand diff --git a/sc2/src/sc2code/libs/graphics/sdl/dcqueue.c b/sc2/src/sc2code/libs/graphics/sdl/dcqueue.c index 0f2e1d609..ea4b3386f 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/dcqueue.c +++ b/sc2/src/sc2code/libs/graphics/sdl/dcqueue.c @@ -130,7 +130,7 @@ Init_DrawCommandQueue (void) DrawCommandQueue.FullSize = 0; DrawCommandQueue.Size = 0; - DCQ_Mutex = CreateRecursiveMutex ("DCQ"); + DCQ_Mutex = CreateRecursiveMutex ("DCQ", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); } void @@ -200,9 +200,6 @@ TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand) return; } - if (DrawCommand->Type == TFB_DRAWCOMMANDTYPE_SENDSIGNAL) - DrawCommand->data.sendsignal.thread = CurrentThreadID (); - if (DrawCommand->Type <= TFB_DRAWCOMMANDTYPE_COPYTOIMAGE && TYPE_GET (_CurFramePtr->TypeIndexAndFlags) == SCREEN_DRAWABLE) { diff --git a/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c b/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c index 2a59391e2..c51816d18 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c +++ b/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c @@ -669,7 +669,7 @@ TFB_FlushGraphics () // Only call from main thread!! break; } case TFB_DRAWCOMMANDTYPE_SENDSIGNAL: - SignalThread (DC.data.sendsignal.thread); + ClearSemaphore (DC.data.sendsignal.sem); break; } } diff --git a/sc2/src/sc2code/libs/graphics/tfb_draw.c b/sc2/src/sc2code/libs/graphics/tfb_draw.c index ee9a40be8..9dea57083 100644 --- a/sc2/src/sc2code/libs/graphics/tfb_draw.c +++ b/sc2/src/sc2code/libs/graphics/tfb_draw.c @@ -197,14 +197,15 @@ void TFB_DrawScreen_WaitForSignal (void) { TFB_DrawCommand DrawCommand; - int channel; + Semaphore s; + s = GetMyThreadLocal ()->flushSem; DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL; + DrawCommand.data.sendsignal.sem = s; Lock_DCQ (1); - channel = FindSignalChannel (); TFB_BatchReset (); TFB_EnqueueDrawCommand(&DrawCommand); Unlock_DCQ(); - WaitForSignal (channel); + SetSemaphore (s); } void @@ -247,7 +248,7 @@ TFB_Image * TFB_DrawImage_New (TFB_Canvas canvas) { TFB_Image *img = HMalloc (sizeof (TFB_Image)); - img->mutex = CreateMutex (); + img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO); img->ScaledImg = NULL; img->MipmapImg = NULL; img->colormap_index = -1; diff --git a/sc2/src/sc2code/libs/memory/w_memlib.c b/sc2/src/sc2code/libs/memory/w_memlib.c index b4ac0c440..f2cdbf09d 100644 --- a/sc2/src/sc2code/libs/memory/w_memlib.c +++ b/sc2/src/sc2code/libs/memory/w_memlib.c @@ -382,7 +382,7 @@ mem_init (void) { int i; - _MemoryLock = CreateMutex (); + _MemoryLock = CreateMutex ("memory lock", SYNC_CLASS_RESOURCE); LockMutex (_MemoryLock); freeListHead = &extents[0]; diff --git a/sc2/src/sc2code/libs/sound/mixsdl/mixer.c b/sc2/src/sc2code/libs/sound/mixsdl/mixer.c index 82c7a9c8e..f7645ae4f 100644 --- a/sc2/src/sc2code/libs/sound/mixsdl/mixer.c +++ b/sc2/src/sc2code/libs/sound/mixsdl/mixer.c @@ -209,9 +209,9 @@ mixSDL_OpenAudio (uint32 frequency, uint32 format, uint32 samples_buf, mixer_datasize = samples_buf * mixer_spec.channels * 2; mixer_data = (sint32 *) HMalloc (sizeof (uint32) * mixer_datasize); - src_mutex = CreateRecursiveMutex("mixSDL_SourceMutex"); - buf_mutex = CreateRecursiveMutex("mixSDL_BufferMutex"); - act_mutex = CreateRecursiveMutex("mixSDL_ActiveMutex"); + src_mutex = CreateRecursiveMutex("mixSDL_SourceMutex", SYNC_CLASS_AUDIO); + buf_mutex = CreateRecursiveMutex("mixSDL_BufferMutex", SYNC_CLASS_AUDIO); + act_mutex = CreateRecursiveMutex("mixSDL_ActiveMutex", SYNC_CLASS_AUDIO); audio_opened = 1; mixer_driver->PauseAudio (0); diff --git a/sc2/src/sc2code/libs/sound/mixsdl/sound_mixsdl.c b/sc2/src/sc2code/libs/sound/mixsdl/sound_mixsdl.c index 5660e2a0d..3f0f505ac 100644 --- a/sc2/src/sc2code/libs/sound/mixsdl/sound_mixsdl.c +++ b/sc2/src/sc2code/libs/sound/mixsdl/sound_mixsdl.c @@ -129,7 +129,7 @@ TFB_mixSDL_InitSound (int driver, int flags) soundSource[i].sample = NULL; soundSource[i].stream_should_be_playing = FALSE; - soundSource[i].stream_mutex = CreateMutex (); + soundSource[i].stream_mutex = CreateMutex ("MixSDL stream mutex", SYNC_CLASS_AUDIO); soundSource[i].sbuffer = NULL; soundSource[i].sbuf_start = 0; soundSource[i].sbuf_size = 0; @@ -241,7 +241,7 @@ TFB_NoSound_InitSound (int driver, int flags) soundSource[i].sample = NULL; soundSource[i].stream_should_be_playing = FALSE; - soundSource[i].stream_mutex = CreateMutex (); + soundSource[i].stream_mutex = CreateMutex ("MixSDL stream mutex", SYNC_CLASS_AUDIO); soundSource[i].sbuffer = NULL; soundSource[i].sbuf_start = 0; soundSource[i].sbuf_size = 0; diff --git a/sc2/src/sc2code/libs/sound/openal/sound_openal.c b/sc2/src/sc2code/libs/sound/openal/sound_openal.c index 45cea706f..2f6d7d74d 100644 --- a/sc2/src/sc2code/libs/sound/openal/sound_openal.c +++ b/sc2/src/sc2code/libs/sound/openal/sound_openal.c @@ -89,7 +89,7 @@ TFB_alInitSound (int driver, int flags) soundSource[i].sample = NULL; soundSource[i].stream_should_be_playing = FALSE; - soundSource[i].stream_mutex = CreateMutex (); + soundSource[i].stream_mutex = CreateMutex ("OpenAL stream mutex", SYNC_CLASS_AUDIO); soundSource[i].sbuffer = NULL; soundSource[i].sbuf_start = 0; soundSource[i].sbuf_size = 0; diff --git a/sc2/src/sc2code/libs/sound/trackplayer.c b/sc2/src/sc2code/libs/sound/trackplayer.c index 2c7692116..d5ede8643 100644 --- a/sc2/src/sc2code/libs/sound/trackplayer.c +++ b/sc2/src/sc2code/libs/sound/trackplayer.c @@ -583,7 +583,7 @@ SpliceTrack (UNICODE *TrackName, UNICODE *TrackText, UNICODE *TimeStamp, TFB_Tra TFB_SoundDecoder *decoder; TFB_SoundChainData* scd; - track_mutex = CreateMutex(); + track_mutex = CreateMutex("trackplayer mutex", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_AUDIO); sound_sample = (TFB_SoundSample *) HMalloc (sizeof (TFB_SoundSample)); scd = (TFB_SoundChainData *) HCalloc (sizeof (TFB_SoundChainData)); sound_sample->data = scd; diff --git a/sc2/src/sc2code/libs/task/tasklib.c b/sc2/src/sc2code/libs/task/tasklib.c index 26e2feedd..0a014a623 100644 --- a/sc2/src/sc2code/libs/task/tasklib.c +++ b/sc2/src/sc2code/libs/task/tasklib.c @@ -121,7 +121,7 @@ InitTaskSystem (void) int i; for (i = 0; i < TASK_MAX; ++i) { - task_array[i].state_mutex = CreateMutex (); + task_array[i].state_mutex = CreateMutex ("task manager lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_RESOURCE); } atexit (CleanupTaskSystem); } diff --git a/sc2/src/sc2code/libs/threadlib.h b/sc2/src/sc2code/libs/threadlib.h index 0f54d01b7..d9a6f209d 100644 --- a/sc2/src/sc2code/libs/threadlib.h +++ b/sc2/src/sc2code/libs/threadlib.h @@ -25,6 +25,7 @@ #define NAMED_SYNCHRO /* Should synchronizable objects have names? */ // #define TRACK_CONTENTION /* Should we report when a thread sleeps on synchronize? */ +/* TRACK_CONTENTION implies NAMED_SYNCHRO. */ #ifdef TRACK_CONTENTION # ifndef NAMED_SYNCHRO # define NAMED_SYNCHRO @@ -38,12 +39,6 @@ #endif /* DEBUG */ #ifdef DEBUG_THREADS -# ifndef THREAD_QUEUE -# define THREAD_QUEUE -# endif -# ifndef THREAD_NAMES -# define THREAD_NAMES -# endif # ifndef PROFILE_THREADS # define PROFILE_THREADS # endif @@ -56,25 +51,8 @@ #define THREAD_NAMES #endif -#if defined (PROFILE_THREADS) -# if !defined (THREAD_QUEUE) -# define THREAD_QUEUE -# endif -#endif - -#if defined (DEBUG_TRACK_SEM) -# if !defined (THREAD_QUEUE) -# define THREAD_QUEUE -# endif -# if !defined (THREAD_NAMES) -# define THREAD_NAMES -# endif -#endif - void InitThreadSystem (void); void UnInitThreadSystem (void); -void init_cond_bank (void); -void uninit_cond_bank (void); typedef int (*ThreadFunction) (void *); @@ -84,27 +62,45 @@ typedef void *Semaphore; typedef void *RecursiveMutex; typedef void *CondVar; +/* Local data associated with each thread */ +typedef struct _threadLocal { + Semaphore flushSem; +} ThreadLocal; + +/* The classes of synchronization objects */ + +enum +{ + SYNC_CLASS_TOPLEVEL = (1 << 0), /* Exposed to the game logic */ + SYNC_CLASS_AUDIO = (1 << 1), /* Involves the audio system */ + SYNC_CLASS_VIDEO = (1 << 2), /* Involves the video system. Very noisy because of FlushGraphics(). */ + SYNC_CLASS_RESOURCE = (1 << 3) /* Involves system resources (_MemoryLock) */ +}; + #ifdef NAMED_SYNCHRO +/* Logical OR of all classes we want to track. */ +#define TRACK_CONTENTION_CLASSES (SYNC_CLASS_TOPLEVEL) + /* 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); +Semaphore CreateSemaphore_Core (DWORD initial, const char *name, DWORD syncClass); +Mutex CreateMutex_Core (const char *name, DWORD syncClass); +RecursiveMutex CreateRecursiveMutex_Core (const char *name, DWORD syncClass); +CondVar CreateCondVar_Core (const char *name, DWORD syncClass); /* 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)) +#define CreateSemaphore(initial, name, syncClass) \ + CreateSemaphore_Core ((initial), (name), (syncClass)) +#define CreateMutex(name, syncClass) \ + CreateMutex_Core ((name), (syncClass)) +#define CreateRecursiveMutex(name, syncClass) \ + CreateRecursiveMutex_Core((name), (syncClass)) +#define CreateCondVar(name, syncClass) \ + CreateCondVar_Core ((name), (syncClass)) #else @@ -119,19 +115,23 @@ 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, syncClass) \ CreateThread_Core ((func), (data), (stackSize)) -#define CreateSemaphore(initial, name) \ +#define CreateSemaphore(initial, name, syncClass) \ CreateSemaphore_Core ((initial)) -#define CreateMutex() \ +#define CreateMutex(name, syncClass) \ CreateMutex_Core () -#define CreateRecursiveMutex(name) \ +#define CreateRecursiveMutex(name, syncClass) \ CreateRecursiveMutex_Core() -#define CreateCondVar(name) \ +#define CreateCondVar(name, syncClass) \ CreateCondVar_Core () #endif +ThreadLocal *CreateThreadLocal (void); +void DestroyThreadLocal (ThreadLocal *tl); +ThreadLocal *GetMyThreadLocal (void); + void SleepThread (TimePeriod timePeriod); void SleepThreadUntil (TimeCount wakeTime); void TaskSwitch (void); @@ -157,15 +157,8 @@ int GetRecursiveMutexDepth (RecursiveMutex m); void DestroyCondVar (CondVar); void WaitCondVar (CondVar); -void WaitProtectedCondVar (CondVar, Mutex); void SignalCondVar (CondVar); void BroadcastCondVar (CondVar); -DWORD CurrentThreadID (void); - -int FindSignalChannel (); -void WaitForSignal (int); -void SignalThread (DWORD); - #endif /* _THREADLIB_H */ diff --git a/sc2/src/sc2code/libs/threads/Makeinfo b/sc2/src/sc2code/libs/threads/Makeinfo index 1d7782d0c..eea248b06 100644 --- a/sc2/src/sc2code/libs/threads/Makeinfo +++ b/sc2/src/sc2code/libs/threads/Makeinfo @@ -1,2 +1,2 @@ uqm_SUBDIRS="sdl" -uqm_CFILES="condbank.c thrcommon.c" +uqm_CFILES="thrcommon.c" diff --git a/sc2/src/sc2code/libs/threads/condbank.c b/sc2/src/sc2code/libs/threads/condbank.c deleted file mode 100644 index 60f4f87ec..000000000 --- a/sc2/src/sc2code/libs/threads/condbank.c +++ /dev/null @@ -1,130 +0,0 @@ -//Copyright Paul Reiche, Fred Ford. 1992-2002 - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* This defines the Condition Variable Bank. Individual threads may - sleep until they are signaled by their Thread ID. If the Condition - Variable Bank is exhausted (unlikely!) it will wait on the DCQ's - condition variable. This may produce stuttering. */ - -#include -#include "starcon.h" -#include "libs/threadlib.h" - -/* The Condition Variable Bank. */ - -#define CONDVAR_BANK_SIZE 10 - -static Mutex bank_mutex; - -static struct { - CondVar var; - DWORD id; - int used; - Mutex control; -} bank[CONDVAR_BANK_SIZE]; - -void -init_cond_bank () -{ - int i; - bank_mutex = CreateMutex (); - for (i = 0; i < CONDVAR_BANK_SIZE; i++) - { - bank[i].var = CreateCondVar ("FlushGraphics Bank"); - bank[i].id = bank[i].used = 0; - bank[i].control = CreateMutex (); - } -} - -void -uninit_cond_bank () -{ - int i; - for (i = 0; i < CONDVAR_BANK_SIZE; i++) - { - DestroyCondVar (bank[i].var); - DestroyMutex (bank[i].control); - } - DestroyMutex (bank_mutex); -} - -int -FindSignalChannel () -{ - int i; - - LockMutex (bank_mutex); - for (i = 0; i < CONDVAR_BANK_SIZE; i++) - { - if (!bank[i].used) - { - LockMutex (bank[i].control); - return i; - } - } - return -1; -} - -void -WaitForSignal (int i) -{ - DWORD me = CurrentThreadID (); - - if (i == -1) - { - /* The bank is full! */ - fprintf(stderr, "Condvar bank is full, %lu is waiting on DCQ.\n", me); - UnlockMutex (bank_mutex); - WaitCondVar (RenderingCond); - } - else - { - bank[i].used = 1; - bank[i].id = me; - UnlockMutex (bank_mutex); - // fprintf (stderr, "Thread %lu waiting on cond var %d (control: %p)\n", me, i, bank[i].control); - WaitProtectedCondVar (bank[i].var, bank[i].control); - // fprintf (stderr, "Thread %lu signaled via cond var %d\n", me, i); - UnlockMutex (bank[i].control); - LockMutex (bank_mutex); - bank[i].used = bank[i].id = 0; - UnlockMutex (bank_mutex); - } -} - -void -SignalThread (DWORD id) -{ - int i; - LockMutex (bank_mutex); - for (i = 0; i < CONDVAR_BANK_SIZE; i++) - { - if (bank[i].used && bank[i].id == id) - { - UnlockMutex (bank_mutex); - // fprintf (stderr, "Blocking on var %d's control: %p\n", i, bank[i].control); - LockMutex (bank[i].control); - // fprintf (stderr, "Signaling var %d, thread %lu, control %p\n", i, id, bank[i].control); - SignalCondVar (bank[i].var); - UnlockMutex (bank[i].control); - return; - } - } - fprintf (stderr, "Warning: Couldn't find thread to signal!\n"); - UnlockMutex (bank_mutex); -} diff --git a/sc2/src/sc2code/libs/threads/sdl/sdlthreads.c b/sc2/src/sc2code/libs/threads/sdl/sdlthreads.c index cfb9930cb..6cf82c5a8 100644 --- a/sc2/src/sc2code/libs/threads/sdl/sdlthreads.c +++ b/sc2/src/sc2code/libs/threads/sdl/sdlthreads.c @@ -38,6 +38,7 @@ typedef struct _thread { #ifdef PROFILE_THREADS int startTime; #endif /* PROFILE_THREADS */ + ThreadLocal *localData; struct _thread *next; } *TrueThread; @@ -117,13 +118,11 @@ InitThreadSystem_SDL (void) #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 @@ -220,7 +219,7 @@ ThreadHelper (void *startInfo) { #endif UnQueueThread (thread); - + DestroyThreadLocal (thread->localData); HFree (thread); return result; } @@ -243,6 +242,8 @@ CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize thread->startTime = GetTimeCounter (); #endif + thread->localData = CreateThreadLocal (); + startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo)); startInfo->func = func; startInfo->data = data; @@ -252,6 +253,7 @@ CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize thread->native = SDL_CreateThread (ThreadHelper, (void *) startInfo); if (!(thread->native)) { + DestroyThreadLocal (thread->localData); HFree (startInfo); HFree (thread); return NULL; @@ -301,13 +303,112 @@ WaitThread_SDL (Thread thread, int *status) { SDL_WaitThread (((TrueThread)thread)->native, status); } +ThreadLocal * +GetMyThreadLocal_SDL (void) +{ + TrueThread t = FindThreadInfo (SDL_ThreadID ()); + return t ? t->localData : NULL; +} + /* 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. */ + * or HFree. Once that goes, this needs to change. */ + +typedef struct _mutex { + SDL_mutex *mutex; +#ifdef TRACK_CONTENTION + Uint32 owner; +#endif +#ifdef NAMED_SYNCHRO + const char *name; + DWORD syncClass; +#endif +} Mut; + + +Mutex +#ifdef NAMED_SYNCHRO +CreateMutex_SDL (const char *name, DWORD syncClass) +#else +CreateMutex_SDL (void) +#endif +{ + Mut *mutex = malloc (sizeof (Mut)); + if (mutex != NULL) + { + mutex->mutex = SDL_CreateMutex(); +#ifdef TRACK_CONTENTION + mutex->owner = 0; +#endif +#ifdef NAMED_SYNCHRO + mutex->name = name; + mutex->syncClass = syncClass; +#endif + } + + if ((mutex == NULL) || (mutex->mutex == NULL)) + { +#ifdef NAMED_SYNCHRO + fprintf (stderr, "Could not initialize mutex '%s': aborting.\n", name); +#else + fprintf (stderr, "Could not initialize mutex: aborting.\n"); +#endif + abort (); + } + + return mutex; +} + +void +DestroyMutex_SDL (Mutex m) +{ + Mut *mutex = (Mut *)m; + SDL_DestroyMutex (mutex->mutex); + free (mutex); +} + +void +LockMutex_SDL (Mutex m) +{ + Mut *mutex = (Mut *)m; +#ifdef TRACK_CONTENTION + /* This code isn't really quite right; race conditions between + * check and lock remain and can produce reports of contention + * where the thread never sleeps, or fail to report in + * situations where it does. If tracking with perfect + * accuracy becomes important, the TRACK_CONTENTION mutex will + * need to handle its own wake/sleep cycles with condition + * variables (check the history of this file for the + * CrossThreadMutex code). This almost-measure is being added + * because for the most part it should suffice. */ + if (mutex->owner && (mutex->syncClass & TRACK_CONTENTION_CLASSES)) + { + fprintf (stderr, "Thread '%s' blocking on mutex '%s'\n", MyThreadName (), mutex->name); + } +#endif + while (SDL_mutexP (mutex->mutex) != 0) + { + TaskSwitch_SDL (); + } +#ifdef TRACK_CONTENTION + mutex->owner = SDL_ThreadID (); +#endif +} + +void +UnlockMutex_SDL (Mutex m) +{ + Mut *mutex = (Mut *)m; +#ifdef TRACK_CONTENTION + mutex->owner = 0; +#endif + while (SDL_mutexV (mutex->mutex) != 0) + { + TaskSwitch_SDL (); + } +} /* Semaphores. */ @@ -315,21 +416,32 @@ typedef struct _sem { SDL_sem *sem; #ifdef NAMED_SYNCHRO const char *name; + DWORD syncClass; #endif } Sem; Semaphore CreateSemaphore_SDL (DWORD initial #ifdef NAMED_SYNCHRO - , const char *name + , const char *name, DWORD syncClass #endif ) { Sem *sem = (Sem *) HMalloc (sizeof (struct _sem)); #ifdef NAMED_SYNCHRO sem->name = name; + sem->syncClass = syncClass; #endif sem->sem = SDL_CreateSemaphore (initial); + if (sem->sem == NULL) + { +#ifdef NAMED_SYNCHRO + fprintf (stderr, "Could not initialize semaphore '%s': aborting.\n", name); +#else + fprintf (stderr, "Could not initialize semaphore: aborting.\n"); +#endif + abort (); + } return sem; } @@ -347,9 +459,9 @@ SetSemaphore_SDL (Semaphore s) Sem *sem = (Sem *)s; #ifdef TRACK_CONTENTION BOOLEAN contention = !(SDL_SemValue (sem->sem)); - if (contention) + if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES)) { - fprintf (stderr, "Thread '%s' goes to sleep, waiting on semaphore '%s'\n", MyThreadName (), sem->name); + fprintf (stderr, "Thread '%s' blocking on semaphore '%s'\n", MyThreadName (), sem->name); } #endif while (SDL_SemWait (sem->sem) == -1) @@ -357,7 +469,7 @@ SetSemaphore_SDL (Semaphore s) TaskSwitch_SDL (); } #ifdef TRACK_CONTENTION - if (contention) + if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES)) { fprintf (stderr, "Thread '%s' awakens, released from semaphore '%s'\n", MyThreadName (), sem->name); } @@ -383,12 +495,13 @@ typedef struct _recm { Uint32 locks; #ifdef NAMED_SYNCHRO const char *name; + DWORD syncClass; #endif } RecM; RecursiveMutex #ifdef NAMED_SYNCHRO -CreateRecursiveMutex_SDL (const char *name) +CreateRecursiveMutex_SDL (const char *name, DWORD syncClass) #else CreateRecursiveMutex_SDL (void) #endif @@ -397,8 +510,18 @@ CreateRecursiveMutex_SDL (void) mtx->thread_id = 0; mtx->mutex = SDL_CreateMutex (); + if (mtx->mutex == NULL) + { +#ifdef NAMED_SYNCHRO + fprintf (stderr, "Could not initialize recursive mutex '%s': aborting.\n", name); +#else + fprintf (stderr, "Could not initialize recursive mutex: aborting.\n"); +#endif + abort (); + } #ifdef NAMED_SYNCHRO mtx->name = name; + mtx->syncClass = syncClass; #endif mtx->locks = 0; return (RecursiveMutex) mtx; @@ -420,7 +543,7 @@ LockRecursiveMutex_SDL (RecursiveMutex val) if (mtx->thread_id != thread_id) { #ifdef TRACK_CONTENTION - if (mtx->thread_id) + if (mtx->thread_id && (mtx->syncClass & TRACK_CONTENTION_CLASSES)) { fprintf (stderr, "Thread '%s' blocking on '%s'\n", MyThreadName (), mtx->name); } @@ -466,12 +589,13 @@ typedef struct _cond { SDL_mutex *mutex; #ifdef NAMED_SYNCHRO const char *name; + DWORD syncClass; #endif } cvar; CondVar #ifdef NAMED_SYNCHRO -CreateCondVar_SDL (const char *name) +CreateCondVar_SDL (const char *name, DWORD syncClass) #else CreateCondVar_SDL (void) #endif @@ -479,8 +603,18 @@ CreateCondVar_SDL (void) cvar *cv = (cvar *) HMalloc (sizeof (cvar)); cv->cond = SDL_CreateCond (); cv->mutex = SDL_CreateMutex (); + if ((cv->cond == NULL) || (cv->mutex == NULL)) + { +#ifdef NAMED_SYNCHRO + fprintf (stderr, "Could not initialize condition variable '%s': aborting.\n", name); +#else + fprintf (stderr, "Could not initialize condition variable: aborting.\n"); +#endif + abort (); + } #ifdef NAMED_SYNCHRO cv->name = name; + cv->syncClass = syncClass; #endif return cv; } @@ -500,33 +634,24 @@ WaitCondVar_SDL (CondVar c) cvar *cv = (cvar *) c; SDL_mutexP (cv->mutex); #ifdef TRACK_CONTENTION - fprintf (stderr, "Thread '%s' waiting for signal from '%s'\n", MyThreadName (), cv->name); + if (cv->syncClass & TRACK_CONTENTION_CLASSES) + { + fprintf (stderr, "Thread '%s' waiting for signal from '%s'\n", MyThreadName (), cv->name); + } #endif while (SDL_CondWait (cv->cond, cv->mutex) != 0) { TaskSwitch_SDL (); } #ifdef TRACK_CONTENTION - fprintf (stderr, "Thread '%s' received signal from '%s', awakening.\n", MyThreadName (), cv->name); + if (cv->syncClass & TRACK_CONTENTION_CLASSES) + { + 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) { diff --git a/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h b/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h index 728eecc85..1a5f7fb5b 100644 --- a/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h +++ b/sc2/src/sc2code/libs/threads/sdl/sdlthreads.h @@ -25,51 +25,42 @@ #include "libs/threadlib.h" #include "libs/timelib.h" -#define NativeGetThreadID(thread) SDL_GetThreadID ((thread)) -#define NativeThreadID() SDL_ThreadID () - -typedef SDL_mutex *NativeMutex; -#define NativeCreateMutex() \ - SDL_CreateMutex () -#define NativeDestroyMutex(mutex) \ - SDL_DestroyMutex ((mutex)) -#define NativeLockMutex(mutex) \ - SDL_mutexP ((mutex)) -#define NativeUnlockMutex(mutex) \ - SDL_mutexV ((mutex)) - -#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); +Mutex CreateMutex_SDL (const char *name, DWORD syncClass); +Semaphore CreateSemaphore_SDL (DWORD initial, const char *name, DWORD syncClass); +RecursiveMutex CreateRecursiveMutex_SDL (const char *name, DWORD syncClass); +CondVar CreateCondVar_SDL (const char *name, DWORD syncClass); #else /* Prototypes without the "name" field. */ Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize); +Mutex CreateMutex_SDL (void); Semaphore CreateSemaphore_SDL (DWORD initial); RecursiveMutex CreateRecursiveMutex_SDL (void); CondVar CreateCondVar_SDL (void); #endif +ThreadLocal *GetMyThreadLocal_SDL (void); + void SleepThread_SDL (TimeCount sleepTime); void SleepThreadUntil_SDL (TimeCount wakeTime); void TaskSwitch_SDL (void); void WaitThread_SDL (Thread thread, int *status); +void DestroyMutex_SDL (Mutex m); +void LockMutex_SDL (Mutex m); +void UnlockMutex_SDL (Mutex m); + 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); @@ -81,12 +72,19 @@ int GetRecursiveMutexDepth_SDL (RecursiveMutex m); #define NativeInitThreadSystem InitThreadSystem_SDL #define NativeUnInitThreadSystem UnInitThreadSystem_SDL +#define NativeGetMyThreadLocal GetMyThreadLocal_SDL + #define NativeCreateThread CreateThread_SDL #define NativeSleepThread SleepThread_SDL #define NativeSleepThreadUntil SleepThreadUntil_SDL #define NativeTaskSwitch TaskSwitch_SDL #define NativeWaitThread WaitThread_SDL +#define NativeCreateMutex CreateMutex_SDL +#define NativeDestroyMutex DestroyMutex_SDL +#define NativeLockMutex LockMutex_SDL +#define NativeUnlockMutex UnlockMutex_SDL + #define NativeCreateSemaphore CreateSemaphore_SDL #define NativeDestroySemaphore DestroySemaphore_SDL #define NativeSetSemaphore SetSemaphore_SDL @@ -95,7 +93,6 @@ int GetRecursiveMutexDepth_SDL (RecursiveMutex m); #define NativeCreateCondVar CreateCondVar_SDL #define NativeDestroyCondVar DestroyCondVar_SDL #define NativeWaitCondVar WaitCondVar_SDL -#define NativeWaitProtectedCondVar WaitProtectedCondVar_SDL #define NativeSignalCondVar SignalCondVar_SDL #define NativeBroadcastCondVar BroadcastCondVar_SDL diff --git a/sc2/src/sc2code/libs/threads/thrcommon.c b/sc2/src/sc2code/libs/threads/thrcommon.c index 50275953b..7afb8bf01 100644 --- a/sc2/src/sc2code/libs/threads/thrcommon.c +++ b/sc2/src/sc2code/libs/threads/thrcommon.c @@ -46,27 +46,27 @@ CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char } Mutex -CreateMutex_Core (void) +CreateMutex_Core (const char *name, DWORD syncClass) { - return (Mutex) NativeCreateMutex (); + return NativeCreateMutex (name, syncClass); } Semaphore -CreateSemaphore_Core (DWORD initial, const char *name) +CreateSemaphore_Core (DWORD initial, const char *name, DWORD syncClass) { - return NativeCreateSemaphore (initial, name); + return NativeCreateSemaphore (initial, name, syncClass); } RecursiveMutex -CreateRecursiveMutex_Core (const char *name) +CreateRecursiveMutex_Core (const char *name, DWORD syncClass) { - return NativeCreateRecursiveMutex (name); + return NativeCreateRecursiveMutex (name, syncClass); } CondVar -CreateCondVar_Core (const char *name) +CreateCondVar_Core (const char *name, DWORD syncClass) { - return NativeCreateCondVar (name); + return NativeCreateCondVar (name, syncClass); } #else @@ -80,7 +80,7 @@ CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize) Mutex CreateMutex_Core (void) { - return (Mutex) NativeCreateMutex (); + return NativeCreateMutex (); } Semaphore @@ -102,6 +102,27 @@ CreateCondVar_Core (void) } #endif +ThreadLocal * +CreateThreadLocal (void) +{ + ThreadLocal *tl = HMalloc (sizeof (ThreadLocal)); + tl->flushSem = CreateSemaphore (0, "FlushGraphics", SYNC_CLASS_VIDEO); + return tl; +} + +void +DestroyThreadLocal (ThreadLocal *tl) +{ + DestroySemaphore (tl->flushSem); + HFree (tl); +} + +ThreadLocal * +GetMyThreadLocal (void) +{ + return NativeGetMyThreadLocal (); +} + void WaitThread (Thread thread, int *status) { @@ -129,22 +150,19 @@ TaskSwitch (void) void DestroyMutex (Mutex sem) { - NativeDestroyMutex ((NativeMutex) sem); + NativeDestroyMutex (sem); } void LockMutex (Mutex sem) { - while (NativeLockMutex ((NativeMutex) sem) == -1) - { - TaskSwitch (); - } + NativeLockMutex (sem); } void UnlockMutex (Mutex sem) { - NativeUnlockMutex ((NativeMutex) sem); + NativeUnlockMutex (sem); } void @@ -177,12 +195,6 @@ WaitCondVar (CondVar cv) NativeWaitCondVar (cv); } -void -WaitProtectedCondVar (CondVar cv, Mutex m) -{ - NativeWaitProtectedCondVar (cv, m); -} - void SignalCondVar (CondVar cv) { @@ -195,12 +207,6 @@ BroadcastCondVar (CondVar cv) NativeBroadcastCondVar (cv); } -DWORD -CurrentThreadID () -{ - return (DWORD)NativeThreadID (); -} - void DestroyRecursiveMutex (RecursiveMutex mutex) { diff --git a/sc2/src/sc2code/libs/video/sdl/sdlvideo.c b/sc2/src/sc2code/libs/video/sdl/sdlvideo.c index b75277cc5..07f8bf45e 100644 --- a/sc2/src/sc2code/libs/video/sdl/sdlvideo.c +++ b/sc2/src/sc2code/libs/video/sdl/sdlvideo.c @@ -37,7 +37,7 @@ TFB_CreateVideoImage (int w, int h) SDL_PixelFormat* fmt = SDL_Video->format; img = HMalloc (sizeof (TFB_Image)); - img->mutex = CreateMutex (); + img->mutex = CreateMutex ("vid image lock", SYNC_CLASS_VIDEO); img->ScaledImg = NULL; img->MipmapImg = NULL; img->colormap_index = -1; diff --git a/sc2/src/sc2code/libs/video/video.c b/sc2/src/sc2code/libs/video/video.c index a57cb4207..1bc4b2d03 100644 --- a/sc2/src/sc2code/libs/video/video.c +++ b/sc2/src/sc2code/libs/video/video.c @@ -127,8 +127,8 @@ _init_video_file(PVOID pStr) vid->length = dec->length; vid->w = vid->decoder->w; vid->h = vid->decoder->h; - vid->guard = CreateMutex (); - vid->frame_lock = CreateCondVar ("frame lock"); + vid->guard = CreateMutex ("video guard", SYNC_CLASS_VIDEO); + vid->frame_lock = CreateCondVar ("frame lock", SYNC_CLASS_VIDEO); return (VIDEO_REF) vid; } diff --git a/sc2/src/sc2code/libs/video/vidplayer.c b/sc2/src/sc2code/libs/video/vidplayer.c index 1c4afdefb..c31a85d22 100644 --- a/sc2/src/sc2code/libs/video/vidplayer.c +++ b/sc2/src/sc2code/libs/video/vidplayer.c @@ -295,7 +295,7 @@ TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y) // creation probably needs better handling if (!vp_interthread_lock) - vp_interthread_lock = CreateSemaphore (1, "inter-thread param lock"); + vp_interthread_lock = CreateSemaphore (1, "inter-thread param lock", SYNC_CLASS_VIDEO); SetSemaphore (vp_interthread_lock); vp_interthread_clip = vid; diff --git a/sc2/src/sc2code/sis.c b/sc2/src/sc2code/sis.c index e6e11e76d..5a30b391f 100644 --- a/sc2/src/sc2code/sis.c +++ b/sc2/src/sc2code/sis.c @@ -1354,7 +1354,7 @@ SetFlashRect (PRECT pRect, FRAME f) int create_flash = 0; if (! flash_mutex) - flash_mutex = CreateMutex (); + flash_mutex = CreateMutex ("FlashRect Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); old_r = flash_rect; old_f = flash_frame; diff --git a/sc2/src/starcon2.c b/sc2/src/starcon2.c index 0130b00f9..02b807093 100644 --- a/sc2/src/starcon2.c +++ b/sc2/src/starcon2.c @@ -350,8 +350,8 @@ main (int argc, char *argv[]) InitTimeSystem (); InitTaskSystem (); - GraphicsLock = CreateMutex (/*"Graphics"*/); - RenderingCond = CreateCondVar ("DCQ empty"); + GraphicsLock = CreateMutex ("Graphics", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); + RenderingCond = CreateCondVar ("DCQ empty", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); TFB_InitGraphics (gfxdriver, gfxflags, width, height, bpp); init_communication ();