summaryrefslogtreecommitdiff
path: root/absl/base
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2018-06-08 08:14:48 -0700
committerGravatar Gennadiy Civil <misterg@google.com>2018-06-08 11:24:58 -0400
commit92020a042c0cd46979db9f6f0cb32783dc07765e (patch)
tree5f4ebb5a8b0a8c4f0e5e2a061e63665b2071d92f /absl/base
parent7aacab8ae05d7115049923da9cfbf584dc1f8338 (diff)
- abacaab4b11a69dd4db627bd183571d7cabb8def Refinement to previous time.h edit (in this same github p... by Greg Falcon <gfalcon@google.com>
- 64db19b773134c6c8004e3b23c9ca892efbf8bae Move SpinLock's adaptive spin count computation from a st... by Derek Mauro <dmauro@google.com> - 6f9533fb44a52485a7c2bbb9b4efc7bf8d6c359a Import of CCTZ from GitHub. by Abseil Team <absl-team@google.com> - a211d7255c986e8dd4ceada362c0d054a6a1969a Cleanup exception flags by Abseil Team <absl-team@google.com> - babdb29c590126fe9bba5229fe91034b5b5c358a Release time benchmarks. by Alex Strelnikov <strel@google.com> - 5803b32a3ff123d1fb57a0c471d199c818357c9f Release memutil microbenchmarks. by Alex Strelnikov <strel@google.com> - 5357d4890d30e80c53beb05af32500fb20e9402b Add parens around expansion of ABSL_PREDICT_{FALSE,TRUE} ... by Abseil Team <absl-team@google.com> - 32023f61a239a5f6b1c59e577bfe81b179bbcd2d Reformat build rule tag. by Alex Strelnikov <strel@google.com> - 833758ecf2b0cf7a42bbd50b5b127e416425c168 Release uint128 microbenchmarks. by Alex Strelnikov <strel@google.com> - c115a9bca1f944b90fdc78a56b2de176466b124f Disambiguate bitwise-not of size_type by Abseil Team <absl-team@google.com> - f6905f5b5f6e425792de646edafde440548d9346 Updates ConstructorTracker and TrackedObjects with 1) a m... by Abseil Team <absl-team@google.com> - 147c553bdd5d2db20a38f75c4d1ef973d6c709c5 Changes the absl::Duration factory functions to disallow ... by Greg Miller <jgm@google.com> - dba2b96d11b5264546b283ba452f2de1303b0f07 White space fix by Alex Strelnikov <strel@google.com> GitOrigin-RevId: abacaab4b11a69dd4db627bd183571d7cabb8def Change-Id: I6fa34f20d0b2f898e7b5475a603111413bb80a67
Diffstat (limited to 'absl/base')
-rw-r--r--absl/base/exception_safety_testing_test.cc71
-rw-r--r--absl/base/internal/exception_safety_testing.cc31
-rw-r--r--absl/base/internal/exception_safety_testing.h182
-rw-r--r--absl/base/internal/spinlock.cc30
-rw-r--r--absl/base/optimization.h4
5 files changed, 224 insertions, 94 deletions
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index a8b82b73..97c8d6f8 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -168,8 +168,58 @@ TEST(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
TEST(ThrowingValueTest, ThrowingStreamOps) {
ThrowingValue<> bomb;
- TestOp([&]() { std::cin >> bomb; });
- TestOp([&]() { std::cout << bomb; });
+ TestOp([&]() {
+ std::istringstream stream;
+ stream >> bomb;
+ });
+ TestOp([&]() {
+ std::stringstream stream;
+ stream << bomb;
+ });
+}
+
+// Tests the operator<< of ThrowingValue by forcing ConstructorTracker to emit
+// a nonfatal failure that contains the std::string representation of the Thrower
+TEST(ThrowingValueTest, StreamOpsOutput) {
+ using ::testing::TypeSpec;
+ exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
+
+ // Test default spec list (kEverythingThrows)
+ EXPECT_NONFATAL_FAILURE(
+ {
+ using Thrower = ThrowingValue<TypeSpec{}>;
+ auto thrower = Thrower(123);
+ thrower.~Thrower();
+ },
+ "ThrowingValue<>(123)");
+
+ // Test with one item in spec list (kNoThrowCopy)
+ EXPECT_NONFATAL_FAILURE(
+ {
+ using Thrower = ThrowingValue<TypeSpec::kNoThrowCopy>;
+ auto thrower = Thrower(234);
+ thrower.~Thrower();
+ },
+ "ThrowingValue<kNoThrowCopy>(234)");
+
+ // Test with multiple items in spec list (kNoThrowMove, kNoThrowNew)
+ EXPECT_NONFATAL_FAILURE(
+ {
+ using Thrower =
+ ThrowingValue<TypeSpec::kNoThrowMove | TypeSpec::kNoThrowNew>;
+ auto thrower = Thrower(345);
+ thrower.~Thrower();
+ },
+ "ThrowingValue<kNoThrowMove | kNoThrowNew>(345)");
+
+ // Test with all items in spec list (kNoThrowCopy, kNoThrowMove, kNoThrowNew)
+ EXPECT_NONFATAL_FAILURE(
+ {
+ using Thrower = ThrowingValue<static_cast<TypeSpec>(-1)>;
+ auto thrower = Thrower(456);
+ thrower.~Thrower();
+ },
+ "ThrowingValue<kNoThrowCopy | kNoThrowMove | kNoThrowNew>(456)");
}
template <typename F>
@@ -653,20 +703,20 @@ struct BasicGuaranteeWithExtraInvariants : public NonNegative {
};
constexpr int BasicGuaranteeWithExtraInvariants::kExceptionSentinel;
-TEST(ExceptionCheckTest, BasicGuaranteeWithInvariants) {
+TEST(ExceptionCheckTest, BasicGuaranteeWithExtraInvariants) {
auto tester_with_val =
tester.WithInitialValue(BasicGuaranteeWithExtraInvariants{});
EXPECT_TRUE(tester_with_val.Test());
EXPECT_TRUE(
tester_with_val
- .WithInvariants([](BasicGuaranteeWithExtraInvariants* w) {
- if (w->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) {
+ .WithInvariants([](BasicGuaranteeWithExtraInvariants* o) {
+ if (o->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) {
return testing::AssertionSuccess();
}
return testing::AssertionFailure()
<< "i should be "
<< BasicGuaranteeWithExtraInvariants::kExceptionSentinel
- << ", but is " << w->i;
+ << ", but is " << o->i;
})
.Test());
}
@@ -846,29 +896,28 @@ TEST(ConstructorTrackerTest, NotDestroyedAfter) {
new (&storage) Tracked;
},
"not destroyed");
-
- // Manual destruction of the Tracked instance is not required because
- // ~ConstructorTracker() handles that automatically when a leak is found
}
TEST(ConstructorTrackerTest, DestroyedTwice) {
+ exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
EXPECT_NONFATAL_FAILURE(
{
Tracked t;
t.~Tracked();
},
- "destroyed improperly");
+ "re-destroyed");
}
TEST(ConstructorTrackerTest, ConstructedTwice) {
+ exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
EXPECT_NONFATAL_FAILURE(
{
new (&storage) Tracked;
new (&storage) Tracked;
+ reinterpret_cast<Tracked*>(&storage)->~Tracked();
},
"re-constructed");
- reinterpret_cast<Tracked*>(&storage)->~Tracked();
}
TEST(ThrowingValueTraitsTest, RelationalOperators) {
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
index d3e94074..f1d081f7 100644
--- a/absl/base/internal/exception_safety_testing.cc
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -21,16 +21,14 @@ namespace testing {
exceptions_internal::NoThrowTag nothrow_ctor;
-bool nothrow_guarantee(const void*) {
- return ::testing::AssertionFailure()
- << "Exception thrown violating NoThrow Guarantee";
-}
exceptions_internal::StrongGuaranteeTagType strong_guarantee;
namespace exceptions_internal {
int countdown = -1;
+ConstructorTracker* ConstructorTracker::current_tracker_instance_ = nullptr;
+
void MaybeThrow(absl::string_view msg, bool throw_bad_alloc) {
if (countdown-- == 0) {
if (throw_bad_alloc) throw TestBadAllocException(msg);
@@ -43,6 +41,31 @@ testing::AssertionResult FailureMessage(const TestException& e,
return testing::AssertionFailure() << "Exception thrown from " << e.what();
}
+std::string GetSpecString(TypeSpec spec) {
+ std::string out;
+ absl::string_view sep;
+ const auto append = [&](absl::string_view s) {
+ absl::StrAppend(&out, sep, s);
+ sep = " | ";
+ };
+ if (static_cast<bool>(TypeSpec::kNoThrowCopy & spec)) {
+ append("kNoThrowCopy");
+ }
+ if (static_cast<bool>(TypeSpec::kNoThrowMove & spec)) {
+ append("kNoThrowMove");
+ }
+ if (static_cast<bool>(TypeSpec::kNoThrowNew & spec)) {
+ append("kNoThrowNew");
+ }
+ return out;
+}
+
+std::string GetSpecString(AllocSpec spec) {
+ return static_cast<bool>(AllocSpec::kNoThrowAllocate & spec)
+ ? "kNoThrowAllocate"
+ : "";
+}
+
} // namespace exceptions_internal
} // namespace testing
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index c3ff34c5..8c2f5093 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -62,6 +62,9 @@ constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) {
namespace exceptions_internal {
+std::string GetSpecString(TypeSpec);
+std::string GetSpecString(AllocSpec);
+
struct NoThrowTag {};
struct StrongGuaranteeTagType {};
@@ -101,70 +104,96 @@ void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
testing::AssertionResult FailureMessage(const TestException& e,
int countdown) noexcept;
-class ConstructorTracker;
+struct TrackedAddress {
+ bool is_alive;
+ std::string description;
+};
-class TrackedObject {
+// Inspects the constructions and destructions of anything inheriting from
+// TrackedObject. This allows us to safely "leak" TrackedObjects, as
+// ConstructorTracker will destroy everything left over in its destructor.
+class ConstructorTracker {
public:
- TrackedObject(const TrackedObject&) = delete;
- TrackedObject(TrackedObject&&) = delete;
+ explicit ConstructorTracker(int count) : countdown_(count) {
+ assert(current_tracker_instance_ == nullptr);
+ current_tracker_instance_ = this;
+ }
- protected:
- explicit TrackedObject(const char* child_ctor) {
- if (!GetInstanceMap().emplace(this, child_ctor).second) {
- ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
- << " re-constructed in ctor " << child_ctor;
+ ~ConstructorTracker() {
+ assert(current_tracker_instance_ == this);
+ current_tracker_instance_ = nullptr;
+
+ for (auto& it : address_map_) {
+ void* address = it.first;
+ TrackedAddress& tracked_address = it.second;
+ if (tracked_address.is_alive) {
+ ADD_FAILURE() << "Object at address " << address
+ << " with countdown of " << countdown_
+ << " was not destroyed [" << tracked_address.description
+ << "]";
+ }
}
}
- ~TrackedObject() noexcept {
- if (GetInstanceMap().erase(this) == 0) {
- ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
- << " destroyed improperly";
+ static void ObjectConstructed(void* address, std::string description) {
+ if (!CurrentlyTracking()) return;
+
+ TrackedAddress& tracked_address =
+ current_tracker_instance_->address_map_[address];
+ if (tracked_address.is_alive) {
+ ADD_FAILURE() << "Object at address " << address << " with countdown of "
+ << current_tracker_instance_->countdown_
+ << " was re-constructed. Previously: ["
+ << tracked_address.description << "] Now: [" << description
+ << "]";
}
+ tracked_address = {true, std::move(description)};
+ }
+
+ static void ObjectDestructed(void* address) {
+ if (!CurrentlyTracking()) return;
+
+ auto it = current_tracker_instance_->address_map_.find(address);
+ // Not tracked. Ignore.
+ if (it == current_tracker_instance_->address_map_.end()) return;
+
+ TrackedAddress& tracked_address = it->second;
+ if (!tracked_address.is_alive) {
+ ADD_FAILURE() << "Object at address " << address << " with countdown of "
+ << current_tracker_instance_->countdown_
+ << " was re-destroyed or created prior to construction "
+ << "tracking [" << tracked_address.description << "]";
+ }
+ tracked_address.is_alive = false;
}
private:
- using InstanceMap = std::unordered_map<TrackedObject*, absl::string_view>;
- static InstanceMap& GetInstanceMap() {
- static auto* instance_map = new InstanceMap();
- return *instance_map;
+ static bool CurrentlyTracking() {
+ return current_tracker_instance_ != nullptr;
}
- friend class ConstructorTracker;
+ std::unordered_map<void*, TrackedAddress> address_map_;
+ int countdown_;
+
+ static ConstructorTracker* current_tracker_instance_;
};
-// Inspects the constructions and destructions of anything inheriting from
-// TrackedObject. This allows us to safely "leak" TrackedObjects, as
-// ConstructorTracker will destroy everything left over in its destructor.
-class ConstructorTracker {
+class TrackedObject {
public:
- explicit ConstructorTracker(int c)
- : init_count_(c), init_instances_(TrackedObject::GetInstanceMap()) {}
- ~ConstructorTracker() {
- auto& cur_instances = TrackedObject::GetInstanceMap();
- for (auto it = cur_instances.begin(); it != cur_instances.end();) {
- if (init_instances_.count(it->first) == 0) {
- ADD_FAILURE() << "Object at address " << static_cast<void*>(it->first)
- << " constructed from " << it->second
- << " where the exception countdown was set to "
- << init_count_ << " was not destroyed";
- // Erasing an item inside an unordered_map invalidates the existing
- // iterator. A new one is returned for iteration to continue.
- it = cur_instances.erase(it);
- } else {
- ++it;
- }
- }
+ TrackedObject(const TrackedObject&) = delete;
+ TrackedObject(TrackedObject&&) = delete;
+
+ protected:
+ explicit TrackedObject(std::string description) {
+ ConstructorTracker::ObjectConstructed(this, std::move(description));
}
- private:
- int init_count_;
- TrackedObject::InstanceMap init_instances_;
+ ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
};
template <typename Factory, typename Operation, typename Invariant>
absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
- const Factory& factory, Operation operation, int count,
+ const Factory& factory, const Operation& operation, int count,
const Invariant& invariant) {
auto t_ptr = factory();
absl::optional<testing::AssertionResult> current_res;
@@ -229,7 +258,6 @@ inline absl::optional<testing::AssertionResult> TestAllInvariantsAtCountdown(
extern exceptions_internal::NoThrowTag nothrow_ctor;
-bool nothrow_guarantee(const void*);
extern exceptions_internal::StrongGuaranteeTagType strong_guarantee;
// A test class which is convertible to bool. The conversion can be
@@ -283,17 +311,18 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
return static_cast<bool>(Spec & spec);
}
+ static constexpr int kDefaultValue = 0;
static constexpr int kBadValue = 938550620;
public:
- ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ ThrowingValue() : TrackedObject(GetInstanceString(kDefaultValue)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
- dummy_ = 0;
+ dummy_ = kDefaultValue;
}
ThrowingValue(const ThrowingValue& other) noexcept(
IsSpecified(TypeSpec::kNoThrowCopy))
- : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ : TrackedObject(GetInstanceString(other.dummy_)) {
if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
@@ -302,20 +331,20 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
ThrowingValue(ThrowingValue&& other) noexcept(
IsSpecified(TypeSpec::kNoThrowMove))
- : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ : TrackedObject(GetInstanceString(other.dummy_)) {
if (!IsSpecified(TypeSpec::kNoThrowMove)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
dummy_ = other.dummy_;
}
- explicit ThrowingValue(int i) : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ explicit ThrowingValue(int i) : TrackedObject(GetInstanceString(i)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
dummy_ = i;
}
ThrowingValue(int i, exceptions_internal::NoThrowTag) noexcept
- : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(i) {}
+ : TrackedObject(GetInstanceString(i)), dummy_(i) {}
// absl expects nothrow destructors
~ThrowingValue() noexcept = default;
@@ -548,9 +577,9 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
void operator&() const = delete; // NOLINT(runtime/operator)
// Stream operators
- friend std::ostream& operator<<(std::ostream& os, const ThrowingValue&) {
+ friend std::ostream& operator<<(std::ostream& os, const ThrowingValue& tv) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
- return os;
+ return os << GetInstanceString(tv.dummy_);
}
friend std::istream& operator>>(std::istream& is, const ThrowingValue&) {
@@ -606,6 +635,12 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
const int& Get() const noexcept { return dummy_; }
private:
+ static std::string GetInstanceString(int dummy) {
+ return absl::StrCat("ThrowingValue<",
+ exceptions_internal::GetSpecString(Spec), ">(", dummy,
+ ")");
+ }
+
int dummy_;
};
// While not having to do with exceptions, explicitly delete comma operator, to
@@ -658,26 +693,30 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
using propagate_on_container_swap = std::true_type;
using is_always_equal = std::false_type;
- ThrowingAllocator() : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ ThrowingAllocator() : TrackedObject(GetInstanceString(next_id_)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
dummy_ = std::make_shared<const int>(next_id_++);
}
template <typename U>
ThrowingAllocator(const ThrowingAllocator<U, Spec>& other) noexcept // NOLINT
- : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {}
+ : TrackedObject(GetInstanceString(*other.State())),
+ dummy_(other.State()) {}
// According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of
// allocator shall not exit via an exception, thus they are marked noexcept.
ThrowingAllocator(const ThrowingAllocator& other) noexcept
- : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {}
+ : TrackedObject(GetInstanceString(*other.State())),
+ dummy_(other.State()) {}
template <typename U>
ThrowingAllocator(ThrowingAllocator<U, Spec>&& other) noexcept // NOLINT
- : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {}
+ : TrackedObject(GetInstanceString(*other.State())),
+ dummy_(std::move(other.State())) {}
ThrowingAllocator(ThrowingAllocator&& other) noexcept
- : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {}
+ : TrackedObject(GetInstanceString(*other.State())),
+ dummy_(std::move(other.State())) {}
~ThrowingAllocator() noexcept = default;
@@ -758,6 +797,12 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject {
friend class ThrowingAllocator;
private:
+ static std::string GetInstanceString(int dummy) {
+ return absl::StrCat("ThrowingAllocator<",
+ exceptions_internal::GetSpecString(Spec), ">(", dummy,
+ ")");
+ }
+
const std::shared_ptr<const int>& State() const { return dummy_; }
std::shared_ptr<const int>& State() { return dummy_; }
@@ -801,6 +846,29 @@ void TestThrowingCtor(Args&&... args) {
}
}
+// Tests the nothrow guarantee of the provided nullary operation. If the an
+// exception is thrown, the result will be AssertionFailure(). Otherwise, it
+// will be AssertionSuccess().
+template <typename Operation>
+testing::AssertionResult TestNothrowOp(const Operation& operation) {
+ struct Cleanup {
+ Cleanup() { exceptions_internal::SetCountdown(); }
+ ~Cleanup() { exceptions_internal::UnsetCountdown(); }
+ } c;
+ try {
+ operation();
+ return testing::AssertionSuccess();
+ } catch (exceptions_internal::TestException) {
+ return testing::AssertionFailure()
+ << "TestException thrown during call to operation() when nothrow "
+ "guarantee was expected.";
+ } catch (...) {
+ return testing::AssertionFailure()
+ << "Unknown exception thrown during call to operation() when "
+ "nothrow guarantee was expected.";
+ }
+}
+
namespace exceptions_internal {
// Dummy struct for ExceptionSafetyTester<> partial state.
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc
index 28a2059f..1b97efbc 100644
--- a/absl/base/internal/spinlock.cc
+++ b/absl/base/internal/spinlock.cc
@@ -18,10 +18,12 @@
#include <atomic>
#include <limits>
+#include "absl/base/attributes.h"
#include "absl/base/internal/atomic_hook.h"
#include "absl/base/internal/cycleclock.h"
#include "absl/base/internal/spinlock_wait.h"
#include "absl/base/internal/sysinfo.h" /* For NumCPUs() */
+#include "absl/base/call_once.h"
// Description of lock-word:
// 31..00: [............................3][2][1][0]
@@ -54,30 +56,10 @@
namespace absl {
namespace base_internal {
-static int adaptive_spin_count = 0;
-
-namespace {
-struct SpinLock_InitHelper {
- SpinLock_InitHelper() {
- // On multi-cpu machines, spin for longer before yielding
- // the processor or sleeping. Reduces idle time significantly.
- if (base_internal::NumCPUs() > 1) {
- adaptive_spin_count = 1000;
- }
- }
-};
-
-// Hook into global constructor execution:
-// We do not do adaptive spinning before that,
-// but nothing lock-intensive should be going on at that time.
-static SpinLock_InitHelper init_helper;
-
ABSL_CONST_INIT static base_internal::AtomicHook<void (*)(const void *lock,
int64_t wait_cycles)>
submit_profile_data;
-} // namespace
-
void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock,
int64_t wait_cycles)) {
submit_profile_data.Store(fn);
@@ -120,6 +102,14 @@ void SpinLock::InitLinkerInitializedAndCooperative() {
// from the lock is returned from the method.
uint32_t SpinLock::SpinLoop(int64_t initial_wait_timestamp,
uint32_t *wait_cycles) {
+ // We are already in the slow path of SpinLock, initialize the
+ // adaptive_spin_count here.
+ ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count;
+ ABSL_CONST_INIT static int adaptive_spin_count = 0;
+ base_internal::LowLevelCallOnce(&init_adaptive_spin_count, []() {
+ adaptive_spin_count = base_internal::NumCPUs() > 1 ? 1000 : 1;
+ });
+
int c = adaptive_spin_count;
uint32_t lock_value;
do {
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index aaaffa49..2fddfc80 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -158,8 +158,8 @@
#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0))
#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
#else
-#define ABSL_PREDICT_FALSE(x) x
-#define ABSL_PREDICT_TRUE(x) x
+#define ABSL_PREDICT_FALSE(x) (x)
+#define ABSL_PREDICT_TRUE(x) (x)
#endif
#endif // ABSL_BASE_OPTIMIZATION_H_