diff --git a/engine/shared/library/CMakeLists.txt b/engine/shared/library/CMakeLists.txt index de404ff4..efa49a23 100644 --- a/engine/shared/library/CMakeLists.txt +++ b/engine/shared/library/CMakeLists.txt @@ -4,8 +4,10 @@ add_subdirectory(sharedDebug) add_subdirectory(sharedFile) add_subdirectory(sharedFoundation) add_subdirectory(sharedFoundationTypes) +add_subdirectory(sharedLog) add_subdirectory(sharedMath) add_subdirectory(sharedMemoryManager) +add_subdirectory(sharedMessageDispatch) add_subdirectory(sharedNetworkMessages) add_subdirectory(sharedRandom) add_subdirectory(sharedSynchronization) diff --git a/engine/shared/library/sharedLog/CMakeLists.txt b/engine/shared/library/sharedLog/CMakeLists.txt new file mode 100644 index 00000000..e717d300 --- /dev/null +++ b/engine/shared/library/sharedLog/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedLog) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/ConfigSharedLog.h b/engine/shared/library/sharedLog/include/public/sharedLog/ConfigSharedLog.h new file mode 100644 index 00000000..8a7875e9 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/ConfigSharedLog.h @@ -0,0 +1 @@ +#include "../../src/shared/ConfigSharedLog.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/FileLogObserver.h b/engine/shared/library/sharedLog/include/public/sharedLog/FileLogObserver.h new file mode 100644 index 00000000..36a69dee --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/FileLogObserver.h @@ -0,0 +1 @@ +#include "../../src/shared/FileLogObserver.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/FirstSharedLog.h b/engine/shared/library/sharedLog/include/public/sharedLog/FirstSharedLog.h new file mode 100644 index 00000000..f90ca280 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/FirstSharedLog.h @@ -0,0 +1 @@ +#include "../../src/shared/FirstSharedLog.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/Log.h b/engine/shared/library/sharedLog/include/public/sharedLog/Log.h new file mode 100644 index 00000000..553c3680 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/Log.h @@ -0,0 +1 @@ +#include "../../src/shared/Log.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/LogManager.h b/engine/shared/library/sharedLog/include/public/sharedLog/LogManager.h new file mode 100644 index 00000000..6259bbf5 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/LogManager.h @@ -0,0 +1 @@ +#include "../../src/shared/LogManager.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/LogObserver.h b/engine/shared/library/sharedLog/include/public/sharedLog/LogObserver.h new file mode 100644 index 00000000..88a88bb6 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/LogObserver.h @@ -0,0 +1 @@ +#include "../../src/shared/LogObserver.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/NetLogConnection.h b/engine/shared/library/sharedLog/include/public/sharedLog/NetLogConnection.h new file mode 100644 index 00000000..6301b68b --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/NetLogConnection.h @@ -0,0 +1 @@ +#include "../../src/shared/NetLogConnection.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/NetLogObserver.h b/engine/shared/library/sharedLog/include/public/sharedLog/NetLogObserver.h new file mode 100644 index 00000000..d19999c1 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/NetLogObserver.h @@ -0,0 +1 @@ +#include "../../src/shared/NetLogObserver.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/SetupSharedLog.h b/engine/shared/library/sharedLog/include/public/sharedLog/SetupSharedLog.h new file mode 100644 index 00000000..921a24e4 --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/SetupSharedLog.h @@ -0,0 +1 @@ +#include "../../src/shared/SetupSharedLog.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/StderrLogger.h b/engine/shared/library/sharedLog/include/public/sharedLog/StderrLogger.h new file mode 100644 index 00000000..2ce9c4ff --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/StderrLogger.h @@ -0,0 +1 @@ +#include "../../src/shared/StderrLogger.h" diff --git a/engine/shared/library/sharedLog/include/public/sharedLog/TailFileLogObserver.h b/engine/shared/library/sharedLog/include/public/sharedLog/TailFileLogObserver.h new file mode 100644 index 00000000..859e0d7a --- /dev/null +++ b/engine/shared/library/sharedLog/include/public/sharedLog/TailFileLogObserver.h @@ -0,0 +1 @@ +#include "../../src/shared/TailFileLogObserver.h" diff --git a/engine/shared/library/sharedLog/src/CMakeLists.txt b/engine/shared/library/sharedLog/src/CMakeLists.txt new file mode 100644 index 00000000..60bbc7e6 --- /dev/null +++ b/engine/shared/library/sharedLog/src/CMakeLists.txt @@ -0,0 +1,56 @@ + +set(SHARED_SOURCES + shared/ConfigSharedLog.cpp + shared/ConfigSharedLog.h + shared/TailFileLogObserver.cpp + shared/TailFileLogObserver.h + shared/FileLogObserver.cpp + shared/FileLogObserver.h + shared/FirstSharedLog.cpp + shared/FirstSharedLog.h + shared/Log.h + shared/LogManager.cpp + shared/LogManager.h + shared/LogObserver.cpp + shared/LogObserver.h + shared/NetLogConnection.cpp + shared/NetLogConnection.h + shared/NetLogObserver.cpp + shared/NetLogObserver.h + shared/SetupSharedLog.cpp + shared/SetupSharedLog.h + shared/StderrLogger.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/StderrLogger.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES + linux/StderrLogger.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/linux) +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMessageDispatch/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetwork/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedSynchronization/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/fileInterface/include/public +) + +add_library(sharedLog STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedLog/src/linux/StderrLogger.cpp b/engine/shared/library/sharedLog/src/linux/StderrLogger.cpp new file mode 100644 index 00000000..963684d2 --- /dev/null +++ b/engine/shared/library/sharedLog/src/linux/StderrLogger.cpp @@ -0,0 +1,74 @@ +// ====================================================================== +// +// StderrLogger.cpp +// +// Copyright 2003 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedLog/StderrLogger.h" +#include "sharedLog/Log.h" +#include +#include + +// ====================================================================== + +namespace StderrLoggerNamespace +{ + bool s_installed; + int s_oldStderr; + int s_stderrPipe[2]; +} +using namespace StderrLoggerNamespace; + +// ====================================================================== + +void StderrLogger::install() +{ + DEBUG_FATAL(s_installed, ("StderrLogger already installed")); + s_installed = true; + s_oldStderr = dup(2); + pipe(s_stderrPipe); + dup2(s_stderrPipe[1], 2); + fcntl(s_stderrPipe[0], F_SETFL, O_NONBLOCK); +} + +// ---------------------------------------------------------------------- + +void StderrLogger::remove() +{ + DEBUG_FATAL(!s_installed, ("StderrLogger not installed")); + dup2(s_oldStderr, 2); + close(s_stderrPipe[0]); + close(s_stderrPipe[1]); + s_installed = false; +} + +// ---------------------------------------------------------------------- + +void StderrLogger::update() +{ + if (!s_installed) + return; + + char buf[8192]; + int nRead = read(s_stderrPipe[0], buf, sizeof(buf)-1); + if (nRead > 0) + { + int startPos = 0; + while (startPos < nRead) + { + int endPos = startPos; + while (buf[endPos] != '\n' && endPos < nRead) + ++endPos; + buf[endPos] = 0; + if (endPos > startPos) + LOG("stderr", ("%s", buf+startPos)); + startPos = endPos+1; + } + } +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/ConfigSharedLog.cpp b/engine/shared/library/sharedLog/src/shared/ConfigSharedLog.cpp new file mode 100644 index 00000000..15439db1 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/ConfigSharedLog.cpp @@ -0,0 +1,114 @@ +// ====================================================================== +// +// ConfigSharedLog.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedLog/ConfigSharedLog.h" + +#include "sharedFoundation/ConfigFile.h" + +#include + +// ====================================================================== + +namespace ConfigSharedLogNamespace +{ + typedef std::vector StringPtrArray; + int ms_logNetQueueSize; + int ms_logNetReconnectTimeMs; + bool ms_logReportLogs; + bool ms_logReportWarnings; + bool ms_logReportFatals; + bool ms_logStderr; + StringPtrArray ms_logTargets; // ConfigFile owns the pointer +} +using namespace ConfigSharedLogNamespace; + +#define KEY_BOOL(a,b) (ms_ ## a = ConfigFile::getKeyBool("SharedLog", #a, b)) +#define KEY_INT(a,b) (ms_ ## a = ConfigFile::getKeyInt("SharedLog", #a, b)) + +// ====================================================================== + +void ConfigSharedLog::install() +{ + KEY_INT (logNetQueueSize, 2000); + KEY_INT (logNetReconnectTimeMs, 10000); + KEY_BOOL(logReportLogs, false); + KEY_BOOL(logReportWarnings, true); + KEY_BOOL(logReportFatals, true); + KEY_BOOL(logStderr, false); + + int index = 0; + char const * result = 0; + do + { + result = ConfigFile::getKeyString("SharedLog", "logTarget", index++, 0); + if (result) + ms_logTargets.push_back(result); + } + while (result); +} + +// ---------------------------------------------------------------------- + +int ConfigSharedLog::getLogNetQueueSize() +{ + return ms_logNetQueueSize; +} + +// ---------------------------------------------------------------------- + +int ConfigSharedLog::getLogNetReconnectTimeMs() +{ + return ms_logNetReconnectTimeMs; +} + +// ---------------------------------------------------------------------- + +bool ConfigSharedLog::getLogReportLogs() +{ + return ms_logReportLogs; +} + +// ---------------------------------------------------------------------- + +bool ConfigSharedLog::getLogReportWarnings() +{ + return ms_logReportWarnings; +} + +// ---------------------------------------------------------------------- + +bool ConfigSharedLog::getLogReportFatals() +{ + return ms_logReportFatals; +} + +// ---------------------------------------------------------------------- + +bool ConfigSharedLog::getLogStderr() +{ + return ms_logStderr; +} + +// ---------------------------------------------------------------------- + +int ConfigSharedLog::getNumberOfLogTargets() +{ + return static_cast(ms_logTargets.size()); +} + +// ---------------------------------------------------------------------- + +char const * ConfigSharedLog::getLogTarget(int const index) +{ + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfLogTargets()); + return ms_logTargets[static_cast(index)]; +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/ConfigSharedLog.h b/engine/shared/library/sharedLog/src/shared/ConfigSharedLog.h new file mode 100644 index 00000000..d8456a7a --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/ConfigSharedLog.h @@ -0,0 +1,32 @@ +// ====================================================================== +// +// ConfigSharedLog.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ConfigSharedLog_H +#define INCLUDED_ConfigSharedLog_H + +// ====================================================================== + +class ConfigSharedLog +{ +public: + static void install(); + + static int getLogNetQueueSize(); + static int getLogNetReconnectTimeMs(); + static bool getLogReportLogs(); + static bool getLogReportWarnings(); + static bool getLogReportFatals(); + static bool getLogStderr(); + static int getNumberOfLogTargets(); + static char const * getLogTarget(int index); +}; + +// ====================================================================== + +#endif // INCLUDED_ConfigSharedLog_H + diff --git a/engine/shared/library/sharedLog/src/shared/FileLogObserver.cpp b/engine/shared/library/sharedLog/src/shared/FileLogObserver.cpp new file mode 100644 index 00000000..9f3f0ae0 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/FileLogObserver.cpp @@ -0,0 +1,122 @@ +// ====================================================================== +// +// FileLogObserver.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedFoundation/NetworkIdArchive.h" +#include "sharedLog/FileLogObserver.h" +#include "sharedLog/LogManager.h" +#include "fileInterface/StdioFile.h" +#include "sharedNetworkMessages/LogMessage.h" +#include "UnicodeUtils.h" +#include +#include + +// ====================================================================== + +const int LOG_FILESIZE_NEXT = 2000000000; // point at which we roll over to another log file + +// ====================================================================== + +void FileLogObserver::install() +{ + LogManager::registerObserverType("file", create); +} + +// ---------------------------------------------------------------------- + +LogObserver *FileLogObserver::create(std::string const &spec) +{ + return new FileLogObserver(spec); +} + +// ---------------------------------------------------------------------- + +FileLogObserver::FileLogObserver(std::string const &filename) : + LogObserver(), + m_filename(filename), + m_file(0), + m_fileIndex(0) +{ + prepareFile(); +} + +// ---------------------------------------------------------------------- + +void FileLogObserver::prepareFile() +{ + while (!m_file || m_file->tell() >= LOG_FILESIZE_NEXT) + { + if (m_file) + { + ++m_fileIndex; + delete m_file; + m_file = 0; + } + if (m_fileIndex == 0) + m_file = new StdioFile(m_filename.c_str(), "a"); + else + { + char buf[512]; + IGNORE_RETURN( snprintf(buf, 512, "%s-%d", m_filename.c_str(), m_fileIndex+1) ); + m_file = new StdioFile(buf, "a"); + } + NOT_NULL(m_file); + FATAL(!m_file->isOpen(), ("Could not open %s", m_filename.c_str())); + } +} + +// ---------------------------------------------------------------------- + +FileLogObserver::~FileLogObserver() +{ + delete m_file; +} + +// ---------------------------------------------------------------------- + +void FileLogObserver::log(LogMessage const &msg) +{ + prepareFile(); + NOT_NULL(m_file); + + uint64 timestamp = msg.getTimestamp(); + std::string const &procId = msg.getProcId(); + std::string const &channel = msg.getChannel(); + std::string const &text = msg.getText(); + std::string uniAttach(Unicode::wideToNarrow(msg.getUnicodeAttach())); + char tsbuf[16]; // yyyymmddhhmmss (14) + + IGNORE_RETURN( sprintf(tsbuf, UINT64_FORMAT_SPECIFIER, timestamp) ); + IGNORE_RETURN( m_file->write(14, tsbuf) ); + IGNORE_RETURN( m_file->write(1, ":") ); + IGNORE_RETURN( m_file->write(static_cast(procId.length()), procId.c_str()) ); + IGNORE_RETURN( m_file->write(1, ":") ); + IGNORE_RETURN( m_file->write(static_cast(channel.length()), channel.c_str()) ); + IGNORE_RETURN( m_file->write(1, ":") ); + IGNORE_RETURN( m_file->write(static_cast(text.length()), text.c_str()) ); + if (uniAttach.length()) + { + IGNORE_RETURN( m_file->write(1, ":") ); + IGNORE_RETURN( m_file->write(static_cast(uniAttach.length()), uniAttach.c_str()) ); + } + IGNORE_RETURN( m_file->write(1, "\n") ); + + if (LogManager::getFlushOnWrite()) + m_file->flush(); +} + +// ---------------------------------------------------------------------- + +void FileLogObserver::flush() +{ + if (m_file) + m_file->flush(); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/FileLogObserver.h b/engine/shared/library/sharedLog/src/shared/FileLogObserver.h new file mode 100644 index 00000000..fe8746d6 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/FileLogObserver.h @@ -0,0 +1,52 @@ +// ====================================================================== +// +// FileLogObserver.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FileLogObserver_H +#define INCLUDED_FileLogObserver_H + +// ====================================================================== + +#include "sharedLog/LogObserver.h" +#include + +// ====================================================================== + +class AbstractFile; + +// ====================================================================== + +// A FileLogObserver saves log messages to a file +class FileLogObserver: public LogObserver +{ +public: + + static void install(); + static LogObserver *create(std::string const &spec); + + FileLogObserver(std::string const &filename); + virtual ~FileLogObserver(); + + virtual void log(LogMessage const &msg); + virtual void flush(); + +private: + FileLogObserver(FileLogObserver const &); + FileLogObserver &operator=(FileLogObserver const &); + + void prepareFile(); + +private: + std::string m_filename; + AbstractFile *m_file; + int m_fileIndex; +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/FirstSharedLog.cpp b/engine/shared/library/sharedLog/src/shared/FirstSharedLog.cpp new file mode 100644 index 00000000..feef0e41 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/FirstSharedLog.cpp @@ -0,0 +1,10 @@ +// ====================================================================== +// +// FirstSharedLog.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" + diff --git a/engine/shared/library/sharedLog/src/shared/FirstSharedLog.h b/engine/shared/library/sharedLog/src/shared/FirstSharedLog.h new file mode 100644 index 00000000..ede85280 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/FirstSharedLog.h @@ -0,0 +1,19 @@ +// ====================================================================== +// +// FirstSharedLog.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstSharedLog_H +#define INCLUDED_FirstSharedLog_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/Log.h b/engine/shared/library/sharedLog/src/shared/Log.h new file mode 100644 index 00000000..c6f51c9e --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/Log.h @@ -0,0 +1,39 @@ +// ====================================================================== +// +// Log.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Log_H +#define INCLUDED_Log_H + +// ====================================================================== + +#include "LogManager.h" + +// ====================================================================== + +// Un-conditional + +#define LOG(stream, varArgs) (LogManager::setArgs(stream), LogManager::log varArgs) +#define LOGU(stream, varArgs, unicodeAttach) (LogManager::setArgs(stream, unicodeAttach), LogManager::log varArgs) + +// Conditional + +#define LOGC(forceLog, stream, varArgs) ((forceLog) ? (LogManager::setArgs(stream), LogManager::log varArgs) : NOP) +#define LOGUC(forceLog, stream, varArgs, unicodeAttach) ((forceLog) ? (LogManager::setArgs(stream, unicodeAttach), LogManager::log varArgs) : NOP) + +#ifdef _DEBUG +#define DEBUG_LOG(stream, varArgs) LOG(stream, varArgs) +#define DEBUG_LOGU(stream, varArgs, unicodeAttach) LOGU(stream, varArgs, unicodeAttach) +#else +#define DEBUG_LOG(stream, varArgs) NOP +#define DEBUG_LOGU(stream, varArgs, unicodeAttach) NOP +#endif + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/LogManager.cpp b/engine/shared/library/sharedLog/src/shared/LogManager.cpp new file mode 100644 index 00000000..2aaa81d0 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/LogManager.cpp @@ -0,0 +1,367 @@ +// ====================================================================== +// +// LogManager.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedFoundation/NetworkIdArchive.h" + +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedLog/LogManager.h" +#include "sharedLog/LogObserver.h" +#include "sharedLog/ConfigSharedLog.h" +#include "sharedLog/StderrLogger.h" +#include "sharedNetworkMessages/LogMessage.h" +#include "sharedSynchronization/RecursiveMutex.h" +#include +#include +#include +#include +#include + +// ====================================================================== + +typedef std::list ObserverList; +typedef std::map ObserverCreateMap; + +// ====================================================================== + +namespace LogManagerNamespace +{ + void remove(); + + const int MaxLogMessageLen = 16384; + + struct LogManagerData + { + LogManagerData() : + mutex(), + observers(), + observerCreateMap(), + channel(), + unicodeAttach(), + processIdentifier(), + flushOnWrite(true), + logging(0) + { + } + RecursiveMutex mutex; + ObserverList observers; + ObserverCreateMap observerCreateMap; + std::string channel; + Unicode::String unicodeAttach; + std::string processIdentifier; + bool flushOnWrite; + int logging; // used for catching recursive log attempts from the same thread + + private: + LogManagerData(LogManagerData const &); + LogManagerData &operator=(LogManagerData const &); + }; + LogManagerData *s_data; +} +using namespace LogManagerNamespace; + +// ====================================================================== + +void LogManager::install(std::string const &procId, bool flushOnWrite) +{ + DEBUG_FATAL(s_data, ("LogManager already installed")); + s_data = new LogManagerData; + s_data->processIdentifier = procId; + s_data->flushOnWrite = flushOnWrite; + ExitChain::add(LogManagerNamespace::remove, "LogManagerNamespace::remove"); + if (ConfigSharedLog::getLogStderr()) + StderrLogger::install(); +} + +// ---------------------------------------------------------------------- + +void LogManager::update() +{ + if (!s_data) + return; + + if (ConfigSharedLog::getLogStderr()) + StderrLogger::update(); + + for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i) + (*i)->update(); +} + +// ---------------------------------------------------------------------- + +void LogManagerNamespace::remove() +{ + DEBUG_FATAL(!s_data, ("LogManager not installed")); + // we own any observers we have, so delete them + while (!s_data->observers.empty()) + { + delete (*s_data->observers.begin()); + IGNORE_RETURN( s_data->observers.erase(s_data->observers.begin()) ); + } + if (ConfigSharedLog::getLogStderr()) + StderrLogger::remove(); + + delete s_data; + s_data = 0; +} + +// ---------------------------------------------------------------------- + +void LogManager::setProcessIdentifier(std::string const &procId) +{ + s_data->processIdentifier = procId; +} + +// ---------------------------------------------------------------------- + +void LogManager::setArgs(std::string const &channel, Unicode::String const &unicodeAttach) +{ + if (!s_data) + return; + + s_data->mutex.enter(); // leave() called from log which should always follow setArgs + if (s_data->logging == 0) + { + ++s_data->logging; + s_data->channel = channel; + s_data->unicodeAttach = unicodeAttach; + } +} + +// ---------------------------------------------------------------------- + +void LogManager::setArgs(std::string const &channel) +{ + if (!s_data) + return; + + s_data->mutex.enter(); // leave() called from log which should always follow setArgs + ++s_data->logging; + if (s_data->logging == 1) + { + s_data->channel = channel; + s_data->unicodeAttach.clear(); + } +} + +// ---------------------------------------------------------------------- + +void LogManager::log(char const *format, ...) +{ + if (!s_data) + return; + // if noone is observing log messages, no need to process them + if (s_data->observers.size() && s_data->logging == 1) + { + ++s_data->logging; + static char text[MaxLogMessageLen]; + { + va_list ap; + va_start(ap, format); //lint !e746 !e1055 + IGNORE_RETURN( _vsnprintf(text, sizeof(text), format, ap) ); + text[sizeof(text)-1] = '\0'; + size_t len = strlen(text); + // if string was truncated, stick a + on the end + if (len == sizeof(text)-1) + text[len-1] = '+'; + va_end(ap); + } + + uint64 timestamp; + { + // format current date/time gmt as yyyymmddhhmmss, in a uint64 + time_t now; + tm t; + + IGNORE_RETURN( time(&now) ); + IGNORE_RETURN( gmtime_r(&now, &t) ); + timestamp = t.tm_year+1900; //lint !e732 !e737 !e776 + timestamp *= 100; + timestamp += t.tm_mon+1; //lint !e737 !e776 + timestamp *= 100; + timestamp += static_cast(t.tm_mday); + timestamp *= 100; + timestamp += static_cast(t.tm_hour); + timestamp *= 100; + timestamp += static_cast(t.tm_min); + timestamp *= 100; + timestamp += static_cast(t.tm_sec); + } + + observeLogMessage(LogMessage(timestamp, s_data->processIdentifier, s_data->channel, text, s_data->unicodeAttach)); + --s_data->logging; + } + + --s_data->logging; + s_data->mutex.leave(); // enter() called from setArgs which should always precede log +} + +// ---------------------------------------------------------------------- + +void LogManager::observeLogMessage(LogMessage const &msg) +{ + for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i) + if (!(*i)->isFiltered(msg)) + (*i)->log(msg); +} + +// ---------------------------------------------------------------------- + +void LogManager::addObserver(LogObserver *observer) +{ + s_data->observers.push_back(observer); +} + +// ---------------------------------------------------------------------- + +void LogManager::removeObserver(LogObserver const *observer) +{ + // we own any observers given to us, so delete it when it is removed + for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i) + { + if ((*i) == observer) + { + delete (*i); + IGNORE_RETURN( s_data->observers.erase(i) ); + break; + } + } +} + +// ---------------------------------------------------------------------- + +void LogManager::registerObserverType(std::string const &name, LogObserverCreateFunc func) +{ + s_data->observerCreateMap[name] = func; + + char buffer[256]; + sprintf(buffer, "%s:", name.c_str()); + + // run through SharedLog keys looking for logTarget=name: + { + int const numberLogTargets = ConfigSharedLog::getNumberOfLogTargets(); + for (int i = 0; i < numberLogTargets; ++i) + { + char const * const result = ConfigSharedLog::getLogTarget(i); + if (result && strstr(result, buffer) == result) + { + setupObserver(result); + } + } + } + + // run through SharedLog keys looking for logTarget#=name: + { + char key[16]; + strcpy(key, "logTarget"); + + for (int i = 0; i < 20; ++i) + { + char const * result = 0; + sprintf(key+9, "%d", i); + int count = 0; + do + { + result = ConfigFile::getKeyString("SharedLog", key, count, 0); + if (result && strstr(result, buffer) == result) + setupObserver(result); + ++count; + } while (result); + } + } +} + +// ---------------------------------------------------------------------- + +bool LogManager::setupObserver(std::string const &desc) +{ + REPORT_LOG(true, ("Log observer setup: %s\n", desc.c_str())); + + // description should be "type:spec{filter1:filter2:...}" + size_t pos = desc.find(":"); + if (pos != std::string::npos) + { + std::string type(desc.substr(0, pos)); + std::string spec; + std::string filt; + + size_t pos2 = desc.find("{"); + if (pos2 == std::string::npos) + spec = desc.substr(pos+1); + else + { + spec = desc.substr(pos+1, (pos2-(pos+1))); + filt = desc.substr(pos2+1); + if (filt.length() && filt[filt.length()-1] == '}') + { + IGNORE_RETURN( filt.erase(filt.length()-1) ); + } + } + + ObserverCreateMap::iterator i = s_data->observerCreateMap.find(type); + if (i != s_data->observerCreateMap.end()) + { + LogObserver *observer = (*(*i).second)(spec); + if (observer) + { + if (filt.length()) + observer->setFilter(filt); + addObserver(observer); + return true; + } + } + } + return false; +} + +// ---------------------------------------------------------------------- + +bool LogManager::getFlushOnWrite() +{ + return s_data->flushOnWrite; +} + +// ---------------------------------------------------------------------- + +void LogManager::flush() +{ + for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i) + (*i)->flush(); +} + +// ---------------------------------------------------------------------- + +void LogManager::logLongText(std::string const & channel, std::string const & longText) +{ + int const targetLineLength=80; + int const maxLineLength=100; + char buffer[maxLineLength+1]; + int pos=0; + for (std::string::const_iterator i=longText.begin(); i!=longText.end(); ++i) + { + buffer[pos++]=*i; + if ((pos>=targetLineLength && *i==' ') || (pos==maxLineLength)) + { + buffer[pos]='\0'; + setArgs(channel); + log("%s", buffer); + pos=0; + } + } + if (pos!=0) + { + buffer[pos]='\0'; + setArgs(channel); + log("%s", buffer); + pos=0; + } +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/LogManager.h b/engine/shared/library/sharedLog/src/shared/LogManager.h new file mode 100644 index 00000000..477fde0b --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/LogManager.h @@ -0,0 +1,58 @@ +// ====================================================================== +// +// LogManager.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_LogManager_H +#define INCLUDED_LogManager_H + +// ====================================================================== + +class LogMessage; +class LogObserver; + +// ====================================================================== + +// The LogManager is a static class which catches log messages, +// processes them from varArgs into straight ascii text, and passes the +// processed string on to any observers, along with a timestamp, +// process identifier, and any unicode text attached. + +typedef LogObserver *(*LogObserverCreateFunc)(std::string const &); + +class LogManager +{ +public: + + static void install(std::string const &procId, bool flushOnWrite = true); + static void update(); + static void setProcessIdentifier(std::string const &procId); + + // note: setArgs() and log() always come as a pair, and must do so to avoid deadlocks + static void setArgs(std::string const &channel, Unicode::String const &unicodeAttach); + static void setArgs(std::string const &channel); + static void log(char const *format, ...); + static void logLongText(std::string const & channel, std::string const & longText); + + static void observeLogMessage(LogMessage const &msg); + + static void addObserver(LogObserver *observer); + static void removeObserver(LogObserver const *observer); + static void registerObserverType(std::string const &name, LogObserverCreateFunc func); + static bool setupObserver(std::string const &desc); + static bool getFlushOnWrite(); + static void flush(); + +private: + LogManager(); + LogManager(LogManager const &); + LogManager &operator=(LogManager const &); +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/LogObserver.cpp b/engine/shared/library/sharedLog/src/shared/LogObserver.cpp new file mode 100644 index 00000000..e33954d6 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/LogObserver.cpp @@ -0,0 +1,111 @@ +// ====================================================================== +// +// LogObserver.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedFoundation/NetworkIdArchive.h" +#include "sharedLog/LogObserver.h" +#include "sharedNetworkMessages/LogMessage.h" +#include +#include + +// ====================================================================== + +LogObserver::LogObserver() : + m_filters(new std::vector()) +{ +} + +// ---------------------------------------------------------------------- + +LogObserver::~LogObserver() +{ + delete m_filters; +} + +// ---------------------------------------------------------------------- + +void LogObserver::setFilter(std::string const &filter) +{ + // filter is a : separated list of filter rules. split them and add + // valid ones to the filter list + m_filters->clear(); + size_t curPos = 0; + while (curPos < filter.length()) + { + size_t endPos = filter.find(":", curPos); + if (endPos == std::string::npos) + endPos = filter.length()+1; + m_filters->push_back(filter.substr(curPos, endPos-curPos)); + curPos = endPos+1; + } +} + +// ---------------------------------------------------------------------- + +bool LogObserver::isFiltered(LogMessage const &msg) const +{ + bool filtered = false; + // run through the list of filters comparing them + std::vector::const_iterator i; + for (i = m_filters->begin(); i != m_filters->end(); ++i) + { + std::string const &filter = (*i); + std::string const *source = 0; + // source specifier (c or d - channel or data) + if (filter[0] == 'c') + source = &msg.getChannel(); + else if (filter[0] == 'd') + source = &msg.getText(); + else if (filter[0] == 'p') + source = &msg.getProcId(); + else + continue; // invalid source specifier, skip filter + // filter action (+ or -) + bool action = false; + if (filter[1] == '+') + action = false; + else if (filter[1] == '-') + action = true; + else + continue; // invalid action specifier, skip filter + // may be a comparison negation specifier + size_t filterPos = 2; + bool negateCompare = false; + if (filter[2] == '!') + { + ++filterPos; + negateCompare = true; + } + bool match; + // either '*' or a substring to search for, may change this to regex + if (filter[filterPos] == '*') + match = true; + else + match = (source->find(filter.c_str()+filterPos) != std::string::npos); + if (negateCompare) + match = !match; + if (match) + filtered = action; + } + return filtered; +} + +// ---------------------------------------------------------------------- + +void LogObserver::flush() +{ +} + +//------------------------------------------------------------------------------------------ + +void LogObserver::update() +{ +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/LogObserver.h b/engine/shared/library/sharedLog/src/shared/LogObserver.h new file mode 100644 index 00000000..60555c04 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/LogObserver.h @@ -0,0 +1,47 @@ +// ====================================================================== +// +// LogObserver.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_LogObserver_H +#define INCLUDED_LogObserver_H + +// ====================================================================== + +class LogMessage; + +// ====================================================================== + +// A LogObserver is derived from in order to provide a mechanism for +// catching log messages and doing something with them. A derived +// observer object must be added to the logManager before it will +// have any effect. + +class LogObserver +{ +public: + LogObserver(); + virtual ~LogObserver(); + + virtual void log(LogMessage const &msg) = 0; + virtual void flush(); + + void setFilter(std::string const &filter); + bool isFiltered(LogMessage const &msg) const; + + virtual void update(); + +private: + LogObserver(LogObserver const &); + LogObserver &operator=(LogObserver const &); + + stdvector::fwd* m_filters; +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/NetLogConnection.cpp b/engine/shared/library/sharedLog/src/shared/NetLogConnection.cpp new file mode 100644 index 00000000..1c85dc73 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/NetLogConnection.cpp @@ -0,0 +1,69 @@ +// ====================================================================== +// +// NetLogConnection.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedFoundation/NetworkIdArchive.h" +#include "sharedLog/NetLogObserver.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "NetLogConnection.h" + +// ====================================================================== + +NetLogConnection::NetLogConnection(NetLogObserver *owner) : + Connection(owner->getRemoteAddress(), owner->getRemotePort(), NetworkSetupData()), + m_owner(owner) +{ +} + +// ---------------------------------------------------------------------- + +NetLogConnection::~NetLogConnection() +{ + if (m_owner) + m_owner->onConnectionDestroyed(this); + m_owner = 0; +} + +// ---------------------------------------------------------------------- + +void NetLogConnection::onConnectionClosed() +{ + if (m_owner) + m_owner->onConnectionClosed(); +} + +// ---------------------------------------------------------------------- + +void NetLogConnection::onConnectionOpened() +{ + if (m_owner) + m_owner->onConnectionOpened(); +} + +// ---------------------------------------------------------------------- + +void NetLogConnection::clearOwner() +{ + m_owner = 0; +} + +// ---------------------------------------------------------------------- + +void NetLogConnection::onReceive(Archive::ByteStream const &) +{ +} + +// ---------------------------------------------------------------------- + +bool NetLogConnection::isNetLogConnection() const +{ + return true; +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/NetLogConnection.h b/engine/shared/library/sharedLog/src/shared/NetLogConnection.h new file mode 100644 index 00000000..8c7748b6 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/NetLogConnection.h @@ -0,0 +1,48 @@ +// ====================================================================== +// +// NetLogConnection.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_NetLogConnection_H +#define INCLUDED_NetLogConnection_H + +// ====================================================================== + +#include "sharedNetwork/Connection.h" + +// ====================================================================== + +class NetLogObserver; + +// ====================================================================== + +class NetLogConnection: public Connection +{ +public: + explicit NetLogConnection(NetLogObserver *owner); + virtual ~NetLogConnection(); + + virtual void onConnectionClosed(); + virtual void onConnectionOpened(); + virtual void onReceive(Archive::ByteStream const &); + + void clearOwner(); + +protected: + virtual bool isNetLogConnection() const; + +private: + NetLogConnection(); + NetLogConnection(NetLogConnection const &); + NetLogConnection &operator=(NetLogConnection const &); + + NetLogObserver *m_owner; +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/NetLogObserver.cpp b/engine/shared/library/sharedLog/src/shared/NetLogObserver.cpp new file mode 100644 index 00000000..c928d964 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/NetLogObserver.cpp @@ -0,0 +1,174 @@ +// ====================================================================== +// +// NetLogObserver.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedLog/NetLogObserver.h" + +#include "sharedFoundation/Clock.h" +#include "sharedFoundation/NetworkIdArchive.h" +#include "sharedLog/LogManager.h" +#include "sharedLog/ConfigSharedLog.h" +#include "sharedNetworkMessages/GameNetworkMessage.h" +#include "sharedNetworkMessages/LogMessage.h" +#include "Archive/ByteStream.h" +#include "NetLogConnection.h" +#include +#include + +// ====================================================================== + +void NetLogObserver::install() +{ + LogManager::registerObserverType("net", create); +} + +// ---------------------------------------------------------------------- + +LogObserver *NetLogObserver::create(std::string const &spec) +{ + // parse spec into address:port + size_t pos = spec.find(":"); + if (pos != std::string::npos) + { + std::string address = spec.substr(0, pos); + unsigned short port = static_cast(atoi(spec.c_str()+pos+1)); + // create a NetLogObserver to forward to the specified address/port + return new NetLogObserver(address, port); + } + return 0; +} + +// ---------------------------------------------------------------------- + +NetLogObserver::NetLogObserver(const std::string &remoteAddress, unsigned short remotePort) : + LogObserver(), + m_mutex(), + m_remoteAddress(remoteAddress), + m_remotePort(remotePort), + m_connection(0), + m_connectionOpen(false), + m_connectTime(0), + m_logMessageQueue1(new std::vector), + m_logMessageQueue2(new std::vector) +{ +} + +// ---------------------------------------------------------------------- + +NetLogObserver::~NetLogObserver() +{ + delete m_logMessageQueue1; + delete m_logMessageQueue2; + + m_logMessageQueue1 = 0; + m_logMessageQueue2 = 0; + + // the observer owns the connection + if (m_connection) + { + m_connection->clearOwner(); + m_connection->setDisconnectReason("NetLogObserver destroyed"); + m_connection->disconnect(); + m_connection = 0; + } +} + +// ---------------------------------------------------------------------- + +void NetLogObserver::onConnectionOpened() +{ + m_connectionOpen = true; +} + +// ---------------------------------------------------------------------- + +void NetLogObserver::onConnectionClosed() +{ + m_connectionOpen = false; + if (m_connection) + { + m_connection->clearOwner(); + m_connection = 0; + } +} + +// ---------------------------------------------------------------------- + +void NetLogObserver::onConnectionDestroyed(NetLogConnection *connection) +{ + if (m_connection == connection) + m_connection = 0; +} + +// ---------------------------------------------------------------------- + +void NetLogObserver::sendLogMessage(LogMessage const &msg) const +{ + if (m_connection) + { + Archive::ByteStream a; + a.clear(); + msg.pack(a); + m_connection->send(a, true); + } +} + +// ---------------------------------------------------------------------- + +void NetLogObserver::log(LogMessage const &msg) +{ + static unsigned int logNetQueueSize = static_cast(ConfigSharedLog::getLogNetQueueSize()); + if (logNetQueueSize) + { + m_mutex.enter(); + if (m_logMessageQueue1->size() < logNetQueueSize) + m_logMessageQueue1->push_back(msg); + m_mutex.leave(); + } +} + +//------------------------------------------------------------------------------------------ + +void NetLogObserver::update() +{ + static unsigned long reconnectTime = static_cast(ConfigSharedLog::getLogNetReconnectTimeMs()); + + if (m_connection && m_connectionOpen) + { + m_mutex.enter(); + std::vector *q = m_logMessageQueue1; + m_logMessageQueue1 = m_logMessageQueue2; + m_logMessageQueue2 = q; + m_mutex.leave(); + // send any queued up log messages + + for (std::vector::const_iterator i = m_logMessageQueue2->begin(); i != m_logMessageQueue2->end(); ++i) + sendLogMessage(*i); + + m_logMessageQueue2->clear(); + } + else if (Clock::timeMs()-m_connectTime > reconnectTime) + connect(); +} + +// ---------------------------------------------------------------------- + +void NetLogObserver::connect() +{ + if (m_connection) + { + m_connection->clearOwner(); + m_connection->setDisconnectReason("About to connect again"); + m_connection->disconnect(); + m_connection = 0; + } + m_connectTime = Clock::timeMs(); + m_connection = new NetLogConnection(this); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedLog/src/shared/NetLogObserver.h b/engine/shared/library/sharedLog/src/shared/NetLogObserver.h new file mode 100644 index 00000000..14023e7a --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/NetLogObserver.h @@ -0,0 +1,80 @@ +// ====================================================================== +// +// NetLogObserver.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_NetLogObserver_H +#define INCLUDED_NetLogObserver_H + +// ====================================================================== + +#include "sharedLog/LogObserver.h" +#include "sharedSynchronization/Mutex.h" +#include + +// ====================================================================== + +class NetLogConnection; + +// ====================================================================== + +// A NetLogObserver sends log messages to a another process +class NetLogObserver: public LogObserver +{ +public: + + static void install(); + static LogObserver *create(std::string const &spec); + + NetLogObserver(std::string const &remoteAddress, unsigned short remotePort); + virtual ~NetLogObserver(); + + virtual void log(LogMessage const &msg); + virtual void onConnectionOpened(); + virtual void onConnectionClosed(); + virtual void onConnectionDestroyed(NetLogConnection *connection); + + virtual void update(); + + std::string const &getRemoteAddress() const; + unsigned short getRemotePort() const; + +private: + NetLogObserver(); + NetLogObserver(NetLogObserver const &); + NetLogObserver &operator=(NetLogObserver const &); + + void connect(); + void sendLogMessage(LogMessage const &msg) const; + + Mutex m_mutex; + std::string m_remoteAddress; + unsigned short m_remotePort; + NetLogConnection * m_connection; + bool m_connectionOpen; + unsigned long m_connectTime; + stdvector::fwd* m_logMessageQueue1; + stdvector::fwd* m_logMessageQueue2; +}; + +// ====================================================================== + +inline std::string const &NetLogObserver::getRemoteAddress() const +{ + return m_remoteAddress; +} + +// ---------------------------------------------------------------------- + +inline unsigned short NetLogObserver::getRemotePort() const +{ + return m_remotePort; +} + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/SetupSharedLog.cpp b/engine/shared/library/sharedLog/src/shared/SetupSharedLog.cpp new file mode 100644 index 00000000..33842584 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/SetupSharedLog.cpp @@ -0,0 +1,92 @@ +// ====================================================================== +// +// SetupSharedLog.cpp +// +// Copyright 2002-2003 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedLog/SetupSharedLog.h" + +#include "sharedDebug/InstallTimer.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedLog/ConfigSharedLog.h" +#include "sharedLog/FileLogObserver.h" +#include "sharedLog/NetLogObserver.h" +#include "sharedLog/LogManager.h" +#include "sharedLog/Log.h" +#include "sharedLog/TailFileLogObserver.h" + +// ====================================================================== + +namespace SetupSharedLogNamespace +{ + void logReportLog(char const *message); + void logReportWarning(char const *message); + void logReportFatal(char const *message); + + bool ms_installed; + std::string const cms_reportLogChannel("reportLog"); + std::string const cms_reportWarningChannel("warning"); + std::string const cms_reportFatalChannel("fatal"); +} +using namespace SetupSharedLogNamespace; + +// ====================================================================== + +void SetupSharedLogNamespace::logReportLog(char const *message) +{ + LOG(cms_reportLogChannel, ("%s", message)); +} + +// ---------------------------------------------------------------------- + +void SetupSharedLogNamespace::logReportWarning(char const *message) +{ + LOG(cms_reportWarningChannel, ("%s", message)); +} + +// ---------------------------------------------------------------------- + +void SetupSharedLogNamespace::logReportFatal(char const *message) +{ + LOG(cms_reportFatalChannel, ("%s", message)); +} + +// ====================================================================== + +void SetupSharedLog::install(std::string const &procId, bool flushOnWrite) +{ + InstallTimer const installTimer("SetupSharedLog::install"); + + DEBUG_FATAL (ms_installed, ("SetupSharedLog::install already installed")); + ms_installed = true; + + ConfigSharedLog::install(); + LogManager::install(procId, flushOnWrite); + FileLogObserver::install(); + NetLogObserver::install(); + TailFileLogObserver::install(); + + if (ConfigSharedLog::getLogReportLogs()) + Report::bindLogCallback(logReportLog); + + if (ConfigSharedLog::getLogReportWarnings()) + Report::bindWarningCallback(logReportWarning); + + if (ConfigSharedLog::getLogReportFatals()) + Report::bindFatalCallback(logReportFatal); +} + +// ---------------------------------------------------------------------- + +void SetupSharedLog::remove() +{ + DEBUG_FATAL (!ms_installed, ("SetupSharedLog::remove not installed")); + ms_installed = false; +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/SetupSharedLog.h b/engine/shared/library/sharedLog/src/shared/SetupSharedLog.h new file mode 100644 index 00000000..26729c34 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/SetupSharedLog.h @@ -0,0 +1,31 @@ +// ====================================================================== +// +// SetupSharedLog.h +// +// Copyright 2002-2003 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_SetupSharedLog_H +#define INCLUDED_SetupSharedLog_H + +// ====================================================================== + +class SetupSharedLog +{ +public: + + static void install(std::string const &procId, bool flushOnWrite = true); + static void remove(); + +private: + + SetupSharedLog(SetupSharedLog const &); + SetupSharedLog &operator=(SetupSharedLog const &); +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/shared/StderrLogger.h b/engine/shared/library/sharedLog/src/shared/StderrLogger.h new file mode 100644 index 00000000..b7914e45 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/StderrLogger.h @@ -0,0 +1,26 @@ +// ====================================================================== +// +// StderrLogger.h +// +// Copyright 2003 Sony Online Entertainment +// +// ====================================================================== + +#ifndef _StderrLogger_H_ +#define _StderrLogger_H_ + +// ====================================================================== + +class StderrLogger +{ +public: + static void install(); + static void remove(); + + static void update(); +}; + +// ====================================================================== + +#endif // _StderrLogger_H_ + diff --git a/engine/shared/library/sharedLog/src/shared/TailFileLogObserver.cpp b/engine/shared/library/sharedLog/src/shared/TailFileLogObserver.cpp new file mode 100644 index 00000000..2a8e0dc7 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/TailFileLogObserver.cpp @@ -0,0 +1,188 @@ +// ====================================================================== +// +// TailFileLogObserver.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedFoundation/NetworkIdArchive.h" +#include "sharedLog/TailFileLogObserver.h" +#include "sharedLog/LogManager.h" +#include "fileInterface/StdioFile.h" +#include "sharedNetworkMessages/LogMessage.h" +#include "UnicodeUtils.h" +#include +#include + +// ====================================================================== + +const int LOG_FILESIZE_NEXT = 2000000000; // point at which we roll over to another log file +const int DEFAULT_NUM_LINES = 100; + +namespace TailFileLogObserverNamespace +{ + std::vector s_tailFileLogObservers; +}; + +using namespace TailFileLogObserverNamespace; + +// ====================================================================== + +void TailFileLogObserver::install() +{ + LogManager::registerObserverType("tailfile", create); +} + +// ---------------------------------------------------------------------- + +LogObserver *TailFileLogObserver::create(std::string const &spec) +{ + return new TailFileLogObserver(spec); +} + +// ---------------------------------------------------------------------- + +TailFileLogObserver::TailFileLogObserver(std::string const &filename) : + LogObserver(), + m_filename(filename), + m_file(0), + m_fileIndex(0), + m_numLines(DEFAULT_NUM_LINES), + m_textBuffer(0), + m_nextTextBufferEntry(0) +{ + int filenameAsNumber = -1; + filenameAsNumber = atoi(m_filename.c_str()); + m_numLines = filenameAsNumber; + std::string::size_type const index = filename.find_last_of(','); + if(index != std::string::npos) + m_filename = filename.substr(index + 1, filename.length()); + else + m_filename.clear(); + m_textBuffer = new std::vector(m_numLines); + + s_tailFileLogObservers.push_back(this); +} + +// ---------------------------------------------------------------------- + +void TailFileLogObserver::setNumberOfLinesToKeep(int numLines) +{ + m_numLines = numLines; + m_textBuffer->clear(); + m_nextTextBufferEntry = 0; + m_textBuffer->reserve(m_numLines); +} + +// ---------------------------------------------------------------------- + +void TailFileLogObserver::prepareFile() +{ + while (!m_file || m_file->tell() >= LOG_FILESIZE_NEXT) + { + if (m_file) + { + ++m_fileIndex; + delete m_file; + m_file = 0; + } + if (m_fileIndex == 0) + m_file = new StdioFile(m_filename.c_str(), "a"); + else + { + char buf[512]; + IGNORE_RETURN( snprintf(buf, 512, "%s-%d", m_filename.c_str(), m_fileIndex+1) ); + m_file = new StdioFile(buf, "a"); + } + NOT_NULL(m_file); + FATAL(!m_file->isOpen(), ("Could not open %s", m_filename.c_str())); + } +} + +// ---------------------------------------------------------------------- + +TailFileLogObserver::~TailFileLogObserver() +{ + delete m_file; + delete m_textBuffer; +} + +// ---------------------------------------------------------------------- + +void TailFileLogObserver::log(LogMessage const &msg) +{ + std::string & newLogMessage = (*m_textBuffer)[m_nextTextBufferEntry++]; + + uint64 timestamp = msg.getTimestamp(); + std::string const &procId = msg.getProcId(); + std::string const &channel = msg.getChannel(); + std::string const &text = msg.getText(); + std::string uniAttach(Unicode::wideToNarrow(msg.getUnicodeAttach())); + char tsbuf[16]; // yyyymmddhhmmss (14) + + newLogMessage.clear(); + IGNORE_RETURN( sprintf(tsbuf, UINT64_FORMAT_SPECIFIER, timestamp) ); + newLogMessage.append(tsbuf); + newLogMessage.append(":"); + newLogMessage.append(procId); + newLogMessage.append(":"); + newLogMessage.append(channel); + newLogMessage.append(":"); + newLogMessage.append(text); + if(uniAttach.length()) + { + newLogMessage.append(":"); + newLogMessage.append(uniAttach); + } + + if(m_nextTextBufferEntry >= m_numLines) + m_nextTextBufferEntry = 0; +} + +// ---------------------------------------------------------------------- + +void TailFileLogObserver::flush(const char * filename) +{ + if(m_filename.empty()) + m_filename = filename; + prepareFile(); + NOT_NULL(m_file); + + int i; + for(i = m_nextTextBufferEntry; i < m_numLines; ++i) + { + if(!(*m_textBuffer)[i].empty()) + { + IGNORE_RETURN(m_file->write((*m_textBuffer)[i].length(), (*m_textBuffer)[i].c_str()) ); + } + } + for(i = 0; i < m_nextTextBufferEntry; ++i) + { + if(!(*m_textBuffer)[i].empty()) + { + IGNORE_RETURN(m_file->write((*m_textBuffer)[i].length(), (*m_textBuffer)[i].c_str()) ); + } + } + if (m_file) + m_file->flush(); + m_file->close(); +} + +// ---------------------------------------------------------------------- + +void TailFileLogObserver::flushAllTailFileLogObservers(const char * filename) +{ + for(std::vector::iterator i = s_tailFileLogObservers.begin(); i != s_tailFileLogObservers.end(); ++i) + { + TailFileLogObserver *tflo = *i; + if(tflo) + { + tflo->flush(filename); + } + } +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedLog/src/shared/TailFileLogObserver.h b/engine/shared/library/sharedLog/src/shared/TailFileLogObserver.h new file mode 100644 index 00000000..433278f2 --- /dev/null +++ b/engine/shared/library/sharedLog/src/shared/TailFileLogObserver.h @@ -0,0 +1,61 @@ +// ====================================================================== +// +// TailFileLogObserver.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_TailFileLogObserver_H +#define INCLUDED_TailFileLogObserver_H + +// ====================================================================== + +#include "sharedLog/LogObserver.h" +#include + +// ====================================================================== + +class AbstractFile; + +// ====================================================================== + +// A TailFileLogObserver saves log messages to a file, but only the last N, +// and only when requested (not constantly). +class TailFileLogObserver: public LogObserver +{ +public: + + static void install(); + static LogObserver *create(std::string const &spec); + + TailFileLogObserver(std::string const &filename); + virtual ~TailFileLogObserver(); + + virtual void log(LogMessage const &msg); + virtual void flush(const char * filename); + + virtual void setNumberOfLinesToKeep(int numLines); + + static void flushAllTailFileLogObservers(const char *filename); + +private: + TailFileLogObserver(TailFileLogObserver const &); + TailFileLogObserver &operator=(TailFileLogObserver const &); + + void prepareFile(); + +private: + std::string m_filename; + AbstractFile *m_file; + int m_fileIndex; + + int m_numLines; + stdvector::fwd *m_textBuffer; + int m_nextTextBufferEntry; +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedLog/src/win32/StderrLogger.cpp b/engine/shared/library/sharedLog/src/win32/StderrLogger.cpp new file mode 100644 index 00000000..c7efe43d --- /dev/null +++ b/engine/shared/library/sharedLog/src/win32/StderrLogger.cpp @@ -0,0 +1,35 @@ +// ====================================================================== +// +// StderrLogger.cpp +// +// Copyright 2003 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedLog/FirstSharedLog.h" +#include "sharedLog/StderrLogger.h" + +// ====================================================================== + +// Unimplemented for win32 + +// ====================================================================== + +void StderrLogger::install() +{ +} + +// ---------------------------------------------------------------------- + +void StderrLogger::remove() +{ +} + +// ---------------------------------------------------------------------- + +void StderrLogger::update() +{ +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedMessageDispatch/CMakeLists.txt b/engine/shared/library/sharedMessageDispatch/CMakeLists.txt new file mode 100644 index 00000000..c3ee4994 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedMessageDispatch) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Emitter.h b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Emitter.h new file mode 100644 index 00000000..ceeab613 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Emitter.h @@ -0,0 +1 @@ +#include "../../src/shared/Emitter.h" diff --git a/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/FirstSharedMessageDispatch.h b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/FirstSharedMessageDispatch.h new file mode 100644 index 00000000..c2414a36 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/FirstSharedMessageDispatch.h @@ -0,0 +1 @@ +#include "../../src/shared/FirstSharedMessageDispatch.h" diff --git a/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Message.h b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Message.h new file mode 100644 index 00000000..ad1e032e --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Message.h @@ -0,0 +1 @@ +#include "../../src/shared/Message.h" diff --git a/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/MessageManager.h b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/MessageManager.h new file mode 100644 index 00000000..eeed6810 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/MessageManager.h @@ -0,0 +1 @@ +#include "../../src/shared/MessageManager.h" diff --git a/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Receiver.h b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Receiver.h new file mode 100644 index 00000000..649a1b0a --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Receiver.h @@ -0,0 +1 @@ +#include "../../src/shared/Receiver.h" diff --git a/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Transceiver.h b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Transceiver.h new file mode 100644 index 00000000..372efeff --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/include/public/sharedMessageDispatch/Transceiver.h @@ -0,0 +1 @@ +#include "../../src/shared/Transceiver.h" diff --git a/engine/shared/library/sharedMessageDispatch/src/CMakeLists.txt b/engine/shared/library/sharedMessageDispatch/src/CMakeLists.txt new file mode 100644 index 00000000..f22167a8 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/CMakeLists.txt @@ -0,0 +1,37 @@ + +set(SHARED_SOURCES + shared/Emitter.cpp + shared/Emitter.h + shared/FirstSharedMessageDispatch.h + shared/Message.cpp + shared/Message.h + shared/MessageManager.cpp + shared/MessageManager.h + shared/Receiver.cpp + shared/Receiver.h + shared/Transceiver.cpp + shared/Transceiver.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstSharedMessageDispatch.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES "") +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public +) + +add_library(sharedMessageDispatch STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Emitter.cpp b/engine/shared/library/sharedMessageDispatch/src/shared/Emitter.cpp new file mode 100644 index 00000000..8d161e58 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Emitter.cpp @@ -0,0 +1,268 @@ +//--------------------------------------------------------------------- + +#include "sharedMessageDispatch/FirstSharedMessageDispatch.h" + +#include "sharedDebug/Profiler.h" +#include "sharedMessageDispatch/Emitter.h" +#include "sharedMessageDispatch/MessageManager.h" +#include "sharedMessageDispatch/Receiver.h" +#include "sharedMessageDispatch/Message.h" + +#include +#include +#include + +namespace MessageDispatch { +struct Emitter::ReceiverList +{ + typedef std::set ReceiverSet; + typedef std::map Container; + mutable Container c; +}; + +//--------------------------------------------------------------------- +/** + @brief Construct an emitter + + An Emitter is an abstract base. This is never directly invoked. + + @author Justin Randall +*/ +Emitter::Emitter() : +receiverList(new ReceiverList) +{ + assert (receiverList != NULL); +} + +//--------------------------------------------------------------------- +/** + @brief pure virtual destructor + + The Emitter destructor cleans up message dispatch relationships +*/ +Emitter::~Emitter() +{ + for(ReceiverList::Container::iterator i = receiverList->c.begin(); i != receiverList->c.end(); ++i) + { + ReceiverList::ReceiverSet & targets = (*i).second; + for(ReceiverList::ReceiverSet::iterator j = targets.begin(); j != targets.end(); ++j) + { + Receiver * r = (*j); + r->emitterDestroyed(*this); + } + } + + delete receiverList; + receiverList = 0; +} + +//--------------------------------------------------------------------- +/** + @brief Attach a Receiver object directly to this Emitter + + When a Receiver needs to specifically target a particular emitter, + it should invoke addReceiver and specify the message type it is + subscribing to. + + @param target The Receiver object that will receive the + specified message when the Emitter emits. + @param messageTypeName The message type that the Emitter should + deliver to the target Receiver. + + @see Receiver + @see MessageManager::addReceiver + + @author Justin Randall +*/ +void Emitter::addReceiver(Receiver & target, const char * const messageTypeName) const +{ + const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName); + addReceiver(target, messageType); +} + +//----------------------------------------------------------------------- + +void Emitter::addReceiver(Receiver & target, const MessageBase & source) const +{ + const unsigned long int messageType = source.getType(); + addReceiver(target, messageType); +} + +//----------------------------------------------------------------------- + +void Emitter::addReceiver(Receiver & target, const unsigned long int messageType) const +{ + ReceiverList::Container::iterator i = receiverList->c.find(messageType); + + if(i == receiverList->c.end()) + { + ReceiverList::ReceiverSet newSet; + newSet.insert(&target); //lint !e534 // ignoring iterator returned from insert + receiverList->c.insert (std::make_pair (messageType, newSet)); + } + else + { + ReceiverList::ReceiverSet & s = (*i).second; + s.insert(&target); //lint !e534 // ignoring iterator returned from insert + } +} + +//--------------------------------------------------------------------- +/** + @brief emit a message + + All targeted Receivers will receive the Message that is emitted by + this Emitter object. The Message is forwarded to the MessageManager + to be dispatched to objects that are interested in receiving ALL + messages of a specific type, regardless of who emits it. Those + Receiver objects will only receive the Message via the MessageManager + if they did NOT receive the message from this Emitter object as a + targetted message. + + @param message A const reference to a MessageBase object that will + be delivered to registered Receivers. + + @see addReceiver + @see MessageManager::addReceiver + + @author Justin Randall +*/ +void Emitter::emitMessage(const MessageBase & message) const +{ + NOT_NULL(receiverList); + + ReceiverList::Container::iterator i = receiverList->c.find(message.getType()); + if(i != receiverList->c.end()) + { + //-- make copy + const ReceiverList::ReceiverSet targets = (*i).second; + for(ReceiverList::ReceiverSet::const_iterator j = targets.begin(); j != targets.end(); ++j) + { + Receiver * r = (*j); + NOT_NULL(r); + r->receiveMessage(*this, message); + } + } + MessageManager::getInstance().emitMessage(*this, message); +} + +//--------------------------------------------------------------------- +/** + @brief helper to determine if an Emitter has a Receiver in it's target + list + + This helper is invoked by the MessageManager to determine if a + Receiver is registered as a target for a message type. When the + Emitter emits a message, all Receiver objects registered with the + Emitter will receive the message. If a Receiver object is ALSO registered + with the MessageManager to receive ALL messages of the requested type, + the MessageManager invokes this method on the source Emitter object + to ensure that the Receiver target is not delivered the same message + twice. + + @param target The receiver to search for + @param messageType The MessageBase typeID of the message that the + receiver is interested in. + + @return true if this Emitter object has the Receiver mapped to the + messageType in it's target list. + + @see MessageManager + + @author Justin Randall +*/ +const bool Emitter::hasReceiver(const Receiver & target, const unsigned long int messageType) const +{ + bool result = false; + ReceiverList::Container::const_iterator i = receiverList->c.find(messageType); + if(i != receiverList->c.end()) + { + const ReceiverList::ReceiverSet & targets = (*i).second; + const ReceiverList::ReceiverSet::const_iterator j = targets.find(const_cast(&target)); //stl is broken + result = (j != targets.end()); + } + return result; +} + +//--------------------------------------------------------------------- +/** + @brief helper to determine if an Emitter has a Receiver in it's target + list + + This helper is invoked to determine if a Receiver is registered as a + target for any message type. + + @param target The receiver to search for + + @return true if this Emitter object has the Receiver mapped to any messageType + + @see MessageManager + + @author John Watson +*/ +const bool Emitter::hasReceiver(const Receiver & target) const +{ + for (ReceiverList::Container::const_iterator i = receiverList->c.begin (); i != receiverList->c.end (); ++i) + { + const ReceiverList::ReceiverSet & targets = (*i).second; + const ReceiverList::ReceiverSet::const_iterator j = targets.find(const_cast(&target)); //stl is broken + if (j != targets.end()) + return true; + } + return false; +} + +//--------------------------------------------------------------------- +/** + @brief remove a receiver from target lists + + This is most often invoked from a Receiver object's destructor. It + removes the receiver from all Message mappings. + + @param target The receiver that is being destroyed + + @author Justin Randall +*/ +void Emitter::receiverDestroyed(const Receiver & target) const +{ + // find the receiver + for(ReceiverList::Container::iterator i = receiverList->c.begin(); i != receiverList->c.end(); ++i) + { + ReceiverList::ReceiverSet & targets = (*i).second; + const ReceiverList::ReceiverSet::iterator j = targets.find(const_cast(&target)); //stl is broken + + if(j != targets.end()) + { + targets.erase(j); + } + } +} + +//--------------------------------------------------------------------- +/** + @brief Remove a Receiver object from a specific message in the target map + + @param target The Receiver object that will break the + Target->message relationship. + @param messageTypeName A message type describing the message type the + Receiver will disassociate itself from + + @author Justin Randall +*/ +void Emitter::removeReceiver(const Receiver & target, const char * const messageTypeName) const +{ + unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName); + + const ReceiverList::Container::iterator i = receiverList->c.find(messageType); + if(i != receiverList->c.end()) + { + ReceiverList::ReceiverSet & rset = (*i).second; + //const cast for broken stl + rset.erase(const_cast(&target)); //lint !e534 // ignoring iterator returned from insert + } +} + +//--------------------------------------------------------------------- + +}//namespace MessageDispatch + diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Emitter.h b/engine/shared/library/sharedMessageDispatch/src/shared/Emitter.h new file mode 100644 index 00000000..ee9a1bfe --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Emitter.h @@ -0,0 +1,60 @@ +// ====================================================================== +// +// Emitter.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Emitter_H +#define INCLUDED_Emitter_H + +// ====================================================================== + +namespace MessageDispatch +{ + + class MessageBase; + class Receiver; + + /** + @brief Class that can emit messages + + Emitter objects can transmit Message objects, which are dispatched + to receivers either directly, or via the MessageManager. + + @see Message + @see MessageBase + @see Receiver + @see MessageManager + + @author Justin Randall + */ + class Emitter + { + public: + Emitter(); + virtual ~Emitter(); + void addReceiver(Receiver & target, const char * const messageTypeName) const; + void addReceiver(Receiver & target, const MessageBase & source) const; + void addReceiver(Receiver & target, const unsigned long int messageType) const; + void emitMessage(const MessageBase & message) const; + + const bool hasReceiver(const Receiver & target, const unsigned long int messageType) const; + const bool hasReceiver(const Receiver & target) const; + + void receiverDestroyed(const Receiver & target) const; + void removeReceiver(const Receiver & target, const char * const messageTypeName) const; + + private: + + struct ReceiverList; + ReceiverList * receiverList; + }; + + +}// namespace MessageDispatch + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/FirstSharedMessageDispatch.h b/engine/shared/library/sharedMessageDispatch/src/shared/FirstSharedMessageDispatch.h new file mode 100644 index 00000000..0aa6d74f --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/FirstSharedMessageDispatch.h @@ -0,0 +1,23 @@ +// ====================================================================== +// +// FirstMessageDispatch.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstMessageDispatch_H +#define INCLUDED_FirstMessageDispatch_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" +#include "sharedMessageDispatch/Emitter.h" +#include "sharedMessageDispatch/MessageManager.h" +#include "sharedMessageDispatch/Message.h" +#include "sharedMessageDispatch/Receiver.h" +#include +#include + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Message.cpp b/engine/shared/library/sharedMessageDispatch/src/shared/Message.cpp new file mode 100644 index 00000000..f3d6f4b5 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Message.cpp @@ -0,0 +1,81 @@ +// ====================================================================== +// +// FirstMessageDispatch.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMessageDispatch/FirstSharedMessageDispatch.h" +#include "sharedMessageDispatch/Message.h" + +#include "sharedFoundation/LabelHash.h" +#include "sharedFoundation/Crc.h" +#include "sharedFoundation/Production.h" + +#include + +namespace MessageDispatch +{ + +// ====================================================================== + +/** + @brief build a message type to identify a message + + Returns an STL hash of the type name. +*/ +const unsigned long int MessageBase::makeMessageTypeFromString(const char * const idString) +{ + unsigned long int result = 0; +#if PRODUCTION == 0 + result = LabelHash::hashLabel("MessageDispatch", idString); +#else + result = Crc::calculate(idString); +#endif + return result; +} + +//--------------------------------------------------------------------- +/** + @brief construct a named message object + + The MessageBase is an abstract base class. Initializes the type + member to the STL hash of the name string. + + @author Justin Randall +*/ +MessageBase::MessageBase(const char * const typeName) : +type(0) +{ + type = makeMessageTypeFromString(typeName); +} + +//--------------------------------------------------------------------- +/** + @brief construct a named message object + + The MessageBase is an abstract base class. Initializes the type + the supplied type. Useful when the hash is cached. + + @author Justin Randall +*/ +MessageBase::MessageBase(const unsigned long int newType) : +type(newType) +{ +} + +//--------------------------------------------------------------------- +/** + @brief destroy the MessageBase object + + Doesn't do anything special. + + @author Justin Randall +*/ +MessageBase::~MessageBase() +{ +} + +// ====================================================================== + +} // namespace MessageDispatch diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Message.h b/engine/shared/library/sharedMessageDispatch/src/shared/Message.h new file mode 100644 index 00000000..0a355f1f --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Message.h @@ -0,0 +1,210 @@ +// ====================================================================== +// +// Message.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Message_H +#define INCLUDED_Message_H + +// ====================================================================== + +namespace MessageDispatch +{ + /** + @brief A base class defining a common interface for Message object + handling. + + The MessageBase is a pure virtual class. All Message objects + handled by Emitter and Receiver objects derive from the MessageBase. + + @see Message + @author Justin Randall + */ + class MessageBase + { + public: + explicit MessageBase(const char * const typeName); + explicit MessageBase(const unsigned long int type); + virtual ~MessageBase(); + static const unsigned long int makeMessageTypeFromString(const char * const id); + const unsigned long int getType() const; + const bool isType(const char * const typeName) const; + void setType(const unsigned long int newType); + void setType(const char * const typeName); + private: + MessageBase(); + unsigned long int type; + }; + + /** + @brief Message template + + Builds a message from some user supplied class. + + @author Justin Randall + */ + template + class Message : public MessageBase + { + public: + explicit Message(const char * const typeName=""); + explicit Message(const unsigned long int type); + Message(const char * const typeName, const ValueType & value); + Message(const unsigned long int type, const ValueType & value); + explicit Message(const Message & source); + virtual ~Message(); + Message & operator = (const Message & rhs); + const ValueType & getValue() const; + void setValue(const ValueType & rhs); + private: + ValueType value; + }; + + //--------------------------------------------------------------------- + /** + @brief get the type of Message + + @return the type of the Message. The type is an STL hash of the + type name. + */ + inline const unsigned long int MessageBase::getType() const + { + return type; + } + + //----------------------------------------------------------------------- + + inline const bool MessageBase::isType(const char * const typeName) const + { + return (makeMessageTypeFromString(typeName) == type); + } + + //----------------------------------------------------------------------- + /** + @brief set or reset the type identifier + */ + inline void MessageBase::setType(const unsigned long newType) + { + type = newType; + } + + //----------------------------------------------------------------------- + /** + @brief set or reset the type identifier + */ + inline void MessageBase::setType(const char * const typeName) + { + setType(makeMessageTypeFromString(typeName)); + } + + //----------------------------------------------------------------------- + /** + @brief construct a named message + + @param typeName A string describing the type of message. This + value is hashed (using the STL hash function) + the result is used for the MessageBase type name. + @author Justin Randall + */ + template + inline Message::Message(const char * const typeName) : + MessageBase(typeName), + value() + { + } + + template + inline Message::Message(const unsigned long int type) : + MessageBase(type), + value() + { + } + + //--------------------------------------------------------------------- + /** + @brief Construct a named message and initialize it to the + supplied ValueType value. + + @param typeName A string describing the type of message. This + value is hashed (using the STL hash function) + the result is used for the MessageBase type name. + @newValue Some value to initialize the message object to. + + @author Justin Randall + */ + template + inline Message::Message(const char * const typeName, const ValueType & newValue) : + MessageBase(typeName), + value(newValue) + { + } + + template + inline Message::Message(const unsigned long int type, const ValueType & newValue) : + MessageBase(type), + value(newValue) + { + } + + //--------------------------------------------------------------------- + /** + @brief Message copy constructor + + Performs a copy of the message type and value members. + + @author Justin Randall + */ + template + inline Message::Message(const Message & source) : + MessageBase(source.getType()), + value(source.getValue()) + { + } + + //--------------------------------------------------------------------- + /** + @brief destroy the Message object + + Doesn't do anything special. + + @author Justin Randall + */ + template + inline Message::~Message() + { + } + + //--------------------------------------------------------------------- + /** + @brief Assign the souce ValueType to the value member + + @param source the new value for the Message::value member + + @author Justin Randall + */ + template + inline void Message::setValue(const ValueType & source) + { + value = source; + } + + //--------------------------------------------------------------------- + /** + @brief return the Message::value member + + @return the value of the value member + */ + template + inline const ValueType & Message::getValue() const + { + return value; + } + + +}//namespace MessageDispatch + +// ====================================================================== + +#endif // _Message_H diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/MessageManager.cpp b/engine/shared/library/sharedMessageDispatch/src/shared/MessageManager.cpp new file mode 100644 index 00000000..6ce7c7da --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/MessageManager.cpp @@ -0,0 +1,225 @@ +#include "sharedMessageDispatch/FirstSharedMessageDispatch.h" + +#include "sharedDebug/Profiler.h" +#include "sharedMessageDispatch/Emitter.h" +#include "sharedMessageDispatch/Message.h" +#include "sharedMessageDispatch/MessageManager.h" +#include "sharedMessageDispatch/Receiver.h" + +#include +#include + +namespace MessageDispatch { + +MessageManager MessageManager::ms_instance; + +struct MessageManager::Data +{ + std::hash_map > receivers; + std::hash_map > staticCallbacks; +}; + +//--------------------------------------------------------------------- +/** + @brief Do NOT construct Singleton objects directly! +*/ +MessageManager::MessageManager() +: data (new Data) +{ +} + +//--------------------------------------------------------------------- +/** + @brief Do NOT destroy Singleton objects directly! +*/ +MessageManager::~MessageManager() +{ + delete data; + data = 0; +} + +//--------------------------------------------------------------------- +/** + @brief add a Receiver object to a map of Receiver objects that will + receive all messages of a certain type. + + Some Receiver objects may wish to receive a message of a certain + type regardless of the Emitter object that originates the message. + + @param target The Receiver object that will listen for all + messages of the requested type + @param messageTypeName The message type to listen for + + @see Emitter::addReceiver + + @author Justin Randall +*/ +void MessageManager::addReceiver(Receiver & target, const char * const messageTypeName) +{ + const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName); + addReceiver(target, messageType); +} + +//----------------------------------------------------------------------- + +void MessageManager::addReceiver(Receiver & target, const MessageBase & source) +{ + const unsigned long int messageType = source.getType(); + addReceiver(target, messageType); +} + +//----------------------------------------------------------------------- + +void MessageManager::addReceiver(Receiver & target, const unsigned long int messageType) +{ + std::hash_map >::iterator i = data->receivers.find(messageType); + if(i != data->receivers.end()) + { + target.setHasTargets(true); + std::set & targets = (*i).second; + targets.insert(&target);//lint !e534 // ignoring iterator returned from insert + } + else + { + target.setHasTargets(true); + std::set newTargets; + newTargets.insert(&target);//lint !e534 // ignoring iterator returned from insert + data->receivers[messageType] = newTargets; + } +} + +//----------------------------------------------------------------------- + +void MessageManager::addStaticCallback(void (*callback)(const Emitter &, const MessageBase &), const unsigned long int messageType) +{ + std::hash_map >::iterator f = data->staticCallbacks.find(messageType); + if(f != data->staticCallbacks.end()) + { + std::set & targets = f->second; + targets.insert(callback); + } + else + { + std::set newTargets; + newTargets.insert(callback); + data->staticCallbacks[messageType] = newTargets; + } +} + +//--------------------------------------------------------------------- +/** + @brief Invoked by Emitter objects when a message is broadcast + + @param emitter The source of the MessageBase object being broadcast + @param message The message being broadcast + + @see Emitter::emit + @see Receiver::onReceive + + @author Justin Randall +*/ +void MessageManager::emitMessage(const Emitter & emitter, const MessageBase & message) const +{ + const unsigned long int messageType = message.getType(); + std::hash_map >::const_iterator i = data->receivers.find(messageType); + if(i != data->receivers.end()) + { + const std::set targets = (*i).second; + std::set::const_iterator j; + for(j = targets.begin(); j != targets.end(); ++j) + { + Receiver * r = (*j); + if(! emitter.hasReceiver(*r, messageType)) + { + r->receiveMessage(emitter, message); + } + } + } + + std::hash_map >::iterator f = data->staticCallbacks.find(messageType); + if(f != data->staticCallbacks.end()) + { + const std::set targets = f->second; + std::set::const_iterator c; + for(c = targets.begin(); c != targets.end(); ++c) + { + void (*callback)(const Emitter &, const MessageBase &) = *c; + callback(emitter, message); + } + } +} + +//--------------------------------------------------------------------- +/** + @brief Invoked by a Receiver during it's destructor +*/ +void MessageManager::receiverDestroyed(const Receiver & target) +{ + if (!target.getHasTargets()) + { + return; + } + // find receiver + std::hash_map >::iterator i; + for(i = data->receivers.begin(); i != data->receivers.end(); ++i) + { + std::set & targets = (*i).second; + // const cast to satisfy STL semantics, target remains unchanged + std::set::iterator j = targets.find(const_cast(&target)); + if(j != targets.end()) + { + targets.erase(j); + } + } +} + +//--------------------------------------------------------------------- +/** + @brief break a Receiver->Message relationship + + When a Receiver no longer wants to receive all messages of a certain + type, it invokes MessageManager::removeReceiver to irradicate + any relationship with the message in the MessageManager target map. + + @param target The Receiver object that is breaking the + connection. + @param messageTypeName Identifies the source MessageBase objects + that the Receiver object will now ignore. + + @see Emitter::removeReceiver + + @author Justin Randall +*/ +void MessageManager::removeReceiver(const Receiver & target, const char * const messageTypeName) +{ + const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName); + removeReceiver(target, messageType); +} + +//----------------------------------------------------------------------- + +void MessageManager::removeReceiver(const Receiver & target, const unsigned long int messageType) +{ + std::hash_map >::iterator i = data->receivers.find(messageType); + if(i != data->receivers.end()) + { + std::set & targets = (*i).second; + std::set::iterator j = targets.find(const_cast(&target)); + if(j != targets.end()) + { + targets.erase(j); + } + } +} + +//----------------------------------------------------------------------- + +void connectToMessage(const char * const messageTypeName, void (*callback)(const Emitter &, const MessageBase &)) +{ + const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName); + MessageManager::getInstance().addStaticCallback(callback, messageType); +} + +//--------------------------------------------------------------------- + +}//namespace MessageDispatch diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/MessageManager.h b/engine/shared/library/sharedMessageDispatch/src/shared/MessageManager.h new file mode 100644 index 00000000..cc53f483 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/MessageManager.h @@ -0,0 +1,57 @@ +#ifndef INCLUDED_MessageManager_H +#define INCLUDED_MessageManager_H + +namespace MessageDispatch +{ + +//--------------------------------------------------------------------- + +class Emitter; +class MessageBase; +class Receiver; + +void connectToMessage(const char * const messageTypeName, void (*)(const Emitter &, const MessageBase &)); + +//--------------------------------------------------------------------- +/** + @brief A Singleton class responsible for broadcasting and tracking + Receiver, Emitter and Message objects + + @author Justin Randall +*/ +class MessageManager +{ +public: + MessageManager(); + ~MessageManager(); + void addReceiver(Receiver & target, const char * const messageTypeName); + void addReceiver(Receiver & target, const MessageBase & source); + void addReceiver(Receiver & target, const unsigned long int messageType); + void addStaticCallback(void (*)(const Emitter &, const MessageBase &), const unsigned long int messageTypeName); + void emitMessage(const Emitter & emitter, const MessageBase & message) const; + void receiverDestroyed(const Receiver & target); + void removeReceiver(const Receiver & target, const char * const messageTypeName); + void removeReceiver(const Receiver & target, const unsigned long int messageType); + + static MessageManager & getInstance (); + + +private: + struct Data; + Data *data; + + static MessageManager ms_instance; +}; + +//---------------------------------------------------------------------- + +inline MessageManager & MessageManager::getInstance () +{ + return ms_instance; +} + +//--------------------------------------------------------------------- + +}//namespace MessageDispatch + +#endif // _MessageManager_H diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Receiver.cpp b/engine/shared/library/sharedMessageDispatch/src/shared/Receiver.cpp new file mode 100644 index 00000000..ff929dc2 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Receiver.cpp @@ -0,0 +1,215 @@ +//--------------------------------------------------------------------- +#include "sharedMessageDispatch/FirstSharedMessageDispatch.h" + +#include "sharedDebug/Profiler.h" +#include "sharedMessageDispatch/Emitter.h" +#include "sharedMessageDispatch/MessageManager.h" +#include "sharedMessageDispatch/Message.h" +#include "sharedMessageDispatch/Receiver.h" + +#include + +#include +#include + +namespace MessageDispatch { + +struct Receiver::EmitterTargets +{ + typedef std::set Container; + Container c; +}; + +//--------------------------------------------------------------------- +/** + @brief construct a Receiver object + + Receiver objects are abstract base classes. Do NOT instantiate + a Receiver directly! + + @author Justin Randall +*/ +Receiver::Receiver() : +emitterTargets(new EmitterTargets), +hasTargets(false) +{ + assert(emitterTargets != NULL); +} + +//--------------------------------------------------------------------- + +void Receiver::setHasTargets(bool targets) +{ + hasTargets = targets; +} + +//--------------------------------------------------------------------- + +bool Receiver::getHasTargets() const +{ + return hasTargets; +} + +//--------------------------------------------------------------------- +/** + @brief destroy the Receiver object + + Finds all Emitter objects that this Receiver is connected to and + invokes Emitter::receiveMessagerDestroyed + + It then advises the MessageManager that the Receiver object is + being destroyed. + + @author Justin Randall +*/ +Receiver::~Receiver() +{ + disconnectAll(); + delete emitterTargets; + emitterTargets = 0; +} + +//--------------------------------------------------------------------- +/** + @brief connect to an Emitter to receive a message that it emits + + When an Emitter object emits a message, it dispatches the message + to all subscribed receivers before sending it to the MessageManager + for broadcasting. + + Receiver objects are inserted into the Emitter target map by way of + this function (which wraps Emitter::addReceiver) + + @param target The Emitter object to connect to + @param messageTypeName The message type to listen for + + @see Emitter::addReceiver + @see MessageManager::addReceiver + + @author Justin Randall +*/ +void Receiver::connectToEmitter(const Emitter & target, const char * const messageTypeName) +{ + target.addReceiver(*this, messageTypeName); + emitterTargets->c.insert (&target); +} + +//----------------------------------------------------------------------- +/** + @brief advise the MessageManager that this Reciever object is + interested in ALL messages of the requested type. + + @param messageTypeName A string describing the type of message + + @author Justin Randall +*/ +void Receiver::connectToMessage(const char * const messageTypeName) +{ + MessageManager::getInstance().addReceiver(*this, messageTypeName); +} + +//----------------------------------------------------------------------- + +void Receiver::connectToMessage(const MessageBase & source) +{ + MessageManager::getInstance().addReceiver(*this, source); +} + +//----------------------------------------------------------------------- + +void Receiver::disconnectAll() +{ + EmitterTargets::Container::iterator i; + for(i = emitterTargets->c.begin(); i != emitterTargets->c.end(); ++i) + { + const Emitter * e = (*i); + e->receiverDestroyed(*this); + } + emitterTargets->c.clear(); + MessageManager::getInstance().receiverDestroyed(*this); +} + +//----------------------------------------------------------------------- + +void Receiver::disconnectFromMessage(const char * const messageTypeName) +{ + disconnectFromMessage(MessageBase::makeMessageTypeFromString(messageTypeName)); +} + +//----------------------------------------------------------------------- + +void Receiver::disconnectFromMessage(const unsigned long int messageType) +{ + MessageManager::getInstance().removeReceiver(*this, messageType); +} + +//----------------------------------------------------------------------- + +void Receiver::disconnectFromMessage(const MessageBase & source) +{ + disconnectFromMessage(source.getType()); +} + +//--------------------------------------------------------------------- +/** + @brief helper to clean up destroyed emitter objects + + If an Emitter is destroyed, but is not removed from it's target + Receiver objects emitterTargets, the Receiver objects could + attempt to invoke methods on an Emitter that no longer exists (e.g. + during destruction, when the Reciever attempts to notify the + Emitter that it is going away). + + @param target The Emitter that is being destroyed + + @see Emitter::receiverDestroyed + + @author Justin Randall +*/ +void Receiver::emitterDestroyed(Emitter & target) +{ + emitterTargets->c.erase (&target); +} + +//---------------------------------------------------------------------- +/** + @brief stop receiving a specific message type from an emitter + + @param target The Emitter that we want to stop listening for messagetype from + @param messageType The messagetype we want to stop listening to on the target + + @see Emitter::connectToEmitter + + @author John Watson +*/ +void Receiver::disconnectFromEmitter(const Emitter & target, const char * const messageType) +{ + target.removeReceiver (*this, messageType); + if (!target.hasReceiver (*this)) + emitterTargets->c.erase (&target); +} + +//--------------------------------------------------------------------- + +GlobalFunctionReceiver::GlobalFunctionReceiver(Function function) +: Receiver(), + m_function(function) +{ +} + +//--------------------------------------------------------------------- + +GlobalFunctionReceiver::~GlobalFunctionReceiver() +{ +} + +//--------------------------------------------------------------------- + +void GlobalFunctionReceiver::receiveMessage(const Emitter &source, const MessageBase &message) +{ + m_function(source, message); +} + +//--------------------------------------------------------------------- + +}//namespace MessageDispatch diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Receiver.h b/engine/shared/library/sharedMessageDispatch/src/shared/Receiver.h new file mode 100644 index 00000000..955c9538 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Receiver.h @@ -0,0 +1,116 @@ +#ifndef INCLUDED_Receiver_H +#define INCLUDED_Receiver_H + +//--------------------------------------------------------------------- +#pragma warning ( disable : 4786 ) // symbol truncated to 255 characters in debug info + +namespace MessageDispatch +{ + +//--------------------------------------------------------------------- + +class MessageBase; +class Emitter; + +//--------------------------------------------------------------------- +/** + @brief Abstract base class describing an object that can receive + messages emitted by Emitter objects. + + @author Justin Randall +*/ +class Receiver +{ +public: + Receiver(); + virtual ~Receiver(); + + void connectToEmitter(const Emitter & target, const char * const messageTypeName); + void connectToEmitter(const Emitter & target, const MessageBase & source); + void connectToMessage(const char * const messageTypeName); + void connectToMessage(const MessageBase & source); + void disconnectFromEmitter(const Emitter & target, const char * const messageType); + void disconnectFromEmitter(const Emitter & target, const MessageBase & source); + void disconnectFromEmitter(const Emitter & target, const unsigned long int messageType); + void disconnectFromMessage(const char * const messageTypeName); + void disconnectFromMessage(const MessageBase & source); + void disconnectFromMessage(const unsigned long int messageType); + void disconnectAll(); + void emitterDestroyed(Emitter & target); + void setHasTargets(bool targets); + bool getHasTargets() const; + + /** Pure virtual */ + virtual void receiveMessage(const Emitter & source, const MessageBase & message) = 0; +private: + + struct EmitterTargets; + EmitterTargets * emitterTargets; + bool hasTargets; +}; + +// ---------------------------------------------------------------------- + +class GlobalFunctionReceiver : public Receiver +{ +public: + + typedef void (*Function)(const Emitter &, const MessageBase &); + +public: + + GlobalFunctionReceiver(Function function); + virtual ~GlobalFunctionReceiver(); + virtual void receiveMessage(const Emitter &source, const MessageBase &message); + +private: + + Function m_function; +}; + +// ---------------------------------------------------------------------- + +template +class MemberFunctionReceiver : public Receiver +{ +public: + + typedef void (T::*TMemberFunction)(const Emitter &, const MessageBase &); + +public: + + MemberFunctionReceiver(T &object, TMemberFunction memberFunction); + virtual ~MemberFunctionReceiver(); + virtual void receiveMessage(const Emitter &source, const MessageBase &message); + +private: + T *m_object; + TMemberFunction m_memberFunction; +}; + +// ---------------------------------------------------------------------- + +template +MemberFunctionReceiver::MemberFunctionReceiver(T &object, TMemberFunction memberFunction) +: Receiver(), + m_object(&object), + m_memberFunction(memberFunction) +{ +} + +template +MemberFunctionReceiver::~MemberFunctionReceiver() +{ +} + +template +void MemberFunctionReceiver::receiveMessage(const Emitter &source, const MessageBase &message) +{ + (m_object->*m_memberFunction)(source, message); +} + +//--------------------------------------------------------------------- + +}//namespace MessageDispatch + +#endif // _Receiver_H diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Transceiver.cpp b/engine/shared/library/sharedMessageDispatch/src/shared/Transceiver.cpp new file mode 100644 index 00000000..00aa2247 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Transceiver.cpp @@ -0,0 +1,69 @@ +// Transceiver.cpp +// Copyright 2000-01, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +//----------------------------------------------------------------------- + +#include "sharedMessageDispatch/FirstSharedMessageDispatch.h" +#include "sharedMessageDispatch/Transceiver.h" + +#include + +namespace MessageDispatch { + +//----------------------------------------------------------------------- + +TransceiverBase::GlobalReceiverInfo::GlobalReceiverInfo() : +receivers(), +pendingAdds(), +pendingRemoves(), +locked(false) +{ +} + +//----------------------------------------------------------------------- + +TransceiverBase::TransceiverBase() : +locked(false) +{ +} + +//----------------------------------------------------------------------- + +TransceiverBase::~TransceiverBase() +{ +} + +//----------------------------------------------------------------------- + +TransceiverBase::GlobalReceiverInfo & TransceiverBase::getGlobalReceiverInfo(const type_info & typeId) +{ + static std::map receiverSets; + GlobalReceiverInfo & result = receiverSets[typeId.name()]; + return result; +} + +//----------------------------------------------------------------------- + +Callback::Callback() : +receivers() +{ +} + +//----------------------------------------------------------------------- + +Callback::~Callback() +{ + std::vector::iterator i; + TransceiverBase * t; + for(i = receivers.begin(); i != receivers.end(); ++i) + { + t = (*i); + delete t; + } +} + +//----------------------------------------------------------------------- + +}//namespace MessageDispatch + diff --git a/engine/shared/library/sharedMessageDispatch/src/shared/Transceiver.h b/engine/shared/library/sharedMessageDispatch/src/shared/Transceiver.h new file mode 100644 index 00000000..3368ab66 --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/shared/Transceiver.h @@ -0,0 +1,670 @@ +// Copyright 2000-01, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +//----------------------------------------------------------------------- + +#ifndef _Transceiver_H +#define _Transceiver_H + +/** + Transceiver.h + + Message dispatch to non-static object member functions + + + <----- Messages from Transceivers + deliver to OBJECT::foo(Type) + which is a non-static member + function. + <----------------------- + +----------------------+ +-------------------+ + | OBJECT | +---+ Transceiver | + +-+--------------------+ | +-------------------+ + | |Callback m_callback | | + | +-+------------------+ | +-------------------+ + | |OwnedTransceiver |------+---+ Transceiver | + | +------------------+ | +-------------------+ + | |ptr to member func| | + | +------------------+ | +-------------------+ + + | +---+ Transceiver + + | +--------------------+ +-------------------+ + +-|void foo(Type) | + | +--------------------+ + | | + +----------------------+ + + Transceivers may be used as anonymous "broadcast" sources + delivering messages to anyone listing for a particular *type* + of message, or they may have direct relationships with other + transceivers. The relationship is defined by the receiving + object, not the transceiver. The receiver will connect() to + either another transceiver, or to a type of message. It specifies + the address of a member function that is capable of receiving + the type of message emitted from another transceiver. + + When the emitting transceiver dispatches a message, it looks + through a vector of receivers for direct message dispatch, then + delivers messages to each receiver in the list. It also maintains + a list of pending removes, and skips transceivers (really + OwnedTransceiver objects) that have been deleted to prevent + dispatch to a destroyed object. + + After direct dispatches are delievered, the transceiver finds + a "gobal" dispatch list that contains receivers that are connected + to the *type* of message this transceiver emits, rahter than this + transceiver specifically. The message is delivered to all recepients + in that global list. Like the local dispatch, the global dispatch also + checks for destroyed objects and skips delivery if they are no + longer valid. + + Clients of dispatch code use to interfaces: Callback objects, which + are responsible for connecting object member functions to transceivers, + and Transceiver objects themselves. Client code may either implement + directed dispatch from object to object using a has-a relationship. The + emitting object will have a Transceiver, the receiving object will + have a Callback. It is the responsibility of the client to ensure + that their Transceiver or Callback objects are destroyed when their + owning objects are destroyed. + + Client code may also emit messages by simply declaring a Transceiver + on the stack and invoking emitMessage() with the correct parameter type + for the message. Any objects that connect() to a message type rather + than a specific transceiver will receive the message. + + Usage: + + // anonymous dispatch example + class MyClass + { + public: + MyClass() + { + m_callback = new Callback; + // connect to any transceiver emitting a bool + m_callback->connect(*this, &MyClass::foo); + } + ~MyClass() + { + // ensure transceivers don't deliver to this + // object anymore + delete m_callback; + } + + protected: + void foo(bool); + private: + Callback * m_callback; + }; + + // code somewhere else (could be anywhere) + void bar() + { + Transceiver t; + bool msg = true; + t.emitMessage(msg); + } + + // directed dispatch example + class MyEmitter + { + public: + //... + + Transceiver & getEmitter() + { + return m_emitter; + } + + void bar() + { + bool msg = true; + m_emitter.emitMessage(msg); + } + + private: + Transceiver m_emitter; + }; + + class MyReceiver + { + public: + explicit MyReceiver(MyEmitter & source) + { + m_callback = new Callback; + m_callback->connect(source, *this, &MyReceiver::foo); + } + ~MyReceiver() + { + delete m_callback; + } + protected: + void foo(bool) + { + // I received a bool message from one of + // the transceivers I am connected too! + } + }; + + // anywhere else in the application + void doStuff() + { + MyEmitter emitter; + MyReceiver receiver(emitter); + + // receiver.foo(true) is called by way of emitter.bar() + emitter.bar(); + } +*/ +//----------------------------------------------------------------------- + +#include +#include +#include + +#if _MSC_VER < 1300 +using std::type_info; +#endif + + +//--------------------------------------------------------------------- + +namespace MessageDispatch { + +class TransceiverBase +{ +public: + TransceiverBase(); + virtual ~TransceiverBase(); + + +protected: + struct GlobalReceiverInfo + { + GlobalReceiverInfo(); + std::vector receivers; + std::vector pendingAdds; + std::vector pendingRemoves; + + bool locked; + }; + +protected: + static GlobalReceiverInfo & getGlobalReceiverInfo(const type_info & typeId); + +protected: + mutable bool locked; +}; + +template +class Transceiver : public TransceiverBase +{ +public: + Transceiver(); + virtual ~Transceiver(); + void emitMessage(MessageType source) const; +protected: + friend class Callback; + void listenForAny(); + //void listenTo(Transceiver & source); + virtual void receiveMessage(MessageType) {}; + +private: + Transceiver(const Transceiver & source); + Transceiver & operator = (const Transceiver & rhs); + + void addReceiver(Transceiver * target); + void removeReceiver(Transceiver * target); + +private: + std::vector *> localReceivers; + std::set *> listenSet; + + // bookkeeping containers and flags to permit safe usage during + // message emission + mutable std::vector *> pendingAdds; + mutable std::vector *> pendingRemoves; +}; + +//--------------------------------------------------------------------- + +template +inline Transceiver::Transceiver() : +TransceiverBase(), +localReceivers(), +listenSet(), +pendingAdds(), +pendingRemoves() +{ + // ensure the global receiver for this type of transceiver + // is fully constructed before this object (which may be the + // first transceiver) is fully constructed. The global + // receiver info is a static with linkage in Transceiver.cpp, + // and should be destroyed only after ALL transceivers of this + // type have been destroyed. + getGlobalReceiverInfo(typeid(this)); +} + +//--------------------------------------------------------------------- + +template +inline Transceiver::~Transceiver() +{ + typename std::vector *>::iterator i; + typename std::set *>::iterator f; + + // advise anyone that this transceiver is connected to that + // they should NOT call removeReceiver in their destructor + // because THIS transceiver is being destroyed + for(i = localReceivers.begin(); i != localReceivers.end(); ++i) + { + // find this receiver on the other transceiver's + // listen set + f = (*i)->listenSet.find(this); + + // if it's found, remove this receiver from the listenSet + if(f != (*i)->listenSet.end()) + (*i)->listenSet.erase(f); + } + + // for every transceiver that this object receives messages + // from, remove thiis object from their distribution + // list, so that they will not attempt to deliver + // messages to this object after it has been destroyed + for(f = listenSet.begin(); f != listenSet.end(); ++f) + { + (*f)->removeReceiver(this); + } + + + // remove this transceiver from the global receiver list + // to prevent anonymous distribution to this delieted + // transceiver + GlobalReceiverInfo & info = getGlobalReceiverInfo(typeid(this)); + std::vector::iterator g; + if(info.locked == false) + { + g = std::find(info.receivers.begin(), info.receivers.end(), this); + if(g != info.receivers.end()) + info.receivers.erase(g); + } + else + { + // the global transceiver list is busy dispatching + // a message that this transceiver may receive, put + // it on the global pending remove list, which is + // checked during message dispatch + info.pendingRemoves.push_back(this); + } +} + +//----------------------------------------------------------------------- + +template +inline void Transceiver::addReceiver(Transceiver * target) +{ + // do not add a transceiver to the receiver list if this transceiver + // is busy dispatching a message. + if(locked) + { + pendingAdds.push_back(target); + } + else + { + typename std::vector *>::const_iterator f = std::find(localReceivers.begin(), localReceivers.end(), target); + if(f == localReceivers.end()) + { + localReceivers.push_back(target); + target->listenSet.insert(this); + } + } +} + +//--------------------------------------------------------------------- + +template +inline void Transceiver::emitMessage(MessageType message) const +{ + + //----------------------------------------------------------------------- + // set our status to locked so that adds to this receiver do not touch + // the localReceiverSet and put it in an undefined state. + // See Transceiver::addReceiver for information on the locked flag + locked = true; + + // iterate through direct connections to this Transceiver + typename std::vector *>::const_iterator i; + + // which dispatch loop will we run? + // fast dispatch + for(i = localReceivers.begin(); i != localReceivers.end(); ++i) + { + // quick and easy check. + // if no removes have been posted as a result of the + // last dispatch, simply deliver the message + if(pendingRemoves.empty()) + { + (*i)->receiveMessage(message); + } + else + { + // it might not be safe, because a transceiver requested a removal. This + // can happen when the owner object of the transceiver has + // deleted itself. Just skip this target and clean up the + // removal at the end of emitMessage. + if(std::find(pendingRemoves.begin(), pendingRemoves.end(), (*i)) != pendingRemoves.end()) + continue; + + (*i)->receiveMessage(message); + } + } + locked = false; + //----------------------------------------------------------------------- + + + //----------------------------------------------------------------------- + // lock the global receiver set to prevent + // unsafe add/remove calls to it while dispatching this message. + GlobalReceiverInfo & info = getGlobalReceiverInfo(typeid(const_cast *>(this))); + info.locked = true; + std::vector::iterator g; + for(g = info.receivers.begin(); g != info.receivers.end(); ++g) + { + // quick and easy check. + // if no removes have been posted as a result of the + // last dispatch, simply deliver the message + if(pendingRemoves.empty()) + { + Transceiver * t = static_cast *>((*g)); + t->receiveMessage(message); + } + else + { + // it might not be safe, because a transceiver requested a removal. This + // can happen when the owner object of the transceiver has + // deleted itself. Just skip this target and clean up the + // removal at the end of emitMessage. + if(std::find(info.pendingRemoves.begin(), info.pendingRemoves.end(), (*g)) != info.pendingRemoves.end()) + continue; + + Transceiver * t = static_cast *>((*g)); + t->receiveMessage(message); + } + } + info.locked = false; + //----------------------------------------------------------------------- + + // process local connections to this transceiver + if(! pendingAdds.empty()) + { + for(i = pendingAdds.begin(); i != pendingAdds.end(); ++i) + { + // place the pending transceive ron the receiver list + const_cast *>(this)->addReceiver(*i); + } + pendingAdds.clear(); + } + + // a transceiver directly connected to this object was destroyed or + // disconnected during the message dispatch. Remove it from the + // list of receivers to ensure messages aren't delivered to the target + // and that it will not receive a notification during the destruction + // of THIS transceiver + if(! pendingRemoves.empty()) + { + typename std::vector *>::const_iterator a;; + for(a = pendingRemoves.begin(); a != pendingRemoves.end(); ++a) + { + const_cast *>(this)->removeReceiver(*a); + } + pendingRemoves.clear(); + } + + // perform the same add/remove logic for the global receiver info + if(! info.pendingAdds.empty()) + { + std::vector::const_iterator addIter; + for(addIter = info.pendingAdds.begin(); addIter != info.pendingAdds.end(); ++addIter) + { + Transceiver * t = static_cast *>((*addIter)); + t->listenForAny(); + } + info.pendingAdds.clear(); + } + + if(! info.pendingRemoves.empty()) + { + std::vector::const_iterator removeIter; + for(removeIter = info.pendingRemoves.begin(); removeIter != info.pendingRemoves.end(); ++removeIter) + { + std::vector::iterator r = std::find(info.receivers.begin(), info.receivers.end(), *removeIter); + if(r != info.receivers.end()) + info.receivers.erase(r); + } + info.pendingRemoves.clear(); + } +} + +//--------------------------------------------------------------------- + +template +inline void Transceiver::listenForAny() +{ + GlobalReceiverInfo & info = getGlobalReceiverInfo(typeid(this)); + + // if the global receiver info is not locked, then it is not + // busy dispatching an anonymous message and it is safe to + // add this transceiver directly to the list of listeners + if(! info.locked) + { + std::vector::const_iterator f = std::find(info.receivers.begin(), info.receivers.end(), this); + if(f == info.receivers.end()) + info.receivers.push_back(this); + } + else + { + // another transceiver of this type is performing + // operations on the global receiver set. Adds will + // be processed by that transceiver when it is done + // working with the global receiver list + info.pendingAdds.push_back(this); + } +} + +//----------------------------------------------------------------------- + +template +inline void Transceiver::removeReceiver(Transceiver * target) +{ + if(locked) + { + // this transceiver is busy dispatching messages. Do not invalidate + // the receiver list by erasing a member while this object is + // iterating through it to dispatch. The remove list, if it is + // not empty, will be checked to see if the target transceiver + // is listed, and messages will not be dispatched to it. + pendingRemoves.push_back(target); + } + else + { + // it is safe to directly remove the target transceiver from the + // receiver list. + typename std::vector *>::iterator f = std::find(localReceivers.begin(), localReceivers.end(), target); + if(f != localReceivers.end()) + localReceivers.erase(f); + } +} + +//--------------------------------------------------------------------- + +/** + An OwnedTransceiver is a helper/interface class that links end-point + objects, Callback objects and transceivers. The abstraction is + present to make message dispatch safe when emitting messages to objects + that may be deleted. Callbacks and OwnedTransceivers notify other + transceivers if an object is being destroyed. Objects that receive + messages have Callback members, and when those callback members + are destroyed, they clean up transceiver receive lists to prevent + operations on the deleted object. +*/ +template +class OwnedTransceiver : public Transceiver +{ +public: + OwnedTransceiver(Transceiver * source, ObjectType & o); + ~OwnedTransceiver(); + + // this where all messages are routed before finally being + // delivered to the object member function specified in the + // callback. receiveMessage overrides the base Transceiver method. + virtual void receiveMessage(MessageType msg) + { + (owner.*callback)(msg); + }; + + void setCallback(void (ObjectType::*cb)(MessageType) ) + { + callback = cb; + }; + +private: + friend class Callback; + OwnedTransceiver & operator = (const OwnedTransceiver &); + OwnedTransceiver(const OwnedTransceiver &); + Transceiver * source; + ObjectType & owner; + void (ObjectType::*callback)(MessageType); +}; + +//--------------------------------------------------------------------- + +template +inline OwnedTransceiver::OwnedTransceiver(Transceiver * s, ObjectType & o) : +source(s), +owner(o), +callback(0) +{ +} + +//----------------------------------------------------------------------- + +template +inline OwnedTransceiver::~OwnedTransceiver() +{ +} + +//----------------------------------------------------------------------- + +class Callback +{ +public: + Callback(); + virtual ~Callback(); + + template + void connect( + Transceiver & source, + ObjectType & object, + void (ObjectType::*callback)(MessageType) + ) + { + OwnedTransceiver * target = new OwnedTransceiver(&source, object); + receivers.push_back(target); + + target->setCallback(callback); + source.addReceiver(target); + //target->listenTo(source); + } + + template + inline void connect(ObjectType & object, void (ObjectType::*callback)(MessageType)) + { + OwnedTransceiver * target = new OwnedTransceiver(0, object); + receivers.push_back(target); + + target->setCallback(callback); + target->listenForAny(); + } + + template + inline void connect(ObjectType & object, void (ObjectType::*callback)(MessageType), IdentifierType *) + { + OwnedTransceiver * target = new OwnedTransceiver(0, object); + receivers.push_back(target); + + target->setCallback(callback); + target->listenForAny(); + } + + template + void disconnect( + Transceiver & source, + ObjectType & object, + void (ObjectType::*callback)(MessageType) + ) + { + removeTarget(&source, object, callback); + } + + template + void disconnectByType( + Transceiver & source, + ObjectType & object, + void (ObjectType::*callback)(MessageType), + IdentifierType * + ) + { + removeTarget(&source, object, callback); + } + + template + void disconnect(ObjectType & object, void (ObjectType::*callback)(MessageType) ) + { + removeTarget(static_cast *>(0), object, callback); + } + + template + void disconnect(ObjectType & object, void (ObjectType::*callback)(MessageType), IdentifierType *) + { + removeTarget(static_cast *>(0), object, callback); + } + +private: + template + void removeTarget( + Transceiver * source, + ObjectType & object, + void (ObjectType::*callback)(MessageType) + ) + { + std::vector::iterator i; + OwnedTransceiver * target; + + for(i = receivers.begin(); i != receivers.end(); ++i) + { + target = dynamic_cast *>((*i)); + if(target) + { + if(&target->owner == &object && target->callback == callback && target->source == source) + { + delete target; + receivers.erase(i); + break; + } + } + } + } + + std::vector receivers; +}; + +template +void emitMessage(MessageType m) +{ + static Transceiver t; + t.emitMessage(m); +} + +//----------------------------------------------------------------------- +}//namespace MessageDispatch + +#endif // _Transceiver_H diff --git a/engine/shared/library/sharedMessageDispatch/src/win32/FirstSharedMessageDispatch.cpp b/engine/shared/library/sharedMessageDispatch/src/win32/FirstSharedMessageDispatch.cpp new file mode 100644 index 00000000..c2403b0f --- /dev/null +++ b/engine/shared/library/sharedMessageDispatch/src/win32/FirstSharedMessageDispatch.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstMessageDispatch.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"