Add utility class Defer (#6382)
Summary: Add a utility class `Defer` to defer the execution of a function until the Defer object goes out of scope. Used in VersionSet:: ProcessManifestWrites as an example. The inline comments for class `Defer` have more details. Pull Request resolved: https://github.com/facebook/rocksdb/pull/6382 Test Plan: `make defer_test version_set_test && ./defer_test && ./version_set_test` Differential Revision: D19797538 Pulled By: cheng-chang fbshipit-source-id: b1a9b7306e4fd4f48ec2ab55783caa561a315f0f
This commit is contained in:
parent
cbf5f3be43
commit
dafb568052
@ -1026,6 +1026,7 @@ if(WITH_TESTS)
|
|||||||
util/bloom_test.cc
|
util/bloom_test.cc
|
||||||
util/coding_test.cc
|
util/coding_test.cc
|
||||||
util/crc32c_test.cc
|
util/crc32c_test.cc
|
||||||
|
util/defer_test.cc
|
||||||
util/dynamic_bloom_test.cc
|
util/dynamic_bloom_test.cc
|
||||||
util/file_reader_writer_test.cc
|
util/file_reader_writer_test.cc
|
||||||
util/filelock_test.cc
|
util/filelock_test.cc
|
||||||
|
4
Makefile
4
Makefile
@ -596,6 +596,7 @@ TESTS = \
|
|||||||
db_secondary_test \
|
db_secondary_test \
|
||||||
block_cache_tracer_test \
|
block_cache_tracer_test \
|
||||||
block_cache_trace_analyzer_test \
|
block_cache_trace_analyzer_test \
|
||||||
|
defer_test \
|
||||||
|
|
||||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||||
TESTS += folly_synchronization_distributed_mutex_test
|
TESTS += folly_synchronization_distributed_mutex_test
|
||||||
@ -1714,6 +1715,9 @@ block_cache_tracer_test: trace_replay/block_cache_tracer_test.o trace_replay/blo
|
|||||||
block_cache_trace_analyzer_test: tools/block_cache_analyzer/block_cache_trace_analyzer_test.o tools/block_cache_analyzer/block_cache_trace_analyzer.o $(LIBOBJECTS) $(TESTHARNESS)
|
block_cache_trace_analyzer_test: tools/block_cache_analyzer/block_cache_trace_analyzer_test.o tools/block_cache_analyzer/block_cache_trace_analyzer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||||
$(AM_LINK)
|
$(AM_LINK)
|
||||||
|
|
||||||
|
defer_test: util/defer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||||
|
$(AM_LINK)
|
||||||
|
|
||||||
#-------------------------------------------------
|
#-------------------------------------------------
|
||||||
# make install related stuff
|
# make install related stuff
|
||||||
INSTALL_PATH ?= /usr/local
|
INSTALL_PATH ?= /usr/local
|
||||||
|
7
TARGETS
7
TARGETS
@ -904,6 +904,13 @@ ROCKS_TESTS = [
|
|||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"defer_test",
|
||||||
|
"util/defer_test.cc",
|
||||||
|
"serial",
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"delete_scheduler_test",
|
"delete_scheduler_test",
|
||||||
"file/delete_scheduler_test.cc",
|
"file/delete_scheduler_test.cc",
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include "table/two_level_iterator.h"
|
#include "table/two_level_iterator.h"
|
||||||
#include "test_util/sync_point.h"
|
#include "test_util/sync_point.h"
|
||||||
#include "util/coding.h"
|
#include "util/coding.h"
|
||||||
|
#include "util/defer.h"
|
||||||
#include "util/stop_watch.h"
|
#include "util/stop_watch.h"
|
||||||
#include "util/string_util.h"
|
#include "util/string_util.h"
|
||||||
#include "util/user_comparator_wrapper.h"
|
#include "util/user_comparator_wrapper.h"
|
||||||
@ -3604,6 +3605,14 @@ Status VersionSet::ProcessManifestWrites(
|
|||||||
autovector<Version*> versions;
|
autovector<Version*> versions;
|
||||||
autovector<const MutableCFOptions*> mutable_cf_options_ptrs;
|
autovector<const MutableCFOptions*> mutable_cf_options_ptrs;
|
||||||
std::vector<std::unique_ptr<BaseReferencedVersionBuilder>> builder_guards;
|
std::vector<std::unique_ptr<BaseReferencedVersionBuilder>> builder_guards;
|
||||||
|
Status s;
|
||||||
|
Defer defer([&s, &versions]() {
|
||||||
|
if (!s.ok()) {
|
||||||
|
for (auto v : versions) {
|
||||||
|
delete v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
|
if (first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
|
||||||
// No group commits for column family add or drop
|
// No group commits for column family add or drop
|
||||||
@ -3689,12 +3698,8 @@ Status VersionSet::ProcessManifestWrites(
|
|||||||
} else if (group_start != std::numeric_limits<size_t>::max()) {
|
} else if (group_start != std::numeric_limits<size_t>::max()) {
|
||||||
group_start = std::numeric_limits<size_t>::max();
|
group_start = std::numeric_limits<size_t>::max();
|
||||||
}
|
}
|
||||||
Status s = LogAndApplyHelper(last_writer->cfd, builder, e, mu);
|
s = LogAndApplyHelper(last_writer->cfd, builder, e, mu);
|
||||||
if (!s.ok()) {
|
if (!s.ok()) {
|
||||||
// free up the allocated memory
|
|
||||||
for (auto v : versions) {
|
|
||||||
delete v;
|
|
||||||
}
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
batch_edits.push_back(e);
|
batch_edits.push_back(e);
|
||||||
@ -3704,12 +3709,8 @@ Status VersionSet::ProcessManifestWrites(
|
|||||||
assert(!builder_guards.empty() &&
|
assert(!builder_guards.empty() &&
|
||||||
builder_guards.size() == versions.size());
|
builder_guards.size() == versions.size());
|
||||||
auto* builder = builder_guards[i]->version_builder();
|
auto* builder = builder_guards[i]->version_builder();
|
||||||
Status s = builder->SaveTo(versions[i]->storage_info());
|
s = builder->SaveTo(versions[i]->storage_info());
|
||||||
if (!s.ok()) {
|
if (!s.ok()) {
|
||||||
// free up the allocated memory
|
|
||||||
for (auto v : versions) {
|
|
||||||
delete v;
|
|
||||||
}
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3752,7 +3753,6 @@ Status VersionSet::ProcessManifestWrites(
|
|||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
|
|
||||||
uint64_t new_manifest_file_size = 0;
|
uint64_t new_manifest_file_size = 0;
|
||||||
Status s;
|
|
||||||
|
|
||||||
assert(pending_manifest_file_number_ == 0);
|
assert(pending_manifest_file_number_ == 0);
|
||||||
if (!descriptor_log_ ||
|
if (!descriptor_log_ ||
|
||||||
@ -3959,9 +3959,6 @@ Status VersionSet::ProcessManifestWrites(
|
|||||||
ROCKS_LOG_ERROR(db_options_->info_log,
|
ROCKS_LOG_ERROR(db_options_->info_log,
|
||||||
"Error in committing version edit to MANIFEST: %s",
|
"Error in committing version edit to MANIFEST: %s",
|
||||||
version_edits.c_str());
|
version_edits.c_str());
|
||||||
for (auto v : versions) {
|
|
||||||
delete v;
|
|
||||||
}
|
|
||||||
// If manifest append failed for whatever reason, the file could be
|
// If manifest append failed for whatever reason, the file could be
|
||||||
// corrupted. So we need to force the next version update to start a
|
// corrupted. So we need to force the next version update to start a
|
||||||
// new manifest file.
|
// new manifest file.
|
||||||
|
1
src.mk
1
src.mk
@ -419,6 +419,7 @@ MAIN_SOURCES = \
|
|||||||
util/bloom_test.cc \
|
util/bloom_test.cc \
|
||||||
util/coding_test.cc \
|
util/coding_test.cc \
|
||||||
util/crc32c_test.cc \
|
util/crc32c_test.cc \
|
||||||
|
util/defer_test.cc \
|
||||||
util/dynamic_bloom_test.cc \
|
util/dynamic_bloom_test.cc \
|
||||||
util/filelock_test.cc \
|
util/filelock_test.cc \
|
||||||
util/log_write_bench.cc \
|
util/log_write_bench.cc \
|
||||||
|
52
util/defer.h
Normal file
52
util/defer.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace rocksdb {
|
||||||
|
|
||||||
|
// Defers the execution of the provided function until the Defer
|
||||||
|
// object goes out of scope.
|
||||||
|
//
|
||||||
|
// Usage example:
|
||||||
|
//
|
||||||
|
// Status DeferTest() {
|
||||||
|
// Status s;
|
||||||
|
// Defer defer([&s]() {
|
||||||
|
// if (!s.ok()) {
|
||||||
|
// // do cleanups ...
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// // do something ...
|
||||||
|
// if (!s.ok()) return;
|
||||||
|
// // do some other things ...
|
||||||
|
// return s;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The above code ensures that cleanups will always happen on returning.
|
||||||
|
//
|
||||||
|
// Without the help of Defer, you can
|
||||||
|
// 1. every time when !s.ok(), do the cleanup;
|
||||||
|
// 2. instead of returning when !s.ok(), continue the work only when s.ok(),
|
||||||
|
// but sometimes, this might lead to nested blocks of "if (s.ok()) {...}".
|
||||||
|
//
|
||||||
|
// With the help of Defer, you can centralize the cleanup logic inside the
|
||||||
|
// lambda passed to Defer, and you can return immediately on failure when necessary.
|
||||||
|
class Defer final {
|
||||||
|
public:
|
||||||
|
Defer(std::function<void()>&& fn) : fn_(std::move(fn)) {}
|
||||||
|
~Defer() { fn_(); }
|
||||||
|
|
||||||
|
// Disallow copy.
|
||||||
|
Defer(const Defer&) = delete;
|
||||||
|
Defer& operator=(const Defer&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<void()> fn_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rocksdb
|
39
util/defer_test.cc
Normal file
39
util/defer_test.cc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
#include "port/port.h"
|
||||||
|
#include "port/stack_trace.h"
|
||||||
|
#include "test_util/testharness.h"
|
||||||
|
#include "util/defer.h"
|
||||||
|
|
||||||
|
namespace rocksdb {
|
||||||
|
|
||||||
|
class DeferTest {};
|
||||||
|
|
||||||
|
TEST(DeferTest, BlockScope) {
|
||||||
|
int v = 1;
|
||||||
|
{
|
||||||
|
Defer defer([&v]() { v *= 2; });
|
||||||
|
}
|
||||||
|
ASSERT_EQ(2, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DeferTest, FunctionScope) {
|
||||||
|
int v = 1;
|
||||||
|
auto f = [&v]() {
|
||||||
|
Defer defer([&v]() { v *= 2; });
|
||||||
|
v = 2;
|
||||||
|
};
|
||||||
|
f();
|
||||||
|
ASSERT_EQ(4, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rocksdb
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
rocksdb::port::InstallStackTraceHandler();
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user