- 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
This commit is contained in:
parent
7aacab8ae0
commit
92020a042c
22 changed files with 1808 additions and 282 deletions
|
@ -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
|
||||
|
|
|
@ -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,36 +104,9 @@ void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
|
|||
testing::AssertionResult FailureMessage(const TestException& e,
|
||||
int countdown) noexcept;
|
||||
|
||||
class ConstructorTracker;
|
||||
|
||||
class TrackedObject {
|
||||
public:
|
||||
TrackedObject(const TrackedObject&) = delete;
|
||||
TrackedObject(TrackedObject&&) = delete;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
~TrackedObject() noexcept {
|
||||
if (GetInstanceMap().erase(this) == 0) {
|
||||
ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
|
||||
<< " destroyed improperly";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using InstanceMap = std::unordered_map<TrackedObject*, absl::string_view>;
|
||||
static InstanceMap& GetInstanceMap() {
|
||||
static auto* instance_map = new InstanceMap();
|
||||
return *instance_map;
|
||||
}
|
||||
|
||||
friend class ConstructorTracker;
|
||||
struct TrackedAddress {
|
||||
bool is_alive;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
// Inspects the constructions and destructions of anything inheriting from
|
||||
|
@ -138,33 +114,86 @@ class TrackedObject {
|
|||
// ConstructorTracker will destroy everything left over in its destructor.
|
||||
class ConstructorTracker {
|
||||
public:
|
||||
explicit ConstructorTracker(int c)
|
||||
: init_count_(c), init_instances_(TrackedObject::GetInstanceMap()) {}
|
||||
explicit ConstructorTracker(int count) : countdown_(count) {
|
||||
assert(current_tracker_instance_ == nullptr);
|
||||
current_tracker_instance_ = this;
|
||||
}
|
||||
|
||||
~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;
|
||||
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
|
||||
<< "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
int init_count_;
|
||||
TrackedObject::InstanceMap init_instances_;
|
||||
static bool CurrentlyTracking() {
|
||||
return current_tracker_instance_ != nullptr;
|
||||
}
|
||||
|
||||
std::unordered_map<void*, TrackedAddress> address_map_;
|
||||
int countdown_;
|
||||
|
||||
static ConstructorTracker* current_tracker_instance_;
|
||||
};
|
||||
|
||||
class TrackedObject {
|
||||
public:
|
||||
TrackedObject(const TrackedObject&) = delete;
|
||||
TrackedObject(TrackedObject&&) = delete;
|
||||
|
||||
protected:
|
||||
explicit TrackedObject(std::string description) {
|
||||
ConstructorTracker::ObjectConstructed(this, std::move(description));
|
||||
}
|
||||
|
||||
~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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue