diff --git a/absl/base/internal/tracing.h b/absl/base/internal/tracing.h index a74da8f1..e7ab7758 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, kBlockingCounter }; +enum class ObjectKind { kUnknown, kBlockingCounter, kNotification }; // `TraceWait` and `TraceContinue` record the start and end of a potentially // blocking wait operation on `object`. `object` typically represents a higher diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 93df38bf..46a23f3b 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -324,6 +324,9 @@ cc_test( tags = ["no_test_lexan"], 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 3e47c3df..a7455747 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -112,6 +112,7 @@ absl_cc_library( absl::raw_logging_internal absl::stacktrace absl::symbolize + absl::tracing_internal absl::time absl::tracing_internal Threads::Threads @@ -217,8 +218,12 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::base + absl::config + absl::core_headers absl::synchronization absl::time + absl::tracing_internal GTest::gmock_main ) diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc index 165ba669..a5853ab3 100644 --- a/absl/synchronization/notification.cc +++ b/absl/synchronization/notification.cc @@ -17,6 +17,7 @@ #include #include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/tracing.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" @@ -24,6 +25,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN void Notification::Notify() { + base_internal::TraceSignal(this, TraceObjectKind()); MutexLock l(&this->mutex_); #ifndef NDEBUG @@ -45,31 +47,37 @@ Notification::~Notification() { } void Notification::WaitForNotification() const { + base_internal::TraceWait(this, TraceObjectKind()); if (!HasBeenNotifiedInternal(&this->notified_yet_)) { - this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal, - &this->notified_yet_)); + this->mutex_.LockWhen( + Condition(&HasBeenNotifiedInternal, &this->notified_yet_)); this->mutex_.Unlock(); } + base_internal::TraceContinue(this, TraceObjectKind()); } bool Notification::WaitForNotificationWithTimeout( absl::Duration timeout) const { + base_internal::TraceWait(this, TraceObjectKind()); bool notified = HasBeenNotifiedInternal(&this->notified_yet_); if (!notified) { notified = this->mutex_.LockWhenWithTimeout( Condition(&HasBeenNotifiedInternal, &this->notified_yet_), timeout); this->mutex_.Unlock(); } + base_internal::TraceContinue(notified ? this : nullptr, TraceObjectKind()); return notified; } bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const { + base_internal::TraceWait(this, TraceObjectKind()); bool notified = HasBeenNotifiedInternal(&this->notified_yet_); if (!notified) { notified = this->mutex_.LockWhenWithDeadline( Condition(&HasBeenNotifiedInternal, &this->notified_yet_), deadline); this->mutex_.Unlock(); } + base_internal::TraceContinue(notified ? this : nullptr, TraceObjectKind()); return notified; } diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 8986d9a4..78cdf296 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -53,6 +53,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/internal/tracing.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" @@ -75,7 +76,11 @@ class Notification { // // Returns the value of the notification's internal "notified" state. ABSL_MUST_USE_RESULT bool HasBeenNotified() const { - return HasBeenNotifiedInternal(&this->notified_yet_); + if (HasBeenNotifiedInternal(&this->notified_yet_)) { + base_internal::TraceObserved(this, TraceObjectKind()); + return true; + } + return false; } // Notification::WaitForNotification() @@ -108,6 +113,11 @@ class Notification { void Notify(); private: + // Convenience helper to reduce verbosity at call sites. + static inline constexpr base_internal::ObjectKind TraceObjectKind() { + return base_internal::ObjectKind::kNotification; + } + static inline bool HasBeenNotifiedInternal( const std::atomic* notified_yet) { return notified_yet->load(std::memory_order_acquire); diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc index 49ce61a5..027bc05b 100644 --- a/absl/synchronization/notification_test.cc +++ b/absl/synchronization/notification_test.cc @@ -15,10 +15,15 @@ #include "absl/synchronization/notification.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/synchronization/mutex.h" +#include "absl/time/time.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -129,5 +134,93 @@ TEST(NotificationTest, SanityTest) { BasicTests(true, &local_notification2); } +#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; +thread_local TraceRecord tls_observed; + +} // 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}; +} + +void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceObserved)(const void* object, + ObjectKind kind) { + tls_observed = {object, kind}; +} + +} // extern "C" + +TEST(NotificationTest, TracesNotify) { + Notification n; + tls_signal = {}; + n.Notify(); + EXPECT_EQ(tls_signal, TraceRecord(&n, ObjectKind::kNotification)); +} + +TEST(NotificationTest, TracesWaitForNotification) { + Notification n; + n.Notify(); + tls_wait = tls_continue = {}; + n.WaitForNotification(); + EXPECT_EQ(tls_wait, TraceRecord(&n, ObjectKind::kNotification)); + EXPECT_EQ(tls_continue, TraceRecord(&n, ObjectKind::kNotification)); +} + +TEST(NotificationTest, TracesWaitForNotificationWithTimeout) { + Notification n; + + tls_wait = tls_continue = {}; + n.WaitForNotificationWithTimeout(absl::Milliseconds(1)); + EXPECT_EQ(tls_wait, TraceRecord(&n, ObjectKind::kNotification)); + EXPECT_EQ(tls_continue, TraceRecord(nullptr, ObjectKind::kNotification)); + + n.Notify(); + tls_wait = tls_continue = {}; + n.WaitForNotificationWithTimeout(absl::Milliseconds(1)); + EXPECT_EQ(tls_wait, TraceRecord(&n, ObjectKind::kNotification)); + EXPECT_EQ(tls_continue, TraceRecord(&n, ObjectKind::kNotification)); +} + +TEST(NotificationTest, TracesHasBeenNotified) { + Notification n; + + tls_observed = {}; + ASSERT_FALSE(n.HasBeenNotified()); + EXPECT_EQ(tls_observed, TraceRecord(nullptr, ObjectKind::kUnknown)); + + n.Notify(); + tls_observed = {}; + ASSERT_TRUE(n.HasBeenNotified()); + EXPECT_EQ(tls_observed, TraceRecord(&n, ObjectKind::kNotification)); +} + +} // namespace base_internal + +#endif // ABSL_HAVE_ATTRIBUTE_WEAK + ABSL_NAMESPACE_END } // namespace absl