diff options
Diffstat (limited to 'src/Core')
-rw-r--r-- | src/Core/CMakeLists.txt | 25 | ||||
-rw-r--r-- | src/Core/ConfigEntry.cpp | 64 | ||||
-rw-r--r-- | src/Core/ConfigEntry.h | 96 | ||||
-rw-r--r-- | src/Core/ConfigManager.cpp | 141 | ||||
-rw-r--r-- | src/Core/ConfigManager.h | 69 | ||||
-rw-r--r-- | src/Core/Configurable.h | 47 | ||||
-rw-r--r-- | src/Core/Exception.cpp | 60 | ||||
-rw-r--r-- | src/Core/Exception.h | 65 | ||||
-rw-r--r-- | src/Core/Initializable.cpp | 69 | ||||
-rw-r--r-- | src/Core/Initializable.h | 54 | ||||
-rw-r--r-- | src/Core/LogManager.cpp | 146 | ||||
-rw-r--r-- | src/Core/LogManager.h | 146 | ||||
-rw-r--r-- | src/Core/Logger.cpp | 89 | ||||
-rw-r--r-- | src/Core/Logger.h | 68 | ||||
-rw-r--r-- | src/Core/LoggerBase.h | 79 | ||||
-rw-r--r-- | src/Core/RemoteLogger.h | 42 | ||||
-rw-r--r-- | src/Core/Signals.h | 27 | ||||
-rw-r--r-- | src/Core/Signals/Connection.h | 53 | ||||
-rw-r--r-- | src/Core/Signals/GenericSignal.h | 61 | ||||
-rw-r--r-- | src/Core/Signals/Signal0.h | 46 | ||||
-rw-r--r-- | src/Core/Signals/Signal1.h | 47 | ||||
-rw-r--r-- | src/Core/Signals/Signal2.h | 47 | ||||
-rw-r--r-- | src/Core/Signals/SignalBase.h | 53 | ||||
-rw-r--r-- | src/Core/ThreadManager.cpp | 143 | ||||
-rw-r--r-- | src/Core/ThreadManager.h | 113 | ||||
-rw-r--r-- | src/Core/Tokenizer.cpp | 131 | ||||
-rw-r--r-- | src/Core/Tokenizer.h | 44 |
27 files changed, 2025 insertions, 0 deletions
diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt new file mode 100644 index 0000000..30947e3 --- /dev/null +++ b/src/Core/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories(${INCLUDES}) +link_directories(${Boost_LIBRARY_DIRS}) + +add_library(Core + Signals/Connection.h + Signals/GenericSignal.h + Signals/Signal0.h + Signals/Signal1.h + Signals/Signal2.h + Signals/SignalBase.h + + ConfigEntry.cpp ConfigEntry.h + ConfigManager.cpp ConfigManager.h + Configurable.h + Exception.cpp Exception.h + Initializable.cpp Initializable.h + Logger.cpp Logger.h + LoggerBase.h + LogManager.cpp LogManager.h + RemoteLogger.h + Signals.h + ThreadManager.cpp ThreadManager.h + Tokenizer.cpp Tokenizer.h +) +target_link_libraries(Core ${Boost_LIBRARIES}) diff --git a/src/Core/ConfigEntry.cpp b/src/Core/ConfigEntry.cpp new file mode 100644 index 0000000..d85235a --- /dev/null +++ b/src/Core/ConfigEntry.cpp @@ -0,0 +1,64 @@ +/* + * ConfigEntry.cpp + * + * Copyright (C) 2008 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 "ConfigEntry.h" + +namespace Mad { +namespace Core { + +ConfigEntry::String& ConfigEntry::Entry::operator[] (size_t i) { + try { + return value.at(i); + } + catch(std::out_of_range &e) { + zero = String(); + return zero; + } +} + +const ConfigEntry::String& ConfigEntry::Entry::operator[] (size_t i) const { + try { + return value.at(i); + } + catch(std::out_of_range &e) { + return constZero; + } +} + +ConfigEntry::Entry& ConfigEntry::operator[] (size_t i) { + try { + return entries.at(i); + } + catch(std::out_of_range &e) { + zero = Entry(); + return zero; + } +} + +const ConfigEntry::Entry& ConfigEntry::operator[] (size_t i) const { + try { + return entries.at(i); + } + catch(std::out_of_range &e) { + return constZero; + } +} + +} +} diff --git a/src/Core/ConfigEntry.h b/src/Core/ConfigEntry.h new file mode 100644 index 0000000..42f8f91 --- /dev/null +++ b/src/Core/ConfigEntry.h @@ -0,0 +1,96 @@ +/* + * ConfigEntry.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_CONFIGENTRY_H_ +#define MAD_CORE_CONFIGENTRY_H_ + +#include <stdexcept> +#include <string> +#include <string.h> +#include <vector> + +namespace Mad { +namespace Core { + +class ConfigEntry { + public: + class String : public std::string { + public: + String() {} + String(const std::string &str) : std::string(str) {} + + bool matches(const std::string &str) const { + return (strcasecmp(c_str(), str.c_str()) == 0); + } + }; + + class Entry { + private: + String key; + std::vector<String> value; + + String zero, constZero; + + public: + Entry() {} + Entry(const std::vector<std::string> &args) { + if(args.empty()) + return; + + key = args.front(); + + value.assign(args.begin()+1, args.end()); + } + + bool empty() const { + return key.empty(); + } + + String &getKey() {return key;} + const String &getKey() const {return key;} + + size_t getSize() const {return value.size();} + + String& operator[] (size_t i); + const String& operator[] (size_t i) const; + }; + + private: + std::vector<Entry> entries; + Entry zero, constZero; + + public: + size_t getSize() const {return entries.size();} + + Entry& operator[] (size_t i); + const Entry& operator[] (size_t i) const; + + void push(const Entry &entry) { + entries.push_back(entry); + } + + void pop() { + entries.pop_back(); + } +}; + +} +} + +#endif /* MAD_CORE_CONFIGENTRY_H_ */ diff --git a/src/Core/ConfigManager.cpp b/src/Core/ConfigManager.cpp new file mode 100644 index 0000000..8b1ebd4 --- /dev/null +++ b/src/Core/ConfigManager.cpp @@ -0,0 +1,141 @@ +/* + * ConfigManager.cpp + * + * Copyright (C) 2008 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 "ConfigManager.h" +#include "ConfigEntry.h" +#include "Configurable.h" +#include "Logger.h" +#include "LogManager.h" +#include "Tokenizer.h" + +#include <fstream> +#include <stdexcept> + +namespace Mad { +namespace Core { + +ConfigManager ConfigManager::configManager; + + +bool ConfigManager::Compare::operator() (const Configurable *c1, const Configurable *c2) { + if(c1->getPriority() != c2->getPriority()) + return c1->getPriority() > c2->getPriority(); + else + return c1 < c2; +} + + +void ConfigManager::handleConfigEntry(const ConfigEntry &entry) { + bool handled = false; + + for(std::set<Configurable*>::iterator c = configurables.begin(); c != configurables.end(); ++c) { + if((*c)->handleConfigEntry(entry, handled)) + handled = true; + } + + if(!handled) + Logger::logf(Logger::WARNING, "Invalid config option '%s'.", entry[entry.getSize()-1].getKey().c_str()); +} + +bool ConfigManager::loadFile(const std::string &filename) { + if(finished) + return false; + + std::ifstream file(filename.c_str()); + ConfigEntry entry; + std::string line, input; + char delim; + std::vector<std::string> splitLine, lastConfigLine; + + if(!file.good()) + return false; + + while(!(file.eof() && line.empty() && input.empty())) { + while(input.empty() && !file.eof()) + std::getline(file, input); + + if(input.empty()) + break; + + size_t pos = input.find_first_of("#{}"); + if(pos == std::string::npos) { + line += input; + delim = '\n'; + input.clear(); + } + else { + line += input.substr(0, pos); + delim = input[pos]; + input = input.substr(pos+1); + } + + if(!Tokenizer::tokenize(line, splitLine)) { + line += delim; + continue; + } + + if(!splitLine.empty()) { + pos = line.find_first_of(" \t"); + + entry.push(splitLine); + handleConfigEntry(entry); + entry.pop(); + + lastConfigLine = splitLine; + } + + switch(delim) { + case '#': + input.clear(); + break; + case '{': + entry.push(lastConfigLine); + break; + case '}': + entry.pop(); + } + + line.clear(); + } + + // TODO Depth check + + return true; +} + +void ConfigManager::finish() { + if(finished) + return; + + for(std::set<Configurable*>::iterator c = configurables.begin(); c != configurables.end(); ++c) + (*c)->configFinished(); + + finished = true; +} + +ConfigManager::ConfigManager() : finished(false) { + registerConfigurable(LogManager::get()); +} + +ConfigManager::~ConfigManager() { + unregisterConfigurable(LogManager::get()); +} + +} +} diff --git a/src/Core/ConfigManager.h b/src/Core/ConfigManager.h new file mode 100644 index 0000000..2d49a97 --- /dev/null +++ b/src/Core/ConfigManager.h @@ -0,0 +1,69 @@ +/* + * ConfigManager.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_CONFIGMANAGER_H_ +#define MAD_CORE_CONFIGMANAGER_H_ + +#include <memory> +#include <set> +#include <string> + +namespace Mad { +namespace Core { + +class ConfigEntry; +class Configurable; + +class ConfigManager { + private: + struct Compare { + bool operator() (const Configurable *c1, const Configurable *c2); + }; + + static ConfigManager configManager; + + std::set<Configurable*, Compare> configurables; + bool finished; + + ConfigManager(); + ~ConfigManager(); + + void handleConfigEntry(const ConfigEntry &entry); + + public: + bool loadFile(const std::string &filename); + void finish(); + + void registerConfigurable(Configurable *c) { + configurables.insert(c); + } + + void unregisterConfigurable(Configurable *c) { + configurables.erase(c); + } + + static ConfigManager *get() { + return &configManager; + } +}; + +} +} + +#endif /* MAD_CORE_CONFIGMANAGER_H_ */ diff --git a/src/Core/Configurable.h b/src/Core/Configurable.h new file mode 100644 index 0000000..db2e4a0 --- /dev/null +++ b/src/Core/Configurable.h @@ -0,0 +1,47 @@ +/* + * Configurable.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_CONFIGURABLE_H_ +#define MAD_CORE_CONFIGURABLE_H_ + +namespace Mad { +namespace Core { + +class ConfigEntry; +class ConfigManager; + +class Configurable { + public: + virtual ~Configurable() {} + + virtual int getPriority() const {return 0;} + + protected: + friend class ConfigManager; + + Configurable() {} + + virtual bool handleConfigEntry(const ConfigEntry&, bool) {return false;} + virtual void configFinished() {} +}; + +} +} + +#endif /* MAD_CORE_CONFIGURABLE_H_ */ diff --git a/src/Core/Exception.cpp b/src/Core/Exception.cpp new file mode 100644 index 0000000..fbae6aa --- /dev/null +++ b/src/Core/Exception.cpp @@ -0,0 +1,60 @@ +/* + * Exception.cpp + * + * Copyright (C) 2008 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 "Exception.h" + +#include <cstring> + +namespace Mad { +namespace Core { + +std::string Exception::strerror() const { + std::string ret; + + if(!where.empty()) + ret = where + ": "; + + switch(errorCode) { + case SUCCESS: + return ret + "Success"; + case UNEXPECTED_PACKET: + return ret + "An unexpected packet was received"; + case INVALID_ACTION: + return ret + "The action is invalid"; + case NOT_AVAILABLE: + return ret + "Not available"; + case NOT_FINISHED: + return ret + "Not finished"; + case NOT_IMPLEMENTED: + return ret + "Not implemented"; + case INTERNAL_ERRNO: + return ret + std::strerror(subCode); + case INVALID_ADDRESS: + return ret + "Invalid address"; + case ALREADY_IDENTIFIED: + return ret + "The host is already identified"; + case UNKNOWN_DAEMON: + return ret + "The daemon is unknown"; + default: + return ret + "Unknown error"; + } +} + +} +} diff --git a/src/Core/Exception.h b/src/Core/Exception.h new file mode 100644 index 0000000..6c89549 --- /dev/null +++ b/src/Core/Exception.h @@ -0,0 +1,65 @@ +/* + * Exception.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_EXCEPTION_H_ +#define MAD_CORE_EXCEPTION_H_ + +#include <string> + +namespace Mad { +namespace Core { + +class Exception { + public: + enum ErrorCode { + SUCCESS = 0x0000, UNEXPECTED_PACKET = 0x0001, INVALID_ACTION = 0x0002, NOT_AVAILABLE = 0x0003, NOT_FINISHED = 0x0004, NOT_IMPLEMENTED = 0x0005, + INTERNAL_ERRNO = 0x0010, + INVALID_ADDRESS = 0x0020, + ALREADY_IDENTIFIED = 0x0030, UNKNOWN_DAEMON = 0x0031 + }; + + private: + std::string where; + + ErrorCode errorCode; + long subCode; + long subSubCode; + + public: + Exception(const std::string &where0, ErrorCode errorCode0 = SUCCESS, long subCode0 = 0, long subSubCode0 = 0) + : where(where0), errorCode(errorCode0), subCode(subCode0), subSubCode(subSubCode0) {} + Exception(ErrorCode errorCode0 = SUCCESS, long subCode0 = 0, long subSubCode0 = 0) : errorCode(errorCode0), subCode(subCode0), subSubCode(subSubCode0) {} + virtual ~Exception() {} + + const std::string& getWhere() const {return where;} + ErrorCode getErrorCode() const {return errorCode;} + long getSubCode() const {return subCode;} + long getSubSubCode() const {return subSubCode;} + + std::string strerror() const; + + operator bool() const { + return (errorCode != SUCCESS); + } +}; + +} +} + +#endif /* MAD_CORE_EXCEPTION_H_ */ diff --git a/src/Core/Initializable.cpp b/src/Core/Initializable.cpp new file mode 100644 index 0000000..95d527f --- /dev/null +++ b/src/Core/Initializable.cpp @@ -0,0 +1,69 @@ +/* + * Initializable.cpp + * + * Copyright (C) 2008 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 "Initializable.h" + +#include "ConfigManager.h" +#include "Configurable.h" +#include "Logger.h" + +namespace Mad { +namespace Core { + +std::stack<Initializable*> Initializable::initializedObjects; + +void Initializable::init() { + if(initialized) + return; + + if(initializing) { + Logger::log(Logger::CRITICAL, "Fatal initialization error: cyclic dependencies."); + std::terminate(); + } + + initializing = true; + + doInit(); + + Configurable *c = dynamic_cast<Configurable*>(this); + if(c) + ConfigManager::get()->registerConfigurable(c); + + initializing = false; + initialized = true; + initializedObjects.push(this); +} + +void Initializable::deinit() { + while(!initializedObjects.empty()) { + Initializable *in = initializedObjects.top(); + + Configurable *c = dynamic_cast<Configurable*>(in); + if(c) + ConfigManager::get()->unregisterConfigurable(c); + + in->doDeinit(); + in->initialized = false; + + initializedObjects.pop(); + } +} + +} +} diff --git a/src/Core/Initializable.h b/src/Core/Initializable.h new file mode 100644 index 0000000..e7d329d --- /dev/null +++ b/src/Core/Initializable.h @@ -0,0 +1,54 @@ +/* + * Initializable.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_INITIALIZABLE_H_ +#define MAD_CORE_INITIALIZABLE_H_ + +#include <stack> + +namespace Mad { +namespace Core { + +class Initializable { + private: + static std::stack<Initializable*> initializedObjects; + + bool initializing; + bool initialized; + + protected: + Initializable() : initializing(false), initialized(false) {} + virtual void doInit() {} + virtual void doDeinit() {} + + public: + virtual ~Initializable() {} + + void init(); + + bool isInitialized() const {return initialized;} + bool isInitializing() const {return initializing;} + + static void deinit(); +}; + +} +} + +#endif /* MAD_CORE_INITIALIZABLE_H_ */ diff --git a/src/Core/LogManager.cpp b/src/Core/LogManager.cpp new file mode 100644 index 0000000..bf9767b --- /dev/null +++ b/src/Core/LogManager.cpp @@ -0,0 +1,146 @@ +/* + * LogManager.cpp + * + * Copyright (C) 2008 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 "LogManager.h" +#include "ConfigEntry.h" + +#include <iostream> + +namespace Mad { +namespace Core { + +LogManager LogManager::logManager; + + +void LogManager::ConsoleLogger::logMessage(MessageCategory category _UNUSED_PARAMETER_, MessageLevel level, time_t timestamp _UNUSED_PARAMETER_, const std::string &message) { + if(level != CRITICAL) {// Critical messages are printed to cerr directly, so don't print them a second time + cerrLock.lock(); + std::cerr << message << std::endl; + cerrLock.unlock(); + } +} + +void LogManager::ConsoleLogger::logMessage(MessageCategory category _UNUSED_PARAMETER_, MessageLevel, time_t timestamp _UNUSED_PARAMETER_, const std::string &message, const std::string &messageSource) { + cerrLock.lock(); + std::cerr << message << " from " << messageSource << std::endl; + cerrLock.unlock(); +} + +void LogManager::ConsoleLogger::logMessageDirect(MessageCategory category _UNUSED_PARAMETER_, MessageLevel level _UNUSED_PARAMETER_, time_t timestamp _UNUSED_PARAMETER_, const std::string &message) { + cerrLock.lock(); + std::cerr << message << std::endl; + cerrLock.unlock(); +} + + +bool LogManager::handleConfigEntry(const ConfigEntry &entry, bool handled) { + if(handled) + return false; + + if(entry[0].getKey().matches("Logger")) { + if(entry[0][0].matches("Console")) { + if(entry[1].empty()) { + registerLogger(static_cast<Logger*>(&consoleLogger)); + return true; + } + } + else if(entry[1].empty()) { + Logger::logf(Logger::WARNING, "Unknown logger '%s'.", entry[0][0].c_str()); + return true; + } + } + + return false; +} + +void LogManager::configFinished() { + if(loggers.empty()) + registerLogger(static_cast<Logger*>(&consoleLogger)); + + // TODO Debug + consoleLogger.Logger::setLevel(LoggerBase::DEBUG); + + queueLock.lock(); + configured = true; + queueLock.unlock(); + queueCond.notify_one(); +} + +void LogManager::log(MessageCategory category, MessageLevel level, time_t timestamp, const std::string &message) { + if(level == LoggerBase::CRITICAL) + consoleLogger.logMessageDirect(category, level, timestamp, message); + + queueLock.lock(); + Message m = {category, level, timestamp, message}; + messageQueue.push(m); + queueLock.unlock(); + queueCond.notify_one(); +} + +void LogManager::log(MessageCategory category, MessageLevel level, time_t timestamp, const std::string &message, const std::string &source) { + queueLock.lock(); + RemoteMessage m = {category, level, timestamp, message, source}; + remoteMessageQueue.push(m); + queueLock.unlock(); + queueCond.notify_one(); +} + +void LogManager::loggerThread() { + boost::unique_lock<boost::mutex> lock(queueLock); + + running = true; + + while(running) { + while(running && ((messageQueue.empty() && messageQueue.empty()) || !configured)) + queueCond.wait(lock); + + while(!messageQueue.empty()) { + Message message = messageQueue.front(); + messageQueue.pop(); + lock.unlock(); + + loggerLock.lock(); + for(std::set<Logger*>::iterator logger = loggers.begin(); logger != loggers.end(); ++logger) { + if((*logger)->getLevel() >= message.level && (*logger)->isCategorySet(message.category)) + (*logger)->logMessage(message.category, message.level, message.timestamp, message.message); + } + loggerLock.unlock(); + + lock.lock(); + } + + while(!remoteMessageQueue.empty()) { + RemoteMessage message = remoteMessageQueue.front(); + remoteMessageQueue.pop(); + lock.unlock(); + + remoteLoggerLock.lock(); + for(std::set<RemoteLogger*>::iterator logger = remoteLoggers.begin(); logger != remoteLoggers.end(); ++logger) { + if((*logger)->getLevel() >= message.level && (*logger)->isCategorySet(message.category)) + (*logger)->logMessage(message.category, message.level, message.timestamp, message.message, message.source); + } + remoteLoggerLock.unlock(); + + lock.lock(); + } + } +} + +} +} diff --git a/src/Core/LogManager.h b/src/Core/LogManager.h new file mode 100644 index 0000000..6d75b82 --- /dev/null +++ b/src/Core/LogManager.h @@ -0,0 +1,146 @@ +/* + * LogManager.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_LOGMANAGER_H_ +#define MAD_CORE_LOGMANAGER_H_ + +#include <config.h> + +#include "Configurable.h" +#include "Logger.h" +#include "RemoteLogger.h" + +#include <queue> +#include <set> + +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/mutex.hpp> + +namespace Mad { +namespace Core { + +class ThreadManager; + +class LogManager : public Configurable { + private: + friend class ThreadManager; + + typedef LoggerBase::MessageCategory MessageCategory; + typedef LoggerBase::MessageLevel MessageLevel; + + struct Message { + MessageCategory category; + MessageLevel level; + time_t timestamp; + std::string message; + }; + + struct RemoteMessage { + MessageCategory category; + MessageLevel level; + time_t timestamp; + std::string message; + std::string source; + }; + + class ConsoleLogger : public Logger, public RemoteLogger { + private: + // For long messages, writing to cerr is not atomic + // -> lock cerr to prevent mixing messages up + boost::mutex cerrLock; + + protected: + virtual void logMessage(MessageCategory category _UNUSED_PARAMETER_, MessageLevel level, time_t timestamp _UNUSED_PARAMETER_, const std::string &message); + virtual void logMessage(MessageCategory category _UNUSED_PARAMETER_, MessageLevel, time_t timestamp _UNUSED_PARAMETER_, const std::string &message, const std::string &messageSource); + + public: + ConsoleLogger() {} + + void logMessageDirect(MessageCategory category _UNUSED_PARAMETER_, MessageLevel level _UNUSED_PARAMETER_, time_t timestamp _UNUSED_PARAMETER_, const std::string &message); + }; + + + static LogManager logManager; + + ConsoleLogger consoleLogger; + + std::set<Logger*> loggers; + std::set<RemoteLogger*> remoteLoggers; + bool configured, running; + + boost::mutex queueLock; + boost::condition_variable queueCond; + + boost::mutex loggerLock; + boost::mutex remoteLoggerLock; + + std::queue<Message> messageQueue; + std::queue<RemoteMessage> remoteMessageQueue; + + void loggerThread(); + void stopLoggerThread() { + queueLock.lock(); + running = false; + queueLock.unlock(); + queueCond.notify_one(); + } + + LogManager() : configured(false), running(false) {} + + protected: + virtual bool handleConfigEntry(const ConfigEntry &entry, bool handled); + virtual void configFinished(); + + public: + void log(MessageCategory category, MessageLevel level, time_t timestamp, const std::string &message); + void log(MessageCategory category, MessageLevel level, time_t timestamp, const std::string &message, const std::string &source); + + void registerLogger(Logger *logger) { + loggerLock.lock(); + loggers.insert(logger); + loggerLock.unlock(); + } + + void unregisterLogger(Logger *logger) { + loggerLock.lock(); + loggers.erase(logger); + loggerLock.unlock(); + } + + void registerLogger(RemoteLogger *logger) { + remoteLoggerLock.lock(); + remoteLoggers.insert(logger); + remoteLoggerLock.unlock(); + } + + void unregisterLogger(RemoteLogger *logger) { + remoteLoggerLock.lock(); + remoteLoggers.erase(logger); + remoteLoggerLock.unlock(); + } + + static LogManager *get() { + return &logManager; + } +}; + +} +} + +#endif /* MAD_CORE_LOGMANAGER_H_ */ diff --git a/src/Core/Logger.cpp b/src/Core/Logger.cpp new file mode 100644 index 0000000..e4e0341 --- /dev/null +++ b/src/Core/Logger.cpp @@ -0,0 +1,89 @@ +/* + * Logger.cpp + * + * Copyright (C) 2008 Johannes Thorn <dante@g4t3.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 "Logger.h" +#include "LogManager.h" + +#include <cstdlib> + +namespace Mad { +namespace Core { + +void Logger::logfv(MessageCategory category, MessageLevel level, const char *format, va_list ap) { + int size = 100; + char *buf = (char*)std::malloc(size); + + // If buffer is too small, try again with bigger buffer + while(true) { + va_list ap2; + + va_copy(ap2, ap); + int n = std::vsnprintf(buf, size, format, ap2); + va_end(ap2); + + if(n > -1 && n < size) { + log(category, level, buf); + std::free(buf); + return; + } + + if(n > -1) + size = n+1; + else + size *= 2; + + buf = (char*)std::realloc(buf, size); + } + +} + +void Logger::log(MessageCategory category, MessageLevel level, const std::string &message) { + LogManager::get()->log(category, level, std::time(0), message); +} + +void Logger::logf(MessageCategory category, MessageLevel level, const char *format, ...) { + va_list ap; + va_start(ap, format); + logfv(category, level, format, ap); + va_end(ap); +} + +void Logger::logf(MessageCategory category, const char *format, ...) { + va_list ap; + va_start(ap, format); + logfv(category, DEFAULT, format, ap); + va_end(ap); +} + +void Logger::logf(MessageLevel level, const char *format, ...) { + va_list ap; + va_start(ap, format); + logfv(GENERAL, level, format, ap); + va_end(ap); +} + +void Logger::logf(const char *format, ...) { + va_list ap; + va_start(ap, format); + logfv(GENERAL, DEFAULT, format, ap); + va_end(ap); +} + +} +} diff --git a/src/Core/Logger.h b/src/Core/Logger.h new file mode 100644 index 0000000..3de22d2 --- /dev/null +++ b/src/Core/Logger.h @@ -0,0 +1,68 @@ +/* + * Logger.h + * + * Copyright (C) 2008 Johannes Thorn <dante@g4t3.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/>. + */ + +#ifndef MAD_CORE_LOGGER_H_ +#define MAD_CORE_LOGGER_H_ + +#include "LoggerBase.h" + +#include <cstdarg> +#include <ctime> +#include <string> + +namespace Mad { +namespace Core { + +class LogManager; + +class Logger : public LoggerBase { + private: + friend class LogManager; + + static void logfv(MessageCategory category, MessageLevel level, const char *format, va_list ap); + + protected: + virtual void logMessage(MessageCategory category, MessageLevel level, time_t timestamp, const std::string &message) = 0; + + public: + static void log(MessageCategory category, MessageLevel level, const std::string &message); + + static void log(MessageCategory category, const std::string &message) { + log(category, DEFAULT, message); + } + + static void log(MessageLevel level, const std::string &message) { + log(GENERAL, level, message); + } + + static void log(const std::string &message) { + log(GENERAL, DEFAULT, message); + } + + + static void logf(MessageCategory category, MessageLevel level, const char *format, ...); + static void logf(MessageCategory category, const char *format, ...); + static void logf(MessageLevel level, const char *format, ...); + static void logf(const char *format, ...); +}; + +} +} + +#endif /* MAD_CORE_LOGGER_H_ */ diff --git a/src/Core/LoggerBase.h b/src/Core/LoggerBase.h new file mode 100644 index 0000000..49be833 --- /dev/null +++ b/src/Core/LoggerBase.h @@ -0,0 +1,79 @@ +/* + * LoggerBase.h + * + * Copyright (C) 2008 Johannes Thorn <dante@g4t3.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/>. + */ + +#ifndef MAD_CORE_LOGGERBASE_H_ +#define MAD_CORE_LOGGERBASE_H_ + +#include <bitset> +#include <list> + +namespace Mad { +namespace Core { + +class LoggerBase { + public: + enum MessageLevel { + CRITICAL, ERROR, WARNING, DEFAULT, VERBOSE, DEBUG + }; + + enum MessageCategory { + SYSTEM, NETWORK, DAEMON, USER, DISK, PROGRAM, GENERAL + }; + + protected: + std::bitset<16> categories; + MessageLevel level; + + LoggerBase() : level(DEFAULT) {setAllCategories();} + virtual ~LoggerBase() {} + + public: + void setCategory(MessageCategory newCategory) { + categories.set(newCategory); + } + + void unsetCategory(MessageCategory oldCategory) { + categories.reset(oldCategory); + } + + void setAllCategories() { + categories.set(); + } + + void unsetAllCategories() { + categories.reset(); + } + + bool isCategorySet(MessageCategory category) const { + return categories.test(category); + } + + MessageLevel getLevel() const { + return level; + } + + void setLevel(MessageLevel newLevel) { + level = newLevel; + } +}; + +} +} + +#endif /* MAD_CORE_LOGGERBASE_H_ */ diff --git a/src/Core/RemoteLogger.h b/src/Core/RemoteLogger.h new file mode 100644 index 0000000..f5c8a6b --- /dev/null +++ b/src/Core/RemoteLogger.h @@ -0,0 +1,42 @@ +/* + * LogRequestLogger.h + * + * Copyright (C) 2008 Johannes Thorn <dante@g4t3.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/>. + */ + +#ifndef MAD_CORE_REMOTELOGGER_H_ +#define MAD_CORE_REMOTELOGGER_H_ + +#include "LoggerBase.h" + +#include <string> + +namespace Mad { +namespace Core { + +class LogManager; + +class RemoteLogger : public LoggerBase { + protected: + friend class LogManager; + + virtual void logMessage(MessageCategory category, MessageLevel level, time_t messageTimestamp, const std::string &message, const std::string &messageSource) = 0; +}; + +} +} + +#endif /* MAD_CORE_REMOTELOGGER_H_ */ diff --git a/src/Core/Signals.h b/src/Core/Signals.h new file mode 100644 index 0000000..3c129af --- /dev/null +++ b/src/Core/Signals.h @@ -0,0 +1,27 @@ +/* + * Signals.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_SIGNALS_H_ +#define MAD_CORE_SIGNALS_SIGNALS_H_ + +#include "Signals/Signal0.h" +#include "Signals/Signal1.h" +#include "Signals/Signal2.h" + +#endif /* MAD_CORE_SIGNALS_SIGNALS_H_ */ diff --git a/src/Core/Signals/Connection.h b/src/Core/Signals/Connection.h new file mode 100644 index 0000000..5ce7b4a --- /dev/null +++ b/src/Core/Signals/Connection.h @@ -0,0 +1,53 @@ +/* + * Connection.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_CONNECTION_H_ +#define MAD_CORE_SIGNALS_CONNECTION_H_ + +namespace Mad { +namespace Core { +namespace Signals { + +class SignalBase; + +class Connection { + private: + friend class SignalBase; + + SignalBase *signal; + unsigned long id; + + Connection(SignalBase *signal0, unsigned long id0) + : signal(signal0), id(id0) {} + + public: + bool operator==(const Connection &o) const { + return (signal == o.signal && id == o.id); + } + + bool operator<(const Connection &o) const { + return (signal != o.signal) ? (signal < o.signal) : (id < o.id); + } +}; + +} +} +} + +#endif /* MAD_CORE_SIGNALS_CONNECTION_H_ */ diff --git a/src/Core/Signals/GenericSignal.h b/src/Core/Signals/GenericSignal.h new file mode 100644 index 0000000..641f575 --- /dev/null +++ b/src/Core/Signals/GenericSignal.h @@ -0,0 +1,61 @@ +/* + * GenericSignal.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_GENERICSIGNAL_H_ +#define MAD_CORE_SIGNALS_GENERICSIGNAL_H_ + +#include "SignalBase.h" + +#include <map> +#include <boost/thread/locks.hpp> + +namespace Mad { +namespace Core { +namespace Signals { + +template <typename FunctionType> +class GenericSignal : protected SignalBase { + public: + typedef FunctionType slot_type; + + protected: + std::map<Connection, slot_type> handlers; + + public: + Connection connect(const slot_type &slot) { + boost::lock_guard<boost::mutex> lock(mutex); + + Connection con = getNewConnection(); + handlers.insert(std::make_pair(con, slot)); + + return con; + } + + bool disconnect(const Connection &connection) { + boost::lock_guard<boost::mutex> lock(mutex); + + return handlers.erase(connection); + } +}; + +} +} +} + +#endif /* MAD_CORE_SIGNALS_GENERICSIGNAL_H_ */ diff --git a/src/Core/Signals/Signal0.h b/src/Core/Signals/Signal0.h new file mode 100644 index 0000000..ccfb548 --- /dev/null +++ b/src/Core/Signals/Signal0.h @@ -0,0 +1,46 @@ +/* + * Signal0.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_SIGNAL0_H_ +#define MAD_CORE_SIGNALS_SIGNAL0_H_ + +#include "GenericSignal.h" +#include "../ThreadManager.h" + +#include <boost/function.hpp> + +namespace Mad { +namespace Core { +namespace Signals { + +class Signal0 : public GenericSignal<boost::function0<void> > { + public: + void emit() { + boost::lock_guard<boost::mutex> lock(mutex); + + for(std::map<Connection, slot_type>::iterator handler = handlers.begin(); handler != handlers.end(); ++handler) + ThreadManager::get()->pushWork(handler->second); + } +}; + +} +} +} + +#endif /* MAD_CORE_SIGNALS_SIGNAL0_H_ */ diff --git a/src/Core/Signals/Signal1.h b/src/Core/Signals/Signal1.h new file mode 100644 index 0000000..e4a946c --- /dev/null +++ b/src/Core/Signals/Signal1.h @@ -0,0 +1,47 @@ +/* + * Signal1.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_SIGNAL1_H_ +#define MAD_CORE_SIGNALS_SIGNAL1_H_ + +#include "GenericSignal.h" +#include "../ThreadManager.h" + +#include <boost/function.hpp> + +namespace Mad { +namespace Core { +namespace Signals { + +template <typename T1> +class Signal1 : public GenericSignal<boost::function1<void, T1> > { + public: + void emit(T1 arg1) { + boost::lock_guard<boost::mutex> lock(this->mutex); + + for(typename std::map<Connection, typename GenericSignal<boost::function1<void, T1> >::slot_type>::iterator handler = this->handlers.begin(); handler != this->handlers.end(); ++handler) + ThreadManager::get()->pushWork(boost::bind(handler->second, arg1)); + } +}; + +} +} +} + +#endif /* MAD_CORE_SIGNALS_SIGNAL1_H_ */ diff --git a/src/Core/Signals/Signal2.h b/src/Core/Signals/Signal2.h new file mode 100644 index 0000000..41045d7 --- /dev/null +++ b/src/Core/Signals/Signal2.h @@ -0,0 +1,47 @@ +/* + * Signal2.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_SIGNAL2_H_ +#define MAD_CORE_SIGNALS_SIGNAL2_H_ + +#include "GenericSignal.h" +#include "../ThreadManager.h" + +#include <boost/function.hpp> + +namespace Mad { +namespace Core { +namespace Signals { + +template <typename T1, typename T2> +class Signal2 : public GenericSignal<boost::function2<void, T1, T2> > { + public: + void emit(T1 arg1, T2 arg2) { + boost::lock_guard<boost::mutex> lock(this->mutex); + + for(typename std::map<Connection, typename GenericSignal<boost::function2<void, T1, T2> >::slot_type>::iterator handler = this->handlers.begin(); handler != this->handlers.end(); ++handler) + ThreadManager::get()->pushWork(boost::bind(handler->second, arg1, arg2)); + } +}; + +} +} +} + +#endif /* MAD_CORE_SIGNALS_SIGNAL2_H_ */ diff --git a/src/Core/Signals/SignalBase.h b/src/Core/Signals/SignalBase.h new file mode 100644 index 0000000..1a5d5a3 --- /dev/null +++ b/src/Core/Signals/SignalBase.h @@ -0,0 +1,53 @@ +/* + * SignalBase.h + * + * 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/>. + */ + +#ifndef MAD_CORE_SIGNALS_SIGNALBASE_H_ +#define MAD_CORE_SIGNALS_SIGNALBASE_H_ + +#include "Connection.h" + +#include <set> +#include <boost/thread/mutex.hpp> + +namespace Mad { +namespace Core { +namespace Signals { + +class SignalBase : private boost::noncopyable { + private: + friend class Connection; + + unsigned long connectionId; + + protected: + boost::mutex mutex; + + Connection getNewConnection() { + return Connection(this, connectionId++); + } + + SignalBase() : connectionId(0) {} + virtual ~SignalBase() {} +}; + +} +} +} + +#endif /* MAD_CORE_SIGNALS_SIGNALBASE_H_ */ 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(); +} + +} +} diff --git a/src/Core/ThreadManager.h b/src/Core/ThreadManager.h new file mode 100644 index 0000000..b512ad4 --- /dev/null +++ b/src/Core/ThreadManager.h @@ -0,0 +1,113 @@ +/* + * ThreadManager.h + * + * 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/>. + */ + +#ifndef MAD_CORE_THREADMANAGER_H_ +#define MAD_CORE_THREADMANAGER_H_ + +#include <config.h> + +#include "Initializable.h" + +#include <queue> +#include <map> + +#include <boost/asio.hpp> + +#include <boost/thread/thread.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/locks.hpp> + +namespace Mad { +namespace Core { + +class ThreadManager : public Initializable { + private: + boost::thread::id mainThreadId; + boost::shared_ptr<boost::thread> workerThread, loggerThread, ioThread; + std::map<boost::thread::id, boost::shared_ptr<boost::thread> > threads; + + boost::mutex threadLock; + + boost::mutex runLock; + bool running; + + boost::mutex workLock; + boost::condition_variable workCond; + std::queue<boost::function0<void> > work; + + boost::scoped_ptr<boost::asio::io_service::work> ioWorker; + + boost::asio::io_service ioService; + + static ThreadManager threadManager; + + ThreadManager() {} + + void workerFunc(); + void ioFunc(); + + void threadFinished(boost::thread::id threadId) { + boost::shared_ptr<boost::thread> thread; + + { + boost::lock_guard<boost::mutex> lock(threadLock); + + std::map<boost::thread::id, boost::shared_ptr<boost::thread> >::iterator it = threads.find(threadId); + + if(it == threads.end()) + return; + + thread = it->second; + threads.erase(it); + } + + thread->join(); + } + + protected: + virtual void doInit(); + virtual void doDeinit(); + + public: + bool isThisMainThread() { + return (mainThreadId == boost::this_thread::get_id()); + } + + bool isThisWorkerThread() { + boost::lock_guard<boost::mutex> lock(threadLock); + return (workerThread->get_id() == boost::this_thread::get_id()); + } + + boost::asio::io_service& getIOService() { + return ioService; + } + + void detach(); + + void pushWork(const boost::function0<void> &newWork); + + static ThreadManager* get() { + return &threadManager; + } +}; + +} +} + +#endif /* MAD_CORE_THREADMANAGER_H_ */ diff --git a/src/Core/Tokenizer.cpp b/src/Core/Tokenizer.cpp new file mode 100644 index 0000000..a812ed1 --- /dev/null +++ b/src/Core/Tokenizer.cpp @@ -0,0 +1,131 @@ +/* + * Tokenizer.cpp + * + * Copyright (C) 2008 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 "Tokenizer.h" + +namespace Mad { +namespace Core { + +const std::string Tokenizer::delimiters = " \t\n\"'\\"; + + +std::vector<std::string> Tokenizer::split(const std::string &str) { + std::vector<std::string> ret; + + for(size_t s = 0; s < str.length();) { + size_t index = str.find_first_of(delimiters, s); + size_t length = (index == std::string::npos) ? std::string::npos : index-s; + + ret.push_back(str.substr(s, length)); + + if(index != std::string::npos) { + size_t index2 = str.find_first_not_of(delimiters, index); + + length = (index2 == std::string::npos) ? std::string::npos : index2-index; + + ret.push_back(str.substr(index, length)); + + if(index2 != std::string::npos) + s = index2; + else + break; + } + else + break; + } + + return ret; +} + +bool Tokenizer::tokenize(const std::string &str, std::vector<std::string> &out) { + std::vector<std::string> splitString = split(str); + + bool singleQuotes = false, doubleQuotes = false, escape = false; + std::string token; + bool forceToken = false; + + out.clear(); + + for(std::vector<std::string>::iterator s = splitString.begin(); s != splitString.end(); ++s) { + token += *s; + escape = false; + + if(++s == splitString.end()) + break; + + for(std::string::iterator c = s->begin(); c != s->end(); ++c) { + if(*c == '\n' && escape) { + escape = false; + + if(doubleQuotes) + continue; + } + + if(escape || (singleQuotes && *c != '\'')) { + token += *c; + + escape = false; + continue; + } + + switch(*c) { + case ' ': + case '\t': + case '\n': + if(doubleQuotes) { + token += *c; + } + else { + if(!token.empty() || forceToken) { + out.push_back(token); + token.clear(); + forceToken = false; + } + } + break; + + case '"': + doubleQuotes = !doubleQuotes; + forceToken = true; + break; + + case '\'': + if(doubleQuotes) { + token += *c; + } + else { + singleQuotes = !singleQuotes; + forceToken = true; + } + break; + + case '\\': + escape = true; + } + } + } + + if(!token.empty() || forceToken) + out.push_back(token); + + return !(singleQuotes || doubleQuotes || escape); +} + +} +} diff --git a/src/Core/Tokenizer.h b/src/Core/Tokenizer.h new file mode 100644 index 0000000..7b29fed --- /dev/null +++ b/src/Core/Tokenizer.h @@ -0,0 +1,44 @@ +/* + * Tokenizer.h + * + * Copyright (C) 2008 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/>. + */ + +#ifndef MAD_CORE_TOKENIZER_H_ +#define MAD_CORE_TOKENIZER_H_ + +#include <string> +#include <vector> + +namespace Mad { +namespace Core { + +class Tokenizer { + private: + static const std::string delimiters; + + Tokenizer(); + + static std::vector<std::string> split(const std::string &str); + + public: + static bool tokenize(const std::string &str, std::vector<std::string> &out); +}; + +} +} + +#endif /* MAD_CORE_TOKENIZER_H_ */ |