]>
Commit | Line | Data |
---|---|---|
e5989e71 JW |
1 | // <experimental/executor> -*- C++ -*- |
2 | ||
a5544970 | 3 | // Copyright (C) 2015-2019 Free Software Foundation, Inc. |
e5989e71 JW |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free | |
6 | // software; you can redistribute it and/or modify it under the | |
7 | // terms of the GNU General Public License as published by the | |
8 | // Free Software Foundation; either version 3, or (at your option) | |
9 | // any later version. | |
10 | ||
11 | // This library is distributed in the hope that it will be useful, | |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | // GNU General Public License for more details. | |
15 | ||
16 | // Under Section 7 of GPL version 3, you are granted additional | |
17 | // permissions described in the GCC Runtime Library Exception, version | |
18 | // 3.1, as published by the Free Software Foundation. | |
19 | ||
20 | // You should have received a copy of the GNU General Public License and | |
21 | // a copy of the GCC Runtime Library Exception along with this program; | |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 | // <http://www.gnu.org/licenses/>. | |
24 | ||
25 | /** @file experimental/executor | |
26 | * This is a TS C++ Library header. | |
27 | */ | |
28 | ||
29 | #ifndef _GLIBCXX_EXPERIMENTAL_EXECUTOR | |
30 | #define _GLIBCXX_EXPERIMENTAL_EXECUTOR 1 | |
31 | ||
32 | #pragma GCC system_header | |
33 | ||
34 | #if __cplusplus >= 201402L | |
35 | ||
36 | #include <algorithm> | |
82a0f2fd | 37 | #include <condition_variable> |
e5989e71 JW |
38 | #include <functional> |
39 | #include <future> | |
40 | #include <list> | |
e5989e71 JW |
41 | #include <queue> |
42 | #include <thread> | |
43 | #include <tuple> | |
44 | #include <unordered_map> | |
45 | #include <utility> | |
46 | #include <experimental/netfwd> | |
47 | #include <bits/unique_ptr.h> | |
48 | #include <experimental/bits/net.h> | |
49 | ||
50 | namespace std _GLIBCXX_VISIBILITY(default) | |
51 | { | |
a70a4be9 | 52 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
e5989e71 JW |
53 | namespace experimental |
54 | { | |
55 | namespace net | |
56 | { | |
57 | inline namespace v1 | |
58 | { | |
e5989e71 JW |
59 | |
60 | /** | |
61 | * @ingroup networking | |
62 | * @{ | |
63 | */ | |
64 | ||
65 | /// Customization point for asynchronous operations. | |
66 | template<typename _CompletionToken, typename _Signature, typename = void> | |
67 | class async_result; | |
68 | ||
69 | /// Convenience utility to help implement asynchronous operations. | |
70 | template<typename _CompletionToken, typename _Signature> | |
71 | class async_completion; | |
72 | ||
73 | template<typename _Tp, typename _ProtoAlloc, typename = __void_t<>> | |
74 | struct __associated_allocator_impl | |
75 | { | |
76 | using type = _ProtoAlloc; | |
77 | ||
78 | static type | |
79 | _S_get(const _Tp&, const _ProtoAlloc& __a) noexcept { return __a; } | |
80 | }; | |
81 | ||
82 | template<typename _Tp, typename _ProtoAlloc> | |
83 | struct __associated_allocator_impl<_Tp, _ProtoAlloc, | |
84 | __void_t<typename _Tp::allocator_type>> | |
85 | { | |
86 | using type = typename _Tp::allocator_type; | |
87 | ||
88 | static type | |
89 | _S_get(const _Tp& __t, const _ProtoAlloc&) noexcept | |
90 | { return __t.get_allocator(); } | |
91 | }; | |
92 | ||
93 | /// Helper to associate an allocator with a type. | |
94 | template<typename _Tp, typename _ProtoAllocator = allocator<void>> | |
95 | struct associated_allocator | |
96 | : __associated_allocator_impl<_Tp, _ProtoAllocator> | |
97 | { | |
98 | static auto | |
99 | get(const _Tp& __t, | |
100 | const _ProtoAllocator& __a = _ProtoAllocator()) noexcept | |
101 | { | |
102 | using _Impl = __associated_allocator_impl<_Tp, _ProtoAllocator>; | |
103 | return _Impl::_S_get(__t, __a); | |
104 | } | |
105 | }; | |
106 | ||
107 | /// Alias template for associated_allocator. | |
108 | template<typename _Tp, typename _ProtoAllocator = allocator<void>> | |
109 | using associated_allocator_t | |
110 | = typename associated_allocator<_Tp, _ProtoAllocator>::type; | |
111 | ||
112 | // get_associated_allocator: | |
113 | ||
114 | template<typename _Tp> | |
115 | inline associated_allocator_t<_Tp> | |
116 | get_associated_allocator(const _Tp& __t) noexcept | |
117 | { return associated_allocator<_Tp>::get(__t); } | |
118 | ||
119 | template<typename _Tp, typename _ProtoAllocator> | |
120 | inline associated_allocator_t<_Tp, _ProtoAllocator> | |
121 | get_associated_allocator(const _Tp& __t, | |
122 | const _ProtoAllocator& __a) noexcept | |
123 | { return associated_allocator<_Tp, _ProtoAllocator>::get(__t, __a); } | |
124 | ||
125 | enum class fork_event { prepare, parent, child }; | |
126 | ||
127 | /// An extensible, type-safe, polymorphic set of services. | |
128 | class execution_context; | |
129 | ||
130 | class service_already_exists : public logic_error { }; | |
131 | ||
132 | template<typename _Tp> struct is_executor; | |
133 | ||
134 | struct executor_arg_t { }; | |
135 | ||
136 | constexpr executor_arg_t executor_arg = executor_arg_t(); | |
137 | ||
138 | /// Trait for determining whether to construct an object with an executor. | |
139 | template<typename _Tp, typename _Executor> struct uses_executor; | |
140 | ||
141 | template<typename _Tp, typename _Executor, typename = __void_t<>> | |
142 | struct __associated_executor_impl | |
143 | { | |
144 | using type = _Executor; | |
145 | ||
146 | static type | |
147 | _S_get(const _Tp&, const _Executor& __e) noexcept { return __e; } | |
148 | }; | |
149 | ||
150 | template<typename _Tp, typename _Executor> | |
151 | struct __associated_executor_impl<_Tp, _Executor, | |
152 | __void_t<typename _Tp::executor_type>> | |
153 | { | |
154 | using type = typename _Tp::executor_type; | |
155 | ||
156 | static type | |
157 | _S_get(const _Tp& __t, const _Executor&) noexcept | |
158 | { return __t.get_executor(); } | |
159 | }; | |
160 | ||
161 | /// Helper to associate an executor with a type. | |
162 | template<typename _Tp, typename _Executor = system_executor> | |
163 | struct associated_executor | |
164 | : __associated_executor_impl<_Tp, _Executor> | |
165 | { | |
166 | static auto | |
167 | get(const _Tp& __t, const _Executor& __e = _Executor()) noexcept | |
168 | { return __associated_executor_impl<_Tp, _Executor>::_S_get(__t, __e); } | |
169 | }; | |
170 | ||
171 | ||
172 | template<typename _Tp, typename _Executor = system_executor> | |
173 | using associated_executor_t | |
174 | = typename associated_executor<_Tp, _Executor>::type; | |
175 | ||
176 | template<typename _ExecutionContext> | |
177 | using __is_exec_context | |
178 | = is_convertible<_ExecutionContext&, execution_context&>; | |
179 | ||
180 | template<typename _Tp> | |
181 | using __executor_t = typename _Tp::executor_type; | |
182 | ||
183 | // get_associated_executor: | |
184 | ||
185 | template<typename _Tp> | |
186 | inline associated_executor_t<_Tp> | |
187 | get_associated_executor(const _Tp& __t) noexcept | |
188 | { return associated_executor<_Tp>::get(__t); } | |
189 | ||
190 | template<typename _Tp, typename _Executor> | |
191 | inline | |
192 | enable_if_t<is_executor<_Executor>::value, | |
193 | associated_executor_t<_Tp, _Executor>> | |
194 | get_associated_executor(const _Tp& __t, const _Executor& __ex) | |
195 | { return associated_executor<_Tp, _Executor>::get(__t, __ex); } | |
196 | ||
197 | template<typename _Tp, typename _ExecutionContext> | |
198 | inline | |
199 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
200 | associated_executor_t<_Tp, __executor_t<_ExecutionContext>>> | |
201 | get_associated_executor(const _Tp& __t, _ExecutionContext& __ctx) noexcept | |
202 | { return net::get_associated_executor(__t, __ctx.get_executor()); } | |
203 | ||
204 | ||
205 | /// Helper to bind an executor to an object or function. | |
206 | template<typename _Tp, typename _Executor> | |
207 | class executor_binder; | |
208 | ||
209 | template<typename _Tp, typename _Executor, typename _Signature> | |
210 | class async_result<executor_binder<_Tp, _Executor>, _Signature>; | |
211 | ||
212 | template<typename _Tp, typename _Executor, typename _ProtoAllocator> | |
213 | struct associated_allocator<executor_binder<_Tp, _Executor>, | |
214 | _ProtoAllocator>; | |
215 | ||
216 | template<typename _Tp, typename _Executor, typename _Executor1> | |
217 | struct associated_executor<executor_binder<_Tp, _Executor>, _Executor1>; | |
218 | ||
219 | // bind_executor: | |
220 | ||
221 | template<typename _Executor, typename _Tp> | |
222 | inline | |
223 | enable_if_t<is_executor<_Executor>::value, | |
224 | executor_binder<decay_t<_Tp>, _Executor>> | |
225 | bind_executor(const _Executor& __ex, _Tp&& __t) | |
226 | { return { std::forward<_Tp>(__t), __ex }; } | |
227 | ||
228 | template<typename _ExecutionContext, typename _Tp> | |
229 | inline | |
230 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
231 | executor_binder<decay_t<_Tp>, __executor_t<_ExecutionContext>>> | |
232 | bind_executor(_ExecutionContext& __ctx, _Tp&& __t) | |
233 | { return { __ctx.get_executor(), forward<_Tp>(__t) }; } | |
234 | ||
235 | ||
236 | /// A scope-guard type to record when work is started and finished. | |
237 | template<typename _Executor> | |
238 | class executor_work_guard; | |
239 | ||
240 | // make_work_guard: | |
241 | ||
242 | template<typename _Executor> | |
243 | inline | |
244 | enable_if_t<is_executor<_Executor>::value, executor_work_guard<_Executor>> | |
245 | make_work_guard(const _Executor& __ex) | |
246 | { return executor_work_guard<_Executor>(__ex); } | |
247 | ||
248 | template<typename _ExecutionContext> | |
249 | inline | |
250 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
251 | executor_work_guard<__executor_t<_ExecutionContext>>> | |
252 | make_work_guard(_ExecutionContext& __ctx) | |
253 | { return net::make_work_guard(__ctx.get_executor()); } | |
254 | ||
255 | template<typename _Tp> | |
256 | inline | |
257 | enable_if_t<__not_<__or_<is_executor<_Tp>, __is_exec_context<_Tp>>>::value, | |
258 | executor_work_guard<associated_executor_t<_Tp>>> | |
259 | make_work_guard(const _Tp& __t) | |
260 | { return net::get_associated_executor(__t); } | |
261 | ||
262 | template<typename _Tp, typename _Up> | |
263 | auto | |
264 | make_work_guard(const _Tp& __t, _Up&& __u) | |
265 | -> decltype(net::make_work_guard( | |
266 | net::get_associated_executor(__t, forward<_Up>(__u)))) | |
267 | { | |
268 | return net::make_work_guard( | |
269 | net::get_associated_executor(__t, forward<_Up>(__u))); | |
270 | } | |
271 | ||
272 | /// Allows function objects to execute on any thread. | |
273 | class system_executor; | |
274 | ||
275 | /// The execution context associated with system_executor objects. | |
276 | class system_context; | |
277 | ||
278 | inline bool | |
279 | operator==(const system_executor&, const system_executor&) { return true; } | |
280 | ||
281 | inline bool | |
282 | operator!=(const system_executor&, const system_executor&) { return false; } | |
283 | ||
284 | /// Exception thrown by empty executors. | |
285 | class bad_executor; | |
286 | ||
287 | /// Polymorphic wrapper for types satisfying the Executor requirements. | |
288 | class executor; | |
289 | ||
290 | bool | |
291 | operator==(const executor& __a, const executor& __b) noexcept; | |
292 | ||
293 | bool | |
294 | operator==(const executor& __e, nullptr_t) noexcept; | |
295 | ||
296 | inline bool | |
297 | operator==(nullptr_t, const executor& __e) noexcept | |
298 | { return __e == nullptr; } | |
299 | ||
300 | inline bool | |
301 | operator!=(const executor& __a, const executor& __b) noexcept | |
302 | { return !(__a == __b); } | |
303 | ||
304 | inline bool | |
305 | operator!=(const executor& __e, nullptr_t) noexcept | |
306 | { return !(__e == nullptr); } | |
307 | ||
308 | inline bool | |
309 | operator!=(nullptr_t, const executor& __e) noexcept | |
310 | { return !(__e == nullptr); } | |
311 | ||
312 | void swap(executor&, executor&) noexcept; | |
313 | ||
314 | // dispatch: | |
315 | ||
316 | template<typename _CompletionToken> | |
317 | __deduced_t<_CompletionToken, void()> | |
318 | dispatch(_CompletionToken&& __token); | |
319 | ||
320 | template<typename _Executor, typename _CompletionToken> | |
321 | __deduced_t<_CompletionToken, void()> | |
322 | dispatch(const _Executor& __ex, _CompletionToken&& __token); | |
323 | ||
324 | template<typename _ExecutionContext, typename _CompletionToken> | |
325 | __deduced_t<_CompletionToken, void()> | |
326 | dispatch(_ExecutionContext& __ctx, _CompletionToken&& __token); | |
327 | ||
328 | // post: | |
329 | ||
330 | template<typename _CompletionToken> | |
331 | __deduced_t<_CompletionToken, void()> | |
332 | post(_CompletionToken&& __token); | |
333 | template<typename _Executor, typename _CompletionToken> | |
334 | enable_if_t<is_executor<_Executor>::value, | |
335 | __deduced_t<_CompletionToken, void()>> | |
336 | post(const _Executor& __ex, _CompletionToken&& __token); | |
337 | template<typename _ExecutionContext, typename _CompletionToken> | |
338 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
339 | __deduced_t<_CompletionToken, void()>> | |
340 | post(_ExecutionContext& __ctx, _CompletionToken&& __token); | |
341 | ||
342 | // defer: | |
343 | ||
344 | template<typename _CompletionToken> | |
345 | __deduced_t<_CompletionToken, void()> | |
346 | defer(_CompletionToken&& __token); | |
347 | template<typename _Executor, typename _CompletionToken> | |
348 | __deduced_t<_CompletionToken, void()> | |
349 | defer(const _Executor& __ex, _CompletionToken&& __token); | |
350 | template<typename _ExecutionContext, typename _CompletionToken> | |
351 | __deduced_t<_CompletionToken, void()> | |
352 | defer(_ExecutionContext& __ctx, _CompletionToken&& __token); | |
353 | ||
354 | template<typename _Executor> | |
355 | class strand; | |
356 | ||
357 | template<typename _Executor> | |
358 | bool | |
359 | operator==(const strand<_Executor>& __a, const strand<_Executor>& __b); | |
360 | ||
361 | template<typename _Executor> | |
362 | bool | |
363 | operator!=(const strand<_Executor>& __a, const strand<_Executor>& __b) | |
364 | { return !(__a == __b); } | |
365 | ||
366 | template<typename _CompletionToken, typename _Signature, typename> | |
367 | class async_result | |
368 | { | |
369 | public: | |
370 | typedef _CompletionToken completion_handler_type; | |
371 | typedef void return_type; | |
372 | ||
373 | explicit async_result(completion_handler_type&) {} | |
374 | async_result(const async_result&) = delete; | |
375 | async_result& operator=(const async_result&) = delete; | |
376 | ||
377 | return_type get() {} | |
378 | }; | |
379 | ||
380 | template<typename _CompletionToken, typename _Signature> | |
381 | class async_completion | |
382 | { | |
383 | using __result_type | |
384 | = async_result<decay_t<_CompletionToken>, _Signature>; | |
385 | ||
386 | public: | |
387 | using completion_handler_type | |
388 | = typename __result_type::completion_handler_type; | |
389 | ||
390 | private: | |
391 | using __handler_type = conditional_t< | |
392 | is_same<_CompletionToken, completion_handler_type>::value, | |
393 | completion_handler_type&, | |
394 | completion_handler_type>; | |
395 | ||
396 | public: | |
397 | explicit | |
398 | async_completion(_CompletionToken& __t) | |
399 | : completion_handler(std::forward<__handler_type>(__t)), | |
400 | result(completion_handler) | |
401 | { } | |
402 | ||
403 | async_completion(const async_completion&) = delete; | |
404 | async_completion& operator=(const async_completion&) = delete; | |
405 | ||
406 | __handler_type completion_handler; | |
407 | __result_type result; | |
408 | }; | |
409 | ||
410 | ||
411 | class execution_context | |
412 | { | |
413 | public: | |
414 | class service | |
415 | { | |
416 | protected: | |
417 | // construct / copy / destroy: | |
418 | ||
419 | explicit | |
420 | service(execution_context& __owner) : _M_context(__owner) { } | |
421 | ||
422 | service(const service&) = delete; | |
423 | service& operator=(const service&) = delete; | |
424 | ||
425 | virtual ~service() { } // TODO should not be inline | |
426 | ||
427 | // service observers: | |
428 | ||
429 | execution_context& context() const noexcept { return _M_context; } | |
430 | ||
431 | private: | |
432 | // service operations: | |
433 | ||
434 | virtual void shutdown() noexcept = 0; | |
435 | virtual void notify_fork(fork_event) { } | |
436 | ||
437 | friend class execution_context; | |
438 | execution_context& _M_context; | |
439 | }; | |
440 | ||
441 | // construct / copy / destroy: | |
442 | ||
443 | execution_context() { } | |
444 | ||
445 | execution_context(const execution_context&) = delete; | |
446 | execution_context& operator=(const execution_context&) = delete; | |
447 | ||
448 | virtual ~execution_context() | |
449 | { | |
450 | shutdown(); | |
451 | destroy(); | |
452 | } | |
453 | ||
454 | // execution context operations: | |
455 | ||
456 | void | |
457 | notify_fork(fork_event __e) | |
458 | { | |
459 | auto __l = [=](auto& __svc) { __svc._M_ptr->notify_fork(__e); }; | |
460 | if (__e == fork_event::prepare) | |
461 | std::for_each(_M_services.rbegin(), _M_services.rend(), __l); | |
462 | else | |
463 | std::for_each(_M_services.begin(), _M_services.end(), __l); | |
464 | } | |
465 | ||
466 | protected: | |
467 | // execution context protected operations: | |
468 | ||
469 | void | |
470 | shutdown() | |
471 | { | |
472 | std::for_each(_M_services.rbegin(), _M_services.rend(), | |
473 | [=](auto& __svc) { | |
474 | if (__svc._M_active) | |
475 | { | |
476 | __svc._M_ptr->shutdown(); | |
477 | __svc._M_active = false; | |
478 | } | |
479 | }); | |
480 | } | |
481 | ||
482 | void | |
483 | destroy() | |
484 | { | |
485 | while (_M_services.size()) | |
486 | _M_services.pop_back(); | |
487 | _M_keys.clear(); | |
488 | } | |
489 | ||
490 | protected: | |
491 | ||
492 | template<typename _Service> | |
493 | static void | |
494 | _S_deleter(service* __svc) { delete static_cast<_Service*>(__svc); } | |
495 | ||
496 | struct _ServicePtr | |
497 | { | |
498 | template<typename _Service> | |
499 | explicit | |
500 | _ServicePtr(_Service* __svc) | |
501 | : _M_ptr(__svc, &_S_deleter<_Service>), _M_active(true) { } | |
502 | ||
503 | std::unique_ptr<service, void(*)(service*)> _M_ptr; | |
504 | bool _M_active; | |
505 | }; | |
506 | ||
507 | mutable std::mutex _M_mutex; | |
508 | ||
509 | // Sorted in order of beginning of service object lifetime. | |
510 | std::list<_ServicePtr> _M_services; | |
511 | ||
512 | template<typename _Service, typename... _Args> | |
513 | service* | |
514 | _M_add_svc(_Args&&... __args) | |
515 | { | |
516 | _M_services.push_back( | |
517 | _ServicePtr{new _Service{*this, std::forward<_Args>(__args)...}} ); | |
518 | return _M_services.back()._M_ptr.get(); | |
519 | } | |
520 | ||
521 | using __key_type = void(*)(); | |
522 | ||
523 | template<typename _Key> | |
524 | static __key_type | |
525 | _S_key() { return reinterpret_cast<__key_type>(&_S_key<_Key>); } | |
526 | ||
527 | std::unordered_map<__key_type, service*> _M_keys; | |
528 | ||
529 | template<typename _Service> | |
530 | friend typename _Service::key_type& | |
531 | use_service(execution_context&); | |
532 | ||
533 | template<typename _Service, typename... _Args> | |
534 | friend _Service& | |
535 | make_service(execution_context&, _Args&&...); | |
536 | ||
537 | template<typename _Service> | |
538 | friend bool | |
539 | has_service(const execution_context&) noexcept; | |
540 | }; | |
541 | ||
542 | // service access: | |
543 | ||
544 | template<typename _Service> | |
545 | typename _Service::key_type& | |
546 | use_service(execution_context& __ctx) | |
547 | { | |
548 | using _Key = typename _Service::key_type; | |
549 | static_assert(is_base_of<execution_context::service, _Key>::value, | |
550 | "a service type must derive from execution_context::service"); | |
551 | static_assert(is_base_of<_Key, _Service>::value, | |
552 | "a service type must match or derive from its key_type"); | |
553 | auto __key = execution_context::_S_key<_Key>(); | |
554 | std::lock_guard<std::mutex> __lock(__ctx._M_mutex); | |
555 | auto& __svc = __ctx._M_keys[__key]; | |
556 | if (__svc == nullptr) | |
557 | { | |
558 | __try { | |
559 | __svc = __ctx._M_add_svc<_Service>(); | |
560 | } __catch(...) { | |
561 | __ctx._M_keys.erase(__key); | |
562 | __throw_exception_again; | |
563 | } | |
564 | } | |
565 | return static_cast<_Key&>(*__svc); | |
566 | } | |
567 | ||
568 | template<typename _Service, typename... _Args> | |
569 | _Service& | |
570 | make_service(execution_context& __ctx, _Args&&... __args) | |
571 | { | |
572 | using _Key = typename _Service::key_type; | |
573 | static_assert(is_base_of<execution_context::service, _Key>::value, | |
574 | "a service type must derive from execution_context::service"); | |
575 | static_assert(is_base_of<_Key, _Service>::value, | |
576 | "a service type must match or derive from its key_type"); | |
577 | auto __key = execution_context::_S_key<_Key>(); | |
578 | std::lock_guard<std::mutex> __lock(__ctx._M_mutex); | |
579 | auto& __svc = __ctx._M_keys[__key]; | |
580 | if (__svc != nullptr) | |
581 | throw service_already_exists(); | |
582 | __try { | |
583 | __svc = __ctx._M_add_svc<_Service>(std::forward<_Args>(__args)...); | |
584 | } __catch(...) { | |
585 | __ctx._M_keys.erase(__key); | |
586 | __throw_exception_again; | |
587 | } | |
588 | return static_cast<_Service&>(*__svc); | |
589 | } | |
590 | ||
591 | template<typename _Service> | |
592 | inline bool | |
593 | has_service(const execution_context& __ctx) noexcept | |
594 | { | |
595 | using _Key = typename _Service::key_type; | |
596 | static_assert(is_base_of<execution_context::service, _Key>::value, | |
597 | "a service type must derive from execution_context::service"); | |
598 | static_assert(is_base_of<_Key, _Service>::value, | |
599 | "a service type must match or derive from its key_type"); | |
600 | std::lock_guard<std::mutex> __lock(__ctx._M_mutex); | |
601 | return __ctx._M_keys.count(execution_context::_S_key<_Key>()); | |
602 | } | |
603 | ||
604 | template<typename _Tp, typename = __void_t<>> | |
605 | struct __is_executor_impl : false_type | |
606 | { }; | |
607 | ||
608 | // Check Executor requirements. | |
609 | template<typename _Tp, typename _Up = remove_const_t<_Tp>> | |
610 | auto | |
611 | __executor_reqs(_Up* __x = 0, const _Up* __cx = 0, void(*__f)() = 0, | |
612 | const allocator<int>& __a = {}) | |
613 | -> enable_if_t<__is_value_constructible<_Tp>::value, __void_t< | |
614 | decltype(*__cx == *__cx), | |
615 | decltype(*__cx != *__cx), | |
616 | decltype(__x->context()), | |
617 | decltype(__x->on_work_started()), | |
618 | decltype(__x->on_work_finished()), | |
619 | decltype(__x->dispatch(std::move(__f), __a)), | |
620 | decltype(__x->post(std::move(__f), __a)), | |
621 | decltype(__x->defer(std::move(__f), __a)) | |
622 | >>; | |
623 | ||
624 | template<typename _Tp> | |
625 | struct __is_executor_impl<_Tp, decltype(__executor_reqs<_Tp>())> | |
626 | : true_type | |
627 | { }; | |
628 | ||
629 | template<typename _Tp> | |
630 | struct is_executor : __is_executor_impl<_Tp> | |
631 | { }; | |
632 | ||
633 | template<typename _Tp> | |
634 | constexpr bool is_executor_v = is_executor<_Tp>::value; | |
635 | ||
636 | template<typename _Tp, typename _Executor, typename = __void_t<>> | |
637 | struct __uses_executor_impl : false_type | |
638 | { }; | |
639 | ||
640 | template<typename _Tp, typename _Executor> | |
641 | struct __uses_executor_impl<_Tp, _Executor, | |
642 | __void_t<typename _Tp::executor_type>> | |
643 | : is_convertible<_Executor, typename _Tp::executor_type> | |
644 | { }; | |
645 | ||
646 | template<typename _Tp, typename _Executor> | |
647 | struct uses_executor : __uses_executor_impl<_Tp, _Executor>::type | |
648 | { }; | |
649 | ||
650 | template<typename _Tp, typename _Executor> | |
651 | constexpr bool uses_executor_v = uses_executor<_Tp, _Executor>::value; | |
652 | ||
653 | template<typename _Tp, typename _Executor> | |
654 | class executor_binder | |
655 | { | |
656 | struct __use_exec { }; | |
657 | ||
658 | public: | |
659 | // types: | |
660 | ||
661 | typedef _Tp target_type; | |
662 | typedef _Executor executor_type; | |
663 | ||
664 | // construct / copy / destroy: | |
665 | ||
666 | executor_binder(_Tp __t, const _Executor& __ex) | |
667 | : executor_binder(__use_exec{}, std::move(__t), __ex) | |
668 | { } | |
669 | ||
670 | executor_binder(const executor_binder&) = default; | |
671 | executor_binder(executor_binder&&) = default; | |
672 | ||
673 | template<typename _Up, typename _OtherExecutor> | |
674 | executor_binder(const executor_binder<_Up, _OtherExecutor>& __other) | |
675 | : executor_binder(__use_exec{}, __other.get(), __other.get_executor()) | |
676 | { } | |
677 | ||
678 | template<typename _Up, typename _OtherExecutor> | |
679 | executor_binder(executor_binder<_Up, _OtherExecutor>&& __other) | |
680 | : executor_binder(__use_exec{}, std::move(__other.get()), | |
681 | __other.get_executor()) | |
682 | { } | |
683 | ||
684 | template<typename _Up, typename _OtherExecutor> | |
685 | executor_binder(executor_arg_t, const _Executor& __ex, | |
686 | const executor_binder<_Up, _OtherExecutor>& __other) | |
687 | : executor_binder(__use_exec{}, __other.get(), __ex) | |
688 | { } | |
689 | ||
690 | template<typename _Up, typename _OtherExecutor> | |
691 | executor_binder(executor_arg_t, const _Executor& __ex, | |
692 | executor_binder<_Up, _OtherExecutor>&& __other) | |
693 | : executor_binder(__use_exec{}, std::move(__other.get()), __ex) | |
694 | { } | |
695 | ||
696 | ~executor_binder(); | |
697 | ||
698 | // executor binder access: | |
699 | ||
700 | _Tp& get() noexcept { return _M_target; } | |
701 | const _Tp& get() const noexcept { return _M_target; } | |
702 | executor_type get_executor() const noexcept { return _M_ex; } | |
703 | ||
704 | // executor binder invocation: | |
705 | ||
706 | template<class... _Args> | |
707 | result_of_t<_Tp&(_Args&&...)> | |
708 | operator()(_Args&&... __args) | |
709 | { return std::__invoke(get(), std::forward<_Args>(__args)...); } | |
710 | ||
711 | template<class... _Args> | |
712 | result_of_t<const _Tp&(_Args&&...)> | |
713 | operator()(_Args&&... __args) const | |
714 | { return std::__invoke(get(), std::forward<_Args>(__args)...); } | |
715 | ||
716 | private: | |
717 | template<typename _Up> | |
718 | using __use_exec_cond | |
719 | = __and_<uses_executor<_Tp, _Executor>, | |
720 | is_constructible<_Tp, executor_arg_t, _Executor, _Up>>; | |
721 | ||
722 | template<typename _Up, typename _Exec, typename = | |
723 | enable_if_t<__use_exec_cond<_Up>::value>> | |
724 | executor_binder(__use_exec, _Up&& __u, _Exec&& __ex) | |
725 | : _M_ex(std::forward<_Exec>(__ex)), | |
726 | _M_target(executor_arg, _M_ex, std::forward<_Up>(__u)) | |
727 | { } | |
728 | ||
729 | template<typename _Up, typename _Exec, typename = | |
730 | enable_if_t<!__use_exec_cond<_Up>::value>> | |
731 | executor_binder(__use_exec, _Up&& __u, const _Exec& __ex) | |
732 | : _M_ex(std::forward<_Exec>(__ex)), | |
733 | _M_target(std::forward<_Up>(__u)) | |
734 | { } | |
735 | ||
736 | _Executor _M_ex; | |
737 | _Tp _M_target; | |
738 | }; | |
739 | ||
740 | template<typename _Tp, typename _Executor, typename _Signature> | |
741 | class async_result<executor_binder<_Tp, _Executor>, _Signature> | |
742 | { | |
743 | using __inner = async_result<_Tp, _Signature>; | |
744 | ||
745 | public: | |
746 | using completion_handler_type = | |
747 | executor_binder<typename __inner::completion_handler_type, _Executor>; | |
748 | ||
749 | using return_type = typename __inner::return_type; | |
750 | ||
751 | explicit | |
752 | async_result(completion_handler_type& __h) | |
753 | : _M_target(__h.get()) { } | |
754 | ||
755 | async_result(const async_result&) = delete; | |
756 | async_result& operator=(const async_result&) = delete; | |
757 | ||
758 | return_type get() { return _M_target.get(); } | |
759 | ||
760 | private: | |
761 | __inner _M_target; | |
762 | }; | |
763 | ||
764 | template<typename _Tp, typename _Executor, typename _ProtoAlloc> | |
765 | struct associated_allocator<executor_binder<_Tp, _Executor>, _ProtoAlloc> | |
766 | { | |
767 | typedef associated_allocator_t<_Tp, _ProtoAlloc> type; | |
768 | ||
769 | static type | |
770 | get(const executor_binder<_Tp, _Executor>& __b, | |
771 | const _ProtoAlloc& __a = _ProtoAlloc()) noexcept | |
772 | { return associated_allocator<_Tp, _ProtoAlloc>::get(__b.get(), __a); } | |
773 | }; | |
774 | ||
775 | template<typename _Tp, typename _Executor, typename _Executor1> | |
776 | struct associated_executor<executor_binder<_Tp, _Executor>, _Executor1> | |
777 | { | |
778 | typedef _Executor type; | |
779 | ||
780 | static type | |
781 | get(const executor_binder<_Tp, _Executor>& __b, | |
782 | const _Executor1& = _Executor1()) noexcept | |
783 | { return __b.get_executor(); } | |
784 | }; | |
785 | ||
786 | template<typename _Executor> | |
787 | class executor_work_guard | |
788 | { | |
789 | public: | |
790 | // types: | |
791 | ||
792 | typedef _Executor executor_type; | |
793 | ||
794 | // construct / copy / destroy: | |
795 | ||
796 | explicit | |
797 | executor_work_guard(const executor_type& __ex) noexcept | |
798 | : _M_ex(__ex), _M_owns(true) | |
799 | { _M_ex.on_work_started(); } | |
800 | ||
801 | executor_work_guard(const executor_work_guard& __other) noexcept | |
802 | : _M_ex(__other._M_ex), _M_owns(__other._M_owns) | |
803 | { | |
804 | if (_M_owns) | |
805 | _M_ex.on_work_started(); | |
806 | } | |
807 | ||
808 | executor_work_guard(executor_work_guard&& __other) noexcept | |
809 | : _M_ex(__other._M_ex), _M_owns(__other._M_owns) | |
810 | { __other._M_owns = false; } | |
811 | ||
812 | executor_work_guard& operator=(const executor_work_guard&) = delete; | |
813 | ||
814 | ~executor_work_guard() | |
815 | { | |
816 | if (_M_owns) | |
817 | _M_ex.on_work_finished(); | |
818 | } | |
819 | ||
820 | // executor work guard observers: | |
821 | ||
822 | executor_type get_executor() const noexcept { return _M_ex; } | |
823 | ||
824 | bool owns_work() const noexcept { return _M_owns; } | |
825 | ||
826 | // executor work guard modifiers: | |
827 | ||
828 | void reset() noexcept | |
829 | { | |
830 | if (_M_owns) | |
831 | _M_ex.on_work_finished(); | |
832 | _M_owns = false; | |
833 | } | |
834 | ||
835 | private: | |
836 | _Executor _M_ex; | |
837 | bool _M_owns; | |
838 | }; | |
839 | ||
840 | ||
841 | class system_context : public execution_context | |
842 | { | |
843 | public: | |
844 | // types: | |
845 | ||
846 | typedef system_executor executor_type; | |
847 | ||
848 | // construct / copy / destroy: | |
849 | ||
850 | system_context() = default; | |
851 | system_context(const system_context&) = delete; | |
852 | system_context& operator=(const system_context&) = delete; | |
853 | ||
854 | ~system_context() | |
855 | { | |
856 | stop(); | |
857 | join(); | |
858 | } | |
859 | ||
860 | // system_context operations: | |
861 | ||
862 | executor_type get_executor() noexcept; | |
863 | ||
864 | void stop() | |
865 | { | |
866 | lock_guard<mutex> __lock(_M_mtx); | |
867 | _M_stopped = true; | |
868 | _M_cv.notify_all(); | |
869 | } | |
870 | ||
871 | bool stopped() const noexcept | |
872 | { | |
873 | lock_guard<mutex> __lock(_M_mtx); | |
874 | return _M_stopped; | |
875 | } | |
876 | ||
877 | void join() | |
878 | { | |
879 | _M_thread.join(); | |
880 | } | |
881 | ||
882 | private: | |
883 | friend system_executor; | |
884 | ||
885 | struct __tag { }; | |
886 | system_context(__tag) { } | |
887 | ||
888 | thread _M_thread; | |
889 | mutable mutex _M_mtx; | |
890 | condition_variable _M_cv; | |
891 | queue<function<void()>> _M_tasks; | |
892 | bool _M_stopped = false; | |
893 | ||
894 | void | |
895 | _M_run() | |
896 | { | |
897 | while (true) | |
898 | { | |
899 | function<void()> __f; | |
900 | { | |
901 | unique_lock<mutex> __lock(_M_mtx); | |
902 | _M_cv.wait(__lock, | |
903 | [this]{ return !_M_stopped && !_M_tasks.empty(); }); | |
904 | if (_M_stopped) | |
905 | return; | |
906 | __f = std::move(_M_tasks.front()); | |
907 | _M_tasks.pop(); | |
908 | } | |
909 | __f(); | |
910 | } | |
911 | } | |
912 | ||
913 | void | |
914 | _M_post(std::function<void()> __f) | |
915 | { | |
916 | lock_guard<mutex> __lock(_M_mtx); | |
917 | if (_M_stopped) | |
918 | return; | |
919 | if (!_M_thread.joinable()) | |
920 | _M_thread = std::thread(&system_context::_M_run, this); | |
921 | _M_tasks.push(std::move(__f)); // XXX allocator not used | |
922 | _M_cv.notify_one(); | |
923 | } | |
924 | ||
925 | static system_context& | |
926 | _S_get() noexcept | |
927 | { | |
928 | static system_context __sc(__tag{}); | |
929 | return __sc; | |
930 | } | |
931 | }; | |
932 | ||
933 | class system_executor | |
934 | { | |
935 | public: | |
936 | // executor operations: | |
937 | ||
938 | system_executor() { } | |
939 | ||
940 | system_context& | |
941 | context() const noexcept { return system_context::_S_get(); } | |
942 | ||
943 | void on_work_started() const noexcept { } | |
944 | void on_work_finished() const noexcept { } | |
945 | ||
946 | template<typename _Func, typename _ProtoAlloc> | |
947 | void | |
948 | dispatch(_Func&& __f, const _ProtoAlloc& __a) const | |
949 | { decay_t<_Func>{std::forward<_Func>(__f)}(); } | |
950 | ||
951 | template<typename _Func, typename _ProtoAlloc> | |
952 | void | |
953 | post(_Func&& __f, const _ProtoAlloc&) const // XXX allocator not used | |
954 | { | |
955 | system_context::_S_get()._M_post(std::forward<_Func>(__f)); | |
956 | } | |
957 | ||
958 | template<typename _Func, typename _ProtoAlloc> | |
959 | void | |
960 | defer(_Func&& __f, const _ProtoAlloc& __a) const | |
961 | { post(std::forward<_Func>(__f), __a); } | |
962 | }; | |
963 | ||
964 | inline system_executor | |
965 | system_context::get_executor() noexcept | |
966 | { return {}; } | |
967 | ||
968 | class bad_executor : public std::exception | |
969 | { | |
970 | virtual const char* what() const noexcept { return "bad executor"; } | |
971 | }; | |
972 | ||
973 | inline void __throw_bad_executor() // TODO make non-inline | |
974 | { | |
975 | #if __cpp_exceptions | |
976 | throw bad_executor(); | |
977 | #else | |
978 | __builtin_abort(); | |
979 | #endif | |
980 | } | |
981 | ||
982 | class executor | |
983 | { | |
984 | public: | |
985 | // construct / copy / destroy: | |
986 | ||
987 | executor() noexcept = default; | |
988 | ||
989 | executor(nullptr_t) noexcept { } | |
990 | executor(const executor&) noexcept = default; | |
991 | executor(executor&&) noexcept = default; | |
992 | ||
993 | template<typename _Executor> | |
994 | executor(_Executor __e) | |
995 | : _M_target(_M_create(std::move(__e))) | |
996 | { } | |
997 | ||
998 | template<typename _Executor, typename _ProtoAlloc> | |
999 | executor(allocator_arg_t, const _ProtoAlloc& __a, _Executor __e) | |
1000 | : _M_target(_M_create(std::move(__e), __a)) | |
1001 | { } | |
1002 | ||
1003 | executor& operator=(const executor&) noexcept = default; | |
1004 | executor& operator=(executor&&) noexcept = default; | |
1005 | ||
1006 | executor& | |
1007 | operator=(nullptr_t) noexcept | |
1008 | { | |
1009 | _M_target = nullptr; | |
1010 | return *this; | |
1011 | } | |
1012 | ||
1013 | template<typename _Executor> | |
1014 | executor& | |
1015 | operator=(_Executor __e) | |
1016 | { | |
1017 | executor(std::move(__e)).swap(*this); | |
1018 | return *this; | |
1019 | } | |
1020 | ||
1021 | ~executor() = default; | |
1022 | ||
1023 | // executor modifiers: | |
1024 | ||
1025 | void | |
1026 | swap(executor& __other) noexcept | |
1027 | { _M_target.swap(__other._M_target); } | |
1028 | ||
1029 | template<typename _Executor, typename _Alloc> | |
1030 | void | |
1031 | assign(_Executor __e, const _Alloc& __a) | |
1032 | { executor(allocator_arg, __a, std::move(__e)).swap(*this); } | |
1033 | ||
1034 | // executor operations: | |
1035 | ||
1036 | execution_context& | |
1037 | context() const noexcept | |
1038 | { | |
1039 | __glibcxx_assert( _M_target ); | |
1040 | return _M_target->context(); | |
1041 | } | |
1042 | ||
1043 | void | |
1044 | on_work_started() const noexcept | |
1045 | { | |
1046 | __glibcxx_assert( _M_target ); | |
1047 | return _M_target->on_work_started(); | |
1048 | } | |
1049 | ||
1050 | void | |
1051 | on_work_finished() const noexcept | |
1052 | { | |
1053 | __glibcxx_assert( _M_target ); | |
1054 | return _M_target->on_work_finished(); | |
1055 | } | |
1056 | ||
1057 | template<typename _Func, typename _Alloc> | |
1058 | void | |
1059 | dispatch(_Func&& __f, const _Alloc& __a) const | |
1060 | { | |
1061 | if (!_M_target) | |
1062 | __throw_bad_executor(); | |
1063 | // _M_target->dispatch({allocator_arg, __a, std::forward<_Func>(__f)}); | |
1064 | _M_target->dispatch(std::forward<_Func>(__f)); | |
1065 | } | |
1066 | ||
1067 | template<typename _Func, typename _Alloc> | |
1068 | void | |
1069 | post(_Func&& __f, const _Alloc& __a) const | |
1070 | { | |
1071 | if (!_M_target) | |
1072 | __throw_bad_executor(); | |
1073 | // _M_target->post({allocator_arg, __a, std::forward<_Func>(__f)}); | |
1074 | _M_target->post(std::forward<_Func>(__f)); | |
1075 | } | |
1076 | ||
1077 | template<typename _Func, typename _Alloc> | |
1078 | void | |
1079 | defer(_Func&& __f, const _Alloc& __a) const | |
1080 | { | |
1081 | if (!_M_target) | |
1082 | __throw_bad_executor(); | |
1083 | // _M_target->defer({allocator_arg, __a, std::forward<_Func>(__f)}); | |
1084 | _M_target->defer(std::forward<_Func>(__f)); | |
1085 | } | |
1086 | ||
1087 | // executor capacity: | |
1088 | ||
1089 | explicit operator bool() const noexcept | |
1090 | { return static_cast<bool>(_M_target); } | |
1091 | ||
1092 | // executor target access: | |
1093 | ||
1094 | #if __cpp_rtti | |
1095 | const type_info& | |
1096 | target_type() const noexcept | |
1097 | { return _M_target ? _M_target->target_type() : typeid(void); } | |
1098 | ||
1099 | template<typename _Executor> | |
1100 | _Executor* | |
1101 | target() noexcept | |
1102 | { | |
1103 | if (_M_target) | |
1104 | if (const auto* __p = _M_target->target(typeid(_Executor))) | |
1105 | return const_cast<_Executor*>(static_cast<const _Executor>(__p)); | |
1106 | return nullptr; | |
1107 | } | |
1108 | ||
1109 | template<typename _Executor> | |
1110 | const _Executor* | |
1111 | target() const noexcept | |
1112 | { | |
1113 | if (_M_target) | |
1114 | if (const auto* __p = _M_target->target(typeid(_Executor))) | |
1115 | return static_cast<const _Executor*>(__p); | |
1116 | return nullptr; | |
1117 | } | |
1118 | #endif | |
1119 | ||
1120 | private: | |
1121 | struct _Tgt | |
1122 | { | |
1123 | virtual void on_work_started() const noexcept = 0; | |
1124 | virtual void on_work_finished() const noexcept = 0; | |
1125 | virtual execution_context& context() const noexcept = 0; | |
1126 | virtual void dispatch(std::function<void()>) const = 0; | |
1127 | virtual void post(std::function<void()>) const = 0; | |
1128 | virtual void defer(std::function<void()>) const = 0; | |
1129 | #if __cpp_rtti | |
1130 | virtual const type_info& target_type() const = 0; | |
1131 | virtual void* target(const std::type_info&) const = 0; | |
1132 | virtual bool _M_equals(_Tgt*) const noexcept = 0; | |
1133 | virtual const void* _M_get_executor() const noexcept = 0; | |
1134 | #endif | |
1135 | }; | |
1136 | ||
1137 | template<typename _Ex, typename _Alloc> | |
1138 | struct _TgtImpl : _Tgt | |
1139 | { | |
1140 | explicit | |
1141 | _TgtImpl(_Ex&& __ex, const _Alloc& __a) | |
1142 | : _M_impl(std::move(__ex), __a) { } | |
1143 | ||
1144 | void on_work_started() const noexcept { _M_ex().on_work_started(); } | |
1145 | void on_work_finished() const noexcept { _M_ex().on_work_finished(); } | |
1146 | execution_context& context() const noexcept { return _M_ex().context(); } | |
1147 | void | |
1148 | dispatch(std::function<void()> __f) const | |
1149 | { _M_ex().dispatch(std::move(__f), _M_alloc()); } | |
1150 | void | |
1151 | post(std::function<void()> __f) const | |
1152 | { _M_ex().post(std::move(__f), _M_alloc()); } | |
1153 | void | |
1154 | defer(std::function<void()> __f) const | |
1155 | { _M_ex().defer(std::move(__f), _M_alloc()); } | |
1156 | ||
1157 | #if __cpp_rtti | |
1158 | virtual const type_info& | |
1159 | target_type() const | |
1160 | { return typeid(_Ex); } | |
1161 | ||
1162 | virtual const void* | |
1163 | target(const std::type_info& __ti) const | |
1164 | { | |
1165 | if (__ti == typeid(_Ex)) | |
1166 | return std::addressof(_M_ex()); | |
1167 | return nullptr; | |
1168 | } | |
1169 | ||
1170 | virtual bool | |
1171 | _M_equals(const _Tgt* __tgt) const noexcept | |
1172 | { | |
1173 | if (__tgt->target_type() == typeid(_Ex)) | |
1174 | *static_cast<const _Ex*>(__tgt->_M_get_executor()) == _M_ex(); | |
1175 | return false; | |
1176 | } | |
1177 | ||
1178 | virtual const void* | |
1179 | _M_get_executor() const noexcept | |
1180 | { return std::addressof(_M_ex()); } | |
1181 | #endif | |
1182 | ||
1183 | _Ex& _M_ex() { return std::get<0>(_M_impl); } | |
1184 | _Alloc& _M_alloc() { return std::get<1>(_M_impl); } | |
1185 | std::tuple<_Ex, _Alloc> _M_impl; | |
1186 | }; | |
1187 | ||
1188 | template<typename _Ex, typename _Alloc = std::allocator<void>> | |
1189 | shared_ptr<_Tgt> | |
1190 | _M_create(_Ex&& __ex, const _Alloc& __a = _Alloc()) | |
1191 | { | |
1192 | return allocate_shared<_TgtImpl<_Ex, _Alloc>>(__a, std::move(__ex), | |
1193 | __a); | |
1194 | } | |
1195 | ||
1196 | friend bool | |
1197 | operator==(const executor& __a, const executor& __b) noexcept | |
1198 | { | |
1199 | if (__a._M_target == __b._M_target) | |
1200 | return true; | |
1201 | if (!__a._M_target || !__b._M_target) | |
1202 | return false; | |
1203 | #if __cpp_rtti | |
1204 | return __a._M_target->_M_equals(__b._M_target.get()); | |
1205 | #else | |
1206 | return false; // XXX can we do better? | |
1207 | #endif | |
1208 | } | |
1209 | ||
1210 | shared_ptr<_Tgt> _M_target; | |
1211 | }; | |
1212 | ||
1213 | template<> struct is_executor<executor> : true_type { }; | |
1214 | ||
1215 | /// executor comparisons | |
1216 | inline bool | |
1217 | operator==(const executor& __e, nullptr_t) noexcept | |
1218 | { return !__e; } | |
1219 | ||
1220 | /// Swap two executor objects. | |
1221 | inline void swap(executor& __a, executor& __b) noexcept { __a.swap(__b); } | |
1222 | ||
1223 | ||
1224 | template<typename _CompletionHandler> | |
1225 | struct __dispatcher | |
1226 | { | |
1227 | explicit | |
1228 | __dispatcher(_CompletionHandler& __h) | |
1229 | : _M_h(std::move(__h)), _M_w(net::make_work_guard(_M_h)) | |
1230 | { } | |
1231 | ||
1232 | void operator()() | |
1233 | { | |
1234 | auto __alloc = net::get_associated_allocator(_M_h); | |
1235 | _M_w.get_executor().dispatch(std::move(_M_h), __alloc); | |
1236 | _M_w.reset(); | |
1237 | } | |
1238 | ||
1239 | _CompletionHandler _M_h; | |
1240 | decltype(net::make_work_guard(_M_h)) _M_w; | |
1241 | }; | |
1242 | ||
1243 | template<typename _CompletionHandler> | |
1244 | inline __dispatcher<_CompletionHandler> | |
1245 | __make_dispatcher(_CompletionHandler& __h) | |
1246 | { return __dispatcher<_CompletionHandler>{__h}; } | |
1247 | ||
1248 | ||
1249 | ||
1250 | // dispatch: | |
1251 | ||
1252 | template<typename _CompletionToken> | |
1253 | inline __deduced_t<_CompletionToken, void()> | |
1254 | dispatch(_CompletionToken&& __token) | |
1255 | { | |
1256 | async_completion<_CompletionToken, void()> __cmpl{__token}; | |
1257 | auto __ex = net::get_associated_executor(__cmpl.completion_handler); | |
1258 | auto __alloc = net::get_associated_allocator(__cmpl.completion_handler); | |
1259 | __ex.dispatch(std::move(__cmpl.completion_handler), __alloc); | |
1260 | return __cmpl.result.get(); | |
1261 | } | |
1262 | ||
1263 | template<typename _Executor, typename _CompletionToken> | |
1264 | inline | |
1265 | enable_if_t<is_executor<_Executor>::value, | |
1266 | __deduced_t<_CompletionToken, void()>> | |
1267 | dispatch(const _Executor& __ex, _CompletionToken&& __token) | |
1268 | { | |
1269 | async_completion<_CompletionToken, void()> __cmpl{__token}; | |
1270 | auto __alloc = net::get_associated_allocator(__cmpl.completion_handler); | |
1271 | __ex.dispatch(net::__make_dispatcher(__cmpl.completion_handler), | |
1272 | __alloc); | |
1273 | return __cmpl.result.get(); | |
1274 | } | |
1275 | ||
1276 | template<typename _ExecutionContext, typename _CompletionToken> | |
1277 | inline | |
1278 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
1279 | __deduced_t<_CompletionToken, void()>> | |
1280 | dispatch(_ExecutionContext& __ctx, _CompletionToken&& __token) | |
1281 | { | |
1282 | return net::dispatch(__ctx.get_executor(), | |
1283 | forward<_CompletionToken>(__token)); | |
1284 | } | |
1285 | ||
1286 | // post: | |
1287 | ||
1288 | template<typename _CompletionToken> | |
1289 | inline __deduced_t<_CompletionToken, void()> | |
1290 | post(_CompletionToken&& __token) | |
1291 | { | |
1292 | async_completion<_CompletionToken, void()> __cmpl{__token}; | |
1293 | auto __ex = net::get_associated_executor(__cmpl.completion_handler); | |
1294 | auto __alloc = net::get_associated_allocator(__cmpl.completion_handler); | |
1295 | __ex.post(std::move(__cmpl.completion_handler), __alloc); | |
1296 | return __cmpl.result.get(); | |
1297 | } | |
1298 | ||
1299 | template<typename _Executor, typename _CompletionToken> | |
1300 | inline | |
1301 | enable_if_t<is_executor<_Executor>::value, | |
1302 | __deduced_t<_CompletionToken, void()>> | |
1303 | post(const _Executor& __ex, _CompletionToken&& __token) | |
1304 | { | |
1305 | async_completion<_CompletionToken, void()> __cmpl{__token}; | |
1306 | auto __alloc = net::get_associated_allocator(__cmpl.completion_handler); | |
1307 | __ex.post(net::__make_dispatcher(__cmpl.completion_handler), __alloc); | |
1308 | return __cmpl.result.get(); | |
1309 | } | |
1310 | ||
1311 | template<typename _ExecutionContext, typename _CompletionToken> | |
1312 | inline | |
1313 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
1314 | __deduced_t<_CompletionToken, void()>> | |
1315 | post(_ExecutionContext& __ctx, _CompletionToken&& __token) | |
1316 | { | |
1317 | return net::post(__ctx.get_executor(), | |
1318 | forward<_CompletionToken>(__token)); | |
1319 | } | |
1320 | ||
1321 | // defer: | |
1322 | ||
1323 | template<typename _CompletionToken> | |
1324 | inline __deduced_t<_CompletionToken, void()> | |
1325 | defer(_CompletionToken&& __token) | |
1326 | { | |
1327 | async_completion<_CompletionToken, void()> __cmpl{__token}; | |
1328 | auto __ex = net::get_associated_executor(__cmpl.completion_handler); | |
1329 | auto __alloc = net::get_associated_allocator(__cmpl.completion_handler); | |
1330 | __ex.defer(std::move(__cmpl.completion_handler), __alloc); | |
1331 | return __cmpl.result.get(); | |
1332 | } | |
1333 | ||
1334 | template<typename _Executor, typename _CompletionToken> | |
1335 | inline | |
1336 | enable_if_t<is_executor<_Executor>::value, | |
1337 | __deduced_t<_CompletionToken, void()>> | |
1338 | defer(const _Executor& __ex, _CompletionToken&& __token) | |
1339 | { | |
1340 | async_completion<_CompletionToken, void()> __cmpl{__token}; | |
1341 | auto __alloc = net::get_associated_allocator(__cmpl.completion_handler); | |
1342 | __ex.defer(net::__make_dispatcher(__cmpl.completion_handler), __alloc); | |
1343 | return __cmpl.result.get(); | |
1344 | } | |
1345 | ||
1346 | template<typename _ExecutionContext, typename _CompletionToken> | |
1347 | inline | |
1348 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
1349 | __deduced_t<_CompletionToken, void()>> | |
1350 | defer(_ExecutionContext& __ctx, _CompletionToken&& __token) | |
1351 | { | |
1352 | return net::defer(__ctx.get_executor(), | |
1353 | forward<_CompletionToken>(__token)); | |
1354 | } | |
1355 | ||
1356 | ||
1357 | template<typename _Executor> | |
1358 | class strand | |
1359 | { | |
1360 | public: | |
1361 | // types: | |
1362 | ||
1363 | typedef _Executor inner_executor_type; | |
1364 | ||
1365 | // construct / copy / destroy: | |
1366 | ||
1367 | strand(); // TODO make state | |
1368 | ||
1369 | explicit strand(_Executor __ex) : _M_inner_ex(__ex) { } // TODO make state | |
1370 | ||
1371 | template<typename _Alloc> | |
1372 | strand(allocator_arg_t, const _Alloc& __a, _Executor __ex) | |
1373 | : _M_inner_ex(__ex) { } // TODO make state | |
1374 | ||
1375 | strand(const strand& __other) noexcept | |
1376 | : _M_state(__other._M_state), _M_inner_ex(__other._M_inner_ex) { } | |
1377 | ||
1378 | strand(strand&& __other) noexcept | |
1379 | : _M_state(std::move(__other._M_state)), | |
1380 | _M_inner_ex(std::move(__other._M_inner_ex)) { } | |
1381 | ||
1382 | template<typename _OtherExecutor> | |
1383 | strand(const strand<_OtherExecutor>& __other) noexcept | |
1384 | : _M_state(__other._M_state), _M_inner_ex(__other._M_inner_ex) { } | |
1385 | ||
1386 | template<typename _OtherExecutor> | |
1387 | strand(strand<_OtherExecutor>&& __other) noexcept | |
1388 | : _M_state(std::move(__other._M_state)), | |
1389 | _M_inner_ex(std::move(__other._M_inner_ex)) { } | |
1390 | ||
1391 | strand& | |
1392 | operator=(const strand& __other) noexcept | |
1393 | { | |
1394 | static_assert(is_copy_assignable<_Executor>::value, | |
1395 | "inner executor type must be CopyAssignable"); | |
1396 | ||
1397 | // TODO lock __other | |
1398 | // TODO copy state | |
1399 | _M_inner_ex = __other._M_inner_ex; | |
1400 | return *this; | |
1401 | } | |
1402 | ||
1403 | strand& | |
1404 | operator=(strand&& __other) noexcept | |
1405 | { | |
1406 | static_assert(is_move_assignable<_Executor>::value, | |
1407 | "inner executor type must be MoveAssignable"); | |
1408 | ||
1409 | // TODO move state | |
1410 | _M_inner_ex = std::move(__other._M_inner_ex); | |
1411 | return *this; | |
1412 | } | |
1413 | ||
1414 | template<typename _OtherExecutor> | |
1415 | strand& | |
1416 | operator=(const strand<_OtherExecutor>& __other) noexcept | |
1417 | { | |
1418 | static_assert(is_convertible<_OtherExecutor, _Executor>::value, | |
1419 | "inner executor type must be compatible"); | |
1420 | ||
1421 | // TODO lock __other | |
1422 | // TODO copy state | |
1423 | _M_inner_ex = __other._M_inner_ex; | |
1424 | return *this; | |
1425 | } | |
1426 | ||
1427 | template<typename _OtherExecutor> | |
1428 | strand& | |
1429 | operator=(strand<_OtherExecutor>&& __other) noexcept | |
1430 | { | |
1431 | static_assert(is_convertible<_OtherExecutor, _Executor>::value, | |
1432 | "inner executor type must be compatible"); | |
1433 | ||
1434 | // TODO move state | |
1435 | _M_inner_ex = std::move(__other._M_inner_ex); | |
1436 | return *this; | |
1437 | } | |
1438 | ||
1439 | ~strand() | |
1440 | { | |
1441 | // the task queue outlives this object if non-empty | |
1442 | // TODO create circular ref in queue? | |
1443 | } | |
1444 | ||
1445 | // strand operations: | |
1446 | ||
1447 | inner_executor_type | |
1448 | get_inner_executor() const noexcept | |
1449 | { return _M_inner_ex; } | |
1450 | ||
1451 | bool | |
1452 | running_in_this_thread() const noexcept | |
1453 | { return std::this_thread::get_id() == _M_state->_M_running_on; } | |
1454 | ||
1455 | execution_context& | |
1456 | context() const noexcept | |
1457 | { return _M_inner_ex.context(); } | |
1458 | ||
1459 | void on_work_started() const noexcept { _M_inner_ex.on_work_started(); } | |
1460 | void on_work_finished() const noexcept { _M_inner_ex.on_work_finished(); } | |
1461 | ||
1462 | template<typename _Func, typename _Alloc> | |
1463 | void | |
1464 | dispatch(_Func&& __f, const _Alloc& __a) const | |
1465 | { | |
1466 | if (running_in_this_thread()) | |
1467 | decay_t<_Func>{std::forward<_Func>(__f)}(); | |
1468 | else | |
1469 | post(std::forward<_Func>(__f), __a); | |
1470 | } | |
1471 | ||
1472 | template<typename _Func, typename _Alloc> | |
1473 | void | |
1474 | post(_Func&& __f, const _Alloc& __a) const; // TODO | |
1475 | ||
1476 | template<typename _Func, typename _Alloc> | |
1477 | void | |
1478 | defer(_Func&& __f, const _Alloc& __a) const | |
1479 | { post(std::forward<_Func>(__f), __a); } | |
1480 | ||
1481 | private: | |
1482 | friend bool | |
1483 | operator==(const strand& __a, const strand& __b) | |
1484 | { return __a._M_state == __b._M_state; } | |
1485 | ||
1486 | // TODO add synchronised queue | |
1487 | struct _State | |
1488 | { | |
1489 | std::thread::id _M_running_on; | |
1490 | }; | |
1491 | shared_ptr<_State> _M_state; | |
1492 | _Executor _M_inner_ex; | |
1493 | }; | |
1494 | ||
1495 | #if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1) | |
1496 | ||
1497 | // Completion token for asynchronous operations initiated with use_future. | |
1498 | template<typename _Func, typename _Alloc> | |
1499 | struct __use_future_ct | |
1500 | { | |
1501 | std::tuple<_Func, _Alloc> _M_t; | |
1502 | }; | |
1503 | ||
1504 | template<typename _ProtoAllocator = allocator<void>> | |
1505 | class use_future_t | |
1506 | { | |
1507 | public: | |
1508 | // use_future_t types: | |
1509 | typedef _ProtoAllocator allocator_type; | |
1510 | ||
1511 | // use_future_t members: | |
1512 | constexpr use_future_t() noexcept : _M_alloc() { } | |
1513 | ||
1514 | explicit | |
1515 | use_future_t(const _ProtoAllocator& __a) noexcept : _M_alloc(__a) { } | |
1516 | ||
1517 | template<class _OtherAllocator> | |
1518 | use_future_t<_OtherAllocator> | |
1519 | rebind(const _OtherAllocator& __a) const noexcept | |
1520 | { return use_future_t<_OtherAllocator>(__a); } | |
1521 | ||
1522 | allocator_type get_allocator() const noexcept { return _M_alloc; } | |
1523 | ||
1524 | template<typename _Func> | |
1525 | auto | |
1526 | operator()(_Func&& __f) const | |
1527 | { | |
1528 | using _Token = __use_future_ct<decay_t<_Func>, _ProtoAllocator>; | |
1529 | return _Token{ {std::forward<_Func>(__f), _M_alloc} }; | |
1530 | } | |
1531 | ||
1532 | private: | |
1533 | _ProtoAllocator _M_alloc; | |
1534 | }; | |
1535 | ||
1536 | constexpr use_future_t<> use_future = use_future_t<>(); | |
1537 | ||
1538 | template<typename _Func, typename _Alloc, typename _Res, typename... _Args> | |
1539 | class async_result<__use_future_ct<_Func, _Alloc>, _Res(_Args...)>; | |
1540 | ||
1541 | template<typename _Result, typename _Executor> | |
1542 | struct __use_future_ex; | |
1543 | ||
1544 | // Completion handler for asynchronous operations initiated with use_future. | |
1545 | template<typename _Func, typename... _Args> | |
1546 | struct __use_future_ch | |
1547 | { | |
1548 | template<typename _Alloc> | |
1549 | explicit | |
1550 | __use_future_ch(__use_future_ct<_Func, _Alloc>&& __token) | |
1551 | : _M_f{ std::move(std::get<0>(__token._M_t)) }, | |
1552 | _M_promise{ std::get<1>(__token._M_t) } | |
1553 | { } | |
1554 | ||
1555 | void | |
1556 | operator()(_Args&&... __args) | |
1557 | { | |
1558 | __try | |
1559 | { | |
1560 | _M_promise.set_value(_M_f(std::forward<_Args>(__args)...)); | |
1561 | } | |
1562 | __catch(__cxxabiv1::__forced_unwind&) | |
1563 | { | |
1564 | __throw_exception_again; | |
1565 | } | |
1566 | __catch(...) | |
1567 | { | |
1568 | _M_promise.set_exception(std::current_exception()); | |
1569 | } | |
1570 | } | |
1571 | ||
1572 | using __result = result_of_t<_Func(decay_t<_Args>...)>; | |
1573 | ||
1574 | future<__result> get_future() { return _M_promise.get_future(); } | |
1575 | ||
1576 | private: | |
1577 | template<typename _Result, typename _Executor> | |
1578 | friend struct __use_future_ex; | |
1579 | ||
1580 | _Func _M_f; | |
1581 | mutable promise<__result> _M_promise; | |
1582 | }; | |
1583 | ||
1584 | // Specialization of async_result for operations initiated with use_future. | |
1585 | template<typename _Func, typename _Alloc, typename _Res, typename... _Args> | |
1586 | class async_result<__use_future_ct<_Func, _Alloc>, _Res(_Args...)> | |
1587 | { | |
1588 | public: | |
1589 | using completion_handler_type = __use_future_ch<_Func, _Args...>; | |
1590 | using return_type = future<typename completion_handler_type::__result>; | |
1591 | ||
1592 | explicit | |
1593 | async_result(completion_handler_type& __h) | |
1594 | : _M_future(__h.get_future()) | |
1595 | { } | |
1596 | ||
1597 | async_result(const async_result&) = delete; | |
1598 | async_result& operator=(const async_result&) = delete; | |
1599 | ||
1600 | return_type get() { return std::move(_M_future); } | |
1601 | ||
1602 | private: | |
1603 | return_type _M_future; | |
1604 | }; | |
1605 | ||
1606 | template<typename _Result, typename _Executor> | |
1607 | struct __use_future_ex | |
1608 | { | |
1609 | template<typename _Handler> | |
1610 | __use_future_ex(const _Handler& __h, _Executor __ex) | |
1611 | : _M_t(__h._M_promise, __ex) | |
1612 | { } | |
1613 | ||
1614 | template<typename _Fn, typename _Alloc> | |
1615 | void | |
1616 | dispatch(_Fn&& __fn) | |
1617 | { | |
1618 | __try | |
1619 | { | |
1620 | std::get<1>(_M_t).dispatch(std::forward<_Fn>(__fn)); | |
1621 | } | |
1622 | __catch(__cxxabiv1::__forced_unwind&) | |
1623 | { | |
1624 | __throw_exception_again; | |
1625 | } | |
1626 | __catch(...) | |
1627 | { | |
1628 | std::get<0>(_M_t).set_exception(std::current_exception()); | |
1629 | } | |
1630 | } | |
1631 | ||
1632 | template<typename _Fn, typename _Alloc> | |
1633 | void | |
1634 | post(_Fn&& __fn) | |
1635 | { | |
1636 | __try | |
1637 | { | |
1638 | std::get<1>(_M_t).post(std::forward<_Fn>(__fn)); | |
1639 | } | |
1640 | __catch(__cxxabiv1::__forced_unwind&) | |
1641 | { | |
1642 | __throw_exception_again; | |
1643 | } | |
1644 | __catch(...) | |
1645 | { | |
1646 | std::get<0>(_M_t).set_exception(std::current_exception()); | |
1647 | } | |
1648 | } | |
1649 | ||
1650 | template<typename _Fn, typename _Alloc> | |
1651 | void | |
1652 | defer(_Fn&& __fn) | |
1653 | { | |
1654 | __try | |
1655 | { | |
1656 | std::get<1>(_M_t).defer(std::forward<_Fn>(__fn)); | |
1657 | } | |
1658 | __catch(__cxxabiv1::__forced_unwind&) | |
1659 | { | |
1660 | __throw_exception_again; | |
1661 | } | |
1662 | __catch(...) | |
1663 | { | |
1664 | std::get<0>(_M_t).set_exception(std::current_exception()); | |
1665 | } | |
1666 | } | |
1667 | ||
1668 | private: | |
1669 | tuple<promise<_Result>&, _Executor> _M_t; | |
1670 | }; | |
1671 | ||
1672 | template<typename _Func, typename... _Args, typename _Executor> | |
1673 | struct associated_executor<__use_future_ch<_Func, _Args...>, _Executor> | |
1674 | { | |
1675 | private: | |
1676 | using __handler = __use_future_ch<_Func, _Args...>; | |
1677 | ||
1678 | using type = __use_future_ex<typename __handler::__result, _Executor>; | |
1679 | ||
1680 | static type | |
1681 | get(const __handler& __h, const _Executor& __ex) | |
1682 | { return { __h, __ex }; } | |
1683 | }; | |
1684 | ||
1685 | #if 0 | |
1686 | ||
1687 | // [async.use.future.traits] | |
1688 | template<typename _Allocator, typename _Ret, typename... _Args> | |
1689 | class handler_type<use_future_t<_Allocator>, _Ret(_Args...)> // TODO uglify name | |
1690 | { | |
1691 | template<typename... _Args> | |
1692 | struct __is_error_result : false_type { }; | |
1693 | ||
1694 | template<typename... _Args> | |
1695 | struct __is_error_result<error_code, _Args...> : true_type { }; | |
1696 | ||
1697 | template<typename... _Args> | |
1698 | struct __is_error_result<exception_ptr, _Args...> : true_type { }; | |
1699 | ||
1700 | static exception_ptr | |
1701 | _S_exptr(exception_ptr& __ex) | |
1702 | { return std::move(__ex); } | |
1703 | ||
1704 | static exception_ptr | |
1705 | _S_exptr(const error_code& __ec) | |
1706 | { return make_exception_ptr(system_error(__ec)); } | |
1707 | ||
1708 | template<bool _IsError, typename... _UArgs> | |
1709 | struct _Type; | |
1710 | ||
1711 | // N == 0 | |
1712 | template<bool _IsError> | |
1713 | struct _Type<_IsError> | |
1714 | { | |
1715 | std::promise<void> _M_promise; | |
1716 | ||
1717 | void | |
1718 | operator()() | |
1719 | { | |
1720 | _M_promise.set_value(); | |
1721 | } | |
1722 | }; | |
1723 | ||
1724 | // N == 1, U0 is error_code or exception_ptr | |
1725 | template<typename _UArg0> | |
1726 | struct _Type<true, _UArg0> | |
1727 | { | |
1728 | std::promise<void> _M_promise; | |
1729 | ||
1730 | template<typename _Arg0> | |
1731 | void | |
1732 | operator()(_Arg0&& __a0) | |
1733 | { | |
1734 | if (__a0) | |
1735 | _M_promise.set_exception(_S_exptr(__a0)); | |
1736 | else | |
1737 | _M_promise.set_value(); | |
1738 | } | |
1739 | }; | |
1740 | ||
1741 | // N == 1, U0 is not error_code or exception_ptr | |
1742 | template<typename _UArg0> | |
1743 | struct _Type<false, _UArg0> | |
1744 | { | |
1745 | std::promise<_UArg0> _M_promise; | |
1746 | ||
1747 | template<typename _Arg0> | |
1748 | void | |
1749 | operator()(_Arg0&& __a0) | |
1750 | { | |
1751 | _M_promise.set_value(std::forward<_Arg0>(__a0)); | |
1752 | } | |
1753 | }; | |
1754 | ||
1755 | // N == 2, U0 is error_code or exception_ptr | |
1756 | template<typename _UArg0, typename _UArg1> | |
1757 | struct _Type<true, _UArg0, _UArg1> | |
1758 | { | |
1759 | std::promise<_UArg1> _M_promise; | |
1760 | ||
1761 | template<typename _Arg0, typename _Arg1> | |
1762 | void | |
1763 | operator()(_Arg0&& __a0, _Arg1&& __a1) | |
1764 | { | |
1765 | if (__a0) | |
1766 | _M_promise.set_exception(_S_exptr(__a0)); | |
1767 | else | |
1768 | _M_promise.set_value(std::forward<_Arg1>(__a1)); | |
1769 | } | |
1770 | }; | |
1771 | ||
1772 | // N >= 2, U0 is not error_code or exception_ptr | |
1773 | template<typename... _UArgs> | |
1774 | struct _Type<false, _UArgs...> | |
1775 | { | |
1776 | static_assert(sizeof...(_UArgs) > 1, "wrong partial specialization"); | |
1777 | ||
1778 | std::promise<tuple<_UArgs...>> _M_promise; | |
1779 | ||
1780 | template<typename... _Args> | |
1781 | void | |
1782 | operator()(_Args&&... __args) | |
1783 | { | |
1784 | _M_promise.set_value( | |
1785 | std::forward_as_tuple(std::forward<_Args>(__args)...)); | |
1786 | } | |
1787 | }; | |
1788 | ||
1789 | // N > 2, U0 is error_code or exception_ptr | |
1790 | template<typename _UArg0, typename... _UArgs> | |
1791 | struct _Type<true, _UArg0, _UArgs...> | |
1792 | { | |
1793 | static_assert(sizeof...(_UArgs) > 1, "wrong partial specialization"); | |
1794 | ||
1795 | std::promise<tuple<_UArgs...>> _M_promise; | |
1796 | ||
1797 | template<typename _Arg0, typename... _Args> | |
1798 | void | |
1799 | operator()(_Arg0&& __a0, _Args&&... __args) | |
1800 | { | |
1801 | if (__a0) | |
1802 | _M_promise.set_exception(_S_exptr(__a0)); | |
1803 | else | |
1804 | _M_promise.set_value( | |
1805 | std::forward_as_tuple(std::forward<_Args>(__args)...)); | |
1806 | } | |
1807 | }; | |
1808 | ||
1809 | public: | |
1810 | using type = | |
1811 | _Type<__is_error_result<_Args...>::value, decay_t<_Args>...>; | |
1812 | }; | |
1813 | ||
1814 | ||
1815 | template<typename _Alloc, typename _Ret, typename... _Args> | |
1816 | struct async_result<use_future_t<_Alloc>, _Ret(_Args...)> | |
1817 | { | |
1818 | using completion_handler_type | |
1819 | = typename handler_type<use_future_t<_Alloc>, _Ret(_Args...)>::type; | |
1820 | ||
1821 | using return_type = void; // XXX TODO ???; | |
1822 | ||
1823 | explicit | |
1824 | async_result(completion_handler_type& __h) : _M_handler(__h) { } | |
1825 | ||
1826 | auto get() { return _M_handler._M_provider.get_future(); } | |
1827 | ||
1828 | async_result(const async_result&) = delete; | |
1829 | async_result& operator=(const async_result&) = delete; | |
1830 | ||
1831 | return_type get() { return _M_handler._M_promise.get_future(); } | |
1832 | ||
1833 | private: | |
1834 | completion_handler_type& _M_handler; | |
1835 | }; | |
1836 | ||
1837 | // TODO specialize associated_executor for | |
1838 | // async_result<use_future_t<A>, Sig>::completion_handler_type | |
1839 | // to use a __use_future_ex | |
1840 | // (probably need to move _Type outside of handler_type so we don't have | |
1841 | // a non-deduced context) | |
1842 | ||
1843 | ||
1844 | #endif | |
1845 | ||
1846 | // [async.packaged.task.specializations] | |
1847 | template<typename _Ret, typename... _Args, typename _Signature> | |
1848 | class async_result<packaged_task<_Ret(_Args...)>, _Signature> | |
1849 | { | |
1850 | public: | |
1851 | using completion_handler_type = packaged_task<_Ret(_Args...)>; | |
1852 | using return_type = future<_Ret>; | |
1853 | ||
1854 | explicit | |
1855 | async_result(completion_handler_type& __h) | |
1856 | : _M_future(__h.get_future()) { } | |
1857 | ||
1858 | async_result(const async_result&) = delete; | |
1859 | async_result& operator=(const async_result&) = delete; | |
1860 | ||
1861 | return_type get() { return std::move(_M_future); } | |
1862 | ||
1863 | private: | |
1864 | return_type _M_future; | |
1865 | }; | |
1866 | ||
1867 | #endif | |
1868 | ||
1869 | /// @} | |
1870 | ||
e5989e71 JW |
1871 | } // namespace v1 |
1872 | } // namespace net | |
1873 | } // namespace experimental | |
1874 | ||
e5989e71 JW |
1875 | template<typename _Alloc> |
1876 | struct uses_allocator<experimental::net::executor, _Alloc> | |
1877 | : true_type {}; | |
1878 | ||
1879 | _GLIBCXX_END_NAMESPACE_VERSION | |
1880 | } // namespace std | |
1881 | ||
1882 | #endif // C++14 | |
1883 | ||
1884 | #endif // _GLIBCXX_EXPERIMENTAL_EXECUTOR |