diff --git a/base/CLog.cpp b/base/CLog.cpp index de22098e..7ee437fa 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -9,7 +9,6 @@ #define vsnprintf _vsnprintf #endif -static int g_maxPriority = -1; static const char* g_priority[] = { "FATAL", "ERROR", @@ -33,6 +32,8 @@ static const int g_newlineLength = 2; // CLog::Outputter CLog::s_outputter = NULL; +CLog::Lock CLog::s_lock = &CLog::dummyLock; +int CLog::s_maxPriority = -1; void CLog::print(const char* fmt, ...) { @@ -100,31 +101,75 @@ void CLog::printt(const char* file, int line, void CLog::setOutputter(Outputter outputter) { + CHoldLock lock(s_lock); s_outputter = outputter; } +CLog::Outputter CLog::getOutputter() +{ + CHoldLock lock(s_lock); + return s_outputter; +} + +void CLog::setLock(Lock newLock) +{ + CHoldLock lock(s_lock); + s_lock = (newLock == NULL) ? dummyLock : newLock; +} + +CLog::Lock CLog::getLock() +{ + CHoldLock lock(s_lock); + return (s_lock == dummyLock) ? NULL : s_lock; +} + +void CLog::setFilter(int maxPriority) +{ + CHoldLock lock(s_lock); + s_maxPriority = maxPriority; +} + +int CLog::getFilter() +{ + CHoldLock lock(s_lock); + return getMaxPriority(); +} + +void CLog::dummyLock(bool) +{ + // do nothing +} + +int CLog::getMaxPriority() +{ + CHoldLock lock(s_lock); + + if (s_maxPriority == -1) { +#if defined(NDEBUG) + s_maxPriority = 4; +#else + s_maxPriority = 5; +#endif + const char* priEnv = getenv("SYN_LOG_PRI"); + if (priEnv != NULL) { + for (int i = 0; i < g_numPriority; ++i) { + if (strcmp(priEnv, g_priority[i]) == 0) { + s_maxPriority = i; + break; + } + } + } + } + + return s_maxPriority; +} + void CLog::output(int priority, char* msg) { assert(priority >= 0 && priority < g_numPriority); assert(msg != 0); - if (g_maxPriority == -1) { -#if defined(NDEBUG) - g_maxPriority = 4; -#else - g_maxPriority = 5; -#endif - const char* priEnv = getenv("SYN_LOG_PRI"); - if (priEnv != NULL) { - for (int i = 0; i < g_numPriority; ++i) - if (strcmp(priEnv, g_priority[i]) == 0) { - g_maxPriority = i; - break; - } - } - } - - if (priority <= g_maxPriority) { + if (priority <= getMaxPriority()) { // insert priority label int n = strlen(g_priority[priority]); sprintf(msg + g_maxPriorityLength - n, "%s:", g_priority[priority]); @@ -138,6 +183,7 @@ void CLog::output(int priority, char* msg) #endif // print it + CHoldLock lock(s_lock); if (s_outputter) { s_outputter(msg + g_maxPriorityLength - n); } diff --git a/base/CLog.h b/base/CLog.h index 66cfa799..0a65a787 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -7,12 +7,45 @@ class CLog { public: typedef void (*Outputter)(const char*); + typedef void (*Lock)(bool lock); + // static void print(const char*, ...); static void printt(const char* file, int line, const char*, ...); + + // get/set the function used to write the log. a NULL outputter + // means to use the default which is fprintf(stderr, ...). note + // that the outputter should not call CLog methods but, if it + // does, the current lock function must permit recursive locks. static void setOutputter(Outputter); + static Outputter getOutputter(); + + // get/set the lock/unlock function. use setLock(NULL) to remove + // the locking function. note that the lock function is used when + // retrieving the lock function. there is no default lock function. + static void setLock(Lock); + static Lock getLock(); + + // get/set the minimum priority filter. any message below this + // priority is discarded. the default priority is 4 (INFO) + // (unless built without NDEBUG in which case it's 5 (DEBUG)). + // the default can be overridden by setting the SYN_LOG_PRI env + // var to "CRIT", "ERR", etc. + static void setFilter(int); + static int getFilter(); private: + class CHoldLock { + public: + CHoldLock(CLog::Lock lock) : m_lock(lock) { m_lock(true); } + ~CHoldLock() { m_lock(false); } + + private: + CLog::Lock m_lock; + }; + + static void dummyLock(bool); + static int getMaxPriority(); static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); @@ -22,6 +55,8 @@ private: private: static Outputter s_outputter; + static Lock s_lock; + static int s_maxPriority; }; #if defined(NOLOGGING) diff --git a/client/client.cpp b/client/client.cpp index 8271c6e3..479dbe06 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -1,14 +1,47 @@ #include "CClient.h" #include "CString.h" +#include "CLog.h" +#include "CMutex.h" #include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" +// +// logging thread safety +// + +static CMutex* s_logMutex = NULL; + +static void logLock(bool lock) +{ + assert(s_logMutex != NULL); + + if (lock) { + s_logMutex->lock(); + } + else { + s_logMutex->unlock(); + } +} + + +// +// main +// + void realMain(const CString& name, const CString& hostname, UInt16 port) { + // initialize threading library CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + + // initialize network library CNetwork::init(); CClient* client = NULL; @@ -18,14 +51,23 @@ void realMain(const CString& name, client->run(addr); delete client; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; } catch (...) { delete client; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; throw; } } + +// +// platform dependent entry points +// + #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" diff --git a/server/server.cpp b/server/server.cpp index da5ccf73..114250ba 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,11 +1,44 @@ #include "CServer.h" #include "CScreenMap.h" -#include "CThread.h" +#include "CLog.h" +#include "CMutex.h" #include "CNetwork.h" +#include "CThread.h" + +// +// logging thread safety +// + +static CMutex* s_logMutex = NULL; + +static void logLock(bool lock) +{ + assert(s_logMutex != NULL); + + if (lock) { + s_logMutex->lock(); + } + else { + s_logMutex->unlock(); + } +} + + +// +// main +// void realMain() { + // initialize threading library CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + + // initialize network library CNetwork::init(); CScreenMap screenMap; @@ -24,14 +57,23 @@ void realMain() server->run(); delete server; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; } catch (...) { delete server; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; throw; } } + +// +// platform dependent entry points +// + #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h"