summaryrefslogtreecommitdiff
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 f9aac5a5..bb7a5981 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 329a3d05..45640562 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 9bd8b9db..4507b946 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 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();