1 /**
2 * Copyright (c) 2006-2011 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty.  In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 *    claim that you wrote the original software. If you use this software
14 *    in a product, an acknowledgment in the product documentation would be
15 *    appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 *    misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20 
21 #include <common/config.h>
22 
23 #include "Thread.h"
24 
25 #ifdef LOVE_BUILD_STANDALONE
26 extern "C" int luaopen_love(lua_State * L);
27 #endif // LOVE_BUILD_STANDALONE
28 extern "C" int luaopen_love_thread(lua_State *L);
29 
30 namespace love
31 {
32 namespace thread
33 {
34 namespace sdl
35 {
threadfunc(ThreadData * comm)36 	int threadfunc(ThreadData *comm)
37 	{
38 		lua_State * L = lua_open();
39 		luaL_openlibs(L);
40 	#ifdef LOVE_BUILD_STANDALONE
41 		love::luax_preload(L, luaopen_love, "love");
42 		luaopen_love(L);
43 	#endif // LOVE_BUILD_STANDALONE
44 		luaopen_love_thread(L);
45 		{
46 			size_t len;
47 			const char *name = comm->getName(&len);
48 			lua_pushlstring(L, name, len);
49 		}
50  		luax_convobj(L, lua_gettop(L), "thread", "getThread");
51 		lua_getglobal(L, "love");
52 		lua_pushvalue(L, -2);
53 		lua_setfield(L, -2, "_curthread");
54 		if(luaL_dostring(L, comm->getCode()) == 1)
55 		{
56 			SDL_mutexP((SDL_mutex*) comm->mutex);
57 			ThreadVariant *v = new ThreadVariant(lua_tostring(L, -1), lua_strlen(L, -1));
58 			comm->setValue("error", v);
59 			v->release();
60 			SDL_mutexV((SDL_mutex*) comm->mutex);
61 			SDL_CondBroadcast((SDL_cond*) comm->cond);
62 		}
63 		lua_close(L);
64 		return 0;
65 	}
66 
ThreadVariant(bool boolean)67 	ThreadVariant::ThreadVariant(bool boolean)
68 	{
69 		type = BOOLEAN;
70 		data.boolean = boolean;
71 	}
72 
ThreadVariant(double number)73 	ThreadVariant::ThreadVariant(double number)
74 	{
75 		type = NUMBER;
76 		data.number = number;
77 	}
78 
ThreadVariant(const char * string,size_t len)79 	ThreadVariant::ThreadVariant(const char *string, size_t len)
80 	{
81 		type = STRING;
82 		char *buf = new char[len+1];
83 		memset(buf, 0, len+1);
84 		memcpy(buf, string, len);
85 		data.string.str = buf;
86 		data.string.len = len;
87 	}
88 
ThreadVariant(void * userdata)89 	ThreadVariant::ThreadVariant(void *userdata)
90 	{
91 		type = LUSERDATA;
92 		data.userdata = userdata;
93 	}
94 
ThreadVariant(Type udatatype,void * userdata)95 	ThreadVariant::ThreadVariant(Type udatatype, void *userdata)
96 	{
97 		type = FUSERDATA;
98 		this->udatatype = udatatype;
99 		Proxy *p = (Proxy *) userdata;
100 		flags = p->flags;
101 		data.userdata = p->data;
102 		((love::Object *) data.userdata)->retain();
103 	}
104 
~ThreadVariant()105 	ThreadVariant::~ThreadVariant()
106 	{
107 		switch(type)
108 		{
109 			case STRING:
110 				delete[] data.string.str;
111 				break;
112 			case FUSERDATA:
113 				((love::Object *) data.userdata)->release();
114 				break;
115 			default:
116 				break;
117 		}
118 	}
119 
ThreadData(const char * name,size_t len,const char * code,void * mutex,void * cond)120 	ThreadData::ThreadData(const char *name, size_t len, const char *code, void *mutex, void *cond)
121 		: len(len), mutex(mutex), cond(cond)
122 	{
123 		this->name = new char[len+1];
124 		memset(this->name, 0, len+1);
125 		memcpy(this->name, name, len);
126 		if (code)
127 		{
128 			len = strlen(code);
129 			this->code = new char[len+1];
130 			memset(this->code, 0, len+1);
131 			memcpy(this->code, code, len);
132 		}
133 		else
134 			this->code = 0;
135 	}
136 
~ThreadData()137 	ThreadData::~ThreadData()
138 	{
139 		delete[] name;
140 		delete[] code;
141 	}
142 
getCode()143 	const char *ThreadData::getCode()
144 	{
145 		return code;
146 	}
147 
getName(size_t * len)148 	const char *ThreadData::getName(size_t *len)
149 	{
150 		if (len)
151 			*len = this->len;
152 		return name;
153 	}
154 
getValue(const std::string & name)155 	ThreadVariant* ThreadData::getValue(const std::string & name)
156 	{
157 		if (shared.count(name) == 0)
158 			return 0;
159 		return shared[name];
160 	}
161 
clearValue(const std::string & name)162 	void ThreadData::clearValue(const std::string & name)
163 	{
164 		if (shared.count(name) == 0)
165 			return;
166 		shared[name]->release();
167 		shared.erase(name);
168 	}
169 
setValue(const std::string & name,ThreadVariant * v)170 	void ThreadData::setValue(const std::string & name, ThreadVariant *v)
171 	{
172 		if (shared.count(name) != 0)
173 			shared[name]->release();
174 		v->retain();
175 		shared[name] = v;
176 	}
177 
Thread(love::thread::ThreadModule * module,const std::string & name,love::Data * data)178 	Thread::Thread(love::thread::ThreadModule *module, const std::string & name, love::Data *data)
179 		: handle(0), module(module), name(name), isThread(true)
180 	{
181 		module->retain();
182 		unsigned int len = data->getSize();
183 		this->data = new char[len+1];
184 		memset(this->data, 0, len+1);
185 		memcpy(this->data, data->getData(), len);
186 		mutex = SDL_CreateMutex();
187 		cond = SDL_CreateCond();
188 		comm = new ThreadData(name.c_str(), name.length(), this->data, mutex, cond);
189 	}
190 
Thread(love::thread::ThreadModule * module,const std::string & name)191 	Thread::Thread(love::thread::ThreadModule *module, const std::string & name)
192 		: handle(0), module(module), name(name), data(0), isThread(false)
193 	{
194 		module->retain();
195 		mutex = SDL_CreateMutex();
196 		cond = SDL_CreateCond();
197 		comm = new ThreadData(name.c_str(), name.length(), NULL, mutex, cond);
198 	}
199 
~Thread()200 	Thread::~Thread()
201 	{
202 		if (data)
203 			delete[] data;
204 		delete comm;
205 		module->unregister(name);
206 		SDL_DestroyMutex(mutex);
207 		SDL_DestroyCond(cond);
208 		module->release();
209 	}
210 
start()211 	void Thread::start()
212 	{
213 		if (!handle && isThread)
214 			handle = SDL_CreateThread((int (*)(void*)) threadfunc, (void*) comm);
215 	}
216 
kill()217 	void Thread::kill()
218 	{
219 		if (handle)
220 		{
221 			SDL_mutexP((SDL_mutex *) _gcmutex);
222 			SDL_KillThread(handle);
223 			handle = 0;
224 			SDL_mutexV((SDL_mutex *) _gcmutex);
225 		}
226 	}
227 
wait()228 	void Thread::wait()
229 	{
230 		if (handle)
231 		{
232 			SDL_WaitThread(handle, NULL);
233 			handle = 0;
234 		}
235 	}
236 
lock()237 	void Thread::lock()
238 	{
239 		SDL_mutexP(mutex);
240 	}
241 
unlock()242 	void Thread::unlock()
243 	{
244 		SDL_mutexV(mutex);
245 	}
246 
getName()247 	std::string Thread::getName()
248 	{
249 		return name;
250 	}
251 
receive(const std::string & name)252 	ThreadVariant *Thread::receive(const std::string & name)
253 	{
254 		ThreadVariant *v = comm->getValue(name);
255 		if (v)
256 			v->retain();
257 		return v;
258 	}
259 
demand(const std::string & name)260 	ThreadVariant *Thread::demand(const std::string & name)
261 	{
262 		ThreadVariant *v = comm->getValue(name);
263 		while (!v)
264 		{
265 			if (comm->getValue("error"))
266 				return 0;
267 			SDL_CondWait(cond, mutex);
268 			v = comm->getValue(name);
269 		}
270 		v->retain();
271 		return v;
272 	}
273 
clear(const std::string & name)274 	void Thread::clear(const std::string & name)
275 	{
276 		comm->clearValue(name);
277 	}
278 
send(const std::string & name,ThreadVariant * v)279 	void Thread::send(const std::string & name, ThreadVariant *v)
280 	{
281 		lock(); //this function explicitly locks
282 		comm->setValue(name, v); //because we need
283 		unlock(); //it to unlock here for the cond
284 		SDL_CondBroadcast(cond);
285 	}
286 
ThreadModule()287 	ThreadModule::ThreadModule()
288 	{
289 		threads["main"] = new Thread(this, "main");
290 	}
291 
~ThreadModule()292 	ThreadModule::~ThreadModule()
293 	{
294 		for (threadlist_t::iterator i = threads.begin(); i != threads.end(); i++)
295 		{
296 			i->second->kill();
297 		}
298 	}
299 
newThread(const std::string & name,love::Data * data)300 	Thread *ThreadModule::newThread(const std::string & name, love::Data *data)
301 	{
302 		if (threads.count(name) != 0)
303 			return 0;
304 		Thread *t = new Thread(this, name, data);
305 		threads[name] = t;
306 		return t;
307 	}
308 
getThread(const std::string & name)309 	Thread *ThreadModule::getThread(const std::string & name)
310 	{
311 		if (threads.count(name) == 0)
312 			return 0;
313 		threadlist_t::iterator i = threads.find(name);
314 		return i->second;
315 	}
316 
getThreads(Thread ** list)317 	void ThreadModule::getThreads(Thread ** list)
318 	{
319 		int c = 0;
320 		for (threadlist_t::iterator i = threads.begin(); i != threads.end(); i++, c++)
321 		{
322 			list[c] = i->second;
323 		}
324 	}
325 
getThreadCount() const326 	unsigned ThreadModule::getThreadCount() const
327 	{
328 		return threads.size();
329 	}
330 
unregister(const std::string & name)331 	void ThreadModule::unregister(const std::string & name)
332 	{
333 		if (threads.count(name) == 0)
334 			return;
335 		threadlist_t::iterator i = threads.find(name);
336 		threads.erase(i);
337 	}
338 
getName() const339 	const char *ThreadModule::getName() const
340 	{
341 		return "love.thread.sdl";
342 	}
343 } // sdl
344 } // thread
345 } // love
346