blob: d120c322971e635d199bd0700d011ff919cee726 [file] [log] [blame]
// Copyright 2021 The Android Open Source Project
//
// 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.
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <utility>
#include "Function.h"
#include "Nullable.h"
namespace android::base {
namespace fit {
// A move-only deferred action wrapper with RAII semantics.
// This class is not thread safe.
//
// The wrapper holds a function-like callable target with no arguments
// which it invokes when it goes out of scope unless canceled, called, or
// moved to a wrapper in a different scope.
//
// See |fit::defer()| for idiomatic usage.
template <typename T>
class DeferredAction final {
public:
// Creates a deferred action without a pending target.
DeferredAction() = default;
explicit DeferredAction(decltype(nullptr)) {}
// Creates a deferred action with a pending target.
explicit DeferredAction(T target) : mTarget(std::move(target)) {}
// Creates a deferred action with a pending target moved from another
// deferred action, leaving the other one without a pending target.
DeferredAction(DeferredAction&& other) : mTarget(std::move(other.mTarget)) {
other.mTarget.reset();
}
// Invokes and releases the deferred action's pending target (if any).
~DeferredAction() { call(); }
// Returns true if the deferred action has a pending target.
explicit operator bool() const { return !!mTarget; }
// Invokes and releases the deferred action's pending target (if any),
// then move-assigns it from another deferred action, leaving the latter
// one without a pending target.
DeferredAction& operator=(DeferredAction&& other) {
if (&other == this)
return *this;
call();
mTarget = std::move(other.mTarget);
other.mTarget.reset();
return *this;
}
// Invokes and releases the deferred action's pending target (if any).
void call() {
if (mTarget) {
// Move to a local to guard against re-entrance.
T local_target = std::move(*mTarget);
mTarget.reset();
local_target();
}
}
// Releases the deferred action's pending target (if any) without
// invoking it.
void cancel() { mTarget.reset(); }
DeferredAction& operator=(decltype(nullptr)) {
cancel();
return *this;
}
// Assigns a new target to the deferred action.
DeferredAction& operator=(T target) {
mTarget = std::move(target);
return *this;
}
DeferredAction(const DeferredAction& other) = delete;
DeferredAction& operator=(const DeferredAction& other) = delete;
private:
Nullable<T> mTarget;
};
template <typename T>
bool operator==(const DeferredAction<T>& action, decltype(nullptr)) {
return !action;
}
template <typename T>
bool operator==(decltype(nullptr), const DeferredAction<T>& action) {
return !action;
}
template <typename T>
bool operator!=(const DeferredAction<T>& action, decltype(nullptr)) {
return !!action;
}
template <typename T>
bool operator!=(decltype(nullptr), const DeferredAction<T>& action) {
return !!action;
}
// Defers execution of a function-like callable target with no arguments
// until the value returned by this function goes out of scope unless canceled,
// called, or moved to a wrapper in a different scope.
//
// // This example prints "Hello..." then "Goodbye!".
// void test() {
// auto d = fit::defer([]{ puts("Goodbye!"); });
// puts("Hello...");
// }
//
// // This example prints nothing because the deferred action is canceled.
// void do_nothing() {
// auto d = fit::defer([]{ puts("I'm not here."); });
// d.cancel();
// }
//
// // This example shows how the deferred action can be reassigned assuming
// // the new target has the same type and the old one, in this case by
// // representing the target as a |fit::Closure|.
// void reassign() {
// auto d = fit::defer<fit::Closure>([] { puts("This runs first."); });
// d = fit::defer<fit::Closure>([] { puts("This runs afterwards."); });
// }
template <typename T>
inline DeferredAction<T> defer(T target) {
return DeferredAction<T>(std::move(target));
}
// Alias for a deferred_action using a fit::Callback.
using DeferredCallback = DeferredAction<fit::Callback<void()>>;
// Defers execution of a fit::Callback with no arguments. See |fit::defer| for
// details.
inline DeferredCallback deferCallback(fit::Callback<void()> target) {
return DeferredCallback(std::move(target));
}
} // namespace fit
} // namespace android::base