diff --git a/platform/unix/exclude b/platform/unix/exclude index 230e16968..1b3e4f3d6 100644 --- a/platform/unix/exclude +++ b/platform/unix/exclude @@ -21,3 +21,6 @@ ./modules/native/tcc/libtcc/stab.h ./libraries/luasocket/libluasocket/wsocket.* ./modules/sound/lullaby/FLACDecoder.* +./modules/thread/sdl/* +./modules/thread/win32/* +./modules/thread/posix/* diff --git a/src/common/delay.cpp b/src/common/delay.cpp new file mode 100644 index 000000000..ff2e95c2d --- /dev/null +++ b/src/common/delay.cpp @@ -0,0 +1,41 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 "delay.h" + +namespace love { + + void delay(unsigned int ms) { +#if LOVE_THREADS == LOVE_THREADS_POSIX + struct timespec ts1, ts2; + + ts1.tv_sec = ms / 1000; + ts1.tv_nsec = (ms % 1000) * 1000000; + // FIXME: handle signals + nanosleep(&ts1, &ts2); +#elif LOVE_THREADS == LOVE_THREADS_WIN32 + Sleep(ms); +#elif LOVE_THREADS == LOVE_THREADS_SDL + SDL_Delay(ms); +#endif + } + +} // namespace love diff --git a/src/modules/thread/ThreadModule.h b/src/common/delay.h similarity index 72% rename from src/modules/thread/ThreadModule.h rename to src/common/delay.h index e914ed46c..8a70ff42b 100644 --- a/src/modules/thread/ThreadModule.h +++ b/src/common/delay.h @@ -18,24 +18,16 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#ifndef LOVE_THREAD_THREAD_H -#define LOVE_THREAD_THREAD_H +#ifndef DELAY_H_ +#define DELAY_H_ -#include -#include +#include -namespace love -{ -namespace thread -{ - class ThreadModule : public Module - { - public: - virtual ~ThreadModule(){}; - virtual void unregister(const std::string & name) = 0; - }; // ThreadModule -} // thread -} // love +namespace love { -#endif // LOVE_THREAD_THREAD_H + void delay(unsigned int ms); + +}; // namespace love + +#endif /* DELAY_H_ */ diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 95e9df64d..039573da8 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -25,17 +25,15 @@ #include "Object.h" #include "Reference.h" #include "StringMap.h" +#include // STD #include -// SDL -#include -#include namespace love { - static SDL_mutex *gcmutex = 0; + static thread::Mutex *gcmutex = 0; void *_gcmutex = 0; unsigned int _gcthread = 0; /** @@ -46,17 +44,16 @@ namespace love { if (!gcmutex) { - gcmutex = SDL_CreateMutex(); + gcmutex = new thread::Mutex(); _gcmutex = (void*) gcmutex; } Proxy * p = (Proxy *)lua_touserdata(L, 1); Object * t = (Object *)p->data; if(p->own) { - SDL_mutexP(gcmutex); - _gcthread = (unsigned int) SDL_ThreadID(); + thread::Lock lock(gcmutex); + _gcthread = thread::ThreadBase::threadId(); t->release(); - SDL_mutexV(gcmutex); } return 0; } diff --git a/src/love.cpp b/src/love.cpp index 38e34d392..f875b486b 100644 --- a/src/love.cpp +++ b/src/love.cpp @@ -52,7 +52,7 @@ #include #include #include -#include +#include // Libraries. #include "libraries/luasocket/luasocket.h" @@ -77,7 +77,7 @@ static const luaL_Reg modules[] = { { "love.physics", love::physics::box2d::luaopen_love_physics }, { "love.sound", love::sound::luaopen_love_sound }, { "love.timer", love::timer::sdl::luaopen_love_timer }, - { "love.thread", love::thread::sdl::luaopen_love_thread }, + { "love.thread", love::thread::luaopen_love_thread }, { 0, 0 } }; diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index ab29346b8..dc31597e4 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -19,6 +19,7 @@ **/ #include "Audio.h" +#include #include @@ -28,8 +29,34 @@ namespace audio { namespace openal { + Audio::PoolThread::PoolThread(Pool* pool) + : pool(pool), finish(false) { + + } + + void Audio::PoolThread::main() + { + while(true) { + { + thread::Lock lock(mutex); + if (finish) { + return; + } + } + + pool->update(); + delay(5); + } + } + + void Audio::PoolThread::setFinish() + { + thread::Lock lock(mutex); + finish = true; + } + + Audio::Audio() - : finish(false) { // Passing zero for default device. device = alcOpenDevice(0); @@ -67,15 +94,16 @@ namespace openal // pool must be allocated after AL context. pool = new Pool(); - thread = SDL_CreateThread(Audio::run, (void*)this); + poolThread = new PoolThread(pool); + poolThread->start(); } Audio::~Audio() { - finish = true; - - SDL_WaitThread(thread, 0); + poolThread->setFinish(); + poolThread->wait(); + delete poolThread; delete pool; alcMakeContextCurrent(0); @@ -84,18 +112,6 @@ namespace openal alcCloseDevice(device); } - int Audio::run(void * d) - { - Audio * instance = (Audio*)d; - - while(!instance->finish) - { - instance->pool->update(); - SDL_Delay(5); - } - - return 0; - } const char * Audio::getName() const { diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index 0a71e20a9..2479ceb5d 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -37,6 +37,7 @@ #include "Source.h" #include "Pool.h" +#include // OpenAL #ifdef LOVE_MACOSX @@ -66,17 +67,30 @@ namespace openal // The OpenAL context. ALCcontext * context; - SDL_Thread * thread; - // The Pool. Pool * pool; - // Set this to true when the thread should finish. - // Main thread will write to this value, and Audio::run - // will read from it. - bool finish; - static int run(void * unused); + class PoolThread: public thread::ThreadBase { + protected: + Pool* pool; + + // Set this to true when the thread should finish. + // Main thread will write to this value, and PoolThread + // will read from it. + volatile bool finish; + + // finish lock + thread::Mutex mutex; + + virtual void main(); + + public: + PoolThread(Pool* pool); + void setFinish(); + }; + + PoolThread* poolThread; public: diff --git a/src/modules/audio/openal/Pool.cpp b/src/modules/audio/openal/Pool.cpp index 74a6f3fe4..80a419602 100644 --- a/src/modules/audio/openal/Pool.cpp +++ b/src/modules/audio/openal/Pool.cpp @@ -44,7 +44,7 @@ namespace openal alGenSources(NUM_SOURCES, sources); // Create the mutex. - mutex = SDL_CreateMutex(); + mutex = new thread::Mutex(); if(alGetError() != AL_NO_ERROR) throw love::Exception("Could not generate sources."); @@ -58,7 +58,7 @@ namespace openal { stop(); - SDL_DestroyMutex(mutex); + delete mutex; // Free all sources. alDeleteSources(NUM_SOURCES, sources); @@ -67,28 +67,30 @@ namespace openal bool Pool::isAvailable() const { bool has = false; - LOCK(mutex); - has = !available.empty(); - UNLOCK(mutex); + { + thread::Lock lock(mutex); + has = !available.empty(); + } return has; } bool Pool::isPlaying(Source * s) { bool p = false; - LOCK(mutex); - for(std::map::iterator i = playing.begin(); i != playing.end(); i++) { - if(i->first == s) - p = true; + thread::Lock lock(mutex); + for(std::map::iterator i = playing.begin(); i != playing.end(); i++) + { + if(i->first == s) + p = true; + } } - UNLOCK(mutex); return p; } void Pool::update() { - LOCK(mutex); + thread::Lock lock(mutex); std::map::iterator i = playing.begin(); @@ -107,8 +109,6 @@ namespace openal i++; } } - - UNLOCK(mutex); } int Pool::getNumSources() const @@ -126,7 +126,7 @@ namespace openal bool ok; out = 0; - LOCK(mutex); + thread::Lock lock(mutex); bool alreadyPlaying = findSource(source, out); @@ -160,14 +160,12 @@ namespace openal ok = true; } - UNLOCK(mutex); - return ok; } void Pool::stop() { - LOCK(mutex); + thread::Lock lock(mutex); for(std::map::iterator i = playing.begin(); i != playing.end(); i++) { i->first->stopAtomic(); @@ -175,66 +173,57 @@ namespace openal } playing.clear(); - - UNLOCK(mutex); } void Pool::stop(Source * source) { - LOCK(mutex); + thread::Lock lock(mutex); removeSource(source); - UNLOCK(mutex); } void Pool::pause() { - LOCK(mutex); + thread::Lock lock(mutex); for(std::map::iterator i = playing.begin(); i != playing.end(); i++) i->first->pauseAtomic(); - UNLOCK(mutex); } void Pool::pause(Source * source) { - LOCK(mutex); + thread::Lock lock(mutex); ALuint out; if(findSource(source, out)) source->pauseAtomic(); - UNLOCK(mutex); } void Pool::resume() { - LOCK(mutex); + thread::Lock lock(mutex); for(std::map::iterator i = playing.begin(); i != playing.end(); i++) i->first->resumeAtomic(); - UNLOCK(mutex); } void Pool::resume(Source * source) { - LOCK(mutex); + thread::Lock lock(mutex); ALuint out; if(findSource(source, out)) source->resumeAtomic(); - UNLOCK(mutex); } void Pool::rewind() { - LOCK(mutex); + thread::Lock lock(mutex); for(std::map::iterator i = playing.begin(); i != playing.end(); i++) i->first->rewindAtomic(); - UNLOCK(mutex); } void Pool::rewind(Source * source) { - LOCK(mutex); + thread::Lock lock(mutex); ALuint out; if(findSource(source, out)) source->rewindAtomic(); - UNLOCK(mutex); } void Pool::release(Source * source) diff --git a/src/modules/audio/openal/Pool.h b/src/modules/audio/openal/Pool.h index f83b5002d..02f0eae27 100644 --- a/src/modules/audio/openal/Pool.h +++ b/src/modules/audio/openal/Pool.h @@ -33,6 +33,7 @@ // LOVE #include #include +#include // OpenAL #ifdef LOVE_MACOSX @@ -70,7 +71,7 @@ namespace openal // Only one thread can access this object at the same time. This mutex will // make sure of that. - SDL_mutex * mutex; + thread::Mutex* mutex; public: diff --git a/src/modules/thread/sdl/Thread.cpp b/src/modules/thread/Thread.cpp similarity index 88% rename from src/modules/thread/sdl/Thread.cpp rename to src/modules/thread/Thread.cpp index e44e2738f..9c273e337 100644 --- a/src/modules/thread/sdl/Thread.cpp +++ b/src/modules/thread/Thread.cpp @@ -31,10 +31,14 @@ namespace love { namespace thread { -namespace sdl -{ - int threadfunc(ThreadData *comm) + + Thread::ThreadThread::ThreadThread(ThreadData* comm) + : comm(comm) { + + } + + void Thread::ThreadThread::main() { lua_State * L = lua_open(); luaL_openlibs(L); #ifdef LOVE_BUILD_STANDALONE @@ -53,17 +57,18 @@ namespace sdl lua_setfield(L, -2, "_curthread"); if(luaL_dostring(L, comm->getCode()) == 1) { - SDL_mutexP((SDL_mutex*) comm->mutex); - ThreadVariant *v = new ThreadVariant(lua_tostring(L, -1), lua_strlen(L, -1)); - comm->setValue("error", v); - v->release(); - SDL_mutexV((SDL_mutex*) comm->mutex); - SDL_CondBroadcast((SDL_cond*) comm->cond); + { + Lock lock((Mutex*) comm->mutex); + ThreadVariant *v = new ThreadVariant(lua_tostring(L, -1), lua_strlen(L, -1)); + comm->setValue("error", v); + v->release(); + } + ((Conditional*) comm->cond)->broadcast(); } lua_close(L); - return 0; } + ThreadVariant::ThreadVariant(bool boolean) { type = BOOLEAN; @@ -183,8 +188,8 @@ namespace sdl this->data = new char[len+1]; memset(this->data, 0, len+1); memcpy(this->data, data->getData(), len); - mutex = SDL_CreateMutex(); - cond = SDL_CreateCond(); + mutex = new Mutex(); + cond = new Conditional(); comm = new ThreadData(name.c_str(), name.length(), this->data, mutex, cond); } @@ -192,8 +197,8 @@ namespace sdl : handle(0), module(module), name(name), data(0), isThread(false) { module->retain(); - mutex = SDL_CreateMutex(); - cond = SDL_CreateCond(); + mutex = new Mutex(); + cond = new Conditional(); comm = new ThreadData(name.c_str(), name.length(), NULL, mutex, cond); } @@ -203,25 +208,27 @@ namespace sdl delete[] data; delete comm; module->unregister(name); - SDL_DestroyMutex(mutex); - SDL_DestroyCond(cond); + delete mutex; + delete cond; module->release(); } void Thread::start() { - if (!handle && isThread) - handle = SDL_CreateThread((int (*)(void*)) threadfunc, (void*) comm); + if (!handle && isThread) { + handle = new ThreadThread(comm); + handle->start(); + } } void Thread::kill() { if (handle) { - SDL_mutexP((SDL_mutex *) _gcmutex); - SDL_KillThread(handle); + Lock lock((Mutex *) _gcmutex); + handle->kill(); + delete handle; handle = 0; - SDL_mutexV((SDL_mutex *) _gcmutex); } } @@ -229,19 +236,20 @@ namespace sdl { if (handle) { - SDL_WaitThread(handle, NULL); + handle->wait(); + delete handle; handle = 0; } } void Thread::lock() { - SDL_mutexP(mutex); + mutex->lock(); } void Thread::unlock() { - SDL_mutexV(mutex); + mutex->unlock(); } std::string Thread::getName() @@ -264,7 +272,7 @@ namespace sdl { if (comm->getValue("error")) return 0; - SDL_CondWait(cond, mutex); + cond->wait(mutex); v = comm->getValue(name); } v->retain(); @@ -281,7 +289,7 @@ namespace sdl lock(); //this function explicitly locks comm->setValue(name, v); //because we need unlock(); //it to unlock here for the cond - SDL_CondBroadcast(cond); + cond->broadcast(); } ThreadModule::ThreadModule() @@ -294,6 +302,7 @@ namespace sdl for (threadlist_t::iterator i = threads.begin(); i != threads.end(); i++) { i->second->kill(); + delete i->second; } } @@ -333,6 +342,7 @@ namespace sdl if (threads.count(name) == 0) return; threadlist_t::iterator i = threads.find(name); + // FIXME: shouldn't the thread be deleted? threads.erase(i); } @@ -340,6 +350,6 @@ namespace sdl { return "love.thread.sdl"; } -} // sdl + } // thread } // love diff --git a/src/modules/thread/sdl/Thread.h b/src/modules/thread/Thread.h similarity index 89% rename from src/modules/thread/sdl/Thread.h rename to src/modules/thread/Thread.h index a6a706c77..2d1d5ace9 100644 --- a/src/modules/thread/sdl/Thread.h +++ b/src/modules/thread/Thread.h @@ -21,25 +21,24 @@ #ifndef LOVE_THREAD_SDL_THREAD_H #define LOVE_THREAD_SDL_THREAD_H -// SDL -#include -#include - // STL #include #include // LOVE -#include #include #include +#include +#include + namespace love { namespace thread { -namespace sdl -{ + + class ThreadModule; + enum ThreadVariantType { UNKNOWN = 0, @@ -98,15 +97,28 @@ namespace sdl class Thread : public love::Object { private: - SDL_Thread *handle; - love::thread::ThreadModule *module; + class ThreadThread: public ThreadBase { + private: + ThreadData* comm; + + protected: + virtual void main(); + + public: + ThreadThread(ThreadData* comm); + }; + + ThreadThread *handle; + + ThreadModule *module; ThreadData *comm; std::string name; char *data; - SDL_mutex *mutex; - SDL_cond *cond; + Mutex *mutex; + Conditional *cond; bool isThread; + public: Thread(love::thread::ThreadModule *module, const std::string & name, love::Data *data); Thread(love::thread::ThreadModule *module, const std::string & name); @@ -125,7 +137,7 @@ namespace sdl typedef std::map threadlist_t; - class ThreadModule : public love::thread::ThreadModule + class ThreadModule : public love::Module { private: threadlist_t threads; @@ -140,7 +152,6 @@ namespace sdl void unregister(const std::string & name); const char *getName() const; }; // ThreadModule -} // sdl } // thread } // love diff --git a/src/modules/thread/posix/threads.cpp b/src/modules/thread/posix/threads.cpp new file mode 100644 index 000000000..b4eee8af9 --- /dev/null +++ b/src/modules/thread/posix/threads.cpp @@ -0,0 +1,196 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 "threads.h" + +namespace love +{ +namespace thread +{ + + Mutex::Mutex() { + pthread_create_mutex(&mutex, NULL); + } + + Mutex::~Mutex() { + pthread_mutex_destroy(&mutex); + } + + void Mutex::lock() { + pthread_mutex_lock(&mutex); + } + + void Mutex::unlock() { + pthread_mutex_unlock(&mutex); + } + + + + + void* ThreadBase::thread_runner(void* param) { + ThreadBase* thread = (ThreadBase*)param; + thread->main(); + return NULL; + } + + + + ThreadBase::ThreadBase() : running(false) { + pthread_t thread; + } + + + + ThreadBase::~ThreadBase() { + if (running) { + wait(); + } + } + + + + bool ThreadBase::start() { + if (pthread_create(&thread, NULL, thread_runner, this)) { + return false; + } else { + running = true; + return true; + } + } + + + void ThreadBase::wait() { + pthread_join(thread, NULL); + running = false; + } + + + void ThreadBase::kill() { + // FIXME: I'm not sure about that one. + pthread_kill(thread, 9); + running = false; + } + + unsigned int ThreadBase::threadId() { + return (unsigned int)((size_t)pthread_self()); + } + + Semaphore::Semaphore(unsigned int initial_value) { + sem_init(&sem, 0, initial_value); + } + + Semaphore::~Semaphore() { + sem_destroy(&sem); + } + + unsigned int Semaphore::value() { + int val = 0; + if (sem_getvalue(&sem, &val)) { + return 0; + } else { + return val; + } + } + + void Semaphore::post() { + sem_post(&sem); + } + + bool Semaphore::wait(int timeout) { + if (timeout < 0) { + if (sem_wait(&sem)) { + return false; + } else { + return true; + } + } else if (timeout == 0) { + if (sem_trywait(&sem)) { + return false; + } else { + return true; + } + } else { + struct timespec ts; + ts.tv_sec = timeout/1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + if (sem_timedwait(&sem, &ts)) { + return false; // either timeout or error... + } else { + return true; + } + } + } + + + bool Semaphore::tryWait() { + if (sem_trywait(&sem)) { + return false; + } else { + return true; + } + } + + + + + Conditional::Conditional() { + pthread_cond_init(&cond, NULL); + } + + Conditional::~Conditional() { + pthread_cond_destroy(&cond); + } + + void Conditional::signal() { + pthread_cond_signal(&cond); + } + + void Conditional::broadcast() { + pthread_cond_broadcast(&cond); + } + + bool Conditional::wait(Mutex* mutex, int timeout) { + if (timeout < 0) { + if (pthread_cond_wait(cond, mutex->mutex)) { + return false; + } else { + return true; + } + } else { + struct timespec ts; + int ret; + + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + + ret = pthread_cond_timedwait(&cond, mutex->mutex, &ts); + if (ret == ETIMEDOUT) { + return false; + } else if (ret == 0) { + return true; + } else { + // something bad happend! + return false; + } + } + } +} // namespace thread +} // namespace love + diff --git a/src/modules/thread/posix/threads.h b/src/modules/thread/posix/threads.h new file mode 100644 index 000000000..352dfbe65 --- /dev/null +++ b/src/modules/thread/posix/threads.h @@ -0,0 +1,103 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 LOVE_PLATFORM_POSIX_THREADS_H_ +#define LOVE_PLATFORM_POSIX_THREADS_H_ + +#include +#include + +namespace love +{ +namespace thread +{ + + class Mutex { + private: + pthread_mutex_t mutex; + Mutex(const Mutex& mutex) {} + + friend class Conditional; + + public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + }; + + + class ThreadBase { + private: + pthread_t thread; + ThreadBase(ThreadBase& thread) {} + + static void* thread_runner(void* param); + + protected: + + virtual void main() = 0; + + public: + ThreadBase(); + virtual ~ThreadBase(); + + bool start(); + void wait(); + void kill(); + + static unsigned int threadId(); + }; + + class Semaphore { + private: + Semaphore(const Semaphore& sem) {} + sem_t semaphore; + + public: + Semaphore(unsigned int initial_value); + ~Semaphore(); + + unsigned int value(); + void post(); + bool wait(int timeout = -1); + bool tryWait(); + }; + + // Should conditional inherit from mutex? + class Conditional { + private: + pthread_cond_t cond; + + public: + Conditional(); + ~Conditional(); + + void signal(); + void broadcast(); + bool wait(Mutex* mutex, int timeout=-1); + }; + +} // namespace thread +} // namespace love + + +#endif // !LOVE_PLATFORM_POSIX_THREADS_H_ diff --git a/src/modules/thread/sdl/threads.cpp b/src/modules/thread/sdl/threads.cpp new file mode 100644 index 000000000..fc4e5dbf8 --- /dev/null +++ b/src/modules/thread/sdl/threads.cpp @@ -0,0 +1,164 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 "threads.h" + +namespace love +{ +namespace thread +{ + + Mutex::Mutex() { + mutex = SDL_CreateMutex(); + } + + Mutex::~Mutex() { + SDL_DestroyMutex(mutex); + } + + void Mutex::lock() { + SDL_mutexP(mutex); + } + + void Mutex::unlock() { + SDL_mutexV(mutex); + } + + + + int ThreadBase::thread_runner(void* param) { + ThreadBase* thread = (ThreadBase*)param; + thread->main(); + return 0; + } + + ThreadBase::ThreadBase() : running(false) { + SDL_Thread* thread; + } + + ThreadBase::~ThreadBase() { + if (running) { + wait(); + } + } + + bool ThreadBase::start() { + thread = SDL_CreateThread(thread_runner, this); + if (thread == NULL) { + return false; + } else { + running = true; + return true; + } + } + + void ThreadBase::wait() { + SDL_WaitThread(thread, NULL); + running = false; + } + + void ThreadBase::kill() { + SDL_KillThread(thread); + running = false; + } + + unsigned int ThreadBase::threadId() { + return (unsigned int)SDL_ThreadID(); + } + + Semaphore::Semaphore(unsigned int initial_value) { + semaphore = SDL_CreateSemaphore(initial_value); + } + + Semaphore::~Semaphore() { + SDL_DestroySemaphore(semaphore); + } + + unsigned int Semaphore::value() { + return SDL_SemValue(semaphore); + } + + void Semaphore::post() { + SDL_SemPost(semaphore); + } + + bool Semaphore::wait(int timeout) { + if (timeout < 0) { + return SDL_SemWait(semaphore) ? false : true; + } else if (timeout == 0) { + return SDL_SemTryWait(semaphore) ? false : true; + } else { + int ret = SDL_SemWaitTimeout(semaphore, timeout); + if (ret == SDL_MUTEX_TIMEDOUT) { + return false; + } else if (ret == 0) { + return true; + } else { + // some nasty error + return false; + } + } + } + + bool Semaphore::tryWait() { + return SDL_SemTryWait(semaphore) ? false : true; + } + + + + Conditional::Conditional() { + cond = SDL_CreateCond(); + } + + Conditional::~Conditional() { + SDL_DestroyCond(cond); + } + + void Conditional::signal() { + SDL_CondSignal(cond); + } + + void Conditional::broadcast() { + SDL_CondBroadcast(cond); + } + + bool Conditional::wait(Mutex* mutex, int timeout) { + if (timeout < 0) { + if (SDL_CondWait(cond, mutex->mutex)) { + // error + return false; + } else { + return true; + } + } else { + int ret = SDL_CondWaitTimeout(cond, mutex->mutex, timeout); + if (ret == SDL_MUTEX_TIMEDOUT) { + return false; + } else if (ret == 0) { + return true; + } else { + // some bad error + return false; + } + } + } + +} // namespace thread +} // namespace love diff --git a/src/modules/thread/sdl/threads.h b/src/modules/thread/sdl/threads.h new file mode 100644 index 000000000..a4e7e03e8 --- /dev/null +++ b/src/modules/thread/sdl/threads.h @@ -0,0 +1,104 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 LOVE_PLATFORM_SDL_THREADS_H_ +#define LOVE_PLATFORM_SDL_THREADS_H_ + +#include "SDL.h" + +namespace love +{ +namespace thread +{ + + class Mutex { + private: + SDL_mutex* mutex; + Mutex(const Mutex& mutex) {} + + friend class Conditional; + + public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + }; + + + + class ThreadBase { + private: + SDL_Thread* thread; + ThreadBase(ThreadBase& thread) {} + bool running; + + static int thread_runner(void* param); + + protected: + + virtual void main() = 0; + + public: + ThreadBase(); + virtual ~ThreadBase(); + + bool start(); + void wait(); + void kill(); // FIXME: not supported by SDL (SDL's kill is probably cancel)? + + static unsigned int threadId(); + }; + + class Semaphore { + private: + Semaphore(const Semaphore& sem) {} + SDL_sem* semaphore; + + public: + Semaphore(unsigned int initial_value); + ~Semaphore(); + + unsigned int value(); + void post(); + bool wait(int timeout = -1); + bool tryWait(); + }; + + // Should conditional inherit from mutex? + class Conditional { + private: + SDL_cond* cond; + + public: + Conditional(); + ~Conditional(); + + void signal(); + void broadcast(); + bool wait(Mutex* mutex, int timeout=-1); + }; + +} // namespace thread +} // namespace love + + +#endif // !LOVE_PLATFORM_SDL_THREADS_H_ diff --git a/src/modules/thread/threads.cpp b/src/modules/thread/threads.cpp new file mode 100644 index 000000000..bc4a9fed2 --- /dev/null +++ b/src/modules/thread/threads.cpp @@ -0,0 +1,49 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 "threads.h" + +#if LOVE_THREADS == LOVE_THREADS_POSIX +# include "posix/threads.cpp" +#elif LOVE_THREADS == LOVE_THREADS_WIN32 +# include "win32/threads.cpp" +#elif LOVE_THREADS == LOVE_THREADS_SDL +# include "sdl/threads.cpp" +#endif + +namespace love +{ +namespace thread +{ + + const char* threadAPI() { +#if LOVE_THREADS == LOVE_THREADS_POSIX + return "posix"; +#elif LOVE_THREADS == LOVE_THREADS_WIN32 + return "win32"; +#elif LOVE_THREADS == LOVE_THREADS_SDL + return "sdl"; +#else + return "unknown"; // ?!?!?! +#endif + } + +} // namespace thread +} // namespace love diff --git a/src/modules/thread/threads.h b/src/modules/thread/threads.h new file mode 100644 index 000000000..fbe68eb20 --- /dev/null +++ b/src/modules/thread/threads.h @@ -0,0 +1,82 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 LOVE_THREADS_H_ +#define LOVE_THREADS_H_ + +#define LOVE_THREADS_SDL 0 +#define LOVE_THREADS_WIN32 1 +#define LOVE_THREADS_POSIX 2 + +// Choose correct threads API. +// Headless love uses either windows threads or posix threads. +// Headed (standard) love uses SDL threads. +#ifdef LOVE_HEADLESS +# ifdef WIN32 +# define LOVE_THREADS LOVE_THREADS_WIN32 +# else +# define LOVE_THREADS LOVE_THREADS_POSIX +# endif +#else +# define LOVE_THREADS LOVE_THREADS_SDL +#endif + + +// include the correct header +#if LOVE_THREADS == LOVE_THREADS_POSIX +# include "posix/threads.h" +#elif LOVE_THREADS == LOVE_THREADS_WIN32 +# include "win32/threads.h" +#elif LOVE_THREADS == LOVE_THREADS_SDL +# include "sdl/threads.h" +#endif + + +namespace love +{ +namespace thread +{ + + const char* threadAPI(); + + class Lock { + private: + Mutex* mutex; + + Lock(Lock& lock) {} + + public: + Lock(Mutex* m): mutex(m) { + mutex->lock(); + } + + Lock(Mutex& m): mutex(&m) { + mutex->lock(); + } + + ~Lock() { + mutex->unlock(); + } + }; + +} // namespace thread +} // namespace love + +#endif /* LOVE_THREADS_H_ */ diff --git a/src/modules/thread/win32/threads.cpp b/src/modules/thread/win32/threads.cpp new file mode 100644 index 000000000..087a61cd2 --- /dev/null +++ b/src/modules/thread/win32/threads.cpp @@ -0,0 +1,202 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 "threads.h" + +namespace love +{ +namespace thread +{ + + Mutex::Mutex() { + InitializeCriticalSection(&mutex); + } + + + + Mutex::~Mutex() { + DeleteCriticalSection(&mutex); + } + + + + void Mutex::lock() { + EnterCriticalSection(&mutex); + } + + + + void Mutex::unlock() { + LeaveCriticalSection(&mutex); + } + + + + + int ThreadBase::thread_runner(void* param) { + ThreadBase* thread = (ThreadBase*)param; + thread->main(); + return 0; + } + + ThreadBase::ThreadBase() : running(false) { + HANDLE thread; + } + + + + ThreadBase::~ThreadBase() { + if (running) { + wait(); + } + } + + + + bool ThreadBase::start() { + thread = CreateThread(NULL, 0, run_thread, rd, NULL); + if (thread == NULL) { + return false; + } else { + running = true; + return true; + } + } + + void ThreadBase::wait() { + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + running = false; + } + + void ThreadBase::kill() { + TerminateThread(thread, FALSE); + running = false; + } + + unsigned int ThreadBase::threadId() { + return (unsigned int)GetCurrentThreadId(); + } + + + + + Semaphore::Semaphore(unsigned int initial_value) + : count(initial_value) { + semaphore = CreateSemaphore(NULL, initial_value, 65535, NULL); + } + + Semaphore::~Semaphore() { + CloseHandle(semaphore); + } + + unsigned int Semaphore::value() { + return count; + } + + void Semaphore::post() { + InterlockedIncrement(&count); + if (ReleaseSemaphore(semaphore, 1, NULL) == FALSE) { + InterlockedDecrement(&count); + } + } + + bool Semaphore::wait(int timeout) { + int result; + + result = WaitForSingleObject(semaphore, timeout < 0 ? INFINITE : timeout); + if (result == WAIT_OBJECT_0) { + InterlockedDecrement(&count); + return true; + } else if (result == WAIT_TIMEOUT) { + return false; + } else { + // error + return false; + } + } + + bool Semaphore::tryWait() { + return wait(0); + } + + + // Conditional variable implementation based on semaphores. + // Vista+ builds should probably use CONDITIONAL_VARIABLE. + // + // based on http://www.cs.wustl.edu/~schmidt/win32-cv-2.html and ACE + + + Conditional::Conditional() + : waiting(0), signals(0) { + + } + + Conditional::~Conditional() { + + } + + void Conditional::signal() { + mutex.lock(); + if (waiting > signals) { + signals++; + sem.post(); + mutex.unlock(); + done.wait(); + } else { + mutex.unlock(); + } + } + + void Conditional::broadcast() { + mutex.lock(); + if (waiting > signals) { + int num = waiting - signals; + signals = waiting; + for(int i = 0; i < num; i++) sem.post(); + mutex.unlock(); + for(int i = 0; i < num; i++) done.wait(); + } else { + mutex.unlock(); + } + } + + bool Conditional::wait(Mutex* cmutex, int timeout) { + mutex.lock(); + waiting++; + mutex.unlock(); + + cmutex->unlock(); + bool ret = sem.wait(timeout); + + + mutex.lock(); + if (signals > 0) { + if (!ret) sem.wait(); + done.post(); + signals--; + } + waiting--; + mutex.unlock(); + cmutex->lock(); + } + +} // namespace thread +} // namespace love diff --git a/src/modules/thread/win32/threads.h b/src/modules/thread/win32/threads.h new file mode 100644 index 000000000..5b197d30f --- /dev/null +++ b/src/modules/thread/win32/threads.h @@ -0,0 +1,106 @@ +/** +* Copyright (c) 2006-2011 LOVE Development Team +* +* 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 LOVE_PLATFROM_WIN32_THREADS_H_ +#define LOVE_PLATFROM_WIN32_THREADS_H_ + +#include + + +namespace love +{ +namespace thread +{ + + class Mutex { + private: + CRITICAL_SECTION mutex; + Mutex(const Mutex& mutex) {} + + public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + }; + + class ThreadBase { + private: + HANDLE thread; + ThreadBase(ThreadBase& thread) {} + bool running; + + static int thread_runner(void* param); + + protected: + + virtual void main() = 0; + + public: + ThreadBase(); + virtual ~ThreadBase(); + + bool start(); + void wait(); + void kill(); + + static unsigned int threadId(); + }; + + + class Semaphore { + private: + Semaphore(const Semaphore& sem) {} + HANDLE semaphore; + unsigned int count; + + public: + Semaphore(unsigned int initial_value); + ~Semaphore(); + + unsigned int value(); + void post(); + bool wait(int timeout = -1); + bool tryWait(); + }; + + + // Should conditional inherit from mutex? + class Conditional { + private: + Mutex mutex; + int waiting; + int signals; + Semaphore sem; + Semaphore done; + + public: + Conditional(); + ~Conditional(); + + void signal(); + void broadcast(); + bool wait(Mutex* cmutex, int timeout=-1); + }; +} // namespace thread +} // namespace love + +#endif /* LOVE_PLATFROM_WIN32_THREADS_H_ */ diff --git a/src/modules/thread/sdl/wrap_Thread.cpp b/src/modules/thread/wrap_Thread.cpp similarity index 99% rename from src/modules/thread/sdl/wrap_Thread.cpp rename to src/modules/thread/wrap_Thread.cpp index 37cd75f1f..5e149cd0c 100644 --- a/src/modules/thread/sdl/wrap_Thread.cpp +++ b/src/modules/thread/wrap_Thread.cpp @@ -35,8 +35,6 @@ namespace namespace love { namespace thread -{ -namespace sdl { Thread *luax_checkthread(lua_State *L, int idx) { @@ -390,4 +388,3 @@ namespace sdl } } } -} diff --git a/src/modules/thread/sdl/wrap_Thread.h b/src/modules/thread/wrap_Thread.h similarity index 98% rename from src/modules/thread/sdl/wrap_Thread.h rename to src/modules/thread/wrap_Thread.h index 186f92140..4a9af3131 100644 --- a/src/modules/thread/sdl/wrap_Thread.h +++ b/src/modules/thread/wrap_Thread.h @@ -29,8 +29,6 @@ namespace love { extern StringMap types; namespace thread -{ -namespace sdl { Thread *luax_checkthread(lua_State *L, int idx); int w_Thread_start(lua_State *L); @@ -49,7 +47,7 @@ namespace sdl int w_getThread(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_thread(lua_State * L); -} // sdl + } // thread } // love diff --git a/src/modules/timer/sdl/Timer.cpp b/src/modules/timer/sdl/Timer.cpp index 49234d046..ec6aa57bc 100644 --- a/src/modules/timer/sdl/Timer.cpp +++ b/src/modules/timer/sdl/Timer.cpp @@ -19,6 +19,7 @@ **/ #include +#include #ifdef LOVE_WINDOWS # include @@ -81,7 +82,7 @@ namespace sdl void Timer::sleep(int ms) { if(ms > 0) - SDL_Delay(ms); + delay(ms); } float Timer::getDelta() const