diff options
Diffstat (limited to 'src/Core/ThreadManager.cpp')
-rw-r--r-- | src/Core/ThreadManager.cpp | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/src/Core/ThreadManager.cpp b/src/Core/ThreadManager.cpp new file mode 100644 index 0000000..abc0bc6 --- /dev/null +++ b/src/Core/ThreadManager.cpp @@ -0,0 +1,143 @@ +/* + * ThreadManager.cpp + * + * Copyright (C) 2009 Matthias Schiffer <matthias@gamezock.de> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ThreadManager.h" + +#include "Logger.h" +#include "LogManager.h" + +#include <boost/bind.hpp> + +namespace Mad { +namespace Core { + +ThreadManager ThreadManager::threadManager; + + +void ThreadManager::workerFunc() { + while(true) { + boost::unique_lock<boost::mutex> lock(runLock); + + if(!running || !isThisWorkerThread()) + break; + + lock.unlock(); + + boost::unique_lock<boost::mutex> lock2(workLock); + while(work.empty()) { + workCond.wait(lock2); + + if(!running) + return; + } + + boost::function0<void> nextWork = work.front(); + work.pop(); + + lock2.unlock(); + + nextWork(); + } + + // And let the new worker thread join us... + pushWork(boost::bind(&ThreadManager::threadFinished, this, boost::this_thread::get_id())); +} + +void ThreadManager::detach() { + if(isThisMainThread()) { + Logger::log(Logger::CRITICAL, "Tried to detach main thread! This is just WRONG!"); + return; + } + + { + boost::lock_guard<boost::mutex> lock(runLock); + if(!running) + return; // There's no point in creating a new worker thread when we aren't running anymore + } + + threadLock.lock(); + + if(workerThread->get_id() == boost::this_thread::get_id()) {// Already detached? + threads.insert(std::make_pair(boost::this_thread::get_id(), workerThread)); + + workerThread.reset(new boost::thread(std::mem_fun(&ThreadManager::workerFunc), this)); + } + + threadLock.unlock(); +} + +void ThreadManager::pushWork(const boost::function0<void> &newWork) { + workLock.lock(); + work.push(newWork); + workLock.unlock(); + + workCond.notify_one(); +} + + +void ThreadManager::doInit() { + running = true; + + threadLock.lock(); + + ioWorker.reset(new boost::asio::io_service::work(ioService)); + + mainThreadId = boost::this_thread::get_id(); + workerThread.reset(new boost::thread(&ThreadManager::workerFunc, this)); + loggerThread.reset(new boost::thread(&LogManager::loggerThread, LogManager::get())); + ioThread.reset(new boost::thread((std::size_t(boost::asio::io_service::*)())&boost::asio::io_service::run, &ioService)); + + threadLock.unlock(); +} + +void ThreadManager::doDeinit() { + if(!isThisMainThread()) { + // TODO Critical error!!! + return; + } + + // First set running = false so the worker threads quit + boost::lock(runLock, workLock); + running = false; + + workLock.unlock(); + runLock.unlock(); + + workCond.notify_one(); + + // We don't have to lock threadLock as detach() won't change workerThread when running is false + workerThread->join(); + + // Now wait for all detached threads + while(!threads.empty()) { + threadFinished(threads.begin()->first); + } + + // IO thread is next + ioWorker.reset(); + ioService.stop(); + ioThread->join(); + + // Finally, the logger thread has to die + LogManager::get()->stopLoggerThread(); + loggerThread->join(); +} + +} +} |