diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index 8f86cb48..b3ebf156 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -614,6 +614,7 @@ set(src_sys_dedicated sys/linux/dedicated.cpp) if (APPLE) set(src_sys_base + sys/threads.cpp sys/sys_local.cpp sys/posix/posix_net.cpp sys/posix/posix_signal.cpp @@ -635,6 +636,7 @@ if (APPLE) ) elseif (WIN32) set(src_sys_base + sys/threads.cpp sys/sys_local.cpp sys/win32/win_cpu.cpp # sys/win32/win_gamma.cpp @@ -654,6 +656,7 @@ elseif (WIN32) ) else() set(src_sys_base + sys/threads.cpp sys/sys_local.cpp sys/posix/posix_net.cpp sys/posix/posix_signal.cpp diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 01903b20..8a54fbc7 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -2783,6 +2783,8 @@ idCommonLocal::Init ================= */ void idCommonLocal::Init( int argc, char **argv ) { + Sys_InitThreads(); + try { // set interface pointers used by idLib @@ -2928,6 +2930,8 @@ void idCommonLocal::Shutdown( void ) { // shutdown idLib idLib::ShutDown(); + + Sys_ShutdownThreads(); } /* diff --git a/neo/sys/posix/posix_threads.cpp b/neo/sys/posix/posix_threads.cpp index 07cd6411..5e2fef00 100644 --- a/neo/sys/posix/posix_threads.cpp +++ b/neo/sys/posix/posix_threads.cpp @@ -45,105 +45,6 @@ If you have questions concerning this license or the applicable additional terms // #define ID_VERBOSE_PTHREADS #endif -/* -====================================================== -locks -====================================================== -*/ - -// we use an extra lock for the local stuff -const int MAX_LOCAL_CRITICAL_SECTIONS = MAX_CRITICAL_SECTIONS + 1; -static pthread_mutex_t global_lock[ MAX_LOCAL_CRITICAL_SECTIONS ]; - -/* -================== -Sys_EnterCriticalSection -================== -*/ -void Sys_EnterCriticalSection( int index ) { - assert( index >= 0 && index < MAX_LOCAL_CRITICAL_SECTIONS ); -#ifdef ID_VERBOSE_PTHREADS - if ( pthread_mutex_trylock( &global_lock[index] ) == EBUSY ) { - Sys_Printf( "busy lock %d in thread '%s'\n", index, Sys_GetThreadName() ); - if ( pthread_mutex_lock( &global_lock[index] ) == EDEADLK ) { - Sys_Printf( "FATAL: DEADLOCK %d, in thread '%s'\n", index, Sys_GetThreadName() ); - } - } -#else - pthread_mutex_lock( &global_lock[index] ); -#endif -} - -/* -================== -Sys_LeaveCriticalSection -================== -*/ -void Sys_LeaveCriticalSection( int index ) { - assert( index >= 0 && index < MAX_LOCAL_CRITICAL_SECTIONS ); -#ifdef ID_VERBOSE_PTHREADS - if ( pthread_mutex_unlock( &global_lock[index] ) == EPERM ) { - Sys_Printf( "FATAL: NOT LOCKED %d, in thread '%s'\n", index, Sys_GetThreadName() ); - } -#else - pthread_mutex_unlock( &global_lock[index] ); -#endif -} - -/* -====================================================== -wait and trigger events -we use a single lock to manipulate the conditions, MAX_LOCAL_CRITICAL_SECTIONS-1 - -the semantics match the win32 version. signals raised while no one is waiting stay raised until a wait happens (which then does a simple pass-through) - -NOTE: we use the same mutex for all the events. I don't think this would become much of a problem -cond_wait unlocks atomically with setting the wait condition, and locks it back before exiting the function -the potential for time wasting lock waits is very low -====================================================== -*/ - -pthread_cond_t event_cond[ MAX_TRIGGER_EVENTS ]; -bool signaled[ MAX_TRIGGER_EVENTS ]; -bool waiting[ MAX_TRIGGER_EVENTS ]; - -/* -================== -Sys_WaitForEvent -================== -*/ -void Sys_WaitForEvent( int index ) { - assert( index >= 0 && index < MAX_TRIGGER_EVENTS ); - Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 ); - assert( !waiting[ index ] ); // WaitForEvent from multiple threads? that wouldn't be good - if ( signaled[ index ] ) { - // emulate windows behaviour: signal has been raised already. clear and keep going - signaled[ index ] = false; - } else { - waiting[ index ] = true; - pthread_cond_wait( &event_cond[ index ], &global_lock[ MAX_LOCAL_CRITICAL_SECTIONS - 1 ] ); - waiting[ index ] = false; - } - Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 ); -} - -/* -================== -Sys_TriggerEvent -================== -*/ -void Sys_TriggerEvent( int index ) { - assert( index >= 0 && index < MAX_TRIGGER_EVENTS ); - Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 ); - if ( waiting[ index ] ) { - pthread_cond_signal( &event_cond[ index ] ); - } else { - // emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going - signaled[ index ] = true; - } - Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 ); -} - /* ====================================================== thread create and destroy @@ -268,22 +169,6 @@ Posix_InitPThreads */ void Posix_InitPThreads( ) { int i; - pthread_mutexattr_t attr; - - // init critical sections - for ( i = 0; i < MAX_LOCAL_CRITICAL_SECTIONS; i++ ) { - pthread_mutexattr_init( &attr ); - pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK ); - pthread_mutex_init( &global_lock[i], &attr ); - pthread_mutexattr_destroy( &attr ); - } - - // init event sleep/triggers - for ( i = 0; i < MAX_TRIGGER_EVENTS; i++ ) { - pthread_cond_init( &event_cond[ i ], NULL ); - signaled[i] = false; - waiting[i] = false; - } // init threads table for ( i = 0; i < MAX_THREADS; i++ ) { diff --git a/neo/sys/scons/SConscript.core b/neo/sys/scons/SConscript.core index ff26403b..b0785ebb 100644 --- a/neo/sys/scons/SConscript.core +++ b/neo/sys/scons/SConscript.core @@ -193,6 +193,7 @@ ui_list = scons_utils.BuildList( 'ui', ui_string ) sys_string = ' \ sys_local.cpp \ + threads.cpp \ posix/posix_net.cpp \ posix/posix_main.cpp \ posix/posix_signal.cpp \ diff --git a/neo/sys/sys_public.h b/neo/sys/sys_public.h index 6813baee..f583ca21 100644 --- a/neo/sys/sys_public.h +++ b/neo/sys/sys_public.h @@ -383,13 +383,17 @@ void Sys_DestroyThread( xthreadInfo& info ); // sets threadHandle back to 0 // if index != NULL, set the index in g_threads array (use -1 for "main" thread) const char * Sys_GetThreadName( int *index = 0 ); -const int MAX_CRITICAL_SECTIONS = 4; +extern void Sys_InitThreads(); +extern void Sys_ShutdownThreads(); + +const int MAX_CRITICAL_SECTIONS = 5; enum { CRITICAL_SECTION_ZERO = 0, CRITICAL_SECTION_ONE, CRITICAL_SECTION_TWO, - CRITICAL_SECTION_THREE + CRITICAL_SECTION_THREE, + CRITICAL_SECTION_SYS }; void Sys_EnterCriticalSection( int index = CRITICAL_SECTION_ZERO ); diff --git a/neo/sys/threads.cpp b/neo/sys/threads.cpp new file mode 100644 index 00000000..c2b64073 --- /dev/null +++ b/neo/sys/threads.cpp @@ -0,0 +1,172 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code 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 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code 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 Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include + +#include "sys/platform.h" +#include "framework/Common.h" + +#include "sys/sys_public.h" + +static SDL_mutex *mutex[MAX_CRITICAL_SECTIONS] = { }; +static SDL_cond *cond[MAX_TRIGGER_EVENTS] = { }; +static bool signaled[MAX_TRIGGER_EVENTS] = { }; +static bool waiting[MAX_TRIGGER_EVENTS] = { }; + +/* +================== +Sys_InitThreads +================== +*/ +void Sys_InitThreads() { + // critical sections + for (int i = 0; i < MAX_CRITICAL_SECTIONS; i++) { + mutex[i] = SDL_CreateMutex(); + + if (!mutex[i]) { + Sys_Printf("ERROR: SDL_CreateMutex failed\n"); + return; + } + } + + // events + for (int i = 0; i < MAX_TRIGGER_EVENTS; i++) { + cond[i] = SDL_CreateCond(); + + if (!cond[i]) { + Sys_Printf("ERROR: SDL_CreateCond failed\n"); + return; + } + + signaled[i] = false; + waiting[i] = false; + } +} + +/* +================== +Sys_ShutdownThreads +================== +*/ +void Sys_ShutdownThreads() { + // events + for (int i = 0; i < MAX_TRIGGER_EVENTS; i++) { + SDL_DestroyCond(cond[i]); + cond[i] = NULL; + signaled[i] = false; + waiting[i] = false; + } + + // critical sections + for (int i = 0; i < MAX_CRITICAL_SECTIONS; i++) { + SDL_DestroyMutex(mutex[i]); + mutex[i] = NULL; + } +} + +/* +================== +Sys_EnterCriticalSection +================== +*/ +void Sys_EnterCriticalSection(int index) { + assert(index >= 0 && index < MAX_CRITICAL_SECTIONS); + + if (SDL_LockMutex(mutex[index]) != 0) + common->Error("ERROR: SDL_LockMutex failed\n"); +} + +/* +================== +Sys_LeaveCriticalSection +================== +*/ +void Sys_LeaveCriticalSection(int index) { + assert(index >= 0 && index < MAX_CRITICAL_SECTIONS); + + if (SDL_UnlockMutex(mutex[index]) != 0) + common->Error("ERROR: SDL_UnlockMutex failed\n"); +} + +/* +====================================================== +wait and trigger events +we use a single lock to manipulate the conditions, CRITICAL_SECTION_SYS + +the semantics match the win32 version. signals raised while no one is waiting stay raised until a wait happens (which then does a simple pass-through) + +NOTE: we use the same mutex for all the events. I don't think this would become much of a problem +cond_wait unlocks atomically with setting the wait condition, and locks it back before exiting the function +the potential for time wasting lock waits is very low +====================================================== +*/ + +/* +================== +Sys_WaitForEvent +================== +*/ +void Sys_WaitForEvent(int index) { + assert(index >= 0 && index < MAX_TRIGGER_EVENTS); + + Sys_EnterCriticalSection(CRITICAL_SECTION_SYS); + + assert(!waiting[index]); // WaitForEvent from multiple threads? that wouldn't be good + if (signaled[index]) { + // emulate windows behaviour: signal has been raised already. clear and keep going + signaled[index] = false; + } else { + waiting[index] = true; + if (SDL_CondWait(cond[index], mutex[CRITICAL_SECTION_SYS]) != 0) + common->Error("ERROR: SDL_CondWait failed\n"); + waiting[index] = false; + } + + Sys_LeaveCriticalSection(CRITICAL_SECTION_SYS); +} + +/* +================== +Sys_TriggerEvent +================== +*/ +void Sys_TriggerEvent(int index) { + assert(index >= 0 && index < MAX_TRIGGER_EVENTS); + + Sys_EnterCriticalSection(CRITICAL_SECTION_SYS); + + if (waiting[index]) { + if (SDL_CondSignal(cond[index]) != 0) + common->Error("ERROR: SDL_CondSignal failed\n"); + } else { + // emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going + signaled[index] = true; + } + + Sys_LeaveCriticalSection(CRITICAL_SECTION_SYS); +} diff --git a/neo/sys/win32/win_local.h b/neo/sys/win32/win_local.h index df29ba6b..30bcda3b 100644 --- a/neo/sys/win32/win_local.h +++ b/neo/sys/win32/win_local.h @@ -165,9 +165,6 @@ struct Win32Vars_t { static idCVar win_timerUpdate; static idCVar win_allowMultipleInstances; - CRITICAL_SECTION criticalSections[MAX_CRITICAL_SECTIONS]; - HANDLE backgroundDownloadSemaphore; - HINSTANCE hInstDI; // direct input LPDIRECTINPUT8 g_pdi; diff --git a/neo/sys/win32/win_main.cpp b/neo/sys/win32/win_main.cpp index 0c46a35b..dd09a649 100644 --- a/neo/sys/win32/win_main.cpp +++ b/neo/sys/win32/win_main.cpp @@ -157,56 +157,6 @@ const char* Sys_GetThreadName(int *index) { return "main"; } - -/* -================== -Sys_EnterCriticalSection -================== -*/ -void Sys_EnterCriticalSection( int index ) { - assert( index >= 0 && index < MAX_CRITICAL_SECTIONS ); - if ( TryEnterCriticalSection( &win32.criticalSections[index] ) == 0 ) { - EnterCriticalSection( &win32.criticalSections[index] ); -// Sys_DebugPrintf( "busy lock '%s' in thread '%s'\n", lock->name, Sys_GetThreadName() ); - } -} - -/* -================== -Sys_LeaveCriticalSection -================== -*/ -void Sys_LeaveCriticalSection( int index ) { - assert( index >= 0 && index < MAX_CRITICAL_SECTIONS ); - LeaveCriticalSection( &win32.criticalSections[index] ); -} - -/* -================== -Sys_WaitForEvent -================== -*/ -void Sys_WaitForEvent( int index ) { - assert( index == 0 ); - if ( !win32.backgroundDownloadSemaphore ) { - win32.backgroundDownloadSemaphore = CreateEvent( NULL, TRUE, FALSE, NULL ); - } - WaitForSingleObject( win32.backgroundDownloadSemaphore, INFINITE ); - ResetEvent( win32.backgroundDownloadSemaphore ); -} - -/* -================== -Sys_TriggerEvent -================== -*/ -void Sys_TriggerEvent( int index ) { - assert( index == 0 ); - SetEvent( win32.backgroundDownloadSemaphore ); -} - - - #pragma optimize( "", on ) #ifdef DEBUG @@ -1235,10 +1185,6 @@ int main(int argc, char *argv[]) { // no abort/retry/fail errors SetErrorMode( SEM_FAILCRITICALERRORS ); - for ( int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) { - InitializeCriticalSection( &win32.criticalSections[i] ); - } - // get the initial time base Sys_Milliseconds();