1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#pragma once 6 7#include <new> 8#include <utility> 9 10#include "traits_internal.h" 11 12namespace fit { 13 14// A move-only deferred action wrapper with RAII semantics. 15// This class is not thread safe. 16// 17// The wrapper holds a function-like callable target with no arguments 18// which it invokes when it goes out of scope unless canceled, called, or 19// moved to a wrapper in a different scope. 20// 21// See |fit::defer()| for idiomatic usage. 22template <typename T> 23class deferred_action { 24public: 25 // Creates a deferred action without a pending target. 26 deferred_action() 27 : pending_(false) {} 28 29 // Creates a deferred action with a pending target. 30 explicit deferred_action(T target) { 31 if (fit::internal::is_null(target)) { 32 pending_ = false; 33 } else { 34 pending_ = true; 35 new (&target_) T(std::move(target)); 36 } 37 } 38 39 // Creates a deferred action with a pending target moved from another 40 // deferred action, leaving the other one without a pending target. 41 deferred_action(deferred_action&& other) { 42 move_from(std::move(other)); 43 } 44 45 // Invokes and releases the deferred action's pending target (if any). 46 ~deferred_action() { 47 call(); 48 } 49 50 // Returns true if the deferred action has a pending target. 51 explicit operator bool() const { 52 return pending_; 53 } 54 55 // Invokes and releases the deferred action's pending target (if any), 56 // then move-assigns it from another deferred action, leaving the latter 57 // one without a pending target. 58 deferred_action& operator=(deferred_action&& other) { 59 call(); 60 move_from(std::move(other)); 61 return *this; 62 } 63 64 // Invokes and releases the deferred action's pending target (if any). 65 void call() { 66 if (pending_) { 67 // Move to a local to guard against re-entrance. 68 T local_target = std::move(target_); 69 pending_ = false; 70 target_.~T(); 71 local_target(); 72 } 73 } 74 75 // Releases the deferred action's pending target (if any) without 76 // invoking it. 77 void cancel() { 78 if (pending_) { 79 pending_ = false; 80 target_.~T(); 81 } 82 } 83 84 deferred_action(const deferred_action& other) = delete; 85 deferred_action& operator=(const deferred_action& other) = delete; 86 87private: 88 void move_from(deferred_action&& other) { 89 if (this == &other) 90 return; 91 pending_ = other.pending_; 92 if (pending_) { 93 new (&target_) T(std::move(other.target_)); 94 other.target_.~T(); 95 other.pending_ = false; 96 } 97 } 98 99 bool pending_; 100 101 // Storage for the target. 102 // The target is only initialized when the call is pending. 103 union { 104 T target_; 105 }; 106}; 107 108// Defers execution of a function-like callable target with no arguments 109// unless the value returned by this function goes out of scope unless canceled, 110// called, or moved to a wrapper in a different scope. 111// 112// // This example prints "Hello..." then "Goodbye!". 113// void test() { 114// auto d = fit::defer([]{ puts("Goodbye!"); }); 115// puts("Hello..."); 116// } 117// 118// // This example prints nothing because the deferred action is canceled. 119// void do_nothing() { 120// auto d = fit::defer([]{ puts("I'm not here."); }); 121// d.cancel(); 122// } 123// 124// // This example shows how the deferred action can be reassigned assuming 125// // the new target has the same type and the old one, in this case by 126// // representing the target as a |fit::closure|. 127// void reassign() { 128// auto d = fit::defer<fit::closure>([] { puts("This runs first."); }); 129// d = fit::defer<fit::closure>([] { puts("This runs afterwards."); }); 130// } 131template <typename T> 132inline deferred_action<T> defer(T target) { 133 return deferred_action<T>(std::move(target)); 134} 135 136} // namespace fit 137