From 8324b947487f72fd8cfc439ea5ae5bd1187fff1b Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 6 May 2009 17:39:30 +0200 Subject: Exception und ThreadHandler nach Net verschoben --- src/Net/ThreadManager.cpp | 215 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 src/Net/ThreadManager.cpp (limited to 'src/Net/ThreadManager.cpp') diff --git a/src/Net/ThreadManager.cpp b/src/Net/ThreadManager.cpp new file mode 100644 index 0000000..9eb965d --- /dev/null +++ b/src/Net/ThreadManager.cpp @@ -0,0 +1,215 @@ +/* + * ThreadManager.cpp + * + * Copyright (C) 2009 Matthias Schiffer + * + * 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 . + */ + +#include "ThreadManager.h" +#include "FdManager.h" + +#include +#include +#include + +#include + +namespace Mad { +namespace Net { + +ThreadManager ThreadManager::threadManager; + + +void ThreadManager::workerFunc() { + while(true) { + gl_lock_lock(runLock); + + if(!running || !isThisWorkerThread()) { + gl_lock_unlock(runLock); + return; + } + + gl_lock_unlock(runLock); + + gl_lock_lock(workLock); + while(work.empty()) { + gl_cond_wait(workCond, workLock); + + if(!running) { + gl_lock_unlock(workLock); + return; + } + } + + sigc::slot nextWork = work.front(); + work.pop(); + + gl_lock_unlock(workLock); + + nextWork(); + } + + // And let the new worker thread join us... + pushWork(sigc::bind(sigc::mem_fun(this, &ThreadManager::threadFinished), (gl_thread_t)gl_thread_self())); +} + +void ThreadManager::detach() { + if(!isThisMainThread()) { + Common::Logger::log(Common::Logger::CRITICAL, "Tried to detach main thread! This is just WRONG!"); + return; + } + + gl_lock_lock(runLock); + bool isRunning = running; + gl_lock_unlock(runLock); + if(!isRunning) // There's no point in creating a new worker thread when we aren't running anymore + return; + + gl_lock_lock(threadLock); + + if(workerThread == (gl_thread_t)gl_thread_self()) {// Already detached? + threads.insert(workerThread); + workerThread = gl_thread_create(&ThreadManager::workerStart, 0); + } + + gl_lock_unlock(threadLock); +} + +void ThreadManager::pushWork(const sigc::slot &newWork) { + gl_lock_lock(workLock); + + work.push(newWork); + + gl_cond_signal(workCond); + gl_lock_unlock(workLock); +} + +void ThreadManager::pushIO(const sigc::slot &newIO) { + gl_lock_lock(ioLock); + + ioQueue.push(newIO); + + if(!hasIO) { + hasIO = true; + ignore_value(write(ioNotifyPipe[1], "", 1)); + } + + gl_lock_unlock(ioLock); +} + +void ThreadManager::runIO() { + gl_lock_lock(ioLock); + + // Empty the pipe + char buf; + ignore_value(read(ioNotifyPipe[0], &buf, 1)); + hasIO = false; + + while(!ioQueue.empty()) { + sigc::slot handler = ioQueue.front(); + ioQueue.pop(); + gl_lock_unlock(ioLock); + + handler(); + + gl_lock_lock(ioLock); + } + + gl_lock_unlock(ioLock); +} + + +void ThreadManager::doInit() { + gl_lock_init(threadLock); + gl_lock_init(runLock); + + gl_lock_init(workLock); + gl_cond_init(workCond); + + gl_lock_init(ioLock); + hasIO = false; + + // TODO Error handling + pipe(ioNotifyPipe); + + fcntl(ioNotifyPipe[0], F_SETFL, fcntl(ioNotifyPipe[0], F_GETFL) | O_NONBLOCK); + fcntl(ioNotifyPipe[1], F_SETFL, fcntl(ioNotifyPipe[1], F_GETFL) | O_NONBLOCK); + + Net::FdManager::get()->registerFd(ioNotifyPipe[0], sigc::hide(sigc::mem_fun(this, &ThreadManager::runIO)), POLLIN); + + running = true; + + gl_lock_lock(threadLock); + + mainThread = (gl_thread_t)gl_thread_self(); + workerThread = gl_thread_create(&ThreadManager::workerStart, 0); + loggerThread = gl_thread_create(&ThreadManager::loggerStart, 0); + + gl_lock_unlock(threadLock); +} + +void ThreadManager::doDeinit() { + if(!isThisMainThread()) { + // TODO Critical error!!! + return; + } + + // First set running = false so the worker threads quit + gl_lock_lock(runLock); + gl_lock_lock(workLock); + + running = false; + gl_cond_signal(workCond); + + gl_lock_unlock(workLock); + gl_lock_unlock(runLock); + + // We don't have to lock threadLock as detach() won't change workerThread when running is false + gl_thread_join(workerThread, 0); + + // Now wait for all detached threads + gl_lock_lock(threadLock); + while(!threads.empty()) { + gl_thread_t thread = *threads.begin(); + gl_lock_unlock(threadLock); + + gl_thread_join(thread, 0); + + gl_lock_lock(threadLock); + threads.erase(thread); + } + gl_lock_unlock(threadLock); + + // Finally, the logger thread has to die + Common::LogManager::get()->stopLoggerThread(); + gl_thread_join(loggerThread, 0); + + // And then we clean everything up + Net::FdManager::get()->unregisterFd(ioNotifyPipe[0]); + + close(ioNotifyPipe[0]); + close(ioNotifyPipe[1]); + + gl_lock_destroy(ioLock); + + gl_cond_destroy(workCond); + gl_lock_destroy(workLock); + + gl_lock_destroy(runLock); + gl_lock_destroy(threadLock); +} + +} +} -- cgit v1.2.3