Browse Source

Multiple thread backends.

Przemysław Grzywacz 14 năm trước cách đây
mục cha
commit
1d5fca6f46

+ 3 - 0
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/*

+ 41 - 0
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

+ 9 - 17
src/modules/thread/ThreadModule.h → 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 <common/Module.h>
-#include <string>
+#include <thread/threads.h>
 
-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_ */

+ 5 - 8
src/common/runtime.cpp

@@ -25,17 +25,15 @@
 #include "Object.h"
 #include "Reference.h"
 #include "StringMap.h"
+#include <thread/threads.h>
 
 // STD
 #include <iostream>
 
-// SDL
-#include <SDL_mutex.h>
-#include <SDL_thread.h>
 
 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;
 	}

+ 2 - 2
src/love.cpp

@@ -52,7 +52,7 @@
 #include <physics/box2d/wrap_Physics.h>
 #include <sound/wrap_Sound.h>
 #include <timer/sdl/wrap_Timer.h>
-#include <thread/sdl/wrap_Thread.h>
+#include <thread/wrap_Thread.h>
 
 // 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 }
 };
 

+ 33 - 17
src/modules/audio/openal/Audio.cpp

@@ -19,6 +19,7 @@
 **/
 
 #include "Audio.h"
+#include <common/delay.h>
 
 #include <sound/Decoder.h>
 
@@ -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
 	{

+ 21 - 7
src/modules/audio/openal/Audio.h

@@ -37,6 +37,7 @@
 
 #include "Source.h"
 #include "Pool.h"
+#include <thread/threads.h>
 
 // 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:
 

+ 22 - 33
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<Source *, ALuint>::iterator i = playing.begin(); i != playing.end(); i++)
 		{
-			if(i->first == s)
-				p = true;
+			thread::Lock lock(mutex);
+			for(std::map<Source *, ALuint>::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<Source *, ALuint>::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<Source *, ALuint>::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<Source *, ALuint>::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<Source *, ALuint>::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<Source *, ALuint>::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)

+ 2 - 1
src/modules/audio/openal/Pool.h

@@ -33,6 +33,7 @@
 // LOVE
 #include <common/config.h>
 #include <common/Exception.h>
+#include <thread/threads.h>
 
 // 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:
 

+ 37 - 27
src/modules/thread/sdl/Thread.cpp → 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

+ 24 - 13
src/modules/thread/sdl/Thread.h → src/modules/thread/Thread.h

@@ -21,25 +21,24 @@
 #ifndef LOVE_THREAD_SDL_THREAD_H
 #define LOVE_THREAD_SDL_THREAD_H
 
-// SDL
-#include <SDL_thread.h>
-#include <SDL_mutex.h>
-
 // STL
 #include <map>
 #include <string>
 
 // LOVE
-#include <thread/ThreadModule.h>
 #include <filesystem/File.h>
 #include <common/runtime.h>
+#include <common/Module.h>
+#include <thread/threads.h>
+
 
 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<std::string, Thread*> 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
 

+ 196 - 0
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
+

+ 103 - 0
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 <pthread.h>
+#include <semaphore.h>
+
+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_

+ 164 - 0
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

+ 104 - 0
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_

+ 49 - 0
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

+ 82 - 0
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_ */

+ 202 - 0
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

+ 106 - 0
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 <windows.h>
+
+
+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_ */

+ 0 - 3
src/modules/thread/sdl/wrap_Thread.cpp → 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
 	}
 }
 }
-}

+ 1 - 3
src/modules/thread/sdl/wrap_Thread.h → src/modules/thread/wrap_Thread.h

@@ -29,8 +29,6 @@ namespace love
 {
 	extern StringMap<Type, TYPE_MAX_ENUM> 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
 

+ 2 - 1
src/modules/timer/sdl/Timer.cpp

@@ -19,6 +19,7 @@
 **/
 
 #include <common/config.h>
+#include <common/delay.h>
 
 #ifdef LOVE_WINDOWS
 #	include <windows.h>
@@ -81,7 +82,7 @@ namespace sdl
 	void Timer::sleep(int ms)
 	{
 		if(ms > 0)
-			SDL_Delay(ms);
+			delay(ms);
 	}
 
 	float Timer::getDelta() const