Apache Mesos
deferred.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License
12 
13 #ifndef __PROCESS_DEFERRED_HPP__
14 #define __PROCESS_DEFERRED_HPP__
15 
16 #include <functional>
17 
18 #include <process/dispatch.hpp>
19 #include <process/pid.hpp>
20 
21 #include <stout/preprocessor.hpp>
22 
23 namespace process {
24 
25 // Acts like a function call but runs within an asynchronous execution
26 // context such as an Executor or a ProcessBase (enforced because only
27 // an executor or the 'defer' routines are allowed to create them).
28 template <typename F>
29 struct Deferred : std::function<F>
30 {
31 private:
32  friend class Executor;
33 
34  template <typename G> friend struct _Deferred;
35 
36  // TODO(benh): Consider removing these in favor of having these
37  // functions return _Deferred.
38  template <typename T>
39  friend Deferred<void()>
40  defer(const PID<T>& pid, void (T::*method)());
41 
42  template <typename R, typename T>
43  friend Deferred<Future<R>()>
44  defer(const PID<T>& pid, Future<R> (T::*method)());
45 
46  template <typename R, typename T>
47  friend Deferred<Future<R>()>
48  defer(const PID<T>& pid, R (T::*method)());
49 
50  /*implicit*/ Deferred(const std::function<F>& f) : std::function<F>(f) {}
51 };
52 
53 
54 // We need an intermediate "deferred" type because when constructing a
55 // Deferred we won't always know the underlying function type (for
56 // example, if we're being passed a std::bind or a lambda). A lambda
57 // won't always implicitly convert to a std::function so instead we
58 // hold onto the functor type F and let the compiler invoke the
59 // necessary cast operator (below) when it actually has determined
60 // what type is needed. This is similar in nature to how std::bind
61 // works with its intermediate _Bind type (which the pre-C++11
62 // implementation relied on).
63 template <typename F>
64 struct _Deferred
65 {
66  // We expect that conversion operators are invoked on rvalues only,
67  // as _Deferred is supposed to be used directly as a result of defer call.
68  operator Deferred<void()>() &&
69  {
70  // The 'pid' differentiates an already dispatched functor versus
71  // one which still needs to be dispatched (which is done
72  // below). We have to delay wrapping the dispatch (for example, in
73  // defer.hpp) as long as possible because we don't always know
74  // what type the functor is or is going to be cast to (i.e., a
75  // std::bind might can be cast to functions that do or do not take
76  // arguments which will just be dropped when invoking the
77  // underlying bound function).
78  if (pid.isNone()) {
79  return std::function<void()>(std::forward<F>(f));
80  }
81 
82  // We need to explicitly copy the members otherwise we'll
83  // implicitly copy 'this' which might not exist at invocation.
84  Option<UPID> pid_ = pid;
85  F&& f_ = std::forward<F>(f);
86 
87  return std::function<void()>(
88  [=]() {
89  dispatch(pid_.get(), f_);
90  });
91  }
92 
93  operator std::function<void()>() &&
94  {
95  if (pid.isNone()) {
96  return std::function<void()>(std::forward<F>(f));
97  }
98 
99  Option<UPID> pid_ = pid;
100  F&& f_ = std::forward<F>(f);
101 
102  return std::function<void()>(
103  [=]() {
104  dispatch(pid_.get(), f_);
105  });
106  }
107 
109  {
110  if (pid.isNone()) {
111  return lambda::CallableOnce<void()>(std::forward<F>(f));
112  }
113 
114  Option<UPID> pid_ = pid;
115 
118  [pid_](typename std::decay<F>::type&& f_) {
119  dispatch(pid_.get(), std::move(f_));
120  },
121  std::forward<F>(f)));
122  }
123 
124  template <typename R>
125  operator Deferred<R()>() &&
126  {
127  if (pid.isNone()) {
128  return std::function<R()>(std::forward<F>(f));
129  }
130 
131  Option<UPID> pid_ = pid;
132  F&& f_ = std::forward<F>(f);
133 
134  return std::function<R()>(
135  [=]() {
136  return dispatch(pid_.get(), f_);
137  });
138  }
139 
140  template <typename R>
141  operator std::function<R()>() &&
142  {
143  if (pid.isNone()) {
144  return std::function<R()>(std::forward<F>(f));
145  }
146 
147  Option<UPID> pid_ = pid;
148  F&& f_ = std::forward<F>(f);
149 
150  return std::function<R()>(
151  [=]() {
152  return dispatch(pid_.get(), f_);
153  });
154  }
155 
156  template <typename R>
158  {
159  if (pid.isNone()) {
160  return lambda::CallableOnce<R()>(std::forward<F>(f));
161  }
162 
163  Option<UPID> pid_ = pid;
164 
167  [pid_](typename std::decay<F>::type&& f_) {
168  return dispatch(pid_.get(), std::move(f_));
169  },
170  std::forward<F>(f)));
171  }
172 
173 // Expands to lambda::_$(N+1). N is zero-based, and placeholders are one-based.
174 #define PLACEHOLDER(Z, N, DATA) CAT(lambda::_, INC(N))
175 
176 // This assumes type and variable base names are `P` and `p` respectively.
177 #define FORWARD(Z, N, DATA) std::forward<P ## N>(p ## N)
178 
179  // Due to a bug (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41933)
180  // with variadic templates and lambdas, we still need to do
181  // preprocessor expansions. In addition, due to a bug with clang (or
182  // libc++) we can't use std::bind with a std::function so we have to
183  // explicitly use the std::function<R(P...)>::operator() (see
184  // http://stackoverflow.com/questions/20097616/stdbind-to-a-stdfunction-crashes-with-clang).
185 #define TEMPLATE(Z, N, DATA) \
186  template <ENUM_PARAMS(N, typename P)> \
187  operator Deferred<void(ENUM_PARAMS(N, P))>() && \
188  { \
189  if (pid.isNone()) { \
190  return std::function<void(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \
191  } \
192  \
193  Option<UPID> pid_ = pid; \
194  F&& f_ = std::forward<F>(f); \
195  \
196  return std::function<void(ENUM_PARAMS(N, P))>( \
197  [=](ENUM_BINARY_PARAMS(N, P, p)) { \
198  std::function<void()> f__([=]() { \
199  f_(ENUM_PARAMS(N, p)); \
200  }); \
201  dispatch(pid_.get(), f__); \
202  }); \
203  } \
204  \
205  template <ENUM_PARAMS(N, typename P)> \
206  operator std::function<void(ENUM_PARAMS(N, P))>() && \
207  { \
208  if (pid.isNone()) { \
209  return std::function<void(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \
210  } \
211  \
212  Option<UPID> pid_ = pid; \
213  F&& f_ = std::forward<F>(f); \
214  \
215  return std::function<void(ENUM_PARAMS(N, P))>( \
216  [=](ENUM_BINARY_PARAMS(N, P, p)) { \
217  std::function<void()> f__([=]() { \
218  f_(ENUM_PARAMS(N, p)); \
219  }); \
220  dispatch(pid_.get(), f__); \
221  }); \
222  } \
223  \
224  template <ENUM_PARAMS(N, typename P)> \
225  operator lambda::CallableOnce<void(ENUM_PARAMS(N, P))>() && \
226  { \
227  if (pid.isNone()) { \
228  return lambda::CallableOnce<void(ENUM_PARAMS(N, P))>( \
229  std::forward<F>(f)); \
230  } \
231  \
232  Option<UPID> pid_ = pid; \
233  \
234  return lambda::CallableOnce<void(ENUM_PARAMS(N, P))>( \
235  lambda::partial( \
236  [pid_](typename std::decay<F>::type&& f_, \
237  ENUM_BINARY_PARAMS(N, P, &&p)) { \
238  lambda::CallableOnce<void()> f__( \
239  lambda::partial(std::move(f_), ENUM(N, FORWARD, _))); \
240  dispatch(pid_.get(), std::move(f__)); \
241  }, \
242  std::forward<F>(f), \
243  ENUM(N, PLACEHOLDER, _))); \
244  }
245 
246  REPEAT_FROM_TO(1, 3, TEMPLATE, _) // Args A0 -> A1.
247 #undef TEMPLATE
248 
249 #define TEMPLATE(Z, N, DATA) \
250  template <typename R, ENUM_PARAMS(N, typename P)> \
251  operator Deferred<R(ENUM_PARAMS(N, P))>() && \
252  { \
253  if (pid.isNone()) { \
254  return std::function<R(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \
255  } \
256  \
257  Option<UPID> pid_ = pid; \
258  F&& f_ = std::forward<F>(f); \
259  \
260  return std::function<R(ENUM_PARAMS(N, P))>( \
261  [=](ENUM_BINARY_PARAMS(N, P, p)) { \
262  std::function<R()> f__([=]() { \
263  return f_(ENUM_PARAMS(N, p)); \
264  }); \
265  return dispatch(pid_.get(), f__); \
266  }); \
267  } \
268  \
269  template <typename R, ENUM_PARAMS(N, typename P)> \
270  operator std::function<R(ENUM_PARAMS(N, P))>() && \
271  { \
272  if (pid.isNone()) { \
273  return std::function<R(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \
274  } \
275  \
276  Option<UPID> pid_ = pid; \
277  F&& f_ = std::forward<F>(f); \
278  \
279  return std::function<R(ENUM_PARAMS(N, P))>( \
280  [=](ENUM_BINARY_PARAMS(N, P, p)) { \
281  std::function<R()> f__([=]() { \
282  return f_(ENUM_PARAMS(N, p)); \
283  }); \
284  return dispatch(pid_.get(), f__); \
285  }); \
286  } \
287  \
288  template <typename R, ENUM_PARAMS(N, typename P)> \
289  operator lambda::CallableOnce<R(ENUM_PARAMS(N, P))>() && \
290  { \
291  if (pid.isNone()) { \
292  return lambda::CallableOnce<R(ENUM_PARAMS(N, P))>( \
293  std::forward<F>(f)); \
294  } \
295  \
296  Option<UPID> pid_ = pid; \
297  \
298  return lambda::CallableOnce<R(ENUM_PARAMS(N, P))>( \
299  lambda::partial( \
300  [pid_](typename std::decay<F>::type&& f_, \
301  ENUM_BINARY_PARAMS(N, P, &&p)) { \
302  lambda::CallableOnce<R()> f__( \
303  lambda::partial(std::move(f_), ENUM(N, FORWARD, _))); \
304  return dispatch(pid_.get(), std::move(f__)); \
305  }, \
306  std::forward<F>(f), \
307  ENUM(N, PLACEHOLDER, _))); \
308  }
309 
310  REPEAT_FROM_TO(1, 3, TEMPLATE, _) // Args A0 -> A1.
311 #undef TEMPLATE
312 
313 #undef FORWARD
314 #undef PLACEHOLDER
315 
316 private:
317  friend class Executor;
318 
319  template <typename G>
320  friend _Deferred<G> defer(const UPID& pid, G&& g);
321 
322 // This assumes type and variable base names are `A` and `a` respectively.
323 #define FORWARD(Z, N, DATA) std::forward<A ## N>(a ## N)
324 
325 #define TEMPLATE(Z, N, DATA) \
326  template <typename T, \
327  ENUM_PARAMS(N, typename P), \
328  ENUM_PARAMS(N, typename A)> \
329  friend auto defer(const PID<T>& pid, \
330  void (T::*method)(ENUM_PARAMS(N, P)), \
331  ENUM_BINARY_PARAMS(N, A, &&a)) \
332  -> _Deferred<decltype( \
333  lambda::partial( \
334  &std::function<void(ENUM_PARAMS(N, P))>::operator(), \
335  std::function<void(ENUM_PARAMS(N, P))>(), \
336  ENUM(N, FORWARD, _)))>;
337 
338  REPEAT_FROM_TO(1, 13, TEMPLATE, _) // Args A0 -> A11.
339 #undef TEMPLATE
340 
341 #define TEMPLATE(Z, N, DATA) \
342  template <typename R, \
343  typename T, \
344  ENUM_PARAMS(N, typename P), \
345  ENUM_PARAMS(N, typename A)> \
346  friend auto defer(const PID<T>& pid, \
347  Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
348  ENUM_BINARY_PARAMS(N, A, &&a)) \
349  -> _Deferred<decltype( \
350  lambda::partial( \
351  &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \
352  std::function<Future<R>(ENUM_PARAMS(N, P))>(), \
353  ENUM(N, FORWARD, _)))>;
354 
355  REPEAT_FROM_TO(1, 13, TEMPLATE, _) // Args A0 -> A11.
356 #undef TEMPLATE
357 
358 #define TEMPLATE(Z, N, DATA) \
359  template <typename R, \
360  typename T, \
361  ENUM_PARAMS(N, typename P), \
362  ENUM_PARAMS(N, typename A)> \
363  friend auto defer(const PID<T>& pid, \
364  R (T::*method)(ENUM_PARAMS(N, P)), \
365  ENUM_BINARY_PARAMS(N, A, &&a)) \
366  -> _Deferred<decltype( \
367  lambda::partial( \
368  &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \
369  std::function<Future<R>(ENUM_PARAMS(N, P))>(), \
370  ENUM(N, FORWARD, _)))>;
371 
372  REPEAT_FROM_TO(1, 13, TEMPLATE, _) // Args A0 -> A11.
373 #undef TEMPLATE
374 #undef FORWARD
375 
376  _Deferred(const UPID& pid, F&& f) : pid(pid), f(std::forward<F>(f)) {}
377 
378  /*implicit*/ _Deferred(F&& f) : f(std::forward<F>(f)) {}
379 
380  Option<UPID> pid;
381  F f;
382 };
383 
384 } // namespace process {
385 
386 #endif // __PROCESS_DEFERRED_HPP__
Definition: option.hpp:29
F && f
Definition: defer.hpp:270
friend struct _Deferred
Definition: deferred.hpp:34
internal::Partial< typename std::decay< F >::type, typename std::decay< Args >::type... > partial(F &&f, Args &&...args)
Definition: lambda.hpp:364
REPEAT_FROM_TO(1, 13, TEMPLATE, _) class AsyncExecutorProcess
Definition: async.hpp:63
Definition: type_utils.hpp:619
Definition: deferred.hpp:29
void dispatch(const PID< T > &pid, void(T::*method)())
Definition: dispatch.hpp:174
An "untyped" PID, used to encapsulate the process ID for lower-layer abstractions (eg...
Definition: pid.hpp:39
Definition: deferred.hpp:64
const T & get() const &
Definition: option.hpp:119
A "process identifier" used to uniquely identify a process when dispatching messages.
Definition: pid.hpp:289
Definition: executor.hpp:48
Try< uint32_t > type(const std::string &path)
#define TEMPLATE(Z, N, DATA)
Definition: deferred.hpp:358
Definition: executor.hpp:29
friend Deferred< void()> defer(const PID< T > &pid, void(T::*method)())
Definition: defer.hpp:35
Definition: lambda.hpp:414