diff --git a/CMakeLists.txt b/CMakeLists.txt index f463cf0a7..951cc086d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ OPTION(BUILD_LIBRARY "Build the library that goes into DF." ON) OPTION(BUILD_PLUGINS "Build the plugins." ON) IF(BUILD_LIBRARY) + #add_subdirectory (dlib) add_subdirectory (library) ## install the default documentation files install(FILES LICENSE Readme.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index b890715e8..f3d9eb302 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -7,8 +7,8 @@ OPTION(BUILD_DOXYGEN "Create/install/package doxygen documentation for DFHack (F include_directories (include) include_directories (depends/md5) -include_directories (depends/libnoise) include_directories (depends/tinyxml) +include_directories (depends/tthread) include_directories (private) SET(PROJECT_HDRS_INTERNAL @@ -68,6 +68,8 @@ depends/tinyxml/tinyxml.cpp depends/tinyxml/tinyxmlerror.cpp depends/tinyxml/tinyxmlparser.cpp +depends/tthread/tinythread.cpp + modules/Buildings.cpp modules/Constructions.cpp modules/Creatures.cpp diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index f96972a90..c32a9065f 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -46,7 +46,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "dfhack/Console.h" #include #include #include @@ -61,9 +60,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include + +#include "dfhack/Console.h" +#include "dfhack/FakeSDL.h" using namespace DFHack; +#include "tinythread.h" +using namespace tthread; + static int isUnsupportedTerm(void) { static const char *unsupported_term[] = {"dumb","cons25",NULL}; @@ -263,7 +267,7 @@ namespace DFHack /// beep. maybe? //void beep (void); /// A simple line edit (raw mode) - int lineedit(const std::string& prompt, std::string& output, SDL::Mutex * lock) + int lineedit(const std::string& prompt, std::string& output, mutex * lock) { output.clear(); this->prompt = prompt; @@ -377,7 +381,7 @@ namespace DFHack if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; } - int prompt_loop(SDL::Mutex * lock) + int prompt_loop(mutex * lock) { int fd = STDIN_FILENO; size_t plen = prompt.size(); @@ -394,9 +398,9 @@ namespace DFHack char c; int nread; char seq[2], seq2; - SDL_mutexV(lock); + lock->unlock(); nread = ::read(fd,&c,1); - SDL_mutexP(lock); + lock->lock(); if (nread <= 0) return raw_buffer.size(); /* Only autocomplete when the callback is set. It returns < 0 when @@ -440,13 +444,13 @@ namespace DFHack } break; case 27: // escape sequence - SDL_mutexV(lock); + lock->unlock(); if (::read(fd,seq,2) == -1) { - SDL_mutexP(lock); + lock->lock(); break; } - SDL_mutexP(lock); + lock->lock(); if(seq[0] == '[') { if (seq[1] == 'D') @@ -508,13 +512,13 @@ namespace DFHack else if (seq[1] > '0' && seq[1] < '7') { // extended escape - SDL_mutexV(lock); + lock->unlock(); if (::read(fd,&seq2,1) == -1) { - SDL_mutexP(lock); - break; + lock->lock(); + return -1; } - SDL_mutexP(lock); + lock->lock(); if (seq[1] == '3' && seq2 == '~' ) { // delete @@ -604,7 +608,7 @@ Console::~Console() if(inited) shutdown(); if(wlock) - SDL_DestroyMutex(wlock); + delete wlock; if(d) delete d; } @@ -614,7 +618,7 @@ bool Console::init(void) d = new Private(); // make our own weird streams so our IO isn't redirected d->dfout_C = fopen("/dev/tty", "w"); - wlock = SDL_CreateMutex(); + wlock = new mutex(); rdbuf(d); std::cin.tie(this); clear(); @@ -624,19 +628,18 @@ bool Console::init(void) bool Console::shutdown(void) { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(d->rawmode) d->disable_raw(); - print("\n"); + d->print("\n"); inited = false; - SDL_mutexV(wlock); return true; } int Console::print( const char* format, ... ) { va_list args; - SDL_mutexP(wlock); + lock_guard g(*wlock); int ret; if(!inited) ret = -1; else @@ -645,14 +648,13 @@ int Console::print( const char* format, ... ) ret = d->vprint(format, args); va_end(args); } - SDL_mutexV(wlock); return ret; } int Console::printerr( const char* format, ... ) { va_list args; - SDL_mutexP(wlock); + lock_guard g(*wlock); int ret; if(!inited) ret = -1; else @@ -661,86 +663,76 @@ int Console::printerr( const char* format, ... ) ret = d->vprinterr(format, args); va_end(args); } - SDL_mutexV(wlock); return ret; } int Console::get_columns(void) { - SDL_mutexP(wlock); + lock_guard g(*wlock); int ret = -1; if(inited) ret = d->get_columns(); - SDL_mutexV(wlock); return ret; } int Console::get_rows(void) { - SDL_mutexP(wlock); + lock_guard g(*wlock); int ret = -1; if(inited) ret = d->get_rows(); - SDL_mutexV(wlock); return ret; } void Console::clear() { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(inited) d->clear(); - SDL_mutexV(wlock); } void Console::gotoxy(int x, int y) { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(inited) d->gotoxy(x,y); - SDL_mutexV(wlock); } void Console::color(color_value index) { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(inited) d->color(index); - SDL_mutexV(wlock); } void Console::reset_color( void ) { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(inited) d->reset_color(); - SDL_mutexV(wlock); } void Console::cursor(bool enable) { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(inited) d->cursor(enable); - SDL_mutexV(wlock); } // push to front, remove from back if we are above maximum. ignore immediate duplicates void Console::history_add(const std::string & command) { - SDL_mutexP(wlock); + lock_guard g(*wlock); if(inited) d->history_add(command); - SDL_mutexV(wlock); } int Console::lineedit(const std::string & prompt, std::string & output) { - SDL_mutexP(wlock); + lock_guard g(*wlock); int ret = -2; if(inited) ret = d->lineedit(prompt,output,wlock); - SDL_mutexV(wlock); return ret; } diff --git a/library/Core.cpp b/library/Core.cpp index 3b4b03785..8c1505130 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -44,43 +44,46 @@ using namespace std; #include "ModuleFactory.h" #include "dfhack/modules/Gui.h" #include "dfhack/modules/World.h" +using namespace DFHack; #include "dfhack/SDL_fakes/events.h" #include #include #include -using namespace DFHack; +#include "tinythread.h" +using namespace tthread; - struct Core::Cond + +struct Core::Cond +{ + Cond() { - Cond() - { - predicate = false; - wakeup = SDL_CreateCond(); - } - ~Cond() - { - SDL_DestroyCond(wakeup); - } - bool Lock(SDL::Mutex * m) - { - while(!predicate) - { - SDL_CondWait(wakeup,m); - } - predicate = false; - return true; - } - bool Unlock() + predicate = false; + wakeup = new tthread::condition_variable(); + } + ~Cond() + { + delete wakeup; + } + bool Lock(tthread::mutex * m) + { + while(!predicate) { - predicate = true; - SDL_CondSignal(wakeup); - return true; + wakeup->wait(*m); } - SDL::Cond * wakeup; - bool predicate; - }; + predicate = false; + return true; + } + bool Unlock() + { + predicate = true; + wakeup->notify_one(); + return true; + } + tthread::condition_variable * wakeup; + bool predicate; +}; void cheap_tokenise(string const& input, vector &output) { @@ -98,14 +101,14 @@ struct IODATA // A thread function... for handling hotkeys. This is needed because // all the plugin commands are expected to be run from foreign threads. // Running them from one of the main DF threads will result in deadlock! -int fHKthread(void * iodata) +void fHKthread(void * iodata) { Core * core = ((IODATA*) iodata)->core; PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr; if(plug_mgr == 0 || core == 0) { cerr << "Hotkey thread has croaked." << endl; - return 0; + return; } while(1) { @@ -119,7 +122,7 @@ int fHKthread(void * iodata) } // A thread function... for the interactive console. -int fIOthread(void * iodata) +void fIOthread(void * iodata) { IODATA * iod = ((IODATA*) iodata); Core * core = iod->core; @@ -128,7 +131,7 @@ int fIOthread(void * iodata) if(plug_mgr == 0 || core == 0) { con.printerr("Something horrible happened in Core's constructor...\n"); - return 0; + return; } con.print("DFHack is ready. Have a nice day!\n" "Type in '?' or 'help' for general help, 'ls' to see all commands.\n"); @@ -140,7 +143,7 @@ int fIOthread(void * iodata) if(ret == -2) { cerr << "Console is shutting down properly." << endl; - return 0; + return; } else if(ret == -1) { @@ -371,6 +374,7 @@ Core::Core() // create mutex for syncing with interactive tasks AccessMutex = 0; + StackMutex = 0; core_cond = 0; // set up hotkey capture memset(hotkey_states,0,sizeof(hotkey_states)); @@ -395,34 +399,21 @@ bool Core::Init() return false; } vinfo = p->getDescriptor(); - // create mutex for syncing with interactive tasks - AccessMutex = SDL_CreateMutex(); - if(!AccessMutex) - { - con.printerr("Mutex creation failed\n"); - errorstate = true; - return false; - } + StackMutex = new mutex(); + AccessMutex = new mutex(); core_cond = new Core::Cond(); // create plugin manager plug_mgr = new PluginManager(this); - if(!plug_mgr) - { - con.printerr("Failed to create the Plugin Manager.\n"); - errorstate = true; - return false; - } - // look for all plugins, // create IO thread IODATA *temp = new IODATA; temp->core = this; temp->plug_mgr = plug_mgr; - SDL::Thread * IO = SDL_CreateThread(fIOthread, (void *) temp); + thread * IO = new thread(fIOthread, (void *) temp); // set up hotkey capture - HotkeyMutex = SDL_CreateMutex(); - HotkeyCond = SDL_CreateCond(); - SDL::Thread * HK = SDL_CreateThread(fHKthread, (void *) temp); + HotkeyMutex = new mutex(); + HotkeyCond = new condition_variable(); + thread * HK = new thread(fHKthread, (void *) temp); started = true; return true; } @@ -430,28 +421,28 @@ bool Core::Init() bool Core::setHotkeyCmd( std::string cmd ) { // access command - SDL_mutexP(HotkeyMutex); + HotkeyMutex->lock(); { hotkey_set = true; hotkey_cmd = cmd; - SDL_CondSignal(HotkeyCond); + HotkeyCond->notify_all(); } - SDL_mutexV(HotkeyMutex); + HotkeyMutex->unlock(); return true; } /// removes the hotkey command and gives it to the caller thread std::string Core::getHotkeyCmd( void ) { string returner; - SDL_mutexP(HotkeyMutex); + HotkeyMutex->lock(); while ( ! hotkey_set ) { - SDL_CondWait(HotkeyCond, HotkeyMutex); + HotkeyCond->wait(*HotkeyMutex); } hotkey_set = false; returner = hotkey_cmd; hotkey_cmd.clear(); - SDL_mutexV(HotkeyMutex); + HotkeyMutex->unlock(); return returner; } @@ -460,26 +451,25 @@ void Core::Suspend() { Core::Cond * nc = new Core::Cond(); // put the condition on a stack - SDL_mutexP(StackMutex); + StackMutex->lock(); suspended_tools.push(nc); - SDL_mutexV(StackMutex); + StackMutex->unlock(); // wait until Core::Update() wakes up the tool - SDL_mutexP(AccessMutex); + AccessMutex->lock(); nc->Lock(AccessMutex); - SDL_mutexV(AccessMutex); + AccessMutex->unlock(); } void Core::Resume() { - SDL_mutexP(AccessMutex); + AccessMutex->lock(); core_cond->Unlock(); - SDL_mutexV(AccessMutex); + AccessMutex->unlock(); } // should always be from simulation thread! int Core::Update() { - if(!started) Init(); if(errorstate) return -1; @@ -487,21 +477,21 @@ int Core::Update() plug_mgr->OnUpdate(); // wake waiting tools // do not allow more tools to join in while we process stuff here - SDL_mutexP(StackMutex); + StackMutex->lock(); while (!suspended_tools.empty()) { Core::Cond * nc = suspended_tools.top(); suspended_tools.pop(); - SDL_mutexP(AccessMutex); + AccessMutex->lock(); // wake tool nc->Unlock(); // wait for tool to wake us core_cond->Lock(AccessMutex); - SDL_mutexV(AccessMutex); + AccessMutex->unlock(); // destroy condition delete nc; } - SDL_mutexV(StackMutex); + StackMutex->unlock(); return 0; }; diff --git a/library/FakeSDL-linux.cpp b/library/FakeSDL-linux.cpp index 5a26aec4b..4bb3c4243 100644 --- a/library/FakeSDL-linux.cpp +++ b/library/FakeSDL-linux.cpp @@ -71,84 +71,15 @@ namespace DFHack /******************************************************************************* * SDL part starts here * *******************************************************************************/ -bool FirstCall(void); -bool inited = false; - +// hook - called for each game tick (or more often) DFhackCExport int SDL_NumJoysticks(void) { DFHack::Core & c = DFHack::Core::getInstance(); - // the 'inited' variable should be normally protected by a lock. It isn't - // this is harmless enough. only thing this can cause is a slight delay before - // DF input events start to be processed by Core - int ret = c.Update(); - if(ret == 0) - inited = true; - return ret; -} - -// ptr to the real functions -//static void (*_SDL_GL_SwapBuffers)(void) = 0; -static void (*_SDL_Quit)(void) = 0; -static int (*_SDL_Init)(uint32_t flags) = 0; -static SDL::Thread * (*_SDL_CreateThread)(int (*fn)(void *), void *data) = 0; -//static int (*_SDL_Flip)(void * some_ptr) = 0; -/* -// hook - called every tick in OpenGL mode of DF -DFhackCExport void SDL_GL_SwapBuffers(void) -{ - if(_SDL_GL_SwapBuffers) - { - if(!errorstate) - { - SHM_Act(); - } - counter ++; - _SDL_GL_SwapBuffers(); - } -} -*/ -/* -// hook - called every tick in the 2D mode of DF -DFhackCExport int SDL_Flip(void * some_ptr) -{ - if(_SDL_Flip) - { - if(!errorstate) - { - SHM_Act(); - } - counter ++; - return _SDL_Flip(some_ptr); - } - return 0; -} -*/ - -static SDL::Mutex * (*_SDL_CreateMutex)(void) = 0; -DFhackCExport SDL::Mutex * SDL_CreateMutex(void) -{ - return _SDL_CreateMutex(); -} - -static int (*_SDL_mutexP)(SDL::Mutex * mutex) = 0; -DFhackCExport int SDL_mutexP(SDL::Mutex * mutex) -{ - return _SDL_mutexP(mutex); -} - -static int (*_SDL_mutexV)(SDL::Mutex * mutex) = 0; -DFhackCExport int SDL_mutexV(SDL::Mutex * mutex) -{ - return _SDL_mutexV(mutex); -} - -static void (*_SDL_DestroyMutex)(SDL::Mutex * mutex) = 0; -DFhackCExport void SDL_DestroyMutex(SDL::Mutex * mutex) -{ - _SDL_DestroyMutex(mutex); + return c.Update(); } // hook - called at program exit +static void (*_SDL_Quit)(void) = 0; DFhackCExport void SDL_Quit(void) { DFHack::Core & c = DFHack::Core::getInstance(); @@ -164,12 +95,8 @@ static int (*_SDL_PollEvent)(SDL::Event* event) = 0; DFhackCExport int SDL_PollEvent(SDL::Event* event) { int orig_return = _SDL_PollEvent(event); - // only send events to Core after we get first SDL_NumJoysticks call - // DF event loop is possibly polling for SDL events before things get inited properly - // SDL handles it. We don't, because we use some other parts of SDL too. - - // possible data race. whatever. it's a flag, we don't mind all that much - if(inited && event != 0) + // if the event is valid, intercept + if( event != 0 ) { DFHack::Core & c = DFHack::Core::getInstance(); return c.SDL_Event(event, orig_return); @@ -177,62 +104,19 @@ DFhackCExport int SDL_PollEvent(SDL::Event* event) return orig_return; } -static uint32_t (*_SDL_ThreadID)(void) = 0; -DFhackCExport uint32_t SDL_ThreadID() -{ - return _SDL_ThreadID(); -} - -static SDL::Cond * (*_SDL_CreateCond)(void) = 0; -DFhackCExport SDL::Cond *SDL_CreateCond(void) -{ - return _SDL_CreateCond(); -} -static void (*_SDL_DestroyCond)(SDL::Cond *) = 0; -DFhackCExport void SDL_DestroyCond(SDL::Cond *cond) -{ - _SDL_DestroyCond(cond); -} -static int (*_SDL_CondSignal)(SDL::Cond *) = 0; -DFhackCExport int SDL_CondSignal(SDL::Cond *cond) -{ - return _SDL_CondSignal(cond); -} -static int (*_SDL_CondWait)(SDL::Cond *, SDL::Mutex *) = 0; -DFhackCExport int SDL_CondWait(SDL::Cond *cond, SDL::Mutex * mut) -{ - return _SDL_CondWait(cond, mut); -} - // hook - called at program start, initialize some stuffs we'll use later +static int (*_SDL_Init)(uint32_t flags) = 0; DFhackCExport int SDL_Init(uint32_t flags) { freopen("stdout.log", "w", stdout); freopen("stderr.log", "w", stderr); - // horrible casts not supported by the C or C++ standards. Only POSIX. Damn you, POSIX. // find real functions - //_SDL_GL_SwapBuffers = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_GL_SwapBuffers"); _SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init"); - //_SDL_Flip = (int (*)( void * )) dlsym(RTLD_NEXT, "SDL_Flip"); _SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit"); - _SDL_CreateThread = (SDL::Thread* (*)(int (*fn)(void *), void *data))dlsym(RTLD_NEXT, "SDL_CreateThread"); - _SDL_CreateMutex = (SDL::Mutex*(*)())dlsym(RTLD_NEXT,"SDL_CreateMutex"); - _SDL_DestroyMutex = (void (*)(SDL::Mutex*))dlsym(RTLD_NEXT,"SDL_DestroyMutex"); - _SDL_mutexP = (int (*)(SDL::Mutex*))dlsym(RTLD_NEXT,"SDL_mutexP"); - _SDL_mutexV = (int (*)(SDL::Mutex*))dlsym(RTLD_NEXT,"SDL_mutexV"); _SDL_PollEvent = (int (*)(SDL::Event*))dlsym(RTLD_NEXT,"SDL_PollEvent"); - _SDL_ThreadID = (uint32_t (*)())dlsym(RTLD_NEXT,"SDL_ThreadID"); - - _SDL_CreateCond = (SDL::Cond * (*)())dlsym(RTLD_NEXT,"SDL_CreateCond"); - _SDL_DestroyCond = (void(*)(SDL::Cond *))dlsym(RTLD_NEXT,"SDL_DestroyCond"); - _SDL_CondSignal = (int (*)(SDL::Cond *))dlsym(RTLD_NEXT,"SDL_CondSignal"); - _SDL_CondWait = (int (*)(SDL::Cond *, SDL::Mutex *))dlsym(RTLD_NEXT,"SDL_CondWait"); // check if we got them - if(_SDL_Init && _SDL_Quit && _SDL_CreateThread - && _SDL_CreateMutex && _SDL_DestroyMutex && _SDL_mutexP - && _SDL_mutexV && _SDL_PollEvent && _SDL_ThreadID - && _SDL_CondSignal && _SDL_CondWait && _SDL_CreateCond && _SDL_DestroyCond) + if(_SDL_Init && _SDL_Quit && _SDL_PollEvent) { fprintf(stderr,"dfhack: hooking successful\n"); } @@ -242,6 +126,8 @@ DFhackCExport int SDL_Init(uint32_t flags) fprintf(stderr,"dfhack: something went horribly wrong\n"); exit(1); } + DFHack::Core & c = DFHack::Core::getInstance(); + c.Init(); int ret = _SDL_Init(flags); return ret; } diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 99a01ea25..9324c20db 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -34,6 +34,9 @@ using namespace DFHack; #include using namespace std; +#include "tinythread.h" +using namespace tthread; + #ifdef LINUX_BUILD #include #include @@ -41,6 +44,8 @@ using namespace std; #include "wdirent.h" #endif +#include + static int getdir (string dir, vector &files) { DIR *dp; @@ -72,53 +77,44 @@ struct Plugin::RefLock RefLock() { refcount = 0; - wakeup = SDL_CreateCond(); - mut = SDL_CreateMutex(); + wakeup = new condition_variable(); + mut = new mutex(); } ~RefLock() { - SDL_DestroyCond(wakeup); - SDL_DestroyMutex(mut); + delete wakeup; + delete mut; } void lock() { - SDL_mutexP(mut); + mut->lock(); } void unlock() { - SDL_mutexV(mut); + mut->unlock(); } void lock_add() { - SDL_mutexP(mut); + mut->lock(); refcount ++; - SDL_mutexV(mut); + mut->unlock(); } void lock_sub() { - SDL_mutexP(mut); - refcount --; - SDL_CondSignal(wakeup); - SDL_mutexV(mut); - } - void operator++() - { - refcount ++; - } - void operator--() - { + mut->lock(); refcount --; - SDL_CondSignal(wakeup); + wakeup->notify_one(); + mut->unlock(); } void wait() { while(refcount) { - SDL_CondWait(wakeup, mut); + wakeup->wait(*mut); } } - SDL::Cond * wakeup; - SDL::Mutex * mut; + condition_variable * wakeup; + mutex * mut; int refcount; }; Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm) @@ -313,7 +309,7 @@ PluginManager::PluginManager(Core * core) string path = core->p->getPath() + "\\plugins\\"; const string searchstr = ".plug.dll"; #endif - cmdlist_mutex = SDL_CreateMutex(); + cmdlist_mutex = new mutex(); vector filez; getdir(path, filez); for(int i = 0; i < filez.size();i++) @@ -335,7 +331,7 @@ PluginManager::~PluginManager() delete all_plugins[i]; } all_plugins.clear(); - SDL_DestroyMutex(cmdlist_mutex); + delete cmdlist_mutex; } Plugin *PluginManager::getPluginByName (const std::string & name) @@ -353,13 +349,13 @@ command_result PluginManager::InvokeCommand( std::string & command, std::vector { command_result cr = CR_NOT_IMPLEMENTED; Core * c = &Core::getInstance(); - SDL_mutexP(cmdlist_mutex); + cmdlist_mutex->lock(); map ::iterator iter = belongs.find(command); if(iter != belongs.end()) { cr = iter->second->invoke(command, parameters); } - SDL_mutexV(cmdlist_mutex); + cmdlist_mutex->unlock(); return cr; } @@ -373,23 +369,23 @@ void PluginManager::OnUpdate( void ) // FIXME: doesn't check name collisions! void PluginManager::registerCommands( Plugin * p ) { - SDL_mutexP(cmdlist_mutex); + cmdlist_mutex->lock(); vector & cmds = p->commands; for(int i = 0; i < cmds.size();i++) { belongs[cmds[i].name] = p; } - SDL_mutexV(cmdlist_mutex); + cmdlist_mutex->unlock(); } // FIXME: doesn't check name collisions! void PluginManager::unregisterCommands( Plugin * p ) { - SDL_mutexP(cmdlist_mutex); + cmdlist_mutex->lock(); vector & cmds = p->commands; for(int i = 0; i < cmds.size();i++) { belongs.erase(cmds[i].name); } - SDL_mutexV(cmdlist_mutex); + cmdlist_mutex->unlock(); } \ No newline at end of file diff --git a/library/depends/tthread/fast_mutex.h b/library/depends/tthread/fast_mutex.h new file mode 100644 index 000000000..b4e712f44 --- /dev/null +++ b/library/depends/tthread/fast_mutex.h @@ -0,0 +1,239 @@ +/* +Copyright (c) 2010 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _FAST_MUTEX_H_ +#define _FAST_MUTEX_H_ + +/// @file + +// Which platform are we on? +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +// Check if we can support the assembly language level implementation (otherwise +// revert to the system API) +#if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || \ + (defined(__GNUC__) && (defined(__ppc__))) + #define _FAST_MUTEX_ASM_ +#else + #define _FAST_MUTEX_SYS_ +#endif + +#if defined(_TTHREAD_WIN32_) + #include +#else + #ifdef _FAST_MUTEX_ASM_ + #include + #else + #include + #endif +#endif + +namespace tthread { + +/// Fast mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. It is similar to the tthread::mutex class, +/// but instead of using system level functions, it is implemented as an atomic +/// spin lock with very low CPU overhead. +/// +/// The \c fast_mutex class is NOT compatible with the \c condition_variable +/// class (however, it IS compatible with the \c lock_guard class). It should +/// also be noted that the \c fast_mutex class typically does not provide +/// as accurate thread scheduling as a the standard \c mutex class does. +/// +/// Because of the limitations of the class, it should only be used in +/// situations where the mutex needs to be locked/unlocked very frequently. +/// +/// @note The "fast" version of this class relies on inline assembler language, +/// which is currently only supported for 32/64-bit Intel x86/AMD64 and +/// PowerPC architectures on a limited number of compilers (GNU g++ and MS +/// Visual C++). +/// For other architectures/compilers, system functions are used instead. +class fast_mutex { + public: + /// Constructor. +#if defined(_FAST_MUTEX_ASM_) + fast_mutex() : mLock(0) {} +#else + fast_mutex() + { + #if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); + #elif defined(_TTHREAD_POSIX_) + pthread_mutex_init(&mHandle, NULL); + #endif + } +#endif + +#if !defined(_FAST_MUTEX_ASM_) + /// Destructor. + ~fast_mutex() + { + #if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); + #elif defined(_TTHREAD_POSIX_) + pthread_mutex_destroy(&mHandle); + #endif + } +#endif + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until \c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_FAST_MUTEX_ASM_) + bool gotLock; + do { + gotLock = try_lock(); + if(!gotLock) + { + #if defined(_TTHREAD_WIN32_) + Sleep(0); + #elif defined(_TTHREAD_POSIX_) + sched_yield(); + #endif + } + } while(!gotLock); +#else + #if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); + #elif defined(_TTHREAD_POSIX_) + pthread_mutex_lock(&mHandle); + #endif +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return \c true if the lock was acquired, or \c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_FAST_MUTEX_ASM_) + int oldLock; + #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + asm volatile ( + "movl $1,%%eax\n\t" + "xchg %%eax,%0\n\t" + "movl %%eax,%1\n\t" + : "=m" (mLock), "=m" (oldLock) + : + : "%eax", "memory" + ); + #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + int *ptrLock = &mLock; + __asm { + mov eax,1 + mov ecx,ptrLock + xchg eax,[ecx] + mov oldLock,eax + } + #elif defined(__GNUC__) && (defined(__ppc__)) + int newLock = 1; + asm volatile ( + "\n1:\n\t" + "lwarx %0,0,%1\n\t" + "cmpwi 0,%0,0\n\t" + "bne- 2f\n\t" + "stwcx. %2,0,%1\n\t" + "bne- 1b\n\t" + "isync\n" + "2:\n\t" + : "=&r" (oldLock) + : "r" (&mLock), "r" (newLock) + : "cr0", "memory" + ); + #endif + return (oldLock == 0); +#else + #if defined(_TTHREAD_WIN32_) + return TryEnterCriticalSection(&mHandle) ? true : false; + #elif defined(_TTHREAD_POSIX_) + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; + #endif +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_FAST_MUTEX_ASM_) + #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + asm volatile ( + "movl $0,%%eax\n\t" + "xchg %%eax,%0\n\t" + : "=m" (mLock) + : + : "%eax", "memory" + ); + #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + int *ptrLock = &mLock; + __asm { + mov eax,0 + mov ecx,ptrLock + xchg eax,[ecx] + } + #elif defined(__GNUC__) && (defined(__ppc__)) + asm volatile ( + "sync\n\t" // Replace with lwsync where possible? + : : : "memory" + ); + mLock = 0; + #endif +#else + #if defined(_TTHREAD_WIN32_) + LeaveCriticalSection(&mHandle); + #elif defined(_TTHREAD_POSIX_) + pthread_mutex_unlock(&mHandle); + #endif +#endif + } + + private: +#if defined(_FAST_MUTEX_ASM_) + int mLock; +#else + #if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; + #elif defined(_TTHREAD_POSIX_) + pthread_mutex_t mHandle; + #endif +#endif +}; + +} + +#endif // _FAST_MUTEX_H_ diff --git a/library/depends/tthread/tinythread.cpp b/library/depends/tthread/tinythread.cpp new file mode 100644 index 000000000..eb2dce0e6 --- /dev/null +++ b/library/depends/tthread/tinythread.cpp @@ -0,0 +1,287 @@ +/* +Copyright (c) 2010 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#include +#include "tinythread.h" + +#if defined(_TTHREAD_POSIX_) + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include +#endif + + +namespace tthread { + +//------------------------------------------------------------------------------ +// condition_variable +//------------------------------------------------------------------------------ +// NOTE 1: The Win32 implementation of the condition_variable class is based on +// the corresponding implementation in GLFW, which in turn is based on a +// description by Douglas C. Schmidt and Irfan Pyarali: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// +// NOTE 2: Windows Vista actually has native support for condition variables +// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to +// be portable with pre-Vista Windows versions, so TinyThread++ does not use +// Vista condition variables. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_WIN32_) + #define _CONDITION_EVENT_ONE 0 + #define _CONDITION_EVENT_ALL 1 +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::condition_variable() : mWaitersCount(0) +{ + mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); + mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); + InitializeCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::~condition_variable() +{ + CloseHandle(mEvents[_CONDITION_EVENT_ONE]); + CloseHandle(mEvents[_CONDITION_EVENT_ALL]); + DeleteCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::_wait() +{ + // Wait for either event to become signaled due to notify_one() or + // notify_all() being called + int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); + + // Check if we are the last waiter + EnterCriticalSection(&mWaitersCountLock); + -- mWaitersCount; + bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && + (mWaitersCount == 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we are the last waiter to be notified to stop waiting, reset the event + if(lastWaiter) + ResetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_one() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ONE]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_all() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + + +//------------------------------------------------------------------------------ +// POSIX pthread_t to unique thread::id mapping logic. +// Note: Here we use a global thread safe std::map to convert instances of +// pthread_t to small thread identifier numbers (unique within one process). +// This method should be portable across different POSIX implementations. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_POSIX_) +static thread::id _pthread_t_to_ID(const pthread_t &aHandle) +{ + static mutex idMapLock; + static std::map idMap; + static unsigned long int idCount(1); + + lock_guard guard(idMapLock); + if(idMap.find(aHandle) == idMap.end()) + idMap[aHandle] = idCount ++; + return thread::id(idMap[aHandle]); +} +#endif // _TTHREAD_POSIX_ + + +//------------------------------------------------------------------------------ +// thread +//------------------------------------------------------------------------------ + +/// Information to pass to the new thread (what to run). +struct _thread_start_info { + void (*mFunction)(void *); ///< Pointer to the function to be executed. + void * mArg; ///< Function argument for the thread function. + thread * mThread; ///< Pointer to the thread object. +}; + +// Thread wrapper function. +#if defined(_TTHREAD_WIN32_) +unsigned WINAPI thread::wrapper_function(void * aArg) +#elif defined(_TTHREAD_POSIX_) +void * thread::wrapper_function(void * aArg) +#endif +{ + // Get thread startup information + _thread_start_info * ti = (_thread_start_info *) aArg; + + try + { + // Call the actual client thread function + ti->mFunction(ti->mArg); + } + catch(...) + { + // Uncaught exceptions will terminate the application (default behavior + // according to the C++0x draft) + std::terminate(); + } + + // The thread is no longer executing + lock_guard guard(ti->mThread->mDataMutex); + ti->mThread->mNotAThread = true; + + // The thread is responsible for freeing the startup information + delete ti; + + return 0; +} + +thread::thread(void (*aFunction)(void *), void * aArg) +{ + // Serialize access to this thread structure + lock_guard guard(mDataMutex); + + // Fill out the thread startup information (passed to the thread wrapper, + // which will eventually free it) + _thread_start_info * ti = new _thread_start_info; + ti->mFunction = aFunction; + ti->mArg = aArg; + ti->mThread = this; + + // The thread is now alive + mNotAThread = false; + + // Create the thread +#if defined(_TTHREAD_WIN32_) + mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) + mHandle = 0; +#endif + + // Did we fail to create the thread? + if(!mHandle) + { + mNotAThread = true; + delete ti; + } +} + +thread::~thread() +{ + if(joinable()) + std::terminate(); +} + +void thread::join() +{ + if(joinable()) + { +#if defined(_TTHREAD_WIN32_) + WaitForSingleObject(mHandle, INFINITE); +#elif defined(_TTHREAD_POSIX_) + pthread_join(mHandle, NULL); +#endif + } +} + +bool thread::joinable() const +{ + mDataMutex.lock(); + bool result = !mNotAThread; + mDataMutex.unlock(); + return result; +} + +thread::id thread::get_id() const +{ + if(!joinable()) + return id(); +#if defined(_TTHREAD_WIN32_) + return id((unsigned long int) mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(mHandle); +#endif +} + +unsigned thread::hardware_concurrency() +{ +#if defined(_TTHREAD_WIN32_) + SYSTEM_INFO si; + GetSystemInfo(&si); + return (int) si.dwNumberOfProcessors; +#elif defined(_SC_NPROCESSORS_ONLN) + return (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(_SC_NPROC_ONLN) + return (int) sysconf(_SC_NPROC_ONLN); +#else + // The standard requires this function to return zero if the number of + // hardware cores could not be determined. + return 0; +#endif +} + + +//------------------------------------------------------------------------------ +// this_thread +//------------------------------------------------------------------------------ + +thread::id this_thread::get_id() +{ +#if defined(_TTHREAD_WIN32_) + return thread::id((unsigned long int) GetCurrentThreadId()); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(pthread_self()); +#endif +} + +} diff --git a/library/depends/tthread/tinythread.h b/library/depends/tthread/tinythread.h new file mode 100644 index 000000000..723370cf8 --- /dev/null +++ b/library/depends/tthread/tinythread.h @@ -0,0 +1,696 @@ +/* +Copyright (c) 2010 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _TINYTHREAD_H_ +#define _TINYTHREAD_H_ + +/// @file +/// @mainpage TinyThread++ API Reference +/// +/// @section intro_sec Introduction +/// TinyThread++ is a minimal, portable implementation of basic threading +/// classes for C++. +/// +/// They closely mimic the functionality and naming of the C++0x standard, and +/// should be easily replaceable with the corresponding std:: variants. +/// +/// @section port_sec Portability +/// The Win32 variant uses the native Win32 API for implementing the thread +/// classes, while for other systems, the POSIX threads API (pthread) is used. +/// +/// @section class_sec Classes +/// In order to mimic the threading API of the C++0x standard, subsets of +/// several classes are provided. The fundamental classes are: +/// @li tthread::thread +/// @li tthread::mutex +/// @li tthread::recursive_mutex +/// @li tthread::condition_variable +/// @li tthread::lock_guard +/// @li tthread::fast_mutex +/// +/// @section misc_sec Miscellaneous +/// The following special keywords are available: #thread_local. +/// +/// For more detailed information (including additional classes), browse the +/// different sections of this documentation. A good place to start is: +/// tinythread.h. + +// Which platform are we on? +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +// Platform specific includes +#if defined(_TTHREAD_WIN32_) + #include +#else + #include + #include + #include + #include +#endif + +// Generic includes +#include + +/// TinyThread++ version (major number). +#define TINYTHREAD_VERSION_MAJOR 1 +/// TinyThread++ version (minor number). +#define TINYTHREAD_VERSION_MINOR 0 +/// TinyThread++ version (full version). +#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) + +// Do we have a fully featured C++0x compiler? +#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) + #define _TTHREAD_CPP0X_ +#endif + +// ...at least partial C++0x? +#if defined(_TTHREAD_CPP0X_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) + #define _TTHREAD_CPP0X_PARTIAL_ +#endif + +// Macro for disabling assignments of objects. +#ifdef _TTHREAD_CPP0X_PARTIAL_ + #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ + name(const name&) = delete; \ + name& operator=(const name&) = delete; +#else + #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ + name(const name&); \ + name& operator=(const name&); +#endif + +/// @def thread_local +/// Thread local storage keyword. +/// A variable that is declared with the \c thread_local keyword makes the +/// value of the variable local to each thread (known as thread-local storage, +/// or TLS). Example usage: +/// @code +/// // This variable is local to each thread. +/// thread_local int variable; +/// @endcode +/// @note The \c thread_local keyword is a macro that maps to the corresponding +/// compiler directive (e.g. \c __declspec(thread)). While the C++0x standard +/// allows for non-trivial types (e.g. classes with constructors and +/// destructors) to be declared with the \c thread_local keyword, most pre-C++0x +/// compilers only allow for trivial types (e.g. \c int). So, to guarantee +/// portable code, only use trivial types for thread local storage. +/// @note This directive is currently not supported on Mac OS X (it will give +/// a compiler error), since compile-time TLS is not supported in the Mac OS X +/// executable format. Also, some older versions of MinGW (before GCC 4.x) do +/// not support this directive. +/// @hideinitializer + +#if !defined(_TTHREAD_CPP0X_) && !defined(thread_local) + #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) + #define thread_local __thread + #else + #define thread_local __declspec(thread) + #endif +#endif + + +/// Main name space for TinyThread++. +/// This namespace is more or less equivalent to the \c std namespace for the +/// C++0x thread classes. For instance, the tthread::mutex class corresponds to +/// the std::mutex class. +namespace tthread { + +/// Mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is non-recursive (i.e. a +/// program may deadlock if the thread that owns a mutex object calls lock() +/// on that object). +/// @see recursive_mutex +class mutex { + public: + /// Constructor. + mutex() +#if defined(_TTHREAD_WIN32_) + : mAlreadyLocked(false) +#endif + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutex_init(&mHandle, NULL); +#endif + } + + /// Destructor. + ~mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until \c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); + while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... + mAlreadyLocked = true; +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return \c true if the lock was acquired, or \c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); + if(ret && mAlreadyLocked) + { + LeaveCriticalSection(&mHandle); + ret = false; + } + return ret; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + mAlreadyLocked = false; + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; + bool mAlreadyLocked; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Recursive mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is recursive (i.e. a thread +/// may lock the mutex several times, as long as it unlocks the mutex the same +/// number of times). +/// @see mutex +class recursive_mutex { + public: + /// Constructor. + recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mHandle, &attr); +#endif + } + + /// Destructor. + ~recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until \c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return \c true if the lock was acquired, or \c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + return TryEnterCriticalSection(&mHandle) ? true : false; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Lock guard class. +/// The constructor locks the mutex, and the destructor unlocks the mutex, so +/// the mutex will automatically be unlocked when the lock guard goes out of +/// scope. Example usage: +/// @code +/// mutex m; +/// int counter; +/// +/// void increment() +/// { +/// lock_guard guard(m); +/// ++ counter; +/// } +/// @endcode + +template +class lock_guard { + public: + typedef T mutex_type; + + lock_guard() : mMutex(0) {} + + /// The constructor locks the mutex. + explicit lock_guard(mutex_type &aMutex) + { + mMutex = &aMutex; + mMutex->lock(); + } + + /// The destructor unlocks the mutex. + ~lock_guard() + { + if(mMutex) + mMutex->unlock(); + } + + private: + mutex_type * mMutex; +}; + +/// Condition variable class. +/// This is a signalling object for synchronizing the execution flow for +/// several threads. Example usage: +/// @code +/// // Shared data and associated mutex and condition variable objects +/// int count; +/// mutex m; +/// condition_variable cond; +/// +/// // Wait for the counter to reach a certain number +/// void wait_counter(int targetCount) +/// { +/// lock_guard guard(m); +/// while(count < targetCount) +/// cond.wait(m); +/// } +/// +/// // Increment the counter, and notify waiting threads +/// void increment() +/// { +/// lock_guard guard(m); +/// ++ count; +/// cond.notify_all(); +/// } +/// @endcode +class condition_variable { + public: + /// Constructor. +#if defined(_TTHREAD_WIN32_) + condition_variable(); +#else + condition_variable() + { + pthread_cond_init(&mHandle, NULL); + } +#endif + + /// Destructor. +#if defined(_TTHREAD_WIN32_) + ~condition_variable(); +#else + ~condition_variable() + { + pthread_cond_destroy(&mHandle); + } +#endif + + /// Wait for the condition. + /// The function will block the calling thread until the condition variable + /// is woken by \c notify_one(), \c notify_all() or a spurious wake up. + /// @param[in] aMutex A mutex that will be unlocked when the wait operation + /// starts, an locked again as soon as the wait operation is finished. + template + inline void wait(_mutexT &aMutex) + { +#if defined(_TTHREAD_WIN32_) + // Increment number of waiters + EnterCriticalSection(&mWaitersCountLock); + ++ mWaitersCount; + LeaveCriticalSection(&mWaitersCountLock); + + // Release the mutex while waiting for the condition (will decrease + // the number of waiters when done)... + aMutex.unlock(); + _wait(); + aMutex.lock(); +#else + pthread_cond_wait(&mHandle, &aMutex.mHandle); +#endif + } + + /// Notify one thread that is waiting for the condition. + /// If at least one thread is blocked waiting for this condition variable, + /// one will be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_one(); +#else + inline void notify_one() + { + pthread_cond_signal(&mHandle); + } +#endif + + /// Notify all threads that are waiting for the condition. + /// All threads that are blocked waiting for this condition variable will + /// be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_all(); +#else + inline void notify_all() + { + pthread_cond_broadcast(&mHandle); + } +#endif + + _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) + + private: +#if defined(_TTHREAD_WIN32_) + void _wait(); + HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. + unsigned int mWaitersCount; ///< Count of the number of waiters. + CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. +#else + pthread_cond_t mHandle; +#endif +}; + + +/// Thread class. +class thread { + public: +#if defined(_TTHREAD_WIN32_) + typedef HANDLE native_handle_type; +#else + typedef pthread_t native_handle_type; +#endif + + class id; + + /// Default constructor. + /// Construct a \c thread object without an associated thread of execution + /// (i.e. non-joinable). + thread() : mHandle(0), mNotAThread(true) +#if defined(_TTHREAD_WIN32_) + , mWin32ThreadID(0) +#endif + {} + + /// Thread starting constructor. + /// Construct a \c thread object with a new thread of execution. + /// @param[in] aFunction A function pointer to a function of type: + /// void fun(void * arg) + /// @param[in] aArg Argument to the thread function. + /// @note This constructor is not fully compatible with the standard C++ + /// thread class. It is more similar to the pthread_create() (POSIX) and + /// CreateThread() (Windows) functions. + thread(void (*aFunction)(void *), void * aArg); + + /// Destructor. + /// @note If the thread is joinable upon destruction, \c std::terminate() + /// will be called, which terminates the process. It is always wise to do + /// \c join() before deleting a thread object. + ~thread(); + + /// Wait for the thread to finish (join execution flows). + void join(); + + /// Check if the thread is joinable. + /// A thread object is joinable if it has an associated thread of execution. + bool joinable() const; + + /// Return the thread ID of a thread object. + id get_id() const; + + /// Get the native handle for this thread. + /// @note Under Windows, this is a \c HANDLE, and under POSIX systems, this + /// is a \c pthread_t. + inline native_handle_type native_handle() + { + return mHandle; + } + + /// Determine the number of threads which can possibly execute concurrently. + /// This function is useful for determining the optimal number of threads to + /// use for a task. + /// @return The number of hardware thread contexts in the system. + /// @note If this value is not defined, the function returns zero (0). + static unsigned hardware_concurrency(); + + _TTHREAD_DISABLE_ASSIGNMENT(thread) + + private: + native_handle_type mHandle; ///< Thread handle. + mutable mutex mDataMutex; ///< Serializer for access to the thread private data. + bool mNotAThread; ///< True if this object is not a thread of execution. +#if defined(_TTHREAD_WIN32_) + unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). +#endif + + // This is the internal thread wrapper function. +#if defined(_TTHREAD_WIN32_) + static unsigned WINAPI wrapper_function(void * aArg); +#else + static void * wrapper_function(void * aArg); +#endif +}; + +/// Thread ID. +/// The thread ID is a unique identifier for each thread. +/// @see thread::get_id() +class thread::id { + public: + /// Default constructor. + /// The default constructed ID is that of thread without a thread of + /// execution. + id() : mId(0) {}; + + id(unsigned long int aId) : mId(aId) {}; + + id(const id& aId) : mId(aId.mId) {}; + + inline id & operator=(const id &aId) + { + mId = aId.mId; + return *this; + } + + inline friend bool operator==(const id &aId1, const id &aId2) + { + return (aId1.mId == aId2.mId); + } + + inline friend bool operator!=(const id &aId1, const id &aId2) + { + return (aId1.mId != aId2.mId); + } + + inline friend bool operator<=(const id &aId1, const id &aId2) + { + return (aId1.mId <= aId2.mId); + } + + inline friend bool operator<(const id &aId1, const id &aId2) + { + return (aId1.mId < aId2.mId); + } + + inline friend bool operator>=(const id &aId1, const id &aId2) + { + return (aId1.mId >= aId2.mId); + } + + inline friend bool operator>(const id &aId1, const id &aId2) + { + return (aId1.mId > aId2.mId); + } + + inline friend std::ostream& operator <<(std::ostream &os, const id &obj) + { + os << obj.mId; + return os; + } + + private: + unsigned long int mId; +}; + + +// Related to - minimal to be able to support chrono. +typedef long long __intmax_t; + +/// Minimal implementation of the \c ratio class. This class provides enough +/// functionality to implement some basic \c chrono classes. +template <__intmax_t N, __intmax_t D = 1> class ratio { + public: + static double _as_double() { return double(N) / double(D); } +}; + +/// Minimal implementation of the \c chrono namespace. +/// The \c chrono namespace provides types for specifying time intervals. +namespace chrono { + /// Duration template class. This class provides enough functionality to + /// implement \c this_thread::sleep_for(). + template > class duration { + private: + _Rep rep_; + public: + typedef _Rep rep; + typedef _Period period; + + /// Construct a duration object with the given duration. + template + explicit duration(const _Rep2& r) : rep_(r) {}; + + /// Return the value of the duration object. + rep count() const + { + return rep_; + } + }; + + // Standard duration types. + typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. + typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. + typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. + typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. + typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. + typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. +} + +/// The namespace \c this_thread provides methods for dealing with the +/// calling thread. +namespace this_thread { + /// Return the thread ID of the calling thread. + thread::id get_id(); + + /// Yield execution to another thread. + /// Offers the operating system the opportunity to schedule another thread + /// that is ready to run on the current processor. + inline void yield() + { +#if defined(_TTHREAD_WIN32_) + Sleep(0); +#else + sched_yield(); +#endif + } + + /// Blocks the calling thread for a period of time. + /// @param[in] aTime Minimum time to put the thread to sleep. + /// Example usage: + /// @code + /// // Sleep for 100 milliseconds + /// this_thread::sleep_for(chrono::milliseconds(100)); + /// @endcode + /// @note Supported duration types are: nanoseconds, microseconds, + /// milliseconds, seconds, minutes and hours. + template void sleep_for(const chrono::duration<_Rep, _Period>& aTime) + { +#if defined(_TTHREAD_WIN32_) + Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); +#else + usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); +#endif + } +} + +} + +// Define/macro cleanup +#undef _TTHREAD_DISABLE_ASSIGNMENT + +#endif // _TINYTHREAD_H_ diff --git a/library/include/dfhack/Console.h b/library/include/dfhack/Console.h index cddaba475..1a1101637 100644 --- a/library/include/dfhack/Console.h +++ b/library/include/dfhack/Console.h @@ -26,7 +26,12 @@ distribution. #include "dfhack/Pragma.h" #include "dfhack/Export.h" #include -#include "FakeSDL.h" +namespace tthread +{ + class mutex; + class condition_variable; + class thread; +} namespace DFHack { class Private; @@ -93,7 +98,7 @@ namespace DFHack void history_clear(); private: Private * d; - SDL::Mutex * wlock; + tthread::mutex * wlock; bool inited; }; } \ No newline at end of file diff --git a/library/include/dfhack/Core.h b/library/include/dfhack/Core.h index 8ca16714f..8b4cf4991 100644 --- a/library/include/dfhack/Core.h +++ b/library/include/dfhack/Core.h @@ -33,6 +33,13 @@ distribution. #include #include "dfhack/Console.h" +namespace tthread +{ + class mutex; + class condition_variable; + class thread; +} + namespace DFHack { class Process; @@ -69,6 +76,7 @@ namespace DFHack friend int ::SDL_NumJoysticks(void); friend void ::SDL_Quit(void); friend int ::SDL_PollEvent(SDL::Event *); + friend int ::SDL_Init(uint32_t flags); public: /// Get the single Core instance or make one. static Core& getInstance() @@ -129,8 +137,8 @@ namespace DFHack bool errorstate; // regulate access to DF struct Cond; - SDL::Mutex * AccessMutex; - SDL::Mutex * StackMutex; + tthread::mutex * AccessMutex; + tthread::mutex * StackMutex; std::stack < Core::Cond * > suspended_tools; Core::Cond * core_cond; // FIXME: shouldn't be kept around like this @@ -158,8 +166,8 @@ namespace DFHack int hotkey_states[16]; std::string hotkey_cmd; bool hotkey_set; - SDL::Mutex * HotkeyMutex; - SDL::Cond * HotkeyCond; + tthread::mutex * HotkeyMutex; + tthread::condition_variable * HotkeyCond; // Very important! bool started; }; diff --git a/library/include/dfhack/FakeSDL.h b/library/include/dfhack/FakeSDL.h index ad2af26ba..d14ae2734 100644 --- a/library/include/dfhack/FakeSDL.h +++ b/library/include/dfhack/FakeSDL.h @@ -34,17 +34,18 @@ distribution. #include // function and variable pointer... we don't try to understand what SDL does here + typedef void * fPtr; typedef void * vPtr; namespace SDL { union Event; - struct Thread; - struct Mutex; - struct Cond; - struct Library; + //struct Thread; + //struct Mutex; + //struct Cond; + //struct Library; } - +/* // mutex stuff DFhackCExport SDL::Mutex * SDL_CreateMutex(void); DFhackCExport int SDL_mutexP(SDL::Mutex *); @@ -58,15 +59,12 @@ DFhackCExport SDL::Cond *SDL_CreateCond(void); DFhackCExport void SDL_DestroyCond(SDL::Cond *cond); DFhackCExport int SDL_CondSignal(SDL::Cond *cond); DFhackCExport int SDL_CondWait(SDL::Cond *cond, SDL::Mutex * mut); - +*/ // these functions are here because they call into DFHack::Core and therefore need to // be declared as friend functions/known DFhackCExport int SDL_NumJoysticks(void); DFhackCExport void SDL_Quit(void); DFhackCExport int SDL_PollEvent(SDL::Event* event); -/* -// not yet. DFhackCExport int SDL_Init(uint32_t flags); -*/ // Other crud is in the OS-specific core files. diff --git a/library/include/dfhack/PluginManager.h b/library/include/dfhack/PluginManager.h index b176f4108..660675bd9 100644 --- a/library/include/dfhack/PluginManager.h +++ b/library/include/dfhack/PluginManager.h @@ -30,6 +30,11 @@ distribution. #include #include "FakeSDL.h" struct DFLibrary; +namespace tthread +{ + class mutex; + class condition_variable; +} namespace DFHack { class Core; @@ -131,7 +136,7 @@ namespace DFHack } // DATA private: - SDL::Mutex * cmdlist_mutex; + tthread::mutex * cmdlist_mutex; std::map belongs; std::vector all_plugins; std::string plugin_path;