mirror of
https://github.com/SWG-Source/src.git
synced 2026-07-28 22:15:49 -04:00
Added sharedLog and sharedMessageDispatch libraries
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/ConfigSharedLog.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FileLogObserver.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FirstSharedLog.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Log.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/LogManager.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/LogObserver.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/NetLogConnection.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/NetLogObserver.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/SetupSharedLog.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/StderrLogger.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/TailFileLogObserver.h"
|
||||
@@ -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}
|
||||
)
|
||||
@@ -0,0 +1,74 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// StderrLogger.cpp
|
||||
//
|
||||
// Copyright 2003 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedLog/FirstSharedLog.h"
|
||||
#include "sharedLog/StderrLogger.h"
|
||||
#include "sharedLog/Log.h"
|
||||
#include <string>
|
||||
#include <fcntl.h>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// ConfigSharedLog.cpp
|
||||
//
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedLog/FirstSharedLog.h"
|
||||
#include "sharedLog/ConfigSharedLog.h"
|
||||
|
||||
#include "sharedFoundation/ConfigFile.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
namespace ConfigSharedLogNamespace
|
||||
{
|
||||
typedef std::vector<char const *> 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<int>(ms_logTargets.size());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
char const * ConfigSharedLog::getLogTarget(int const index)
|
||||
{
|
||||
VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfLogTargets());
|
||||
return ms_logTargets[static_cast<size_t>(index)];
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <string>
|
||||
#include <cstdio>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<int>(procId.length()), procId.c_str()) );
|
||||
IGNORE_RETURN( m_file->write(1, ":") );
|
||||
IGNORE_RETURN( m_file->write(static_cast<int>(channel.length()), channel.c_str()) );
|
||||
IGNORE_RETURN( m_file->write(1, ":") );
|
||||
IGNORE_RETURN( m_file->write(static_cast<int>(text.length()), text.c_str()) );
|
||||
if (uniAttach.length())
|
||||
{
|
||||
IGNORE_RETURN( m_file->write(1, ":") );
|
||||
IGNORE_RETURN( m_file->write(static_cast<int>(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();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileLogObserver.h
|
||||
//
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_FileLogObserver_H
|
||||
#define INCLUDED_FileLogObserver_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedLog/LogObserver.h"
|
||||
#include <string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstSharedLog.cpp
|
||||
//
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedLog/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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <time.h>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
typedef std::list<LogObserver *> ObserverList;
|
||||
typedef std::map<std::string, LogObserverCreateFunc> 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<unsigned int>(t.tm_mday);
|
||||
timestamp *= 100;
|
||||
timestamp += static_cast<unsigned int>(t.tm_hour);
|
||||
timestamp *= 100;
|
||||
timestamp += static_cast<unsigned int>(t.tm_min);
|
||||
timestamp *= 100;
|
||||
timestamp += static_cast<unsigned int>(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;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
LogObserver::LogObserver() :
|
||||
m_filters(new std::vector<std::string>())
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
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<std::string>::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()
|
||||
{
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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<std::string>::fwd* m_filters;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<unsigned short>(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<LogMessage>),
|
||||
m_logMessageQueue2(new std::vector<LogMessage>)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
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<unsigned int>(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<unsigned long>(ConfigSharedLog::getLogNetReconnectTimeMs());
|
||||
|
||||
if (m_connection && m_connectionOpen)
|
||||
{
|
||||
m_mutex.enter();
|
||||
std::vector<LogMessage> *q = m_logMessageQueue1;
|
||||
m_logMessageQueue1 = m_logMessageQueue2;
|
||||
m_logMessageQueue2 = q;
|
||||
m_mutex.leave();
|
||||
// send any queued up log messages
|
||||
|
||||
for (std::vector<LogMessage>::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);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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 <string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<LogMessage>::fwd* m_logMessageQueue1;
|
||||
stdvector<LogMessage>::fwd* m_logMessageQueue2;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
inline std::string const &NetLogObserver::getRemoteAddress() const
|
||||
{
|
||||
return m_remoteAddress;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
inline unsigned short NetLogObserver::getRemotePort() const
|
||||
{
|
||||
return m_remotePort;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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 <string>
|
||||
#include <cstdio>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<TailFileLogObserver *> 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<std::string>(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<TailFileLogObserver *>::iterator i = s_tailFileLogObservers.begin(); i != s_tailFileLogObservers.end(); ++i)
|
||||
{
|
||||
TailFileLogObserver *tflo = *i;
|
||||
if(tflo)
|
||||
{
|
||||
tflo->flush(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// TailFileLogObserver.h
|
||||
//
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_TailFileLogObserver_H
|
||||
#define INCLUDED_TailFileLogObserver_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedLog/LogObserver.h"
|
||||
#include <string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<std::string>::fwd *m_textBuffer;
|
||||
int m_nextTextBufferEntry;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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)
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Emitter.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FirstSharedMessageDispatch.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Message.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/MessageManager.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Receiver.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Transceiver.h"
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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 <cassert>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace MessageDispatch {
|
||||
struct Emitter::ReceiverList
|
||||
{
|
||||
typedef std::set<Receiver *> ReceiverSet;
|
||||
typedef std::map<unsigned long int, ReceiverSet> 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<Receiver *>(&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<Receiver *>(&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<Receiver *>(&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<Receiver *>(&target)); //lint !e534 // ignoring iterator returned from insert
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
}//namespace MessageDispatch
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <set>
|
||||
#include <hash_map>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -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 <string>
|
||||
|
||||
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
|
||||
@@ -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 ValueType>
|
||||
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<class ValueType>
|
||||
inline Message<ValueType>::Message(const char * const typeName) :
|
||||
MessageBase(typeName),
|
||||
value()
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline Message<ValueType>::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<class ValueType>
|
||||
inline Message<ValueType>::Message(const char * const typeName, const ValueType & newValue) :
|
||||
MessageBase(typeName),
|
||||
value(newValue)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline Message<ValueType>::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<class ValueType>
|
||||
inline Message<ValueType>::Message(const Message<ValueType> & source) :
|
||||
MessageBase(source.getType()),
|
||||
value(source.getValue())
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief destroy the Message object
|
||||
|
||||
Doesn't do anything special.
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
template<class ValueType>
|
||||
inline Message<ValueType>::~Message()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief Assign the souce ValueType to the value member
|
||||
|
||||
@param source the new value for the Message::value member
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
template<class ValueType>
|
||||
inline void Message<ValueType>::setValue(const ValueType & source)
|
||||
{
|
||||
value = source;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief return the Message::value member
|
||||
|
||||
@return the value of the value member
|
||||
*/
|
||||
template<class ValueType>
|
||||
inline const ValueType & Message<ValueType>::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}//namespace MessageDispatch
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif // _Message_H
|
||||
@@ -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 <set>
|
||||
#include <hash_map>
|
||||
|
||||
namespace MessageDispatch {
|
||||
|
||||
MessageManager MessageManager::ms_instance;
|
||||
|
||||
struct MessageManager::Data
|
||||
{
|
||||
std::hash_map<unsigned long int, std::set<Receiver *> > receivers;
|
||||
std::hash_map<unsigned long int, std::set<void (*)(const Emitter &, const MessageBase &)> > 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<unsigned long int, std::set<Receiver *> >::iterator i = data->receivers.find(messageType);
|
||||
if(i != data->receivers.end())
|
||||
{
|
||||
target.setHasTargets(true);
|
||||
std::set<Receiver *> & targets = (*i).second;
|
||||
targets.insert(&target);//lint !e534 // ignoring iterator returned from insert
|
||||
}
|
||||
else
|
||||
{
|
||||
target.setHasTargets(true);
|
||||
std::set<Receiver *> 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<unsigned long int, std::set<void (*)(const Emitter &, const MessageBase &)> >::iterator f = data->staticCallbacks.find(messageType);
|
||||
if(f != data->staticCallbacks.end())
|
||||
{
|
||||
std::set<void (*)(const Emitter &, const MessageBase &)> & targets = f->second;
|
||||
targets.insert(callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::set<void (*)(const Emitter &, const MessageBase &)> 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<unsigned long int, std::set<Receiver *> >::const_iterator i = data->receivers.find(messageType);
|
||||
if(i != data->receivers.end())
|
||||
{
|
||||
const std::set<Receiver *> targets = (*i).second;
|
||||
std::set<Receiver *>::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<unsigned long int, std::set<void (*)(const Emitter &, const MessageBase &)> >::iterator f = data->staticCallbacks.find(messageType);
|
||||
if(f != data->staticCallbacks.end())
|
||||
{
|
||||
const std::set<void (*)(const Emitter &, const MessageBase &)> targets = f->second;
|
||||
std::set<void (*)(const Emitter &, const MessageBase &)>::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<unsigned long int, std::set<Receiver *> >::iterator i;
|
||||
for(i = data->receivers.begin(); i != data->receivers.end(); ++i)
|
||||
{
|
||||
std::set<Receiver *> & targets = (*i).second;
|
||||
// const cast to satisfy STL semantics, target remains unchanged
|
||||
std::set<Receiver *>::iterator j = targets.find(const_cast<Receiver *>(&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<unsigned long int, std::set<Receiver *> >::iterator i = data->receivers.find(messageType);
|
||||
if(i != data->receivers.end())
|
||||
{
|
||||
std::set<Receiver *> & targets = (*i).second;
|
||||
std::set<Receiver *>::iterator j = targets.find(const_cast<Receiver *>(&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
|
||||
@@ -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
|
||||
@@ -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 <cassert>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace MessageDispatch {
|
||||
|
||||
struct Receiver::EmitterTargets
|
||||
{
|
||||
typedef std::set<const Emitter *> 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
|
||||
@@ -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 <typename T>
|
||||
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 <typename T>
|
||||
MemberFunctionReceiver<T>::MemberFunctionReceiver(T &object, TMemberFunction memberFunction)
|
||||
: Receiver(),
|
||||
m_object(&object),
|
||||
m_memberFunction(memberFunction)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MemberFunctionReceiver<T>::~MemberFunctionReceiver()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void MemberFunctionReceiver<T>::receiveMessage(const Emitter &source, const MessageBase &message)
|
||||
{
|
||||
(m_object->*m_memberFunction)(source, message);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
}//namespace MessageDispatch
|
||||
|
||||
#endif // _Receiver_H
|
||||
@@ -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 <map>
|
||||
|
||||
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<const char * const, GlobalReceiverInfo> receiverSets;
|
||||
GlobalReceiverInfo & result = receiverSets[typeId.name()];
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
Callback::Callback() :
|
||||
receivers()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
Callback::~Callback()
|
||||
{
|
||||
std::vector<TransceiverBase *>::iterator i;
|
||||
TransceiverBase * t;
|
||||
for(i = receivers.begin(); i != receivers.end(); ++i)
|
||||
{
|
||||
t = (*i);
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
}//namespace MessageDispatch
|
||||
|
||||
@@ -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<Type> |
|
||||
+-+--------------------+ | +-------------------+
|
||||
| |Callback m_callback | |
|
||||
| +-+------------------+ | +-------------------+
|
||||
| |OwnedTransceiver |------+---+ Transceiver<Type> |
|
||||
| +------------------+ | +-------------------+
|
||||
| |ptr to member func| |
|
||||
| +------------------+ | +-------------------+
|
||||
+ | +---+ Transceiver<Type> +
|
||||
| +--------------------+ +-------------------+
|
||||
+-|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<bool> t;
|
||||
bool msg = true;
|
||||
t.emitMessage(msg);
|
||||
}
|
||||
|
||||
// directed dispatch example
|
||||
class MyEmitter
|
||||
{
|
||||
public:
|
||||
//...
|
||||
|
||||
Transceiver<bool> & getEmitter()
|
||||
{
|
||||
return m_emitter;
|
||||
}
|
||||
|
||||
void bar()
|
||||
{
|
||||
bool msg = true;
|
||||
m_emitter.emitMessage(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
Transceiver<bool> 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 <set>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
#if _MSC_VER < 1300
|
||||
using std::type_info;
|
||||
#endif
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace MessageDispatch {
|
||||
|
||||
class TransceiverBase
|
||||
{
|
||||
public:
|
||||
TransceiverBase();
|
||||
virtual ~TransceiverBase();
|
||||
|
||||
|
||||
protected:
|
||||
struct GlobalReceiverInfo
|
||||
{
|
||||
GlobalReceiverInfo();
|
||||
std::vector<TransceiverBase *> receivers;
|
||||
std::vector<TransceiverBase *> pendingAdds;
|
||||
std::vector<TransceiverBase *> pendingRemoves;
|
||||
|
||||
bool locked;
|
||||
};
|
||||
|
||||
protected:
|
||||
static GlobalReceiverInfo & getGlobalReceiverInfo(const type_info & typeId);
|
||||
|
||||
protected:
|
||||
mutable bool locked;
|
||||
};
|
||||
|
||||
template<typename MessageType, typename IdentifierType = void *>
|
||||
class Transceiver : public TransceiverBase
|
||||
{
|
||||
public:
|
||||
Transceiver();
|
||||
virtual ~Transceiver();
|
||||
void emitMessage(MessageType source) const;
|
||||
protected:
|
||||
friend class Callback;
|
||||
void listenForAny();
|
||||
//void listenTo(Transceiver<MessageType, IdentifierType> & 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<Transceiver<MessageType, IdentifierType> *> localReceivers;
|
||||
std::set<Transceiver<MessageType, IdentifierType> *> listenSet;
|
||||
|
||||
// bookkeeping containers and flags to permit safe usage during
|
||||
// message emission
|
||||
mutable std::vector<Transceiver<MessageType, IdentifierType> *> pendingAdds;
|
||||
mutable std::vector<Transceiver<MessageType, IdentifierType> *> pendingRemoves;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template<typename MessageType, typename IdentifierType>
|
||||
inline Transceiver<MessageType, IdentifierType>::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<typename MessageType, typename IdentifierType>
|
||||
inline Transceiver<MessageType, IdentifierType>::~Transceiver()
|
||||
{
|
||||
typename std::vector<Transceiver<MessageType, IdentifierType> *>::iterator i;
|
||||
typename std::set<Transceiver<MessageType, IdentifierType> *>::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<TransceiverBase *>::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<typename MessageType, typename IdentifierType>
|
||||
inline void Transceiver<MessageType, IdentifierType>::addReceiver(Transceiver<MessageType, IdentifierType> * 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<Transceiver<MessageType, IdentifierType> *>::const_iterator f = std::find(localReceivers.begin(), localReceivers.end(), target);
|
||||
if(f == localReceivers.end())
|
||||
{
|
||||
localReceivers.push_back(target);
|
||||
target->listenSet.insert(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template<typename MessageType, typename IdentifierType>
|
||||
inline void Transceiver<MessageType, IdentifierType>::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<Transceiver<MessageType, IdentifierType> *>::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<Transceiver<MessageType, IdentifierType> *>(this)));
|
||||
info.locked = true;
|
||||
std::vector<TransceiverBase *>::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<MessageType, IdentifierType> * t = static_cast<Transceiver<MessageType, IdentifierType> *>((*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<MessageType, IdentifierType> * t = static_cast<Transceiver<MessageType, IdentifierType> *>((*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<Transceiver<MessageType, IdentifierType> *>(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<Transceiver<MessageType, IdentifierType> *>::const_iterator a;;
|
||||
for(a = pendingRemoves.begin(); a != pendingRemoves.end(); ++a)
|
||||
{
|
||||
const_cast<Transceiver<MessageType, IdentifierType> *>(this)->removeReceiver(*a);
|
||||
}
|
||||
pendingRemoves.clear();
|
||||
}
|
||||
|
||||
// perform the same add/remove logic for the global receiver info
|
||||
if(! info.pendingAdds.empty())
|
||||
{
|
||||
std::vector<TransceiverBase *>::const_iterator addIter;
|
||||
for(addIter = info.pendingAdds.begin(); addIter != info.pendingAdds.end(); ++addIter)
|
||||
{
|
||||
Transceiver<MessageType, IdentifierType> * t = static_cast<Transceiver<MessageType, IdentifierType> *>((*addIter));
|
||||
t->listenForAny();
|
||||
}
|
||||
info.pendingAdds.clear();
|
||||
}
|
||||
|
||||
if(! info.pendingRemoves.empty())
|
||||
{
|
||||
std::vector<TransceiverBase *>::const_iterator removeIter;
|
||||
for(removeIter = info.pendingRemoves.begin(); removeIter != info.pendingRemoves.end(); ++removeIter)
|
||||
{
|
||||
std::vector<TransceiverBase *>::iterator r = std::find(info.receivers.begin(), info.receivers.end(), *removeIter);
|
||||
if(r != info.receivers.end())
|
||||
info.receivers.erase(r);
|
||||
}
|
||||
info.pendingRemoves.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template<typename MessageType, typename IdentifierType>
|
||||
inline void Transceiver<MessageType, IdentifierType>::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<TransceiverBase *>::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<typename MessageType, typename IdentifierType>
|
||||
inline void Transceiver<MessageType, IdentifierType>::removeReceiver(Transceiver<MessageType, IdentifierType> * 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<Transceiver<MessageType, IdentifierType> *>::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<typename MessageType, typename ObjectType, typename IdentifierType = void *>
|
||||
class OwnedTransceiver : public Transceiver<MessageType, IdentifierType>
|
||||
{
|
||||
public:
|
||||
OwnedTransceiver(Transceiver<MessageType, IdentifierType> * 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<MessageType, ObjectType, IdentifierType> & operator = (const OwnedTransceiver<MessageType, ObjectType, IdentifierType> &);
|
||||
OwnedTransceiver(const OwnedTransceiver<MessageType, ObjectType, IdentifierType> &);
|
||||
Transceiver<MessageType, IdentifierType> * source;
|
||||
ObjectType & owner;
|
||||
void (ObjectType::*callback)(MessageType);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
inline OwnedTransceiver<MessageType, ObjectType, IdentifierType>::OwnedTransceiver(Transceiver<MessageType, IdentifierType> * s, ObjectType & o) :
|
||||
source(s),
|
||||
owner(o),
|
||||
callback(0)
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
inline OwnedTransceiver<MessageType, ObjectType, IdentifierType>::~OwnedTransceiver()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
Callback();
|
||||
virtual ~Callback();
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
void connect(
|
||||
Transceiver<MessageType, IdentifierType> & source,
|
||||
ObjectType & object,
|
||||
void (ObjectType::*callback)(MessageType)
|
||||
)
|
||||
{
|
||||
OwnedTransceiver<MessageType, ObjectType, IdentifierType> * target = new OwnedTransceiver<MessageType, ObjectType, IdentifierType>(&source, object);
|
||||
receivers.push_back(target);
|
||||
|
||||
target->setCallback(callback);
|
||||
source.addReceiver(target);
|
||||
//target->listenTo(source);
|
||||
}
|
||||
|
||||
template<typename MessageType, typename ObjectType>
|
||||
inline void connect(ObjectType & object, void (ObjectType::*callback)(MessageType))
|
||||
{
|
||||
OwnedTransceiver<MessageType, ObjectType> * target = new OwnedTransceiver<MessageType, ObjectType>(0, object);
|
||||
receivers.push_back(target);
|
||||
|
||||
target->setCallback(callback);
|
||||
target->listenForAny();
|
||||
}
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
inline void connect(ObjectType & object, void (ObjectType::*callback)(MessageType), IdentifierType *)
|
||||
{
|
||||
OwnedTransceiver<MessageType, ObjectType, IdentifierType> * target = new OwnedTransceiver<MessageType, ObjectType, IdentifierType>(0, object);
|
||||
receivers.push_back(target);
|
||||
|
||||
target->setCallback(callback);
|
||||
target->listenForAny();
|
||||
}
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
void disconnect(
|
||||
Transceiver<MessageType, IdentifierType> & source,
|
||||
ObjectType & object,
|
||||
void (ObjectType::*callback)(MessageType)
|
||||
)
|
||||
{
|
||||
removeTarget(&source, object, callback);
|
||||
}
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
void disconnectByType(
|
||||
Transceiver<MessageType, IdentifierType> & source,
|
||||
ObjectType & object,
|
||||
void (ObjectType::*callback)(MessageType),
|
||||
IdentifierType *
|
||||
)
|
||||
{
|
||||
removeTarget(&source, object, callback);
|
||||
}
|
||||
|
||||
template<typename MessageType, typename ObjectType>
|
||||
void disconnect(ObjectType & object, void (ObjectType::*callback)(MessageType) )
|
||||
{
|
||||
removeTarget(static_cast<Transceiver<MessageType> *>(0), object, callback);
|
||||
}
|
||||
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
void disconnect(ObjectType & object, void (ObjectType::*callback)(MessageType), IdentifierType *)
|
||||
{
|
||||
removeTarget(static_cast<Transceiver<MessageType, IdentifierType> *>(0), object, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename MessageType, typename ObjectType, typename IdentifierType>
|
||||
void removeTarget(
|
||||
Transceiver<MessageType, IdentifierType> * source,
|
||||
ObjectType & object,
|
||||
void (ObjectType::*callback)(MessageType)
|
||||
)
|
||||
{
|
||||
std::vector<TransceiverBase *>::iterator i;
|
||||
OwnedTransceiver<MessageType, ObjectType, IdentifierType> * target;
|
||||
|
||||
for(i = receivers.begin(); i != receivers.end(); ++i)
|
||||
{
|
||||
target = dynamic_cast<OwnedTransceiver<MessageType, ObjectType, IdentifierType> *>((*i));
|
||||
if(target)
|
||||
{
|
||||
if(&target->owner == &object && target->callback == callback && target->source == source)
|
||||
{
|
||||
delete target;
|
||||
receivers.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TransceiverBase *> receivers;
|
||||
};
|
||||
|
||||
template<typename MessageType>
|
||||
void emitMessage(MessageType m)
|
||||
{
|
||||
static Transceiver<MessageType> t;
|
||||
t.emitMessage(m);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
}//namespace MessageDispatch
|
||||
|
||||
#endif // _Transceiver_H
|
||||
@@ -0,0 +1,8 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstMessageDispatch.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"
|
||||
Reference in New Issue
Block a user