/** * Copyright (c) 2006-2013 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 "common/config.h" #include "Thread.h" #ifdef LOVE_BUILD_STANDALONE extern "C" int luaopen_love(lua_State *L); #endif // LOVE_BUILD_STANDALONE extern "C" int luaopen_love_thread(lua_State *L); namespace love { namespace thread { Thread::ThreadThread::ThreadThread(ThreadData *comm) : comm(comm) { } void Thread::ThreadThread::main() { 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); { size_t len; const char *name = comm->getName(&len); lua_pushlstring(L, name, len); } luax_convobj(L, lua_gettop(L), "thread", "getThread"); lua_getglobal(L, "love"); lua_pushvalue(L, -2); lua_setfield(L, -2, "_curthread"); if (luaL_dostring(L, comm->getCode()) == 1) { { Lock lock((Mutex *) comm->mutex); Variant *v = new Variant(lua_tostring(L, -1), lua_strlen(L, -1)); comm->setValue("error", v); v->release(); } ((Conditional *) comm->cond)->broadcast(); } lua_close(L); } ThreadData::ThreadData(const char *name, size_t len, const char *code, void *mutex, void *cond) : mutex(mutex), cond(cond), len(len) { this->name = new char[len+1]; memset(this->name, 0, len+1); memcpy(this->name, name, len); if (code) { len = strlen(code); this->code = new char[len+1]; memset(this->code, 0, len+1); memcpy(this->code, code, len); } else this->code = 0; } ThreadData::~ThreadData() { delete[] name; delete[] code; } const char *ThreadData::getCode() { return code; } const char *ThreadData::getName(size_t *len) { if (len) *len = this->len; return name; } Variant *ThreadData::getValue(const std::string &name) { if (shared.count(name) == 0) return 0; return shared[name]; } void ThreadData::clearValue(const std::string &name) { if (shared.count(name) == 0) return; shared[name]->release(); shared.erase(name); } void ThreadData::setValue(const std::string &name, Variant *v) { if (shared.count(name) != 0) shared[name]->release(); v->retain(); shared[name] = v; } std::vector ThreadData::getKeys() { std::vector keys; for (std::map::iterator it = shared.begin(); it != shared.end(); it++) { keys.push_back(it->first); } return keys; } Thread::Thread(love::thread::ThreadModule *module, const std::string &name, love::Data *data) : handle(0), module(module), name(name), isThread(true) { module->retain(); unsigned int len = data->getSize(); this->data = new char[len+1]; memset(this->data, 0, len+1); memcpy(this->data, data->getData(), len); mutex = new Mutex(); cond = new Conditional(); comm = new ThreadData(name.c_str(), name.length(), this->data, mutex, cond); } Thread::Thread(love::thread::ThreadModule *module, const std::string &name) : handle(0), module(module), name(name), data(0), isThread(false) { module->retain(); mutex = new Mutex(); cond = new Conditional(); comm = new ThreadData(name.c_str(), name.length(), NULL, mutex, cond); } Thread::~Thread() { if (data) delete[] data; delete comm; module->unregister(name); delete mutex; delete cond; module->release(); } void Thread::start() { if (!handle && isThread) { handle = new ThreadThread(comm); handle->start(); } } void Thread::kill() { if (handle) { Lock lock((Mutex *) _gcmutex); handle->kill(); delete handle; handle = 0; } } void Thread::wait() { if (handle) { handle->wait(); delete handle; handle = 0; } } void Thread::lock() { mutex->lock(); } void Thread::unlock() { mutex->unlock(); } std::string Thread::getName() { return name; } Variant *Thread::get(const std::string &name) { Variant *v = comm->getValue(name); if (v) v->retain(); return v; } std::vector Thread::getKeys() { return comm->getKeys(); } Variant *Thread::demand(const std::string &name) { Variant *v = comm->getValue(name); while (!v) { if (comm->getValue("error")) return 0; cond->wait(mutex); v = comm->getValue(name); } v->retain(); return v; } void Thread::clear(const std::string &name) { comm->clearValue(name); } void Thread::set(const std::string &name, Variant *v) { lock(); //this function explicitly locks comm->setValue(name, v); //because we need unlock(); //it to unlock here for the cond cond->broadcast(); } ThreadModule::ThreadModule() { threads["main"] = new Thread(this, "main"); } ThreadModule::~ThreadModule() { for (threadlist_t::iterator i = threads.begin(); i != threads.end(); i++) { i->second->kill(); delete i->second; } } Thread *ThreadModule::newThread(const std::string &name, love::Data *data) { if (threads.count(name) != 0) return 0; Thread *t = new Thread(this, name, data); threads[name] = t; return t; } Thread *ThreadModule::getThread(const std::string &name) { if (threads.count(name) == 0) return 0; threadlist_t::iterator i = threads.find(name); return i->second; } void ThreadModule::getThreads(Thread **list) { int c = 0; for (threadlist_t::iterator i = threads.begin(); i != threads.end(); i++, c++) { list[c] = i->second; } } unsigned ThreadModule::getThreadCount() const { return threads.size(); } void ThreadModule::unregister(const std::string &name) { if (threads.count(name) == 0) return; threadlist_t::iterator i = threads.find(name); // FIXME: shouldn't the thread be deleted? threads.erase(i); } const char *ThreadModule::getName() const { return "love.thread.sdl"; } } // thread } // love