diff options
author | davemoore@chromium.org <davemoore@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-14 22:24:55 +0000 |
---|---|---|
committer | davemoore@chromium.org <davemoore@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-14 22:24:55 +0000 |
commit | 5b84fe3a0f6b9319e18f31db4d3c943616a91090 (patch) | |
tree | e6c4afcd14264fdfb4503514a1572a20684389be /base | |
parent | a5e84bda360798580ed0eb6f27dc0342999c9675 (diff) | |
download | chromium_src-5b84fe3a0f6b9319e18f31db4d3c943616a91090.zip chromium_src-5b84fe3a0f6b9319e18f31db4d3c943616a91090.tar.gz chromium_src-5b84fe3a0f6b9319e18f31db4d3c943616a91090.tar.bz2 |
Lock log files when reiniting them.
This is a second attempt of this cl:
http://codereview.chromium.org/3195014/show
BUG=NONE
TEST=NONE
Review URL: http://codereview.chromium.org/3347016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59439 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/logging.cc | 248 |
1 files changed, 148 insertions, 100 deletions
diff --git a/base/logging.cc b/base/logging.cc index f90a369..9808860 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -60,7 +60,6 @@ const char* const log_severity_names[LOG_NUM_SEVERITIES] = { "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; int min_log_level = 0; -LogLockingState lock_log_file = LOCK_LOG_FILE; // The default set here for logging_destination will only be used if // InitLogging is not called. On Windows, use a file next to the exe; @@ -109,19 +108,6 @@ LogReportHandlerFunction log_report_handler = NULL; // A log message handler that gets notified of every log message we process. LogMessageHandlerFunction log_message_handler = NULL; -// The lock is used if log file locking is false. It helps us avoid problems -// with multiple threads writing to the log file at the same time. Use -// LockImpl directly instead of using Lock, because Lock makes logging calls. -static LockImpl* log_lock = NULL; - -// When we don't use a lock, we are using a global mutex. We need to do this -// because LockFileEx is not thread safe. -#if defined(OS_WIN) -MutexHandle log_mutex = NULL; -#elif defined(OS_POSIX) -pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - // Helper functions to wrap platform differences. int32 CurrentProcessId() { @@ -178,6 +164,126 @@ void DeleteFilePath(const PathString& log_name) { #endif } +void GetDefaultLogFile(PathString default_log_file) { +#if defined(OS_WIN) + // On Windows we use the same path as the exe. + wchar_t module_name[MAX_PATH]; + GetModuleFileName(NULL, module_name, MAX_PATH); + default_log_file = module_name; + std::wstring::size_type last_backslash = + default_log_file.rfind('\\', default_log_file.size()); + if (last_backslash != std::wstring::npos) + default_log_file.erase(last_backslash + 1); + default_log_file += L"debug.log"; +#elif defined(OS_POSIX) + // On other platforms we just use the current directory. + default_log_file = "debug.log"; +#endif +} + +// This class acts as a wrapper for locking the logging files. +// LoggingLock::Init() should be called from the main thread before any logging +// is done. Then whenever logging, be sure to have a local LoggingLock +// instance on the stack. This will ensure that the lock is unlocked upon +// exiting the frame. +// LoggingLocks can not be nested. +class LoggingLock { + public: + LoggingLock() { + LockLogging(); + } + + ~LoggingLock() { + UnlockLogging(); + } + + static void Init(LogLockingState lock_log, const PathChar* new_log_file) { + if (initialized) + return; + lock_log_file = lock_log; + if (lock_log_file == LOCK_LOG_FILE) { +#if defined(OS_WIN) + if (!log_mutex) { + std::wstring safe_name; + if (new_log_file) + safe_name = new_log_file; + else + GetDefaultLogFile(safe_name); + // \ is not a legal character in mutex names so we replace \ with / + std::replace(safe_name.begin(), safe_name.end(), '\\', '/'); + std::wstring t(L"Global\\"); + t.append(safe_name); + log_mutex = ::CreateMutex(NULL, FALSE, t.c_str()); + } +#endif + } else { + log_lock = new LockImpl(); + } + initialized = true; + } + + private: + static void LockLogging() { + if (lock_log_file == LOCK_LOG_FILE) { +#if defined(OS_WIN) + ::WaitForSingleObject(log_mutex, INFINITE); + // WaitForSingleObject could have returned WAIT_ABANDONED. We don't + // abort the process here. UI tests might be crashy sometimes, + // and aborting the test binary only makes the problem worse. + // We also don't use LOG macros because that might lead to an infinite + // loop. For more info see http://crbug.com/18028. +#elif defined(OS_POSIX) + pthread_mutex_lock(&log_mutex); +#endif + } else { + // use the lock + log_lock->Lock(); + } + } + + static void UnlockLogging() { + if (lock_log_file == LOCK_LOG_FILE) { +#if defined(OS_WIN) + ReleaseMutex(log_mutex); +#elif defined(OS_POSIX) + pthread_mutex_unlock(&log_mutex); +#endif + } else { + log_lock->Unlock(); + } + } + + // The lock is used if log file locking is false. It helps us avoid problems + // with multiple threads writing to the log file at the same time. Use + // LockImpl directly instead of using Lock, because Lock makes logging calls. + static LockImpl* log_lock; + + // When we don't use a lock, we are using a global mutex. We need to do this + // because LockFileEx is not thread safe. +#if defined(OS_WIN) + static MutexHandle log_mutex; +#elif defined(OS_POSIX) + static pthread_mutex_t log_mutex; +#endif + + static bool initialized; + static LogLockingState lock_log_file; +}; + +// static +bool LoggingLock::initialized = false; +// static +LockImpl* LoggingLock::log_lock = NULL; +// static +LogLockingState LoggingLock::lock_log_file = LOCK_LOG_FILE; + +#if defined(OS_WIN) +// static +MutexHandle LoggingLock::log_mutex = NULL; +#elif defined(OS_POSIX) +pthread_mutex_t LoggingLock::log_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + // Called by logging functions to ensure that debug_file is initialized // and can be used for writing. Returns false if the file could not be // initialized. debug_file will be NULL in this case. @@ -188,20 +294,8 @@ bool InitializeLogFileHandle() { if (!log_file_name) { // Nobody has called InitLogging to specify a debug log file, so here we // initialize the log file name to a default. -#if defined(OS_WIN) - // On Windows we use the same path as the exe. - wchar_t module_name[MAX_PATH]; - GetModuleFileName(NULL, module_name, MAX_PATH); - log_file_name = new std::wstring(module_name); - std::wstring::size_type last_backslash = - log_file_name->rfind('\\', log_file_name->size()); - if (last_backslash != std::wstring::npos) - log_file_name->erase(last_backslash + 1); - *log_file_name += L"debug.log"; -#elif defined(OS_POSIX) - // On other platforms we just use the current directory. - log_file_name = new std::string("debug.log"); -#endif + log_file_name = new PathString(); + GetDefaultLogFile(*log_file_name); } if (logging_destination == LOG_ONLY_TO_FILE || @@ -231,27 +325,15 @@ bool InitializeLogFileHandle() { return true; } -void InitLogMutex() { -#if defined(OS_WIN) - if (!log_mutex) { - // \ is not a legal character in mutex names so we replace \ with / - std::wstring safe_name(*log_file_name); - std::replace(safe_name.begin(), safe_name.end(), '\\', '/'); - std::wstring t(L"Global\\"); - t.append(safe_name); - log_mutex = ::CreateMutex(NULL, FALSE, t.c_str()); - } -#elif defined(OS_POSIX) - // statically initialized -#endif -} - void BaseInitLoggingImpl(const PathChar* new_log_file, LoggingDestination logging_dest, LogLockingState lock_log, OldFileDeletionState delete_old) { g_enable_dcheck = CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableDCHECK); + LoggingLock::Init(lock_log, new_log_file); + + LoggingLock logging_lock; if (log_file) { // calling InitLogging twice or after some log call has already opened the @@ -260,7 +342,6 @@ void BaseInitLoggingImpl(const PathChar* new_log_file, log_file = NULL; } - lock_log_file = lock_log; logging_destination = logging_dest; // ignore file options if logging is disabled or only to system @@ -274,13 +355,8 @@ void BaseInitLoggingImpl(const PathChar* new_log_file, if (delete_old == DELETE_OLD_LOG_FILE) DeleteFilePath(*log_file_name); - if (lock_log_file == LOCK_LOG_FILE) { - InitLogMutex(); - } else if (!log_lock) { - log_lock = new LockImpl(); - } - InitializeLogFileHandle(); + } void SetMinLogLevel(int level) { @@ -517,61 +593,31 @@ LogMessage::~LogMessage() { fflush(stderr); } + // We can have multiple threads and/or processes, so try to prevent them + // from clobbering each other's writes. + // If the client app did not call InitLogging, and the lock has not + // been created do it now. We do this on demand, but if two threads try + // to do this at the same time, there will be a race condition to create + // the lock. This is why InitLogging should be called from the main + // thread at the beginning of execution. + LoggingLock::Init(LOCK_LOG_FILE, NULL); // write to log file if (logging_destination != LOG_NONE && - logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG && - InitializeLogFileHandle()) { - // We can have multiple threads and/or processes, so try to prevent them - // from clobbering each other's writes. - if (lock_log_file == LOCK_LOG_FILE) { - // Ensure that the mutex is initialized in case the client app did not - // call InitLogging. This is not thread safe. See below. - InitLogMutex(); - + logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG) { + LoggingLock logging_lock; + if (InitializeLogFileHandle()) { #if defined(OS_WIN) - ::WaitForSingleObject(log_mutex, INFINITE); - // WaitForSingleObject could have returned WAIT_ABANDONED. We don't - // abort the process here. UI tests might be crashy sometimes, - // and aborting the test binary only makes the problem worse. - // We also don't use LOG macros because that might lead to an infinite - // loop. For more info see http://crbug.com/18028. -#elif defined(OS_POSIX) - pthread_mutex_lock(&log_mutex); -#endif - } else { - // use the lock - if (!log_lock) { - // The client app did not call InitLogging, and so the lock has not - // been created. We do this on demand, but if two threads try to do - // this at the same time, there will be a race condition to create - // the lock. This is why InitLogging should be called from the main - // thread at the beginning of execution. - log_lock = new LockImpl(); - } - log_lock->Lock(); - } - -#if defined(OS_WIN) - SetFilePointer(log_file, 0, 0, SEEK_END); - DWORD num_written; - WriteFile(log_file, - static_cast<const void*>(str_newline.c_str()), - static_cast<DWORD>(str_newline.length()), - &num_written, - NULL); + SetFilePointer(log_file, 0, 0, SEEK_END); + DWORD num_written; + WriteFile(log_file, + static_cast<const void*>(str_newline.c_str()), + static_cast<DWORD>(str_newline.length()), + &num_written, + NULL); #else - fprintf(log_file, "%s", str_newline.c_str()); - fflush(log_file); -#endif - - if (lock_log_file == LOCK_LOG_FILE) { -#if defined(OS_WIN) - ReleaseMutex(log_mutex); -#elif defined(OS_POSIX) - pthread_mutex_unlock(&log_mutex); + fprintf(log_file, "%s", str_newline.c_str()); + fflush(log_file); #endif - } else { - log_lock->Unlock(); } } @@ -695,6 +741,8 @@ ErrnoLogMessage::~ErrnoLogMessage() { #endif // OS_WIN void CloseLogFile() { + LoggingLock logging_lock; + if (!log_file) return; |