summaryrefslogtreecommitdiff
path: root/absl/base/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base/internal')
-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
4 files changed, 160 insertions, 25 deletions
diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h
index 47d40139..b458511b 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 00000000..cf740757
--- /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 32450465..bec3ab30 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 00000000..3ae57317
--- /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();