Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use standard atomics and call_once #1026

Merged
merged 1 commit into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,6 @@ set (CMAKE_REQUIRED_LIBRARIES dbghelp)
check_cxx_symbol_exists (UnDecorateSymbolName "windows.h;dbghelp.h" HAVE_DBGHELP)
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)
# Disable compiler demangler if fuzzing is active; we only want to use the
# glog demangler then.
Expand Down
3 changes: 0 additions & 3 deletions src/config.h.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@
/* define if you 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 */
#cmakedefine HAVE_SYMBOLIZE

Expand Down
8 changes: 4 additions & 4 deletions src/raw_logging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
//
// logging_unittest.cc covers the functionality herein

#include <cerrno>
#include <cstdarg>
#include <cstdio>
#include <iomanip>
#include <mutex>
#include <ostream>
#include <streambuf>
#include <thread>
Expand Down Expand Up @@ -124,7 +124,7 @@ inline static bool VADoRawLog(char** buf, size_t* size, const char* format,
}

static const int kLogBufSize = 3000;
static bool crashed = false;
static std::once_flag crashed;
static CrashReason crash_reason;
static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0'

Expand Down Expand Up @@ -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__.
safe_write(STDERR_FILENO, buffer, strlen(buffer));
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.line_number = line;
memcpy(crash_buf, msg_start, msg_size); // Don't include prefix
Expand All @@ -207,7 +207,7 @@ void RawLog__(LogSeverity severity, const char* file, int line,
crash_reason.depth = 0;
#endif
SetCrashReason(&crash_reason);
}
});
LogMessage::Fail(); // abort()
}
}
Expand Down
69 changes: 28 additions & 41 deletions src/signalhandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <algorithm>
#include <csignal>
#include <ctime>
#include <mutex>
#include <sstream>
#include <thread>

Expand Down Expand Up @@ -266,50 +267,19 @@ void InvokeDefaultSignalHandler(int signal_number) {
#endif
}

// This variable is used for protecting FailureSignalHandler() from
// dumping stuff while another thread is doing it. Our policy is to let
// the first thread dump stuff and let other threads wait.
// This variable is used for protecting FailureSignalHandler() from dumping
// stuff while another thread is doing it. Our policy is to let the first
// thread dump stuff and let other threads do nothing.
// 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
// 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)
static void HandleSignal(int signal_number
#if !defined(GLOG_OS_WINDOWS)
,
siginfo_t* signal_info, void* ucontext
#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
// do some interesting stuff from here.
// TODO(satorux): We might want to set timeout here using alarm(), but
Expand Down Expand Up @@ -359,6 +329,23 @@ void FailureSignalHandler(int signal_number, siginfo_t* signal_info,
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 glog_internal_namespace_ {
Expand Down
15 changes: 8 additions & 7 deletions src/utilities.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2023, Google Inc.
// Copyright (c) 2024, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -31,15 +31,18 @@

#include "utilities.h"

#include <atomic>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <ctime>

#include "base/googleinit.h"
#include "config.h"

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <ctime>
#if defined(HAVE_SYSCALL_H)
# include <syscall.h> // for syscall()
#elif defined(HAVE_SYS_SYSCALL_H)
Expand All @@ -58,8 +61,6 @@
# include <android/log.h>
#endif

#include "base/googleinit.h"

using std::string;

namespace google {
Expand Down Expand Up @@ -254,11 +255,11 @@ void DumpStackTraceToString(string* stacktrace) {

// We use an atomic operation to prevent problems with calling CrashReason
// 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) {
sync_val_compare_and_swap(&g_reason, reinterpret_cast<const CrashReason*>(0),
r);
const CrashReason* expected = nullptr;
g_reason.compare_exchange_strong(expected, r);
}

void InitGoogleLoggingUtilities(const char* argv0) {
Expand Down
28 changes: 0 additions & 28 deletions src/utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,34 +173,6 @@ const std::string& MyUserName();
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
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);

struct CrashReason {
Expand Down
7 changes: 0 additions & 7 deletions src/utilities_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,6 @@ using namespace GFLAGS_NAMESPACE;

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) {
ASSERT_DEATH(InitGoogleLogging("foobar"), "");
}
Expand Down