Bart van Strien avatar Bart van Strien committed ee1448b

Fix race conditions and deadlocks in Channels (hopefully) (issue #554)

Also, this is a giant commit because of the official Code Style (tm) was applied.

Comments (0)

Files changed (15)

src/modules/thread/Channel.cpp

 
 namespace
 {
-	union uslong
-	{
-		unsigned long u;
-		long i;
-	};
+union uslong
+{
+	unsigned long u;
+	long i;
+};
 
-	// target <= current, but semi-wrapsafe, one wrap, anyway
-	inline bool past(unsigned int target, unsigned int current)
-	{
-		if (target > current)
-			return false;
-		if (target == current)
-			return true;
+// target <= current, but semi-wrapsafe, one wrap, anyway
+inline bool past(unsigned int target, unsigned int current)
+{
+	if (target > current)
+		return false;
+	if (target == current)
+		return true;
 
-		uslong t, c;
-		t.u = target;
-		c.u = current;
+	uslong t, c;
+	t.u = target;
+	c.u = current;
 
-		return !(t.i < 0 && c.i > 0);
-	}
+	return !(t.i < 0 && c.i > 0);
+}
 }
 
 namespace love
 {
 namespace thread
 {
-static std::map<std::string, Channel*> namedChannels;
+static std::map<std::string, Channel *> namedChannels;
 static Mutex *namedChannelMutex;
+
 Channel *Channel::getChannel(const std::string &name)
 {
 	if (!namedChannelMutex)
 		namedChannelMutex = newMutex();
+
 	Lock l(namedChannelMutex);
 	if (!namedChannels.count(name))
 		namedChannels[name] = new Channel(name);
 	else
 		namedChannels[name]->retain();
+
 	return namedChannels[name];
 }
 
 Channel::Channel()
-	: named(false), sent(0), received(0)
+	: named(false)
+	, sent(0)
+	, received(0)
 {
 	mutex = newMutex();
 	cond = newConditional();
 }
 
 Channel::Channel(const std::string &name)
-	: named(true), name(name), sent(0), received(0)
+	: named(true)
+	, name(name)
+	, sent(0)
+	, received(0)
 {
 	mutex = newMutex();
 	cond = newConditional();
 
 	delete mutex;
 	delete cond;
+
 	if (named)
 		namedChannels.erase(name);
 }
 {
 	if (!var)
 		return 0;
+
 	Lock l(mutex);
 	var->retain();
+
 	// Keep a reference to ourselves
 	// if we're non-empty and named.
 	if (named && queue.empty())
 		retain();
+
 	queue.push(var);
 	cond->broadcast();
 
 	if (!var)
 		return;
 
+	mutex->lock();
 	unsigned long id = push(var);
 
-	mutex->lock();
 	while (!past(id, received))
-	{
 		cond->wait(mutex);
-	}
+
 	mutex->unlock();
 }
 
 	// if we're empty and named.
 	if (named && queue.empty())
 		release();
+
 	return var;
 } // NOTE: Returns a retained Variant
 
 Variant *Channel::demand()
 {
 	Variant *var;
+	mutex->lock();
 	while (!(var = pop()))
-	{
-		mutex->lock();
 		cond->wait(mutex);
-		mutex->unlock();
-	}
+
+	mutex->unlock();
 	return var;
 }
 
 		queue.pop();
 	}
 
+	// Finish all the supply waits
+	received = sent;
+	cond->broadcast();
+
 	// Once again, release our own
 	// reference if we're named.
 	if (named)
 		release();
 }
 
+void Channel::lockMutex()
+{
+	mutex->lock();
+}
+
+void Channel::unlockMutex()
+{
+	mutex->unlock();
+}
+
 void Channel::retain()
 {
 	if (named)

src/modules/thread/Channel.h

 {
 class Channel : public love::Object
 {
-private:
-	Mutex *mutex;
-	Conditional *cond;
-	std::queue<Variant*> queue;
-	bool named;
-	std::string name;
-	Channel(const std::string &name);
-
-	unsigned long sent;
-	unsigned long received;
+// FOR WRAPPER USE ONLY
+friend void retainVariant(Channel *, Variant *);
+friend void releaseVariant(Channel *, Variant *);
 
 public:
 	Channel();
 
 	void retain();
 	void release();
+
+private:
+	Channel(const std::string &name);
+	void lockMutex();
+	void unlockMutex();
+
+	Mutex *mutex;
+	Conditional *cond;
+	std::queue<Variant *> queue;
+	bool named;
+	std::string name;
+
+	unsigned long sent;
+	unsigned long received;
 }; // Channel
 } // thread
 } // love

src/modules/thread/LuaThread.cpp

 {
 namespace thread
 {
-	LuaThread::LuaThread(const std::string &name, love::Data *code)
-		: name(name), code(code)
-	{
-		code->retain();
-	}
+LuaThread::LuaThread(const std::string &name, love::Data *code)
+	: name(name), code(code)
+{
+	code->retain();
+}
 
-	LuaThread::~LuaThread()
-	{
-		code->release();
-	}
+LuaThread::~LuaThread()
+{
+	code->release();
+}
 
-	void LuaThread::threadFunction()
-	{
-		this->retain();
-		lua_State * L = lua_open();
-		luaL_openlibs(L);
-	#ifdef LOVE_BUILD_STANDALONE
-		love::luax_preload(L, luaopen_love, "love");
-		luaopen_love(L);
-	#endif // LOVE_BUILD_STANDALONE
-		luaopen_love_thread(L);
-		if (luaL_loadbuffer(L, (const char*) code->getData(), code->getSize(), name.c_str()) != 0)
+void LuaThread::threadFunction()
+{
+	this->retain();
+	lua_State * L = lua_open();
+	luaL_openlibs(L);
+#ifdef LOVE_BUILD_STANDALONE
+	love::luax_preload(L, luaopen_love, "love");
+	luaopen_love(L);
+#endif // LOVE_BUILD_STANDALONE
+	luaopen_love_thread(L);
+	if (luaL_loadbuffer(L, (const char *) code->getData(), code->getSize(), name.c_str()) != 0)
+		error = luax_tostring(L, -1);
+	else
+		if (lua_pcall(L, 0, 0, 0) != 0)
 			error = luax_tostring(L, -1);
-		else
-			if (lua_pcall(L, 0, 0, 0) != 0)
-				error = luax_tostring(L, -1);
-		lua_close(L);
-		this->release();
-	}
+	lua_close(L);
+	this->release();
+}
 
-	const std::string &LuaThread::getError()
-	{
-		return error;
-	}
+const std::string &LuaThread::getError()
+{
+	return error;
+}
 } // thread
 } // love

src/modules/thread/LuaThread.h

 {
 namespace thread
 {
-	class LuaThread : public love::Object, public Threadable
-	{
-	private:
-		love::Data *code;
-		std::string name;
-		std::string error;
+class LuaThread : public love::Object, public Threadable
+{
+public:
+	LuaThread(const std::string &name, love::Data *code);
+	~LuaThread();
+	void threadFunction();
+	const std::string &getError();
 
-	public:
-		LuaThread(const std::string &name, love::Data *code);
-		~LuaThread();
-		void threadFunction();
-		const std::string &getError();
-	};
+private:
+	love::Data *code;
+	std::string name;
+	std::string error;
+};
 } // thread
 } // love
 

src/modules/thread/ThreadModule.cpp

 {
 namespace thread
 {
-	const char *ThreadModule::getName() const
-	{
-		return "love.thread.sdl";
-	}
+const char *ThreadModule::getName() const
+{
+	return "love.thread.sdl";
+}
 
-	LuaThread *ThreadModule::newThread(const std::string &name, love::Data *data)
-	{
-		LuaThread *lt = new LuaThread(name, data);
-		return lt;
-	}
+LuaThread *ThreadModule::newThread(const std::string &name, love::Data *data)
+{
+	LuaThread *lt = new LuaThread(name, data);
+	return lt;
+}
 
-	Channel *ThreadModule::newChannel()
-	{
-		return new Channel();
-	}
+Channel *ThreadModule::newChannel()
+{
+	return new Channel();
+}
 
-	Channel *ThreadModule::getChannel(const std::string &name)
-	{
-		return Channel::getChannel(name);
-	}
-
+Channel *ThreadModule::getChannel(const std::string &name)
+{
+	return Channel::getChannel(name);
+}
 } // thread
 } // love

src/modules/thread/ThreadModule.h

 {
 namespace thread
 {
-	class ThreadModule : public love::Module
-	{
-	public:
-		virtual ~ThreadModule() {}
-		LuaThread *newThread(const std::string &name, love::Data *data);
-		Channel *newChannel();
-		Channel *getChannel(const std::string &name);
+class ThreadModule : public love::Module
+{
+public:
+	virtual ~ThreadModule() {}
+	LuaThread *newThread(const std::string &name, love::Data *data);
+	Channel *newChannel();
+	Channel *getChannel(const std::string &name);
 
-		const char *getName() const;
-	}; // ThreadModule
+	const char *getName() const;
+}; // ThreadModule
 } // thread
 } // love
 

src/modules/thread/sdl/Thread.cpp

 {
 namespace sdl
 {
-	Thread::Thread(Threadable *t)
-		: t(t), running(false), thread(0)
-	{
-	}
+Thread::Thread(Threadable *t)
+	: t(t)
+	, running(false)
+	, thread(0)
+{
+}
 
-	Thread::~Thread()
-	{
-		if (!running) // Clean up handle
-			wait();
-		/*
-		if (running)
-			wait();
-		FIXME: Needed for proper thread cleanup
-		*/
-	}
+Thread::~Thread()
+{
+	if (!running) // Clean up handle
+		wait();
+	/*
+	if (running)
+		wait();
+	FIXME: Needed for proper thread cleanup
+	*/
+}
 
-	bool Thread::start()
-	{
-		Lock l(mutex);
-		if (running)
-			return false;
-		if (thread) // Clean old handle up
-			SDL_WaitThread(thread, 0);
-		thread = SDL_CreateThread(thread_runner, this);
-		running = (thread != 0);
-		return running;
-	}
+bool Thread::start()
+{
+	Lock l(mutex);
+	if (running)
+		return false;
+	if (thread) // Clean old handle up
+		SDL_WaitThread(thread, 0);
+	thread = SDL_CreateThread(thread_runner, this);
+	running = (thread != 0);
+	return running;
+}
 
-	void Thread::wait()
-	{
-		mutex.lock();
-		if (!thread)
-			return;
-		mutex.unlock();
-		SDL_WaitThread(thread, 0);
-		Lock l(mutex);
-		running = false;
-		thread = 0;
-	}
+void Thread::wait()
+{
+	mutex.lock();
+	if (!thread)
+		return;
+	mutex.unlock();
+	SDL_WaitThread(thread, 0);
+	Lock l(mutex);
+	running = false;
+	thread = 0;
+}
 
-	void Thread::kill()
-	{
-		Lock l(mutex);
-		if (!running)
-			return;
-		SDL_KillThread(thread);
-		SDL_WaitThread(thread, 0);
-		running = false;
-		thread = 0;
-	}
+void Thread::kill()
+{
+	Lock l(mutex);
+	if (!running)
+		return;
+	SDL_KillThread(thread);
+	SDL_WaitThread(thread, 0);
+	running = false;
+	thread = 0;
+}
 
-	int Thread::thread_runner(void *data)
-	{
-		Thread *self = (Thread*) data; // some compilers don't like 'this'
-		self->t->threadFunction();
-		Lock l(self->mutex);
-		self->running = false;
-		return 0;
-	}
+int Thread::thread_runner(void *data)
+{
+	Thread *self = (Thread *) data; // some compilers don't like 'this'
+	self->t->threadFunction();
+	Lock l(self->mutex);
+	self->running = false;
+	return 0;
+}
 } // sdl
 } // thread
 } // love

src/modules/thread/sdl/Thread.h

 {
 namespace sdl
 {
-	class Thread : public thread::Thread
-	{
-	private:
-		Threadable *t;
-		bool running;
-		SDL_Thread *thread;
-		static int thread_runner(void *data);
-		Mutex mutex;
+class Thread : public thread::Thread
+{
+private:
+	Threadable *t;
+	bool running;
+	SDL_Thread *thread;
+	static int thread_runner(void *data);
+	Mutex mutex;
 
-	public:
-		Thread(Threadable *t);
-		~Thread();
-		bool start();
-		void kill();
-		void wait();
-	}; // Thread
+public:
+	Thread(Threadable *t);
+	~Thread();
+	bool start();
+	void kill();
+	void wait();
+}; // Thread
 } // sdl
 } // thread
 } // love

src/modules/thread/sdl/threads.cpp

 	// Yes, I realise this can be dangerous,
 	// however, you're asking for it if you're
 	// mixing thread implementations.
-	Mutex *mutex = (Mutex*) _mutex;
+	Mutex *mutex = (Mutex *) _mutex;
 	if (timeout < 0)
 		return !SDL_CondWait(cond, mutex->mutex);
 	else

src/modules/thread/sdl/threads.h

 {
 namespace sdl
 {
-	class Conditional;
+class Conditional;
 
-	class Mutex : public thread::Mutex
-	{
-	public:
-		Mutex();
-		~Mutex();
+class Mutex : public thread::Mutex
+{
+public:
+	Mutex();
+	~Mutex();
 
-		void lock();
-		void unlock();
+	void lock();
+	void unlock();
 
-	private:
-		SDL_mutex* mutex;
-		Mutex(const Mutex&/* mutex*/) {}
+private:
+	SDL_mutex *mutex;
+	Mutex(const Mutex&/* mutex*/) {}
 
-		friend class Conditional;
-	};
+	friend class Conditional;
+};
 
-	class Conditional : public thread::Conditional
-	{
-	public:
-		Conditional();
-		~Conditional();
+class Conditional : public thread::Conditional
+{
+public:
+	Conditional();
+	~Conditional();
 
-		void signal();
-		void broadcast();
-		bool wait(thread::Mutex* mutex, int timeout=-1);
+	void signal();
+	void broadcast();
+	bool wait(thread::Mutex *mutex, int timeout=-1);
 
-	private:
-		SDL_cond* cond;
-	};
+private:
+	SDL_cond *cond;
+};
 
 } // sdl
 } // thread

src/modules/thread/threads.h

 
 	virtual void signal() = 0;
 	virtual void broadcast() = 0;
-	virtual bool wait(Mutex* mutex, int timeout=-1) = 0;
+	virtual bool wait(Mutex *mutex, int timeout=-1) = 0;
 };
 
 class Lock
 {
 public:
-	Lock(Mutex* m);
-	Lock(Mutex& m);
+	Lock(Mutex *m);
+	Lock(Mutex &m);
 	~Lock();
 
 private:

src/modules/thread/wrap_Channel.cpp

 {
 namespace thread
 {
-	Channel *luax_checkchannel(lua_State *L, int idx)
+void retainVariant(Channel *c, Variant *v)
+{
+	c->lockMutex();
+	v->retain();
+	c->unlockMutex();
+}
+
+void releaseVariant(Channel *c, Variant *v)
+{
+	c->lockMutex();
+	v->release();
+	c->unlockMutex();
+}
+
+Channel *luax_checkchannel(lua_State *L, int idx)
+{
+	return luax_checktype<Channel>(L, idx, "Channel", THREAD_CHANNEL_T);
+}
+
+int w_Channel_push(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	Variant *var = Variant::fromLua(L, 2);
+	c->push(var);
+	releaseVariant(c, var);
+	return 0;
+}
+
+int w_Channel_supply(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	Variant *var = Variant::fromLua(L, 2);
+	c->supply(var);
+	releaseVariant(c, var);
+	return 0;
+}
+
+int w_Channel_pop(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	Variant *var = c->pop();
+	if (var)
 	{
-		return luax_checktype<Channel>(L, idx, "Channel", THREAD_CHANNEL_T);
+		var->toLua(L);
+		releaseVariant(c, var);
 	}
+	else
+		lua_pushnil(L);
+	return 1;
+}
 
-	int w_Channel_push(lua_State *L)
+int w_Channel_demand(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	Variant *var = c->demand();
+	var->toLua(L);
+	releaseVariant(c, var);
+	return 1;
+}
+
+int w_Channel_peek(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	Variant *var = c->peek();
+	if (var)
 	{
-		Channel *c = luax_checkchannel(L, 1);
-		Variant *var = Variant::fromLua(L, 2);
-		c->push(var);
-		var->release();
-		return 0;
+		var->toLua(L);
+		releaseVariant(c, var);
 	}
+	else
+		lua_pushnil(L);
+	return 1;
+}
 
-	int w_Channel_supply(lua_State *L)
-	{
-		Channel *c = luax_checkchannel(L, 1);
-		Variant *var = Variant::fromLua(L, 2);
-		c->supply(var);
-		var->release();
-		return 0;
-	}
+int w_Channel_count(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	lua_pushnumber(L, c->count());
+	return 1;
+}
 
-	int w_Channel_pop(lua_State *L)
-	{
-		Channel *c = luax_checkchannel(L, 1);
-		Variant *var = c->pop();
-		if (var)
-		{
-			var->toLua(L);
-			var->release();
-		}
-		else
-			lua_pushnil(L);
-		return 1;
-	}
+int w_Channel_clear(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	c->clear();
+	return 0;
+}
 
-	int w_Channel_demand(lua_State *L)
-	{
-		Channel *c = luax_checkchannel(L, 1);
-		Variant *var = c->demand();
-		var->toLua(L);
-		var->release();
-		return 1;
-	}
+static const luaL_Reg type_functions[] = {
+	{ "push", w_Channel_push },
+	{ "supply", w_Channel_supply },
+	{ "pop", w_Channel_pop },
+	{ "demand", w_Channel_demand },
+	{ "peek", w_Channel_peek },
+	{ "count", w_Channel_count },
+	{ "clear", w_Channel_clear },
+	{ 0, 0 }
+};
 
-	int w_Channel_peek(lua_State *L)
-	{
-		Channel *c = luax_checkchannel(L, 1);
-		Variant *var = c->peek();
-		if (var)
-		{
-			var->toLua(L);
-			var->release();
-		}
-		else
-			lua_pushnil(L);
-		return 1;
-	}
-
-	int w_Channel_count(lua_State *L)
-	{
-		Channel *c = luax_checkchannel(L, 1);
-		lua_pushnumber(L, c->count());
-		return 1;
-	}
-
-	int w_Channel_clear(lua_State *L)
-	{
-		Channel *c = luax_checkchannel(L, 1);
-		c->clear();
-		return 0;
-	}
-
-	static const luaL_Reg type_functions[] = {
-		{ "push", w_Channel_push },
-		{ "supply", w_Channel_supply },
-		{ "pop", w_Channel_pop },
-		{ "demand", w_Channel_demand },
-		{ "peek", w_Channel_peek },
-		{ "count", w_Channel_count },
-		{ "clear", w_Channel_clear },
-		{ 0, 0 }
-	};
-
-	extern "C" int luaopen_channel(lua_State *L)
-	{
-		return luax_register_type(L, "Channel", type_functions);
-	}
+extern "C" int luaopen_channel(lua_State *L)
+{
+	return luax_register_type(L, "Channel", type_functions);
 }
 }
+}

src/modules/thread/wrap_Channel.h

 {
 namespace thread
 {
-	Channel *luax_checkchannel(lua_State *L, int idx);
-	int w_Channel_push(lua_State *L);
-	int w_Channel_supply(lua_State *L);
-	int w_Channel_pop(lua_State *L);
-	int w_Channel_demand(lua_State *L);
-	int w_Channel_peek(lua_State *L);
-	int w_Channel_count(lua_State *L);
-	int w_Channel_clear(lua_State *L);
+Channel *luax_checkchannel(lua_State *L, int idx);
+int w_Channel_push(lua_State *L);
+int w_Channel_supply(lua_State *L);
+int w_Channel_pop(lua_State *L);
+int w_Channel_demand(lua_State *L);
+int w_Channel_peek(lua_State *L);
+int w_Channel_count(lua_State *L);
+int w_Channel_clear(lua_State *L);
 
-	extern "C" int luaopen_channel(lua_State *L);
+extern "C" int luaopen_channel(lua_State *L);
 } // thread
 } // love
 

src/modules/thread/wrap_ThreadModule.cpp

 {
 namespace thread
 {
-	static ThreadModule *instance = 0;
+static ThreadModule *instance = 0;
 
-	int w_newThread(lua_State *L)
+int w_newThread(lua_State *L)
+{
+	std::string name = "Thread code";
+	love::Data *data;
+	if (lua_isstring(L, 1))
+		luax_convobj(L, 1, "filesystem", "newFile");
+	if (luax_istype(L, 1, FILESYSTEM_FILE_T))
 	{
-		std::string name = "Thread code";
-		love::Data *data;
-		if (lua_isstring(L, 1))
-			luax_convobj(L, 1, "filesystem", "newFile");
-		if (luax_istype(L, 1, FILESYSTEM_FILE_T))
+		try
 		{
-			try
-			{
-				love::filesystem::File * file = luax_checktype<love::filesystem::File>(L, 1, "File", FILESYSTEM_FILE_T);
-				name = std::string("@") + file->getFilename();
-				data = file->read();
-			}
-			catch (love::Exception & e)
-			{
-				return luaL_error(L, "%s", e.what());
-			}
+			love::filesystem::File * file = luax_checktype<love::filesystem::File>(L, 1, "File", FILESYSTEM_FILE_T);
+			name = std::string("@") + file->getFilename();
+			data = file->read();
 		}
-		else if (luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
+		catch (love::Exception & e)
 		{
-			love::filesystem::FileData * fdata = luax_checktype<love::filesystem::FileData>(L, 1, "FileData", FILESYSTEM_FILE_DATA_T);
-			name = std::string("@") + fdata->getFilename();
-			data = fdata;
-			data->retain();
+			return luaL_error(L, "%s", e.what());
 		}
-		else
+	}
+	else if (luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
+	{
+		love::filesystem::FileData * fdata = luax_checktype<love::filesystem::FileData>(L, 1, "FileData", FILESYSTEM_FILE_DATA_T);
+		name = std::string("@") + fdata->getFilename();
+		data = fdata;
+		data->retain();
+	}
+	else
+	{
+		data = luax_checktype<love::Data>(L, 1, "Data", DATA_T);
+		data->retain();
+	}
+	LuaThread *t = instance->newThread(name, data);
+	// do not worry, file->read() returns retained data
+	data->release();
+	luax_newtype(L, "Thread", THREAD_THREAD_T, (void *)t);
+	return 1;
+}
+
+int w_newChannel(lua_State *L)
+{
+	Channel *c = instance->newChannel();
+	luax_newtype(L, "Channel", THREAD_CHANNEL_T, (void *)c);
+	return 1;
+}
+
+int w_getChannel(lua_State *L)
+{
+	std::string name = luax_checkstring(L, 1);
+	Channel *c = instance->getChannel(name);
+	luax_newtype(L, "Channel", THREAD_CHANNEL_T, (void *)c);
+	return 1;
+}
+
+// List of functions to wrap.
+static const luaL_Reg module_functions[] = {
+	{ "newThread", w_newThread },
+	{ "newChannel", w_newChannel },
+	{ "getChannel", w_getChannel },
+	{ 0, 0 }
+};
+
+static const lua_CFunction types[] = {
+	luaopen_thread,
+	luaopen_channel,
+	0
+};
+
+extern "C" int luaopen_love_thread(lua_State *L)
+{
+	if (instance == 0)
+	{
+		try
 		{
-			data = luax_checktype<love::Data>(L, 1, "Data", DATA_T);
-			data->retain();
+			instance = new ThreadModule();
 		}
-		LuaThread *t = instance->newThread(name, data);
-		// do not worry, file->read() returns retained data
-		data->release();
-		luax_newtype(L, "Thread", THREAD_THREAD_T, (void*)t);
-		return 1;
+		catch (Exception & e)
+		{
+			return luaL_error(L, "%s", e.what());
+		}
 	}
+	else
+		instance->retain();
 
-	int w_newChannel(lua_State *L)
-	{
-		Channel *c = instance->newChannel();
-		luax_newtype(L, "Channel", THREAD_CHANNEL_T, (void*)c);
-		return 1;
-	}
+	WrappedModule w;
+	w.module = instance;
+	w.name = "thread";
+	w.flags = MODULE_T;
+	w.functions = module_functions;
+	w.types = types;
 
-	int w_getChannel(lua_State *L)
-	{
-		std::string name = luax_checkstring(L, 1);
-		Channel *c = instance->getChannel(name);
-		luax_newtype(L, "Channel", THREAD_CHANNEL_T, (void*)c);
-		return 1;
-	}
-
-	// List of functions to wrap.
-	static const luaL_Reg module_functions[] = {
-		{ "newThread", w_newThread },
-		{ "newChannel", w_newChannel },
-		{ "getChannel", w_getChannel },
-		{ 0, 0 }
-	};
-
-	static const lua_CFunction types[] = {
-		luaopen_thread,
-		luaopen_channel,
-		0
-	};
-
-	extern "C" int luaopen_love_thread(lua_State *L)
-	{
-		if (instance == 0)
-		{
-			try
-			{
-				instance = new ThreadModule();
-			}
-			catch (Exception & e)
-			{
-				return luaL_error(L, "%s", e.what());
-			}
-		}
-		else
-			instance->retain();
-
-		WrappedModule w;
-		w.module = instance;
-		w.name = "thread";
-		w.flags = MODULE_T;
-		w.functions = module_functions;
-		w.types = types;
-
-		return luax_register_module(L, w);
-	}
+	return luax_register_module(L, w);
 }
 }
+}

src/modules/thread/wrap_ThreadModule.h

 {
 namespace thread
 {
-	int w_newThread(lua_State *L);
-	int w_newChannel(lua_State *L);
-	int w_getChannel(lua_State *L);
+int w_newThread(lua_State *L);
+int w_newChannel(lua_State *L);
+int w_getChannel(lua_State *L);
 
-	extern "C" LOVE_EXPORT int luaopen_love_thread(lua_State * L);
+extern "C" LOVE_EXPORT int luaopen_love_thread(lua_State * L);
 
 } // thread
 } // love
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.