1 | /** |
2 | * Copyright 2016 Facebook |
3 | * @author Tudor Bosman (tudorb@fb.com) |
4 | */ |
5 | |
6 | #pragma once |
7 | |
8 | #include <cstddef> |
9 | #include <functional> |
10 | #include <new> |
11 | #include <type_traits> |
12 | #include <utility> |
13 | |
14 | namespace caffe2 { |
15 | |
16 | // Copied from folly/ScopeGuard.h |
17 | |
18 | namespace detail { |
19 | |
20 | class ScopeGuardImplBase { |
21 | public: |
22 | void dismiss() noexcept { |
23 | dismissed_ = true; |
24 | } |
25 | |
26 | protected: |
27 | ScopeGuardImplBase() noexcept : dismissed_(false) {} |
28 | |
29 | static ScopeGuardImplBase makeEmptyScopeGuard() noexcept { |
30 | return ScopeGuardImplBase{}; |
31 | } |
32 | |
33 | template <typename T> |
34 | static const T& asConst(const T& t) noexcept { |
35 | return t; |
36 | } |
37 | |
38 | bool dismissed_; |
39 | }; |
40 | |
41 | template <typename FunctionType> |
42 | class ScopeGuardImpl : public ScopeGuardImplBase { |
43 | public: |
44 | explicit ScopeGuardImpl(FunctionType& fn) noexcept( |
45 | std::is_nothrow_copy_constructible<FunctionType>::value) |
46 | : ScopeGuardImpl( |
47 | asConst(fn), |
48 | makeFailsafe(std::is_nothrow_copy_constructible<FunctionType>{}, |
49 | &fn)) {} |
50 | |
51 | explicit ScopeGuardImpl(const FunctionType& fn) noexcept( |
52 | std::is_nothrow_copy_constructible<FunctionType>::value) |
53 | : ScopeGuardImpl( |
54 | fn, |
55 | makeFailsafe(std::is_nothrow_copy_constructible<FunctionType>{}, |
56 | &fn)) {} |
57 | |
58 | explicit ScopeGuardImpl(FunctionType&& fn) noexcept( |
59 | std::is_nothrow_move_constructible<FunctionType>::value) |
60 | : ScopeGuardImpl( |
61 | std::move_if_noexcept(fn), |
62 | makeFailsafe(std::is_nothrow_move_constructible<FunctionType>{}, |
63 | &fn)) {} |
64 | |
65 | ScopeGuardImpl(ScopeGuardImpl&& other) noexcept( |
66 | std::is_nothrow_move_constructible<FunctionType>::value) |
67 | : function_(std::move_if_noexcept(other.function_)) { |
68 | // If the above line attempts a copy and the copy throws, other is |
69 | // left owning the cleanup action and will execute it (or not) depending |
70 | // on the value of other.dismissed_. The following lines only execute |
71 | // if the move/copy succeeded, in which case *this assumes ownership of |
72 | // the cleanup action and dismisses other. |
73 | dismissed_ = other.dismissed_; |
74 | other.dismissed_ = true; |
75 | } |
76 | |
77 | ~ScopeGuardImpl() noexcept { |
78 | if (!dismissed_) { |
79 | execute(); |
80 | } |
81 | } |
82 | |
83 | private: |
84 | static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept { |
85 | return makeEmptyScopeGuard(); |
86 | } |
87 | |
88 | template <typename Fn> |
89 | static auto makeFailsafe(std::false_type, Fn* fn) noexcept |
90 | -> ScopeGuardImpl<decltype(std::ref(*fn))> { |
91 | return ScopeGuardImpl<decltype(std::ref(*fn))>{std::ref(*fn)}; |
92 | } |
93 | |
94 | template <typename Fn> |
95 | explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe) |
96 | : ScopeGuardImplBase{}, function_(std::forward<Fn>(fn)) { |
97 | failsafe.dismiss(); |
98 | } |
99 | |
100 | void* operator new(std::size_t) = delete; |
101 | |
102 | void execute() noexcept { function_(); } |
103 | |
104 | FunctionType function_; |
105 | }; |
106 | |
107 | template <typename F> |
108 | using ScopeGuardImplDecay = ScopeGuardImpl<typename std::decay<F>::type>; |
109 | |
110 | } // namespace detail |
111 | |
112 | /** |
113 | * ScopeGuard is a general implementation of the "Initialization is |
114 | * Resource Acquisition" idiom. Basically, it guarantees that a function |
115 | * is executed upon leaving the current scope unless otherwise told. |
116 | * |
117 | * The MakeGuard() function is used to create a new ScopeGuard object. |
118 | * It can be instantiated with a lambda function, a std::function<void()>, |
119 | * a functor, or a void(*)() function pointer. |
120 | * |
121 | * |
122 | * Usage example: Add a friend to memory iff it is also added to the db. |
123 | * |
124 | * void User::addFriend(User& newFriend) { |
125 | * // add the friend to memory |
126 | * friends_.push_back(&newFriend); |
127 | * |
128 | * // If the db insertion that follows fails, we should |
129 | * // remove it from memory. |
130 | * auto guard = MakeGuard([&] { friends_.pop_back(); }); |
131 | * |
132 | * // this will throw an exception upon error, which |
133 | * // makes the ScopeGuard execute UserCont::pop_back() |
134 | * // once the Guard's destructor is called. |
135 | * db_->addFriend(GetName(), newFriend.GetName()); |
136 | * |
137 | * // an exception was not thrown, so don't execute |
138 | * // the Guard. |
139 | * guard.dismiss(); |
140 | * } |
141 | * |
142 | * Examine ScopeGuardTest.cpp for some more sample usage. |
143 | * |
144 | * Stolen from: |
145 | * Andrei's and Petru Marginean's CUJ article: |
146 | * http://drdobbs.com/184403758 |
147 | * and the loki library: |
148 | * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer |
149 | * and triendl.kj article: |
150 | * http://www.codeproject.com/KB/cpp/scope_guard.aspx |
151 | */ |
152 | template <typename F> |
153 | detail::ScopeGuardImplDecay<F> MakeGuard(F&& f) noexcept( |
154 | noexcept(detail::ScopeGuardImplDecay<F>(static_cast<F&&>(f)))) { |
155 | return detail::ScopeGuardImplDecay<F>(static_cast<F&&>(f)); |
156 | } |
157 | |
158 | } // namespaces |
159 | |