aboutsummaryrefslogtreecommitdiff
path: root/third_party/fuchsia/repo/sdk/lib/fit/include/lib/fit/defer.h
blob: 907a3b77ec9efb7756f4ac83bdfd486b6bb9ae58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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.

#ifndef LIB_FIT_DEFER_H_
#define LIB_FIT_DEFER_H_

#include <utility>

#include "function.h"
#include "nullable.h"

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 deferred_action final {
 public:
  // Creates a deferred action without a pending target.
  deferred_action() = default;
  explicit deferred_action(decltype(nullptr)) {}

  // Creates a deferred action with a pending target.
  explicit deferred_action(T target) : target_(std::move(target)) {}

  // Creates a deferred action with a pending target moved from another
  // deferred action, leaving the other one without a pending target.
  deferred_action(deferred_action&& other) : target_(std::move(other.target_)) {
    other.target_.reset();
  }

  // Invokes and releases the deferred action's pending target (if any).
  ~deferred_action() { call(); }

  // Returns true if the deferred action has a pending target.
  explicit operator bool() const { return !!target_; }

  // 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.
  deferred_action& operator=(deferred_action&& other) {
    if (&other == this)
      return *this;
    call();
    target_ = std::move(other.target_);
    other.target_.reset();
    return *this;
  }

  // Invokes and releases the deferred action's pending target (if any).
  void call() {
    if (target_) {
      // Move to a local to guard against re-entrance.
      T local_target = std::move(*target_);
      target_.reset();
      local_target();
    }
  }

  // Releases the deferred action's pending target (if any) without
  // invoking it.
  void cancel() { target_.reset(); }
  deferred_action& operator=(decltype(nullptr)) {
    cancel();
    return *this;
  }

  // Assigns a new target to the deferred action.
  deferred_action& operator=(T target) {
    target_ = std::move(target);
    return *this;
  }

  deferred_action(const deferred_action& other) = delete;
  deferred_action& operator=(const deferred_action& other) = delete;

 private:
  nullable<T> target_;
};

template <typename T>
bool operator==(const deferred_action<T>& action, decltype(nullptr)) {
  return !action;
}
template <typename T>
bool operator==(decltype(nullptr), const deferred_action<T>& action) {
  return !action;
}
template <typename T>
bool operator!=(const deferred_action<T>& action, decltype(nullptr)) {
  return !!action;
}
template <typename T>
bool operator!=(decltype(nullptr), const deferred_action<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>
__attribute__((__warn_unused_result__)) inline deferred_action<T> defer(T target) {
  return deferred_action<T>(std::move(target));
}

// Alias for a deferred_action using a fit::callback.
using deferred_callback = deferred_action<fit::callback<void()>>;

// Defers execution of a fit::callback with no arguments. See |fit::defer| for
// details.
__attribute__((__warn_unused_result__)) inline deferred_callback defer_callback(
    fit::callback<void()> target) {
  return deferred_callback(std::move(target));
}

}  // namespace fit

#endif  // LIB_FIT_DEFER_H_