summaryrefslogtreecommitdiff
path: root/absl/cleanup
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2021-08-10 08:52:44 -0700
committerGravatar Derek Mauro <dmauro@google.com>2021-08-10 12:30:11 -0400
commit8e088c5f3c290c5ac53dd5010fd501d80b483115 (patch)
tree7deaab3f9a34b0ea1b7b7f917a73de30f02a2614 /absl/cleanup
parentbf31a10b65d945665cecfb9d8807702ae4a7fde1 (diff)
Export of internal Abseil changes
-- 77cd6291781bc39e8472c706163d6951fe2ae573 by Derek Mauro <dmauro@google.com>: absl::uint128: Use intrinsics for more operations when available This change also inlines the division and modulus operators when intrinsics are available for better code generation. Fixes #987 PiperOrigin-RevId: 389895706 -- fa23339584599e07ebcb4d0a857e2553b017757c by Abseil Team <absl-team@google.com>: only hide retired flags if human-readable output is requested PiperOrigin-RevId: 389835452 -- f1111f2b88359d4b253d4d81681c8a488458a36e by Martijn Vels <mvels@google.com>: Add helpers IsFlat(), IsExternal(), etc to improve readability PiperOrigin-RevId: 389779333 -- 785b8712261e41695ebeeb64b4317f93b37adc11 by Martijn Vels <mvels@google.com>: Split off 'concat' and 'btree' RepMemoryUsageLeaf and RepMemoryUsageDataEdge PiperOrigin-RevId: 389701120 -- 5264bffebffc2b377bf7e18f0ce69a3ed38c6629 by CJ Johnson <johnsoncj@google.com>: Eagerly destroy `Callback` in `absl::Cleanup` PiperOrigin-RevId: 389678813 -- a05312f0668458e97c50ca932c8f974c1508ebf2 by Abseil Team <absl-team@google.com>: Have one instance of empty_group per program, rather than one per translation unit. https://stackoverflow.com/questions/185624/static-variables-in-an-inlined-function PiperOrigin-RevId: 389185845 GitOrigin-RevId: 77cd6291781bc39e8472c706163d6951fe2ae573 Change-Id: Iac8d9cb27707a9562c831c77a552d1fb4bb0405f
Diffstat (limited to 'absl/cleanup')
-rw-r--r--absl/cleanup/cleanup.h8
-rw-r--r--absl/cleanup/cleanup_test.cc44
-rw-r--r--absl/cleanup/internal/cleanup.h39
3 files changed, 77 insertions, 14 deletions
diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h
index 61b53d55..960ccd08 100644
--- a/absl/cleanup/cleanup.h
+++ b/absl/cleanup/cleanup.h
@@ -86,25 +86,25 @@ class ABSL_MUST_USE_RESULT Cleanup final {
"Callbacks that return values are not supported.");
public:
- Cleanup(Callback callback) // NOLINT
- : storage_(std::move(callback), /* is_callback_engaged = */ true) {}
+ Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT
Cleanup(Cleanup&& other) = default;
void Cancel() && {
ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
- storage_.DisengageCallback();
+ storage_.DestroyCallback();
}
void Invoke() && {
ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
- storage_.DisengageCallback();
storage_.InvokeCallback();
+ storage_.DestroyCallback();
}
~Cleanup() {
if (storage_.IsCallbackEngaged()) {
storage_.InvokeCallback();
+ storage_.DestroyCallback();
}
}
diff --git a/absl/cleanup/cleanup_test.cc b/absl/cleanup/cleanup_test.cc
index 792595d6..46b88589 100644
--- a/absl/cleanup/cleanup_test.cc
+++ b/absl/cleanup/cleanup_test.cc
@@ -264,4 +264,48 @@ TYPED_TEST(CleanupTest, Move) {
EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
}
+int DestructionCount = 0;
+
+struct DestructionCounter {
+ void operator()() {}
+
+ ~DestructionCounter() { ++DestructionCount; }
+};
+
+TYPED_TEST(CleanupTest, DestructorDestroys) {
+ {
+ auto cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
+ DestructionCount = 0;
+ }
+
+ EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys
+}
+
+TYPED_TEST(CleanupTest, CancelDestroys) {
+ {
+ auto cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
+ DestructionCount = 0;
+
+ std::move(cleanup).Cancel();
+ EXPECT_EQ(DestructionCount, 1); // Cancel destroys
+ }
+
+ EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy
+}
+
+TYPED_TEST(CleanupTest, InvokeDestroys) {
+ {
+ auto cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
+ DestructionCount = 0;
+
+ std::move(cleanup).Invoke();
+ EXPECT_EQ(DestructionCount, 1); // Invoke destroys
+ }
+
+ EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy
+}
+
} // namespace
diff --git a/absl/cleanup/internal/cleanup.h b/absl/cleanup/internal/cleanup.h
index b4c40737..2783fcb7 100644
--- a/absl/cleanup/internal/cleanup.h
+++ b/absl/cleanup/internal/cleanup.h
@@ -15,10 +15,12 @@
#ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_
#define ABSL_CLEANUP_INTERNAL_CLEANUP_H_
+#include <new>
#include <type_traits>
#include <utility>
#include "absl/base/internal/invoke.h"
+#include "absl/base/macros.h"
#include "absl/base/thread_annotations.h"
#include "absl/utility/utility.h"
@@ -45,14 +47,22 @@ class Storage {
public:
Storage() = delete;
- Storage(Callback callback, bool is_callback_engaged)
- : callback_(std::move(callback)),
- is_callback_engaged_(is_callback_engaged) {}
+ explicit Storage(Callback callback) {
+ // Placement-new into a character buffer is used for eager destruction when
+ // the cleanup is invoked or cancelled. To ensure this optimizes well, the
+ // behavior is implemented locally instead of using an absl::optional.
+ ::new (GetCallbackBuffer()) Callback(std::move(callback));
+ is_callback_engaged_ = true;
+ }
+
+ Storage(Storage&& other) {
+ ABSL_HARDENING_ASSERT(other.IsCallbackEngaged());
- Storage(Storage&& other)
- : callback_(std::move(other.callback_)),
- is_callback_engaged_(
- absl::exchange(other.is_callback_engaged_, false)) {}
+ ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback()));
+ is_callback_engaged_ = true;
+
+ other.DestroyCallback();
+ }
Storage(const Storage& other) = delete;
@@ -60,17 +70,26 @@ class Storage {
Storage& operator=(const Storage& other) = delete;
+ void* GetCallbackBuffer() { return static_cast<void*>(+callback_buffer_); }
+
+ Callback& GetCallback() {
+ return *reinterpret_cast<Callback*>(GetCallbackBuffer());
+ }
+
bool IsCallbackEngaged() const { return is_callback_engaged_; }
- void DisengageCallback() { is_callback_engaged_ = false; }
+ void DestroyCallback() {
+ is_callback_engaged_ = false;
+ GetCallback().~Callback();
+ }
void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS {
- std::move(callback_)();
+ std::move(GetCallback())();
}
private:
- Callback callback_;
bool is_callback_engaged_;
+ alignas(Callback) char callback_buffer_[sizeof(Callback)];
};
} // namespace cleanup_internal