/* * 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); } } }