proxygen
trampoline.h
Go to the documentation of this file.
1 /*
2  * Copyright 2018-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
19 #include <algorithm>
20 #include <chrono>
21 #include <deque>
22 #include <thread>
23 
24 namespace folly {
25 namespace pushmi {
26 
27 struct recurse_t {};
28 constexpr const recurse_t recurse{};
29 
31 
32 namespace detail {
33 
35 } const ownordelegate{};
36 PUSHMI_INLINE_VAR constexpr struct ownornest_t {
37 } const ownornest{};
38 
40  std::thread::id threadid;
41  uintptr_t trampolineid;
42 
43  public:
44  template <class T>
46  : threadid(std::this_thread::get_id()), trampolineid(trampoline) {}
47 };
48 
49 template <class E = std::exception_ptr>
50 class trampoline;
51 
52 template <class E = std::exception_ptr>
54  public:
55  using properties = property_set<
61 
63  return {};
64  }
65  PUSHMI_TEMPLATE(class SingleReceiver)
66  (requires ReceiveValue<
69  void submit(SingleReceiver&& what) {
70  trampoline<E>::submit(ownordelegate, std::forward<SingleReceiver>(what));
71  }
72 };
73 
74 template <class E = std::exception_ptr>
76  public:
77  using properties = property_set<
78  is_sender<>,
79  is_executor<>,
80  is_maybe_blocking<>,
81  is_fifo_sequence<>,
83 
85  return {};
86  }
87  PUSHMI_TEMPLATE(class SingleReceiver)
88  (requires ReceiveValue<
89  remove_cvref_t<SingleReceiver>,
91  void submit(SingleReceiver&& what) {
92  trampoline<E>::submit(ownornest, std::forward<SingleReceiver>(what));
93  }
94 };
95 
96 template <class E>
97 class trampoline {
98  private:
99  using error_type = std::decay_t<E>;
101  using queue_type = std::deque<work_type>;
102  using pending_type = std::tuple<int, queue_type, bool>;
103 
104  inline static pending_type*& owner() {
105  static thread_local pending_type* pending = nullptr;
106  return pending;
107  }
108 
109  inline static int& depth(pending_type& p) {
110  return std::get<0>(p);
111  }
112 
113  inline static queue_type& pending(pending_type& p) {
114  return std::get<1>(p);
115  }
116 
117  inline static bool& repeat(pending_type& p) {
118  return std::get<2>(p);
119  }
120 
121  public:
122  inline static trampoline_id get_id() {
123  return {owner()};
124  }
125 
126  inline static bool is_owned() {
127  return owner() != nullptr;
128  }
129 
130  template <class Selector, class Derived>
131  static void submit(Selector, Derived&, recurse_t) {
132  if (!is_owned()) {
133  abort();
134  }
135  repeat(*owner()) = true;
136  }
137 
138  PUSHMI_TEMPLATE(class SingleReceiver)
139  (requires not Same<SingleReceiver, recurse_t>)
140  static void submit(
142  SingleReceiver awhat) {
143  delegator<E> that;
144 
145  if (is_owned()) {
146  // thread already owned
147 
148  // poor mans scope guard
149  try {
150  if (++depth(*owner()) > 100) {
151  // defer work to owner
152  pending(*owner()).push_back(work_type{std::move(awhat)});
153  } else {
154  // dynamic recursion - optimization to balance queueing and
155  // stack usage and value interleaving on the same thread.
156  set_value(awhat, that);
157  set_done(awhat);
158  }
159  } catch (...) {
160  --depth(*owner());
161  throw;
162  }
163  --depth(*owner());
164  return;
165  }
166 
167  // take over the thread
168 
169  pending_type pending_store;
170  owner() = &pending_store;
171  depth(pending_store) = 0;
172  repeat(pending_store) = false;
173  // poor mans scope guard
174  try {
176  } catch (...) {
177  // ignore exceptions while delivering the exception
178  try {
179  set_error(awhat, std::current_exception());
180  for (auto& what : pending(pending_store)) {
181  set_error(what, std::current_exception());
182  }
183  } catch (...) {
184  }
185  pending(pending_store).clear();
186 
187  if (!is_owned()) {
188  std::abort();
189  }
190  if (!pending(pending_store).empty()) {
191  std::abort();
192  }
193  owner() = nullptr;
194  throw;
195  }
196  if (!is_owned()) {
197  std::abort();
198  }
199  if (!pending(pending_store).empty()) {
200  std::abort();
201  }
202  owner() = nullptr;
203  }
204 
205  PUSHMI_TEMPLATE(class SingleReceiver)
206  (requires not Same<SingleReceiver, recurse_t>)
207  static void submit(
208  ownornest_t,
209  SingleReceiver awhat) {
210  delegator<E> that;
211 
212  if (!is_owned()) {
214  return;
215  }
216 
217  auto& pending_store = *owner();
218 
219  // static recursion - tail call optimization
220  if (pending(pending_store).empty()) {
221  bool go = true;
222  while (go) {
223  repeat(pending_store) = false;
224  set_value(awhat, that);
225  set_done(awhat);
226  go = repeat(pending_store);
227  }
228  } else {
229  pending(pending_store).push_back(work_type{std::move(awhat)});
230  }
231 
232  if (pending(pending_store).empty()) {
233  return;
234  }
235 
236  while (!pending(pending_store).empty()) {
237  auto what = std::move(pending(pending_store).front());
238  pending(pending_store).pop_front();
240  set_done(what);
241  }
242  }
243 };
244 
245 } // namespace detail
246 
247 template <class E = std::exception_ptr>
250  std::abort();
251  }
253 }
254 
255 template <class E = std::exception_ptr>
258 }
259 
260 template <class E = std::exception_ptr>
262  return {};
263 }
264 template <class E = std::exception_ptr>
266  return {};
267 }
268 
269 // see boosters.h
271  auto operator()() {
272  return trampoline();
273  }
274 };
275 
276 namespace detail {
277 
278 PUSHMI_TEMPLATE(class E)
279 (requires SenderTo<delegator<E>, recurse_t>)
280 decltype(auto) repeat(delegator<E>& exec) {
281  submit(exec, recurse);
282 }
283 template <class AnyExec>
284 [[noreturn]] void repeat(AnyExec&) {
285  std::abort();
286 }
287 
288 } // namespace detail
289 
290 inline auto repeat() {
291  return [](auto& exec) { detail::repeat(exec); };
292 }
293 
294 } // namespace pushmi
295 } // namespace folly
requires ReceiveValue< remove_cvref_t< SingleReceiver >, any_executor_ref< E > > void submit(SingleReceiver &&what)
Definition: trampoline.h:91
auto repeat()
Definition: trampoline.h:290
detail::trampoline_id get_trampoline_id()
Definition: trampoline.h:248
detail::delegator< E > trampoline()
Definition: trampoline.h:261
std::remove_cv_t< std::remove_reference_t< T >> remove_cvref_t
Definition: traits.h:81
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
bool owned_by_trampoline()
Definition: trampoline.h:256
STL namespace.
detail::nester< E > nested_trampoline()
Definition: trampoline.h:265
PUSHMI_INLINE_VAR constexpr __adl::set_error_fn set_error
requires ReceiveValue< remove_cvref_t< SingleReceiver >, any_executor_ref< E > > void submit(SingleReceiver &&what)
Definition: trampoline.h:69
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static void submit(Selector, Derived &, recurse_t)
Definition: trampoline.h:131
constexpr const recurse_t recurse
Definition: trampoline.h:28
static int & depth(pending_type &p)
Definition: trampoline.h:109
std::decay_t< E > error_type
Definition: trampoline.h:99
requires not static Same< SingleReceiver, recurse_t > void submit(ownornest_t, SingleReceiver awhat)
Definition: trampoline.h:207
static queue_type & pending(pending_type &p)
Definition: trampoline.h:113
constexpr auto empty(C const &c) -> decltype(c.empty())
Definition: Access.h:55
static bool & repeat(pending_type &p)
Definition: trampoline.h:117
static trampoline_id get_id()
Definition: trampoline.h:122
PUSHMI_INLINE_VAR constexpr struct folly::pushmi::detail::ownornest_t ownornest
std::deque< work_type > queue_type
Definition: trampoline.h:101
#define PUSHMI_INLINE_VAR
Definition: concept_def.h:60
requires requires(detail::apply_impl(std::declval< F >(), std::declval< Tuple >(), detail::tupidxs< Tuple >{}))) const expr decltype(auto) apply(F &&f
PUSHMI_TEMPLATE(class E=std::exception_ptr, class Wrapped)(requires Sender< detail
Definition: executor.h:102
PUSHMI_INLINE_VAR constexpr __adl::set_value_fn set_value
PUSHMI_INLINE_VAR constexpr struct folly::pushmi::detail::ownordelegate_t ownordelegate
PUSHMI_INLINE_VAR constexpr __adl::do_submit_fn submit
std::tuple< int, queue_type, bool > pending_type
Definition: trampoline.h:102
requiresdecltype(auto) SenderTo< delegator< E >, recurse_t > repeat(delegator< E > &exec)
Definition: trampoline.h:280
static pending_type *& owner()
Definition: trampoline.h:104
requires not static Same< SingleReceiver, recurse_t > void submit(ownordelegate_t, SingleReceiver awhat)
Definition: trampoline.h:140
PUSHMI_INLINE_VAR constexpr __adl::set_done_fn set_done