diff --git a/absl/base/internal/tracing.h b/absl/base/internal/tracing.h index a87854b9..a74da8f1 100644 --- a/absl/base/internal/tracing.h +++ b/absl/base/internal/tracing.h @@ -22,7 +22,7 @@ ABSL_NAMESPACE_BEGIN namespace base_internal { // Well known Abseil object types that have causality. -enum class ObjectKind { kUnknown }; +enum class ObjectKind { kUnknown, kBlockingCounter }; // `TraceWait` and `TraceContinue` record the start and end of a potentially // blocking wait operation on `object`. `object` typically represents a higher @@ -47,14 +47,14 @@ void TraceObserved(const void* object, ObjectKind kind); // --------------------------------------------------------------------------- extern "C" { -void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceWait)(const void* object, - ObjectKind kind); -void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceContinue)(const void* object, - ObjectKind kind); -void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceSignal)(const void* object, + void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceWait)(const void* object, ObjectKind kind); -void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceObserved)(const void* object, + void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceContinue)(const void* object, + ObjectKind kind); + void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceSignal)(const void* object, ObjectKind kind); + void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceObserved)(const void* object, + ObjectKind kind); } // extern "C" diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index dafeba33..93df38bf 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -141,6 +141,7 @@ cc_library( "//absl/base:dynamic_annotations", "//absl/base:malloc_internal", "//absl/base:raw_logging_internal", + "//absl/base:tracing_internal", "//absl/debugging:stacktrace", "//absl/debugging:symbolize", "//absl/time", @@ -177,6 +178,9 @@ cc_test( ], deps = [ ":synchronization", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:tracing_internal", "//absl/time", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index a0f64e5c..3e47c3df 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -113,6 +113,7 @@ absl_cc_library( absl::stacktrace absl::symbolize absl::time + absl::tracing_internal Threads::Threads PUBLIC ) @@ -140,6 +141,7 @@ absl_cc_test( DEPS absl::synchronization absl::time + absl::tracing_internal GTest::gmock_main ) diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc index d2f82da3..a530baf4 100644 --- a/absl/synchronization/blocking_counter.cc +++ b/absl/synchronization/blocking_counter.cc @@ -17,6 +17,7 @@ #include #include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/tracing.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -40,6 +41,7 @@ bool BlockingCounter::DecrementCount() { ABSL_RAW_CHECK(count >= 0, "BlockingCounter::DecrementCount() called too many times"); if (count == 0) { + base_internal::TraceSignal(this, TraceObjectKind()); MutexLock l(&lock_); done_ = true; return true; @@ -48,19 +50,23 @@ bool BlockingCounter::DecrementCount() { } void BlockingCounter::Wait() { - MutexLock l(&this->lock_); + base_internal::TraceWait(this, TraceObjectKind()); + { + MutexLock l(&this->lock_); - // only one thread may call Wait(). To support more than one thread, - // implement a counter num_to_exit, like in the Barrier class. - ABSL_RAW_CHECK(num_waiting_ == 0, "multiple threads called Wait()"); - num_waiting_++; + // only one thread may call Wait(). To support more than one thread, + // implement a counter num_to_exit, like in the Barrier class. + ABSL_RAW_CHECK(num_waiting_ == 0, "multiple threads called Wait()"); + num_waiting_++; - this->lock_.Await(Condition(IsDone, &this->done_)); + this->lock_.Await(Condition(IsDone, &this->done_)); - // At this point, we know that all threads executing DecrementCount - // will not touch this object again. - // Therefore, the thread calling this method is free to delete the object - // after we return from this method. + // At this point, we know that all threads executing DecrementCount + // will not touch this object again. + // Therefore, the thread calling this method is free to delete the object + // after we return from this method. + } + base_internal::TraceContinue(this, TraceObjectKind()); } ABSL_NAMESPACE_END diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h index 1908fdb1..d0504a19 100644 --- a/absl/synchronization/blocking_counter.h +++ b/absl/synchronization/blocking_counter.h @@ -22,6 +22,7 @@ #include +#include "absl/base/internal/tracing.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" @@ -89,6 +90,11 @@ class BlockingCounter { void Wait(); private: + // Convenience helper to reduce verbosity at call sites. + static inline constexpr base_internal::ObjectKind TraceObjectKind() { + return base_internal::ObjectKind::kBlockingCounter; + } + Mutex lock_; std::atomic count_; int num_waiting_ ABSL_GUARDED_BY(lock_); diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc index 06885f57..0c42b56a 100644 --- a/absl/synchronization/blocking_counter_test.cc +++ b/absl/synchronization/blocking_counter_test.cc @@ -15,9 +15,13 @@ #include "absl/synchronization/blocking_counter.h" #include // NOLINT(build/c++11) +#include #include #include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/tracing.h" #include "absl/time/clock.h" #include "absl/time/time.h" @@ -76,5 +80,67 @@ TEST(BlockingCounterTest, WaitNegativeInitialCount) { #endif } // namespace + +#if ABSL_HAVE_ATTRIBUTE_WEAK + +namespace base_internal { + +namespace { + +using TraceRecord = std::tuple; + +thread_local TraceRecord tls_signal; +thread_local TraceRecord tls_wait; +thread_local TraceRecord tls_continue; + +} // namespace + +// Strong extern "C" implementation. +extern "C" { + +void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceWait)(const void* object, + ObjectKind kind) { + tls_wait = {object, kind}; +} + +void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceContinue)(const void* object, + ObjectKind kind) { + tls_continue = {object, kind}; +} + +void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceSignal)(const void* object, + ObjectKind kind) { + tls_signal = {object, kind}; +} + +} // extern "C" + +TEST(BlockingCounterTest, TracesSignal) { + BlockingCounter counter(2); + + tls_signal = {}; + counter.DecrementCount(); + EXPECT_EQ(tls_signal, TraceRecord(nullptr, ObjectKind::kUnknown)); + + tls_signal = {}; + counter.DecrementCount(); + EXPECT_EQ(tls_signal, TraceRecord(&counter, ObjectKind::kBlockingCounter)); +} + +TEST(BlockingCounterTest, TracesWaitContinue) { + BlockingCounter counter(1); + counter.DecrementCount(); + + tls_wait = {}; + tls_continue = {}; + counter.Wait(); + EXPECT_EQ(tls_wait, TraceRecord(&counter, ObjectKind::kBlockingCounter)); + EXPECT_EQ(tls_continue, TraceRecord(&counter, ObjectKind::kBlockingCounter)); +} + +} // namespace base_internal + +#endif // ABSL_HAVE_ATTRIBUTE_WEAK + ABSL_NAMESPACE_END } // namespace absl