about summary refs log tree commit diff
path: root/absl/base
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base')
-rw-r--r--absl/base/BUILD.bazel25
-rw-r--r--absl/base/CMakeLists.txt33
-rw-r--r--absl/base/exception_safety_testing_test.cc21
-rw-r--r--absl/base/internal/atomic_hook.h59
-rw-r--r--absl/base/internal/atomic_hook_test.cc70
-rw-r--r--absl/base/internal/exception_safety_testing.h16
-rw-r--r--absl/base/internal/thread_identity_benchmark.cc40
7 files changed, 236 insertions, 28 deletions
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index f9aac5a5aac9..bb7a59817712 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -148,6 +148,18 @@ cc_library(
 )
 
 cc_test(
+    name = "atomic_hook_test",
+    size = "small",
+    srcs = ["internal/atomic_hook_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        ":base",
+        ":core_headers",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "bit_cast_test",
     size = "small",
     srcs = [
@@ -393,3 +405,16 @@ cc_test(
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_binary(
+    name = "thread_identity_benchmark",
+    testonly = 1,
+    srcs = ["internal/thread_identity_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":base",
+        "//absl/synchronization",
+        "@com_github_google_benchmark//:benchmark",
+    ],
+)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 329a3d055560..45640562786f 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -99,14 +99,18 @@ absl_library(
 
 if(BUILD_TESTING)
   # exception-safety testing library
-  set(EXCEPTION_SAFETY_TESTING_SRC "internal/exception_safety_testing.cc")
+  set(EXCEPTION_SAFETY_TESTING_SRC
+    "internal/exception_safety_testing.h"
+    "internal/exception_safety_testing.cc"
+  )
   set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
     ${ABSL_TEST_COMMON_LIBRARIES}
     absl::base
     absl::memory
     absl::meta
     absl::strings
-    absl::types
+    absl::optional
+    gtest
   )
 
 absl_library(
@@ -116,6 +120,8 @@ absl_library(
     ${EXCEPTION_SAFETY_TESTING_SRC}
   PUBLIC_LIBRARIES
     ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
+  PRIVATE_COMPILE_FLAGS
+    ${ABSL_EXCEPTIONS_FLAG}
 )
 endif()
 
@@ -163,6 +169,20 @@ absl_library(
 #
 
 # call once test
+set(ATOMIC_HOOK_TEST_SRC "internal/atomic_hook_test.cc")
+set(ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES absl::base)
+
+absl_test(
+  TARGET
+    atomic_hook_test
+  SOURCES
+    ${ATOMIC_HOOK_TEST_SRC}
+  PUBLIC_LIBRARIES
+    ${ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES}
+)
+
+
+# call once test
 set(CALL_ONCE_TEST_SRC "call_once_test.cc")
 set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
 
@@ -344,7 +364,14 @@ absl_test(
 
 #test exceptions_safety_testing_test
 set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
-set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES absl::base absl::memory absl::meta absl::strings absl::optional)
+set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES
+  absl::base
+  absl_base_internal_exception_safety_testing
+  absl::memory
+  absl::meta
+  absl::strings
+  absl::optional
+)
 
 absl_test(
   TARGET
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 9bd8b9dbd040..4507b94658db 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -83,6 +83,27 @@ TEST(ThrowingValueTest, ThrowingAssignment) {
 
   TestOp([&]() { bomb = bomb1; });
   TestOp([&]() { bomb = std::move(bomb1); });
+
+  // Test that when assignment throws, the assignment should fail (lhs != rhs)
+  // and strong guarantee fails (lhs != lhs_copy).
+  {
+    ThrowingValue<> lhs(39), rhs(42);
+    ThrowingValue<> lhs_copy(lhs);
+    SetCountdown();
+    EXPECT_THROW(lhs = rhs, TestException);
+    UnsetCountdown();
+    EXPECT_NE(lhs, rhs);
+    EXPECT_NE(lhs_copy, lhs);
+  }
+  {
+    ThrowingValue<> lhs(39), rhs(42);
+    ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
+    SetCountdown();
+    EXPECT_THROW(lhs = std::move(rhs), TestException);
+    UnsetCountdown();
+    EXPECT_NE(lhs, rhs_copy);
+    EXPECT_NE(lhs_copy, lhs);
+  }
 }
 
 TEST(ThrowingValueTest, ThrowingComparisons) {
diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h
index 47d4013928a0..b458511b0c73 100644
--- a/absl/base/internal/atomic_hook.h
+++ b/absl/base/internal/atomic_hook.h
@@ -21,6 +21,12 @@
 #include <cstdint>
 #include <utility>
 
+#ifdef _MSC_FULL_VER
+#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
+#else
+#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
+#endif
+
 namespace absl {
 namespace base_internal {
 
@@ -29,9 +35,15 @@ class AtomicHook;
 
 // AtomicHook is a helper class, templatized on a raw function pointer type, for
 // implementing Abseil customization hooks.  It is a callable object that
-// dispatches to the registered hook, or performs a no-op (and returns a default
+// dispatches to the registered hook.
+//
+// A default constructed object performs a no-op (and returns a default
 // constructed object) if no hook has been registered.
 //
+// Hooks can be pre-registered via constant initialization, for example,
+// ABSL_CONST_INIT static AtomicHook<void(*)()> my_hook(DefaultAction);
+// and then changed at runtime via a call to Store().
+//
 // Reads and writes guarantee memory_order_acquire/memory_order_release
 // semantics.
 template <typename ReturnType, typename... Args>
@@ -39,7 +51,19 @@ class AtomicHook<ReturnType (*)(Args...)> {
  public:
   using FnPtr = ReturnType (*)(Args...);
 
-  constexpr AtomicHook() : hook_(kInitialValue) {}
+  // Constructs an object that by default performs a no-op (and
+  // returns a default constructed object) when no hook as been registered.
+  constexpr AtomicHook() : AtomicHook(DummyFunction) {}
+
+  // Constructs an object that by default dispatches to/returns the
+  // pre-registered default_fn when no hook has been registered at runtime.
+#if ABSL_HAVE_WORKING_ATOMIC_POINTER
+  explicit constexpr AtomicHook(FnPtr default_fn)
+      : hook_(default_fn), default_fn_(default_fn) {}
+#else
+  explicit constexpr AtomicHook(FnPtr default_fn)
+      : hook_(kUninitialized), default_fn_(default_fn) {}
+#endif
 
   // Stores the provided function pointer as the value for this hook.
   //
@@ -86,16 +110,7 @@ class AtomicHook<ReturnType (*)(Args...)> {
   //
   // This causes an issue when building with LLVM under Windows.  To avoid this,
   // we use a less-efficient, intptr_t-based implementation on Windows.
-
-#ifdef _MSC_FULL_VER
-#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
-#else
-#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
-#endif
-
 #if ABSL_HAVE_WORKING_ATOMIC_POINTER
-  static constexpr FnPtr kInitialValue = &DummyFunction;
-
   // Return the stored value, or DummyFunction if no value has been stored.
   FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); }
 
@@ -103,10 +118,9 @@ class AtomicHook<ReturnType (*)(Args...)> {
   // stored to this object.
   bool DoStore(FnPtr fn) {
     assert(fn);
-    FnPtr expected = DummyFunction;
-    hook_.compare_exchange_strong(expected, fn, std::memory_order_acq_rel,
-                                  std::memory_order_acquire);
-    const bool store_succeeded = (expected == DummyFunction);
+    FnPtr expected = default_fn_;
+    const bool store_succeeded = hook_.compare_exchange_strong(
+        expected, fn, std::memory_order_acq_rel, std::memory_order_acquire);
     const bool same_value_already_stored = (expected == fn);
     return store_succeeded || same_value_already_stored;
   }
@@ -114,15 +128,15 @@ class AtomicHook<ReturnType (*)(Args...)> {
   std::atomic<FnPtr> hook_;
 #else  // !ABSL_HAVE_WORKING_ATOMIC_POINTER
   // Use a sentinel value unlikely to be the address of an actual function.
-  static constexpr intptr_t kInitialValue = 0;
+  static constexpr intptr_t kUninitialized = 0;
 
   static_assert(sizeof(intptr_t) >= sizeof(FnPtr),
                 "intptr_t can't contain a function pointer");
 
   FnPtr DoLoad() const {
     const intptr_t value = hook_.load(std::memory_order_acquire);
-    if (value == 0) {
-      return DummyFunction;
+    if (value == kUninitialized) {
+      return default_fn_;
     }
     return reinterpret_cast<FnPtr>(value);
   }
@@ -130,16 +144,17 @@ class AtomicHook<ReturnType (*)(Args...)> {
   bool DoStore(FnPtr fn) {
     assert(fn);
     const auto value = reinterpret_cast<intptr_t>(fn);
-    intptr_t expected = 0;
-    hook_.compare_exchange_strong(expected, value, std::memory_order_acq_rel,
-                                  std::memory_order_acquire);
-    const bool store_succeeded = (expected == 0);
+    intptr_t expected = kUninitialized;
+    const bool store_succeeded = hook_.compare_exchange_strong(
+        expected, value, std::memory_order_acq_rel, std::memory_order_acquire);
     const bool same_value_already_stored = (expected == value);
     return store_succeeded || same_value_already_stored;
   }
 
   std::atomic<intptr_t> hook_;
 #endif
+
+  const FnPtr default_fn_;
 };
 
 #undef ABSL_HAVE_WORKING_ATOMIC_POINTER
diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc
new file mode 100644
index 000000000000..cf7407573a52
--- /dev/null
+++ b/absl/base/internal/atomic_hook_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/atomic_hook.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+
+namespace {
+
+int value = 0;
+void TestHook(int x) { value = x; }
+
+TEST(AtomicHookTest, NoDefaultFunction) {
+  ABSL_CONST_INIT static absl::base_internal::AtomicHook<void(*)(int)> hook;
+  value = 0;
+
+  // Test the default DummyFunction.
+  EXPECT_TRUE(hook.Load() == nullptr);
+  EXPECT_EQ(value, 0);
+  hook(1);
+  EXPECT_EQ(value, 0);
+
+  // Test a stored hook.
+  hook.Store(TestHook);
+  EXPECT_TRUE(hook.Load() == TestHook);
+  EXPECT_EQ(value, 0);
+  hook(1);
+  EXPECT_EQ(value, 1);
+
+  // Calling Store() with the same hook should not crash.
+  hook.Store(TestHook);
+  EXPECT_TRUE(hook.Load() == TestHook);
+  EXPECT_EQ(value, 1);
+  hook(2);
+  EXPECT_EQ(value, 2);
+}
+
+TEST(AtomicHookTest, WithDefaultFunction) {
+  // Set the default value to TestHook at compile-time.
+  ABSL_CONST_INIT static absl::base_internal::AtomicHook<void (*)(int)> hook(
+      TestHook);
+  value = 0;
+
+  // Test the default value is TestHook.
+  EXPECT_TRUE(hook.Load() == TestHook);
+  EXPECT_EQ(value, 0);
+  hook(1);
+  EXPECT_EQ(value, 1);
+
+  // Calling Store() with the same hook should not crash.
+  hook.Store(TestHook);
+  EXPECT_TRUE(hook.Load() == TestHook);
+  EXPECT_EQ(value, 1);
+  hook(2);
+  EXPECT_EQ(value, 2);
+}
+
+}  // namespace
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 32450465a3d1..bec3ab30460b 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -164,7 +164,7 @@ class ConstructorTracker {
 
 template <typename Factory, typename Operation, typename Invariant>
 absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
-    const Factory& factory, const Operation& operation, int count,
+    const Factory& factory, Operation operation, int count,
     const Invariant& invariant) {
   auto t_ptr = factory();
   absl::optional<testing::AssertionResult> current_res;
@@ -277,10 +277,12 @@ enum class TypeSpec {
  */
 template <TypeSpec Spec = TypeSpec::kEverythingThrows>
 class ThrowingValue : private exceptions_internal::TrackedObject {
-  constexpr static bool IsSpecified(TypeSpec spec) {
+  static constexpr bool IsSpecified(TypeSpec spec) {
     return static_cast<bool>(Spec & spec);
   }
 
+  static constexpr int kBadValue = 938550620;
+
  public:
   ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) {
     exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
@@ -318,6 +320,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
 
   ThrowingValue& operator=(const ThrowingValue& other) noexcept(
       IsSpecified(TypeSpec::kNoThrowCopy)) {
+    dummy_ = kBadValue;
     if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
       exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
     }
@@ -327,6 +330,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
 
   ThrowingValue& operator=(ThrowingValue&& other) noexcept(
       IsSpecified(TypeSpec::kNoThrowMove)) {
+    dummy_ = kBadValue;
     if (!IsSpecified(TypeSpec::kNoThrowMove)) {
       exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
     }
@@ -630,7 +634,7 @@ enum class AllocSpec {
  */
 template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows>
 class ThrowingAllocator : private exceptions_internal::TrackedObject {
-  constexpr static bool IsSpecified(AllocSpec spec) {
+  static constexpr bool IsSpecified(AllocSpec spec) {
     return static_cast<bool>(Spec & spec);
   }
 
@@ -1030,6 +1034,12 @@ MakeExceptionSafetyTester() {
   return {};
 }
 
+// Always return false, intended to be used as a checker with
+// TestExceptionSafety() to check that no exception is thrown.
+inline bool nothrow_guarantee(const void*) {
+  return ::testing::AssertionFailure() << "Violating NoThrowGuarantee";
+}
+
 }  // namespace testing
 
 #endif  // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/internal/thread_identity_benchmark.cc b/absl/base/internal/thread_identity_benchmark.cc
new file mode 100644
index 000000000000..3ae57317eabe
--- /dev/null
+++ b/absl/base/internal/thread_identity_benchmark.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/thread_identity.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
+#include "absl/synchronization/internal/per_thread_sem.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+void BM_SafeCurrentThreadIdentity(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(
+        absl::synchronization_internal::GetOrCreateCurrentThreadIdentity());
+  }
+}
+BENCHMARK(BM_SafeCurrentThreadIdentity);
+
+void BM_UnsafeCurrentThreadIdentity(benchmark::State& state) {
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(
+        absl::base_internal::CurrentThreadIdentityIfPresent());
+  }
+}
+BENCHMARK(BM_UnsafeCurrentThreadIdentity);
+
+}  // namespace
+
+BENCHMARK_MAIN();