feat: use standard atomics and call_once (#1026)
This commit is contained in:
parent
54b2c17172
commit
cafba580ec
@ -163,13 +163,6 @@ set (CMAKE_REQUIRED_LIBRARIES dbghelp)
|
|||||||
check_cxx_symbol_exists (UnDecorateSymbolName "windows.h;dbghelp.h" HAVE_DBGHELP)
|
check_cxx_symbol_exists (UnDecorateSymbolName "windows.h;dbghelp.h" HAVE_DBGHELP)
|
||||||
cmake_pop_check_state ()
|
cmake_pop_check_state ()
|
||||||
|
|
||||||
check_cxx_source_compiles ("
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
int a; if (__sync_val_compare_and_swap(&a, 0, 1)) return 1; return 0;
|
|
||||||
}
|
|
||||||
" HAVE___SYNC_VAL_COMPARE_AND_SWAP)
|
|
||||||
|
|
||||||
if (WITH_FUZZING STREQUAL none)
|
if (WITH_FUZZING STREQUAL none)
|
||||||
# Disable compiler demangler if fuzzing is active; we only want to use the
|
# Disable compiler demangler if fuzzing is active; we only want to use the
|
||||||
# glog demangler then.
|
# glog demangler then.
|
||||||
|
|||||||
@ -82,9 +82,6 @@
|
|||||||
/* define if you have libunwind */
|
/* define if you have libunwind */
|
||||||
#cmakedefine HAVE_LIBUNWIND
|
#cmakedefine HAVE_LIBUNWIND
|
||||||
|
|
||||||
/* define if your compiler has __sync_val_compare_and_swap */
|
|
||||||
#cmakedefine HAVE___SYNC_VAL_COMPARE_AND_SWAP
|
|
||||||
|
|
||||||
/* define if symbolize support is available */
|
/* define if symbolize support is available */
|
||||||
#cmakedefine HAVE_SYMBOLIZE
|
#cmakedefine HAVE_SYMBOLIZE
|
||||||
|
|
||||||
|
|||||||
@ -31,10 +31,10 @@
|
|||||||
//
|
//
|
||||||
// logging_unittest.cc covers the functionality herein
|
// logging_unittest.cc covers the functionality herein
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <mutex>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -124,7 +124,7 @@ inline static bool VADoRawLog(char** buf, size_t* size, const char* format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const int kLogBufSize = 3000;
|
static const int kLogBufSize = 3000;
|
||||||
static bool crashed = false;
|
static std::once_flag crashed;
|
||||||
static CrashReason crash_reason;
|
static CrashReason crash_reason;
|
||||||
static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0'
|
static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0'
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ void RawLog__(LogSeverity severity, const char* file, int line,
|
|||||||
// We write just once to avoid races with other invocations of RawLog__.
|
// We write just once to avoid races with other invocations of RawLog__.
|
||||||
safe_write(STDERR_FILENO, buffer, strlen(buffer));
|
safe_write(STDERR_FILENO, buffer, strlen(buffer));
|
||||||
if (severity == GLOG_FATAL) {
|
if (severity == GLOG_FATAL) {
|
||||||
if (!sync_val_compare_and_swap(&crashed, false, true)) {
|
std::call_once(crashed, [file, line, msg_start, msg_size] {
|
||||||
crash_reason.filename = file;
|
crash_reason.filename = file;
|
||||||
crash_reason.line_number = line;
|
crash_reason.line_number = line;
|
||||||
memcpy(crash_buf, msg_start, msg_size); // Don't include prefix
|
memcpy(crash_buf, msg_start, msg_size); // Don't include prefix
|
||||||
@ -207,7 +207,7 @@ void RawLog__(LogSeverity severity, const char* file, int line,
|
|||||||
crash_reason.depth = 0;
|
crash_reason.depth = 0;
|
||||||
#endif
|
#endif
|
||||||
SetCrashReason(&crash_reason);
|
SetCrashReason(&crash_reason);
|
||||||
}
|
});
|
||||||
LogMessage::Fail(); // abort()
|
LogMessage::Fail(); // abort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -266,50 +267,19 @@ void InvokeDefaultSignalHandler(int signal_number) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// This variable is used for protecting FailureSignalHandler() from
|
// This variable is used for protecting FailureSignalHandler() from dumping
|
||||||
// dumping stuff while another thread is doing it. Our policy is to let
|
// stuff while another thread is doing it. Our policy is to let the first
|
||||||
// the first thread dump stuff and let other threads wait.
|
// thread dump stuff and let other threads do nothing.
|
||||||
// See also comments in FailureSignalHandler().
|
// See also comments in FailureSignalHandler().
|
||||||
static std::thread::id* g_entered_thread_id_pointer = nullptr;
|
static std::once_flag signaled;
|
||||||
|
|
||||||
// Dumps signal and stack frame information, and invokes the default
|
static void HandleSignal(int signal_number
|
||||||
// signal handler once our job is done.
|
#if !defined(GLOG_OS_WINDOWS)
|
||||||
#if defined(GLOG_OS_WINDOWS)
|
,
|
||||||
void FailureSignalHandler(int signal_number)
|
siginfo_t* signal_info, void* ucontext
|
||||||
#else
|
|
||||||
void FailureSignalHandler(int signal_number, siginfo_t* signal_info,
|
|
||||||
void* ucontext)
|
|
||||||
#endif
|
#endif
|
||||||
{
|
) {
|
||||||
// First check if we've already entered the function. We use an atomic
|
|
||||||
// compare and swap operation for platforms that support it. For other
|
|
||||||
// platforms, we use a naive method that could lead to a subtle race.
|
|
||||||
|
|
||||||
std::thread::id my_thread_id = std::this_thread::get_id();
|
|
||||||
// NOTE: We could simply use std::thread::id rather than std::thread::id* for
|
|
||||||
// this, if std::thread::get_id() is guaranteed to return non-zero value for
|
|
||||||
// thread ids, but there is no such guarantee. We need to distinguish if the
|
|
||||||
// old value (value returned from __sync_val_compare_and_swap) is
|
|
||||||
// different from the original value (in this case nullptr).
|
|
||||||
std::thread::id* old_thread_id_pointer =
|
|
||||||
glog_internal_namespace_::sync_val_compare_and_swap(
|
|
||||||
&g_entered_thread_id_pointer, static_cast<std::thread::id*>(nullptr),
|
|
||||||
&my_thread_id);
|
|
||||||
if (old_thread_id_pointer != nullptr) {
|
|
||||||
// We've already entered the signal handler. What should we do?
|
|
||||||
if (my_thread_id == *g_entered_thread_id_pointer) {
|
|
||||||
// It looks the current thread is reentering the signal handler.
|
|
||||||
// Something must be going wrong (maybe we are reentering by another
|
|
||||||
// type of signal?). Kill ourself by the default signal handler.
|
|
||||||
InvokeDefaultSignalHandler(signal_number);
|
|
||||||
}
|
|
||||||
// Another thread is dumping stuff. Let's wait until that thread
|
|
||||||
// finishes the job and kills the process.
|
|
||||||
while (true) {
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
std::this_thread::sleep_for(1s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This is the first time we enter the signal handler. We are going to
|
// This is the first time we enter the signal handler. We are going to
|
||||||
// do some interesting stuff from here.
|
// do some interesting stuff from here.
|
||||||
// TODO(satorux): We might want to set timeout here using alarm(), but
|
// TODO(satorux): We might want to set timeout here using alarm(), but
|
||||||
@ -359,6 +329,23 @@ void FailureSignalHandler(int signal_number, siginfo_t* signal_info,
|
|||||||
InvokeDefaultSignalHandler(signal_number);
|
InvokeDefaultSignalHandler(signal_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dumps signal and stack frame information, and invokes the default
|
||||||
|
// signal handler once our job is done.
|
||||||
|
#if defined(GLOG_OS_WINDOWS)
|
||||||
|
void FailureSignalHandler(int signal_number)
|
||||||
|
#else
|
||||||
|
void FailureSignalHandler(int signal_number, siginfo_t* signal_info,
|
||||||
|
void* ucontext)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
std::call_once(signaled, &HandleSignal, signal_number
|
||||||
|
#if !defined(GLOG_OS_WINDOWS)
|
||||||
|
,
|
||||||
|
signal_info, ucontext
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace glog_internal_namespace_ {
|
namespace glog_internal_namespace_ {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2023, Google Inc.
|
// Copyright (c) 2024, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -31,15 +31,18 @@
|
|||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include "base/googleinit.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TIME_H
|
#ifdef HAVE_SYS_TIME_H
|
||||||
# include <sys/time.h>
|
# include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
#include <ctime>
|
|
||||||
#if defined(HAVE_SYSCALL_H)
|
#if defined(HAVE_SYSCALL_H)
|
||||||
# include <syscall.h> // for syscall()
|
# include <syscall.h> // for syscall()
|
||||||
#elif defined(HAVE_SYS_SYSCALL_H)
|
#elif defined(HAVE_SYS_SYSCALL_H)
|
||||||
@ -58,8 +61,6 @@
|
|||||||
# include <android/log.h>
|
# include <android/log.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/googleinit.h"
|
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
@ -254,11 +255,11 @@ void DumpStackTraceToString(string* stacktrace) {
|
|||||||
|
|
||||||
// We use an atomic operation to prevent problems with calling CrashReason
|
// We use an atomic operation to prevent problems with calling CrashReason
|
||||||
// from inside the Mutex implementation (potentially through RAW_CHECK).
|
// from inside the Mutex implementation (potentially through RAW_CHECK).
|
||||||
static const CrashReason* g_reason = nullptr;
|
static std::atomic<const CrashReason*> g_reason{nullptr};
|
||||||
|
|
||||||
void SetCrashReason(const CrashReason* r) {
|
void SetCrashReason(const CrashReason* r) {
|
||||||
sync_val_compare_and_swap(&g_reason, reinterpret_cast<const CrashReason*>(0),
|
const CrashReason* expected = nullptr;
|
||||||
r);
|
g_reason.compare_exchange_strong(expected, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitGoogleLoggingUtilities(const char* argv0) {
|
void InitGoogleLoggingUtilities(const char* argv0) {
|
||||||
|
|||||||
@ -173,34 +173,6 @@ const std::string& MyUserName();
|
|||||||
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
|
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
|
||||||
const char* const_basename(const char* filepath);
|
const char* const_basename(const char* filepath);
|
||||||
|
|
||||||
// Wrapper of __sync_val_compare_and_swap. If the GCC extension isn't
|
|
||||||
// defined, we try the CPU specific logics (we only support x86 and
|
|
||||||
// x86_64 for now) first, then use a naive implementation, which has a
|
|
||||||
// race condition.
|
|
||||||
template <typename T>
|
|
||||||
inline T sync_val_compare_and_swap(T* ptr, T oldval, T newval) {
|
|
||||||
#if defined(HAVE___SYNC_VAL_COMPARE_AND_SWAP)
|
|
||||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
|
||||||
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
|
|
||||||
T ret;
|
|
||||||
__asm__ __volatile__("lock; cmpxchg %1, (%2);"
|
|
||||||
: "=a"(ret)
|
|
||||||
// GCC may produces %sil or %dil for
|
|
||||||
// constraint "r", but some of apple's gas
|
|
||||||
// doesn't know the 8 bit registers.
|
|
||||||
// We use "q" to avoid these registers.
|
|
||||||
: "q"(newval), "q"(ptr), "a"(oldval)
|
|
||||||
: "memory", "cc");
|
|
||||||
return ret;
|
|
||||||
#else
|
|
||||||
T ret = *ptr;
|
|
||||||
if (ret == oldval) {
|
|
||||||
*ptr = newval;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DumpStackTraceToString(std::string* stacktrace);
|
void DumpStackTraceToString(std::string* stacktrace);
|
||||||
|
|
||||||
struct CrashReason {
|
struct CrashReason {
|
||||||
|
|||||||
@ -40,13 +40,6 @@ using namespace GFLAGS_NAMESPACE;
|
|||||||
|
|
||||||
using namespace google;
|
using namespace google;
|
||||||
|
|
||||||
TEST(utilities, sync_val_compare_and_swap) {
|
|
||||||
bool now_entering = false;
|
|
||||||
EXPECT_FALSE(sync_val_compare_and_swap(&now_entering, false, true));
|
|
||||||
EXPECT_TRUE(sync_val_compare_and_swap(&now_entering, false, true));
|
|
||||||
EXPECT_TRUE(sync_val_compare_and_swap(&now_entering, false, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(utilities, InitGoogleLoggingDeathTest) {
|
TEST(utilities, InitGoogleLoggingDeathTest) {
|
||||||
ASSERT_DEATH(InitGoogleLogging("foobar"), "");
|
ASSERT_DEATH(InitGoogleLogging("foobar"), "");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user