Skip to content

Commit

Permalink
Move Cleanup to folly/futures
Browse files Browse the repository at this point in the history
Summary:
The following targets were moved to folly/futures:
```
//folly/experimental:cleanup
//folly/experimental/test:cleanup_test
```

`arc f` was applied

This is a codemod. It was automatically generated and will be landed once it is approved and tests are passing in sandcastle.
You have been added as a reviewer by Sentinel or Butterfly.

p:ad_meclen.folly

Reviewed By: ot

Differential Revision: D58024166

fbshipit-source-id: 65701585d243ec55be1799f8b1e2c490a6bfab65
  • Loading branch information
Gownta authored and facebook-github-bot committed Jun 5, 2024
1 parent c10a7c1 commit ade5b95
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 178 deletions.
9 changes: 4 additions & 5 deletions folly/experimental/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,11 @@ cpp_library(

cpp_library(
name = "cleanup",
headers = ["Cleanup.h"],
exported_deps = [
"//folly/futures:core",
headers = [
"Cleanup.h",
],
exported_external_deps = [
"glog",
exported_deps = [
"//folly/futures:cleanup",
],
)

Expand Down
162 changes: 1 addition & 161 deletions folly/experimental/Cleanup.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,164 +14,4 @@
* limitations under the License.
*/

#pragma once

#include <memory>
#include <mutex>

#include <glog/logging.h>

#include <folly/futures/Future.h>

namespace folly {

// Structured Async Cleanup
//

// Structured Async Cleanup - traits
//

namespace detail {
struct cleanup_fn {
template <
class T,
class R = decltype(std::declval<T>().cleanup()),
std::enable_if_t<std::is_same_v<R, folly::SemiFuture<folly::Unit>>, int> =
0>
R operator()(T&& t) const {
return ((T&&)t).cleanup();
}
};
} // namespace detail

template <class T>
constexpr bool is_cleanup_v = folly::is_invocable_v<detail::cleanup_fn, T>;

template <typename T>
using is_cleanup = std::bool_constant<is_cleanup_v<T>>;

// Structured Async Cleanup
//
// This helps compose a task with async cleanup
// The task result is stored until cleanup completes and is then produced
// The cleanup task is not allowed to fail.
//
// This can be used with collectAll to combine multiple async resources
//
// ensureCleanupAfterTask(collectAll(a.run(), b.run()), collectAll(a.cleanup(),
// b.cleanup())).wait();
//
template <typename T>
folly::SemiFuture<T> ensureCleanupAfterTask(
folly::SemiFuture<T> task, folly::SemiFuture<folly::Unit> cleanup) {
return folly::makeSemiFuture()
.deferValue([task_ = std::move(task)](folly::Unit) mutable {
return std::move(task_);
})
.defer([cleanup_ = std::move(cleanup)](folly::Try<T> taskResult) mutable {
return std::move(cleanup_).defer(
[taskResult_ = std::move(taskResult)](folly::Try<folly::Unit> t) {
if (t.hasException()) {
terminate_with<std::logic_error>("cleanup must not throw");
}
return std::move(taskResult_).value();
});
});
}

// Structured Async Cleanup
//
// This implementation is a base class that collects a set of cleanup tasks
// and runs them in reverse order.
//
// A class derived from Cleanup
// - only allows cleanup to be run once
// - is required to complete cleanup before running the destructor
// - *should not* run cleanup tasks. Running the cleanup task should be
// delegated to the owner of the derived class
// - *should not* be owned by a shared_ptr. Cleanup is intended to remove
// shared ownership.
//
class Cleanup {
public:
Cleanup() : safe_to_destruct_(false), cleanup_(folly::makeSemiFuture()) {}
~Cleanup() {
if (!safe_to_destruct_) {
LOG(FATAL) << "Cleanup must complete before it is destructed.";
}
}

// Returns: a SemiFuture that, just like destructors, sequences the cleanup
// tasks added in reverse of the order they were added.
//
// calls to cleanup() do not mutate state. The returned SemiFuture, once
// it has been given an executor, does mutate state and must not overlap with
// any calls to addCleanup().
//
folly::SemiFuture<folly::Unit> cleanup() {
return folly::makeSemiFuture()
.deferValue([this](folly::Unit) {
if (!cleanup_.valid()) {
LOG(FATAL) << "cleanup already run - cleanup task invalid.";
}
return std::move(cleanup_);
})
.defer([this](folly::Try<folly::Unit> t) {
if (t.hasException()) {
LOG(FATAL) << "Cleanup actions must be noexcept.";
}
this->safe_to_destruct_ = true;
});
}

protected:
// includes the provided SemiFuture under the scope of this.
//
// when the cleanup() for this started it will get this SemiFuture first.
//
// order matters, just like destructors, cleanup tasks will be run in reverse
// of the order they were added.
//
// all gets will use the Executor provided to the SemiFuture returned by
// cleanup()
//
// calls to addCleanup() must not overlap with each other and must not overlap
// with a running SemiFuture returned from addCleanup().
//
void addCleanup(folly::SemiFuture<folly::Unit> c) {
if (!cleanup_.valid()) {
LOG(FATAL)
<< "Cleanup::addCleanup must not be called after Cleanup::cleanup.";
}
cleanup_ = std::move(c).deferValue(
[nested = std::move(cleanup_)](folly::Unit) mutable {
return std::move(nested);
});
}

// includes the provided model of Cleanup under the scope of this
//
// when the cleanup() for this started it will cleanup this first.
//
// order matters, just like destructors, cleanup tasks will be run in reverse
// of the order they were added.
//
// all gets will use the Executor provided to the SemiFuture returned by
// cleanup()
//
// calls to addCleanup() must not overlap with each other and must not overlap
// with a running SemiFuture returned from addCleanup().
//
template <
class OtherCleanup,
std::enable_if_t<is_cleanup_v<OtherCleanup>, int> = 0>
void addCleanup(OtherCleanup&& c) {
addCleanup(((OtherCleanup&&)c).cleanup());
}

private:
bool safe_to_destruct_;
folly::SemiFuture<folly::Unit> cleanup_;
};

} // namespace folly
#include <folly/futures/Cleanup.h>
12 changes: 0 additions & 12 deletions folly/experimental/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,6 @@ cpp_unittest(
],
)

cpp_unittest(
name = "cleanup_test",
srcs = ["CleanupTest.cpp"],
headers = [],
deps = [
"//folly/executors:cpu_thread_pool_executor",
"//folly/executors:manual_executor",
"//folly/experimental:cleanup",
"//folly/portability:gtest",
],
)

cpp_library(
name = "coding_test_utils",
srcs = ["CodingTestUtils.cpp"],
Expand Down
11 changes: 11 additions & 0 deletions folly/futures/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,14 @@ cpp_library(
"//folly/lang:exception",
],
)

cpp_library(
name = "cleanup",
headers = ["Cleanup.h"],
exported_deps = [
"//folly/futures:core",
],
exported_external_deps = [
"glog",
],
)
177 changes: 177 additions & 0 deletions folly/futures/Cleanup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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.
*/

#pragma once

#include <memory>
#include <mutex>

#include <glog/logging.h>

#include <folly/futures/Future.h>

namespace folly {

// Structured Async Cleanup
//

// Structured Async Cleanup - traits
//

namespace detail {
struct cleanup_fn {
template <
class T,
class R = decltype(std::declval<T>().cleanup()),
std::enable_if_t<std::is_same_v<R, folly::SemiFuture<folly::Unit>>, int> =
0>
R operator()(T&& t) const {
return ((T&&)t).cleanup();
}
};
} // namespace detail

template <class T>
constexpr bool is_cleanup_v = folly::is_invocable_v<detail::cleanup_fn, T>;

template <typename T>
using is_cleanup = std::bool_constant<is_cleanup_v<T>>;

// Structured Async Cleanup
//
// This helps compose a task with async cleanup
// The task result is stored until cleanup completes and is then produced
// The cleanup task is not allowed to fail.
//
// This can be used with collectAll to combine multiple async resources
//
// ensureCleanupAfterTask(collectAll(a.run(), b.run()), collectAll(a.cleanup(),
// b.cleanup())).wait();
//
template <typename T>
folly::SemiFuture<T> ensureCleanupAfterTask(
folly::SemiFuture<T> task, folly::SemiFuture<folly::Unit> cleanup) {
return folly::makeSemiFuture()
.deferValue([task_ = std::move(task)](folly::Unit) mutable {
return std::move(task_);
})
.defer([cleanup_ = std::move(cleanup)](folly::Try<T> taskResult) mutable {
return std::move(cleanup_).defer(
[taskResult_ = std::move(taskResult)](folly::Try<folly::Unit> t) {
if (t.hasException()) {
terminate_with<std::logic_error>("cleanup must not throw");
}
return std::move(taskResult_).value();
});
});
}

// Structured Async Cleanup
//
// This implementation is a base class that collects a set of cleanup tasks
// and runs them in reverse order.
//
// A class derived from Cleanup
// - only allows cleanup to be run once
// - is required to complete cleanup before running the destructor
// - *should not* run cleanup tasks. Running the cleanup task should be
// delegated to the owner of the derived class
// - *should not* be owned by a shared_ptr. Cleanup is intended to remove
// shared ownership.
//
class Cleanup {
public:
Cleanup() : safe_to_destruct_(false), cleanup_(folly::makeSemiFuture()) {}
~Cleanup() {
if (!safe_to_destruct_) {
LOG(FATAL) << "Cleanup must complete before it is destructed.";
}
}

// Returns: a SemiFuture that, just like destructors, sequences the cleanup
// tasks added in reverse of the order they were added.
//
// calls to cleanup() do not mutate state. The returned SemiFuture, once
// it has been given an executor, does mutate state and must not overlap with
// any calls to addCleanup().
//
folly::SemiFuture<folly::Unit> cleanup() {
return folly::makeSemiFuture()
.deferValue([this](folly::Unit) {
if (!cleanup_.valid()) {
LOG(FATAL) << "cleanup already run - cleanup task invalid.";
}
return std::move(cleanup_);
})
.defer([this](folly::Try<folly::Unit> t) {
if (t.hasException()) {
LOG(FATAL) << "Cleanup actions must be noexcept.";
}
this->safe_to_destruct_ = true;
});
}

protected:
// includes the provided SemiFuture under the scope of this.
//
// when the cleanup() for this started it will get this SemiFuture first.
//
// order matters, just like destructors, cleanup tasks will be run in reverse
// of the order they were added.
//
// all gets will use the Executor provided to the SemiFuture returned by
// cleanup()
//
// calls to addCleanup() must not overlap with each other and must not overlap
// with a running SemiFuture returned from addCleanup().
//
void addCleanup(folly::SemiFuture<folly::Unit> c) {
if (!cleanup_.valid()) {
LOG(FATAL)
<< "Cleanup::addCleanup must not be called after Cleanup::cleanup.";
}
cleanup_ = std::move(c).deferValue(
[nested = std::move(cleanup_)](folly::Unit) mutable {
return std::move(nested);
});
}

// includes the provided model of Cleanup under the scope of this
//
// when the cleanup() for this started it will cleanup this first.
//
// order matters, just like destructors, cleanup tasks will be run in reverse
// of the order they were added.
//
// all gets will use the Executor provided to the SemiFuture returned by
// cleanup()
//
// calls to addCleanup() must not overlap with each other and must not overlap
// with a running SemiFuture returned from addCleanup().
//
template <
class OtherCleanup,
std::enable_if_t<is_cleanup_v<OtherCleanup>, int> = 0>
void addCleanup(OtherCleanup&& c) {
addCleanup(((OtherCleanup&&)c).cleanup());
}

private:
bool safe_to_destruct_;
folly::SemiFuture<folly::Unit> cleanup_;
};

} // namespace folly
Loading

0 comments on commit ade5b95

Please sign in to comment.