1 // <experimental/executor> -*- C++ -*-
3 // Copyright (C) 2015-2022 Free Software Foundation, Inc.
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)
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.
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.
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/>.
25 /** @file experimental/executor
26 * This is a TS C++ Library header.
27 * @ingroup networking-ts
30 #ifndef _GLIBCXX_EXPERIMENTAL_EXECUTOR
31 #define _GLIBCXX_EXPERIMENTAL_EXECUTOR 1
33 #pragma GCC system_header
35 #if __cplusplus >= 201402L
38 #include <condition_variable>
45 #include <unordered_map>
46 #include <experimental/netfwd>
47 #include <bits/unique_ptr.h>
48 #include <experimental/bits/net.h>
50 namespace std _GLIBCXX_VISIBILITY(default)
52 _GLIBCXX_BEGIN_NAMESPACE_VERSION
53 namespace experimental
60 /** @addtogroup networking-ts
64 /// Customization point for asynchronous operations.
65 template<typename _CompletionToken, typename _Signature, typename = void>
68 /// Convenience utility to help implement asynchronous operations.
69 template<typename _CompletionToken, typename _Signature>
70 class async_completion;
72 template<typename _Tp, typename _ProtoAlloc, typename = __void_t<>>
73 struct __associated_allocator_impl
75 using type = _ProtoAlloc;
78 _S_get(const _Tp&, const _ProtoAlloc& __a) noexcept { return __a; }
81 template<typename _Tp, typename _ProtoAlloc>
82 struct __associated_allocator_impl<_Tp, _ProtoAlloc,
83 __void_t<typename _Tp::allocator_type>>
85 using type = typename _Tp::allocator_type;
88 _S_get(const _Tp& __t, const _ProtoAlloc&) noexcept
89 { return __t.get_allocator(); }
92 /// Helper to associate an allocator with a type.
93 template<typename _Tp, typename _ProtoAllocator = allocator<void>>
94 struct associated_allocator
95 : __associated_allocator_impl<_Tp, _ProtoAllocator>
99 const _ProtoAllocator& __a = _ProtoAllocator()) noexcept
101 using _Impl = __associated_allocator_impl<_Tp, _ProtoAllocator>;
102 return _Impl::_S_get(__t, __a);
106 /// Alias template for associated_allocator.
107 template<typename _Tp, typename _ProtoAllocator = allocator<void>>
108 using associated_allocator_t
109 = typename associated_allocator<_Tp, _ProtoAllocator>::type;
111 // get_associated_allocator:
113 template<typename _Tp>
114 inline associated_allocator_t<_Tp>
115 get_associated_allocator(const _Tp& __t) noexcept
116 { return associated_allocator<_Tp>::get(__t); }
118 template<typename _Tp, typename _ProtoAllocator>
119 inline associated_allocator_t<_Tp, _ProtoAllocator>
120 get_associated_allocator(const _Tp& __t,
121 const _ProtoAllocator& __a) noexcept
122 { return associated_allocator<_Tp, _ProtoAllocator>::get(__t, __a); }
124 enum class fork_event { prepare, parent, child };
126 /// An extensible, type-safe, polymorphic set of services.
127 class execution_context;
129 class service_already_exists : public logic_error
132 // _GLIBCXX_RESOLVE_LIB_DEFECTS
133 // 3414. service_already_exists has no usable constructors
134 service_already_exists() : logic_error("service already exists") { }
137 template<typename _Tp> struct is_executor;
139 struct executor_arg_t { };
141 constexpr executor_arg_t executor_arg = executor_arg_t();
143 /// Trait for determining whether to construct an object with an executor.
144 template<typename _Tp, typename _Executor> struct uses_executor;
146 template<typename _Tp, typename _Executor, typename = __void_t<>>
147 struct __associated_executor_impl
149 using type = _Executor;
152 _S_get(const _Tp&, const _Executor& __e) noexcept { return __e; }
155 template<typename _Tp, typename _Executor>
156 struct __associated_executor_impl<_Tp, _Executor,
157 __void_t<typename _Tp::executor_type>>
159 using type = typename _Tp::executor_type;
162 _S_get(const _Tp& __t, const _Executor&) noexcept
163 { return __t.get_executor(); }
166 /// Helper to associate an executor with a type.
167 template<typename _Tp, typename _Executor = system_executor>
168 struct associated_executor
169 : __associated_executor_impl<_Tp, _Executor>
172 get(const _Tp& __t, const _Executor& __e = _Executor()) noexcept
173 { return __associated_executor_impl<_Tp, _Executor>::_S_get(__t, __e); }
177 template<typename _Tp, typename _Executor = system_executor>
178 using associated_executor_t
179 = typename associated_executor<_Tp, _Executor>::type;
181 template<typename _ExecutionContext>
182 using __is_exec_context
183 = is_convertible<_ExecutionContext&, execution_context&>;
185 template<typename _Tp>
186 using __executor_t = typename _Tp::executor_type;
188 // get_associated_executor:
190 template<typename _Tp>
191 inline associated_executor_t<_Tp>
192 get_associated_executor(const _Tp& __t) noexcept
193 { return associated_executor<_Tp>::get(__t); }
195 template<typename _Tp, typename _Executor>
197 enable_if_t<is_executor<_Executor>::value,
198 associated_executor_t<_Tp, _Executor>>
199 get_associated_executor(const _Tp& __t, const _Executor& __ex)
200 { return associated_executor<_Tp, _Executor>::get(__t, __ex); }
202 template<typename _Tp, typename _ExecutionContext>
204 enable_if_t<__is_exec_context<_ExecutionContext>::value,
205 associated_executor_t<_Tp, __executor_t<_ExecutionContext>>>
206 get_associated_executor(const _Tp& __t, _ExecutionContext& __ctx) noexcept
207 { return net::get_associated_executor(__t, __ctx.get_executor()); }
210 /// Helper to bind an executor to an object or function.
211 template<typename _Tp, typename _Executor>
212 class executor_binder;
214 template<typename _Tp, typename _Executor, typename _Signature>
215 class async_result<executor_binder<_Tp, _Executor>, _Signature>;
217 template<typename _Tp, typename _Executor, typename _ProtoAllocator>
218 struct associated_allocator<executor_binder<_Tp, _Executor>,
221 template<typename _Tp, typename _Executor, typename _Executor1>
222 struct associated_executor<executor_binder<_Tp, _Executor>, _Executor1>;
226 template<typename _Executor, typename _Tp>
228 enable_if_t<is_executor<_Executor>::value,
229 executor_binder<decay_t<_Tp>, _Executor>>
230 bind_executor(const _Executor& __ex, _Tp&& __t)
231 { return { std::forward<_Tp>(__t), __ex }; }
233 template<typename _ExecutionContext, typename _Tp>
235 enable_if_t<__is_exec_context<_ExecutionContext>::value,
236 executor_binder<decay_t<_Tp>, __executor_t<_ExecutionContext>>>
237 bind_executor(_ExecutionContext& __ctx, _Tp&& __t)
238 { return { __ctx.get_executor(), forward<_Tp>(__t) }; }
241 /// A scope-guard type to record when work is started and finished.
242 template<typename _Executor>
243 class executor_work_guard;
247 template<typename _Executor>
249 enable_if_t<is_executor<_Executor>::value, executor_work_guard<_Executor>>
250 make_work_guard(const _Executor& __ex)
251 { return executor_work_guard<_Executor>(__ex); }
253 template<typename _ExecutionContext>
255 enable_if_t<__is_exec_context<_ExecutionContext>::value,
256 executor_work_guard<__executor_t<_ExecutionContext>>>
257 make_work_guard(_ExecutionContext& __ctx)
258 { return net::make_work_guard(__ctx.get_executor()); }
260 template<typename _Tp>
262 enable_if_t<__not_<__or_<is_executor<_Tp>, __is_exec_context<_Tp>>>::value,
263 executor_work_guard<associated_executor_t<_Tp>>>
264 make_work_guard(const _Tp& __t)
265 { return net::get_associated_executor(__t); }
267 template<typename _Tp, typename _Up>
269 make_work_guard(const _Tp& __t, _Up&& __u)
270 -> decltype(net::make_work_guard(
271 net::get_associated_executor(__t, forward<_Up>(__u))))
273 return net::make_work_guard(
274 net::get_associated_executor(__t, forward<_Up>(__u)));
277 /// Allows function objects to execute on any thread.
278 class system_executor;
280 /// The execution context associated with system_executor objects.
281 class system_context;
284 operator==(const system_executor&, const system_executor&) { return true; }
287 operator!=(const system_executor&, const system_executor&) { return false; }
289 /// Exception thrown by empty executors.
292 /// Polymorphic wrapper for types satisfying the Executor requirements.
296 operator==(const executor&, const executor&) noexcept;
299 operator==(const executor&, nullptr_t) noexcept;
302 operator==(nullptr_t, const executor&) noexcept;
305 operator!=(const executor&, const executor&) noexcept;
308 operator!=(const executor&, nullptr_t) noexcept;
311 operator!=(nullptr_t, const executor&) noexcept;
313 void swap(executor&, executor&) noexcept;
317 template<typename _CompletionToken>
318 __deduced_t<_CompletionToken, void()>
319 dispatch(_CompletionToken&& __token);
321 template<typename _Executor, typename _CompletionToken>
322 __deduced_t<_CompletionToken, void()>
323 dispatch(const _Executor& __ex, _CompletionToken&& __token);
325 template<typename _ExecutionContext, typename _CompletionToken>
326 __deduced_t<_CompletionToken, void()>
327 dispatch(_ExecutionContext& __ctx, _CompletionToken&& __token);
331 template<typename _CompletionToken>
332 __deduced_t<_CompletionToken, void()>
333 post(_CompletionToken&& __token);
334 template<typename _Executor, typename _CompletionToken>
335 enable_if_t<is_executor<_Executor>::value,
336 __deduced_t<_CompletionToken, void()>>
337 post(const _Executor& __ex, _CompletionToken&& __token);
338 template<typename _ExecutionContext, typename _CompletionToken>
339 enable_if_t<__is_exec_context<_ExecutionContext>::value,
340 __deduced_t<_CompletionToken, void()>>
341 post(_ExecutionContext& __ctx, _CompletionToken&& __token);
345 template<typename _CompletionToken>
346 __deduced_t<_CompletionToken, void()>
347 defer(_CompletionToken&& __token);
348 template<typename _Executor, typename _CompletionToken>
349 __deduced_t<_CompletionToken, void()>
350 defer(const _Executor& __ex, _CompletionToken&& __token);
351 template<typename _ExecutionContext, typename _CompletionToken>
352 __deduced_t<_CompletionToken, void()>
353 defer(_ExecutionContext& __ctx, _CompletionToken&& __token);
355 template<typename _Executor>
358 template<typename _Executor>
360 operator==(const strand<_Executor>& __a, const strand<_Executor>& __b);
362 template<typename _Executor>
364 operator!=(const strand<_Executor>& __a, const strand<_Executor>& __b)
365 { return !(__a == __b); }
367 template<typename _CompletionToken, typename _Signature, typename>
371 using completion_handler_type = _CompletionToken;
372 using return_type = void;
374 explicit async_result(completion_handler_type&) {}
375 async_result(const async_result&) = delete;
376 async_result& operator=(const async_result&) = delete;
381 template<typename _CompletionToken, typename _Signature>
382 class async_completion
385 = async_result<decay_t<_CompletionToken>, _Signature>;
388 using completion_handler_type
389 = typename __result_type::completion_handler_type;
392 using __handler_type = __conditional_t<
393 is_same<_CompletionToken, completion_handler_type>::value,
394 completion_handler_type&,
395 completion_handler_type>;
399 async_completion(_CompletionToken& __t)
400 : completion_handler(std::forward<__handler_type>(__t)),
401 result(completion_handler)
404 async_completion(const async_completion&) = delete;
405 async_completion& operator=(const async_completion&) = delete;
407 __handler_type completion_handler;
408 __result_type result;
412 class execution_context
418 // construct / copy / destroy:
421 service(execution_context& __owner) : _M_context(__owner) { }
423 service(const service&) = delete;
424 service& operator=(const service&) = delete;
426 virtual ~service() { } // TODO should not be inline
428 // service observers:
430 execution_context& context() const noexcept { return _M_context; }
433 // service operations:
435 virtual void shutdown() noexcept = 0;
436 virtual void notify_fork(fork_event) { }
438 friend class execution_context;
439 execution_context& _M_context;
442 // construct / copy / destroy:
444 execution_context() { }
446 execution_context(const execution_context&) = delete;
447 execution_context& operator=(const execution_context&) = delete;
449 virtual ~execution_context()
455 // execution context operations:
458 notify_fork(fork_event __e)
460 auto __l = [=](auto& __svc) { __svc._M_ptr->notify_fork(__e); };
461 if (__e == fork_event::prepare)
462 std::for_each(_M_services.rbegin(), _M_services.rend(), __l);
464 std::for_each(_M_services.begin(), _M_services.end(), __l);
468 // execution context protected operations:
473 std::for_each(_M_services.rbegin(), _M_services.rend(),
477 __svc._M_ptr->shutdown();
478 __svc._M_active = false;
486 while (_M_services.size())
487 _M_services.pop_back();
493 template<typename _Service>
495 _S_deleter(service* __svc) { delete static_cast<_Service*>(__svc); }
499 template<typename _Service>
501 _ServicePtr(_Service* __svc)
502 : _M_ptr(__svc, &_S_deleter<_Service>), _M_active(true) { }
504 std::unique_ptr<service, void(*)(service*)> _M_ptr;
508 #if defined(_GLIBCXX_HAS_GTHREADS)
509 using mutex_type = std::mutex;
513 void lock() const { }
514 void unlock() const { }
517 mutable mutex_type _M_mutex;
519 // Sorted in order of beginning of service object lifetime.
520 std::list<_ServicePtr> _M_services;
522 template<typename _Service, typename... _Args>
524 _M_add_svc(_Args&&... __args)
526 _M_services.push_back(
527 _ServicePtr{new _Service{*this, std::forward<_Args>(__args)...}} );
528 return _M_services.back()._M_ptr.get();
531 using __key_type = void(*)();
533 template<typename _Key>
535 _S_key() { return reinterpret_cast<__key_type>(&_S_key<_Key>); }
537 std::unordered_map<__key_type, service*> _M_keys;
539 template<typename _Service>
540 friend typename _Service::key_type&
541 use_service(execution_context&);
543 template<typename _Service, typename... _Args>
545 make_service(execution_context&, _Args&&...);
547 template<typename _Service>
549 has_service(const execution_context&) noexcept;
554 template<typename _Service>
555 typename _Service::key_type&
556 use_service(execution_context& __ctx)
558 using _Key = typename _Service::key_type;
559 static_assert(is_base_of<execution_context::service, _Key>::value,
560 "a service type must derive from execution_context::service");
561 static_assert(is_base_of<_Key, _Service>::value,
562 "a service type must match or derive from its key_type");
563 auto __key = execution_context::_S_key<_Key>();
564 lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex);
565 auto& __svc = __ctx._M_keys[__key];
566 if (__svc == nullptr)
569 __svc = __ctx._M_add_svc<_Service>();
571 __ctx._M_keys.erase(__key);
572 __throw_exception_again;
575 return static_cast<_Key&>(*__svc);
578 template<typename _Service, typename... _Args>
580 make_service(execution_context& __ctx, _Args&&... __args)
582 using _Key = typename _Service::key_type;
583 static_assert(is_base_of<execution_context::service, _Key>::value,
584 "a service type must derive from execution_context::service");
585 static_assert(is_base_of<_Key, _Service>::value,
586 "a service type must match or derive from its key_type");
587 auto __key = execution_context::_S_key<_Key>();
588 lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex);
589 auto& __svc = __ctx._M_keys[__key];
590 if (__svc != nullptr)
591 throw service_already_exists();
593 __svc = __ctx._M_add_svc<_Service>(std::forward<_Args>(__args)...);
595 __ctx._M_keys.erase(__key);
596 __throw_exception_again;
598 return static_cast<_Service&>(*__svc);
601 template<typename _Service>
603 has_service(const execution_context& __ctx) noexcept
605 using _Key = typename _Service::key_type;
606 static_assert(is_base_of<execution_context::service, _Key>::value,
607 "a service type must derive from execution_context::service");
608 static_assert(is_base_of<_Key, _Service>::value,
609 "a service type must match or derive from its key_type");
610 lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex);
611 return __ctx._M_keys.count(execution_context::_S_key<_Key>());
614 template<typename _Tp, typename = __void_t<>>
615 struct __is_executor_impl : false_type
618 // Check Executor requirements.
619 template<typename _Tp, typename _Up = remove_const_t<_Tp>>
621 __executor_reqs(_Up* __x = 0, const _Up* __cx = 0, void(*__f)() = 0,
622 const allocator<int>& __a = {})
623 -> enable_if_t<__is_value_constructible<_Tp>::value, __void_t<
624 decltype(*__cx == *__cx),
625 decltype(*__cx != *__cx),
626 decltype(__x->context()),
627 decltype(__x->on_work_started()),
628 decltype(__x->on_work_finished()),
629 decltype(__x->dispatch(std::move(__f), __a)),
630 decltype(__x->post(std::move(__f), __a)),
631 decltype(__x->defer(std::move(__f), __a))
634 template<typename _Tp>
635 struct __is_executor_impl<_Tp, decltype(__executor_reqs<_Tp>())>
639 template<typename _Tp>
640 struct is_executor : __is_executor_impl<_Tp>
643 template<typename _Tp>
644 constexpr bool is_executor_v = is_executor<_Tp>::value;
646 template<typename _Tp, typename _Executor, typename = __void_t<>>
647 struct __uses_executor_impl : false_type
650 template<typename _Tp, typename _Executor>
651 struct __uses_executor_impl<_Tp, _Executor,
652 __void_t<typename _Tp::executor_type>>
653 : is_convertible<_Executor, typename _Tp::executor_type>
656 template<typename _Tp, typename _Executor>
657 struct uses_executor : __uses_executor_impl<_Tp, _Executor>::type
660 template<typename _Tp, typename _Executor>
661 constexpr bool uses_executor_v = uses_executor<_Tp, _Executor>::value;
663 template<typename _Tp, typename _Executor>
664 class executor_binder
666 struct __use_exec { };
671 using target_type = _Tp;
672 using executor_type = _Executor;
674 // construct / copy / destroy:
676 executor_binder(_Tp __t, const _Executor& __ex)
677 : executor_binder(__use_exec{}, std::move(__t), __ex)
680 executor_binder(const executor_binder&) = default;
681 executor_binder(executor_binder&&) = default;
683 template<typename _Up, typename _OtherExecutor>
684 executor_binder(const executor_binder<_Up, _OtherExecutor>& __other)
685 : executor_binder(__use_exec{}, __other.get(), __other.get_executor())
688 template<typename _Up, typename _OtherExecutor>
689 executor_binder(executor_binder<_Up, _OtherExecutor>&& __other)
690 : executor_binder(__use_exec{}, std::move(__other.get()),
691 __other.get_executor())
694 template<typename _Up, typename _OtherExecutor>
695 executor_binder(executor_arg_t, const _Executor& __ex,
696 const executor_binder<_Up, _OtherExecutor>& __other)
697 : executor_binder(__use_exec{}, __other.get(), __ex)
700 template<typename _Up, typename _OtherExecutor>
701 executor_binder(executor_arg_t, const _Executor& __ex,
702 executor_binder<_Up, _OtherExecutor>&& __other)
703 : executor_binder(__use_exec{}, std::move(__other.get()), __ex)
708 // executor binder access:
710 _Tp& get() noexcept { return _M_target; }
711 const _Tp& get() const noexcept { return _M_target; }
712 executor_type get_executor() const noexcept { return _M_ex; }
714 // executor binder invocation:
716 template<class... _Args>
717 result_of_t<_Tp&(_Args&&...)>
718 operator()(_Args&&... __args)
719 { return std::__invoke(get(), std::forward<_Args>(__args)...); }
721 template<class... _Args>
722 result_of_t<const _Tp&(_Args&&...)>
723 operator()(_Args&&... __args) const
724 { return std::__invoke(get(), std::forward<_Args>(__args)...); }
727 template<typename _Up>
728 using __use_exec_cond
729 = __and_<uses_executor<_Tp, _Executor>,
730 is_constructible<_Tp, executor_arg_t, _Executor, _Up>>;
732 template<typename _Up, typename _Exec, typename =
733 enable_if_t<__use_exec_cond<_Up>::value>>
734 executor_binder(__use_exec, _Up&& __u, _Exec&& __ex)
735 : _M_ex(std::forward<_Exec>(__ex)),
736 _M_target(executor_arg, _M_ex, std::forward<_Up>(__u))
739 template<typename _Up, typename _Exec, typename =
740 enable_if_t<!__use_exec_cond<_Up>::value>>
741 executor_binder(__use_exec, _Up&& __u, const _Exec& __ex)
742 : _M_ex(std::forward<_Exec>(__ex)),
743 _M_target(std::forward<_Up>(__u))
750 template<typename _Tp, typename _Executor, typename _Signature>
751 class async_result<executor_binder<_Tp, _Executor>, _Signature>
753 using __inner = async_result<_Tp, _Signature>;
756 using completion_handler_type =
757 executor_binder<typename __inner::completion_handler_type, _Executor>;
759 using return_type = typename __inner::return_type;
762 async_result(completion_handler_type& __h)
763 : _M_target(__h.get()) { }
765 async_result(const async_result&) = delete;
766 async_result& operator=(const async_result&) = delete;
768 return_type get() { return _M_target.get(); }
774 template<typename _Tp, typename _Executor, typename _ProtoAlloc>
775 struct associated_allocator<executor_binder<_Tp, _Executor>, _ProtoAlloc>
777 using type = associated_allocator_t<_Tp, _ProtoAlloc>;
780 get(const executor_binder<_Tp, _Executor>& __b,
781 const _ProtoAlloc& __a = _ProtoAlloc()) noexcept
782 { return associated_allocator<_Tp, _ProtoAlloc>::get(__b.get(), __a); }
785 template<typename _Tp, typename _Executor, typename _Executor1>
786 struct associated_executor<executor_binder<_Tp, _Executor>, _Executor1>
788 using type = _Executor;
791 get(const executor_binder<_Tp, _Executor>& __b,
792 const _Executor1& = _Executor1()) noexcept
793 { return __b.get_executor(); }
796 template<typename _Executor>
797 class executor_work_guard
802 using executor_type = _Executor;
804 // construct / copy / destroy:
807 executor_work_guard(const executor_type& __ex) noexcept
808 : _M_ex(__ex), _M_owns(true)
809 { _M_ex.on_work_started(); }
811 executor_work_guard(const executor_work_guard& __other) noexcept
812 : _M_ex(__other._M_ex), _M_owns(__other._M_owns)
815 _M_ex.on_work_started();
818 executor_work_guard(executor_work_guard&& __other) noexcept
819 : _M_ex(__other._M_ex), _M_owns(__other._M_owns)
820 { __other._M_owns = false; }
822 executor_work_guard& operator=(const executor_work_guard&) = delete;
824 ~executor_work_guard()
827 _M_ex.on_work_finished();
830 // executor work guard observers:
832 executor_type get_executor() const noexcept { return _M_ex; }
834 bool owns_work() const noexcept { return _M_owns; }
836 // executor work guard modifiers:
838 void reset() noexcept
841 _M_ex.on_work_finished();
851 class system_context : public execution_context
856 using executor_type = system_executor;
858 // construct / copy / destroy:
860 system_context() = delete;
861 system_context(const system_context&) = delete;
862 system_context& operator=(const system_context&) = delete;
870 // system_context operations:
872 executor_type get_executor() noexcept;
876 lock_guard<mutex_type> __lock(_M_mtx);
881 bool stopped() const noexcept
883 lock_guard<mutex_type> __lock(_M_mtx);
889 if (_M_thread.joinable())
894 friend system_executor;
896 struct __tag { explicit __tag() = default; };
897 system_context(__tag) { }
899 #ifndef _GLIBCXX_HAS_GTHREADS
902 bool joinable() const { return false; }
905 struct condition_variable
907 void notify_all() { }
912 mutable mutex_type _M_mtx; // XXX can we reuse base's _M_mutex?
913 condition_variable _M_cv;
914 queue<function<void()>> _M_tasks;
915 bool _M_stopped = false;
917 #ifdef _GLIBCXX_HAS_GTHREADS
923 function<void()> __f;
925 unique_lock<mutex_type> __lock(_M_mtx);
927 [this]{ return _M_stopped || !_M_tasks.empty(); });
930 __f = std::move(_M_tasks.front());
939 _M_post(std::function<void()> __f __attribute__((__unused__)))
941 lock_guard<mutex_type> __lock(_M_mtx);
944 #ifdef _GLIBCXX_HAS_GTHREADS
945 if (!_M_thread.joinable())
946 _M_thread = std::thread(&system_context::_M_run, this);
947 _M_tasks.push(std::move(__f)); // XXX allocator not used
950 __throw_system_error(EOPNOTSUPP);
954 static system_context&
957 static system_context __sc(__tag{});
962 class system_executor
965 // executor operations:
967 system_executor() { }
970 context() const noexcept { return system_context::_S_get(); }
972 void on_work_started() const noexcept { }
973 void on_work_finished() const noexcept { }
975 template<typename _Func, typename _ProtoAlloc>
977 dispatch(_Func&& __f, const _ProtoAlloc& __a) const
978 { decay_t<_Func>{std::forward<_Func>(__f)}(); }
980 template<typename _Func, typename _ProtoAlloc>
982 post(_Func&& __f, const _ProtoAlloc&) const // XXX allocator not used
984 system_context::_S_get()._M_post(std::forward<_Func>(__f));
987 template<typename _Func, typename _ProtoAlloc>
989 defer(_Func&& __f, const _ProtoAlloc& __a) const
990 { post(std::forward<_Func>(__f), __a); }
993 inline system_executor
994 system_context::get_executor() noexcept
997 class bad_executor : public std::exception
999 virtual const char* what() const noexcept { return "bad executor"; }
1002 inline void __throw_bad_executor() // TODO make non-inline
1004 #if __cpp_exceptions
1005 throw bad_executor();
1014 // construct / copy / destroy:
1016 executor() noexcept = default;
1018 executor(nullptr_t) noexcept { }
1019 executor(const executor&) noexcept = default;
1020 executor(executor&&) noexcept = default;
1022 template<typename _Executor>
1023 executor(_Executor __e)
1024 : _M_target(make_shared<_Tgt1<_Executor>>(std::move(__e)))
1027 template<typename _Executor, typename _ProtoAlloc>
1028 executor(allocator_arg_t, const _ProtoAlloc& __a, _Executor __e)
1029 : _M_target(allocate_shared<_Tgt2<_Executor, _ProtoAlloc>>(__a,
1030 std::move(__e), __a))
1033 executor& operator=(const executor&) noexcept = default;
1034 executor& operator=(executor&&) noexcept = default;
1037 operator=(nullptr_t) noexcept
1039 _M_target = nullptr;
1043 template<typename _Executor>
1045 operator=(_Executor __e)
1047 executor(std::move(__e)).swap(*this);
1051 ~executor() = default;
1053 // executor modifiers:
1056 swap(executor& __other) noexcept
1057 { _M_target.swap(__other._M_target); }
1059 template<typename _Executor, typename _Alloc>
1061 assign(_Executor __e, const _Alloc& __a)
1062 { executor(allocator_arg, __a, std::move(__e)).swap(*this); }
1064 // executor operations:
1067 context() const noexcept
1069 __glibcxx_assert( _M_target );
1070 return _M_target->context();
1074 on_work_started() const noexcept
1076 __glibcxx_assert( _M_target );
1077 return _M_target->on_work_started();
1081 on_work_finished() const noexcept
1083 __glibcxx_assert( _M_target );
1084 return _M_target->on_work_finished();
1087 template<typename _Func, typename _Alloc>
1089 dispatch(_Func&& __f, const _Alloc& __a) const
1092 __throw_bad_executor();
1093 // _M_target->dispatch({allocator_arg, __a, std::forward<_Func>(__f)});
1094 _M_target->dispatch(std::forward<_Func>(__f));
1097 template<typename _Func, typename _Alloc>
1099 post(_Func&& __f, const _Alloc& __a) const
1102 __throw_bad_executor();
1103 // _M_target->post({allocator_arg, __a, std::forward<_Func>(__f)});
1104 _M_target->post(std::forward<_Func>(__f));
1107 template<typename _Func, typename _Alloc>
1109 defer(_Func&& __f, const _Alloc& __a) const
1112 __throw_bad_executor();
1113 // _M_target->defer({allocator_arg, __a, std::forward<_Func>(__f)});
1114 _M_target->defer(std::forward<_Func>(__f));
1117 // executor capacity:
1119 explicit operator bool() const noexcept
1120 { return static_cast<bool>(_M_target); }
1122 // executor target access:
1126 target_type() const noexcept
1129 return *static_cast<const type_info*>(_M_target->target_type());
1130 return typeid(void);
1134 template<typename _Executor>
1138 void* __p = nullptr;
1141 if (_M_target->_M_func == &_Tgt1<remove_cv_t<_Executor>>::_S_func)
1142 __p = _M_target->_M_func(_M_target.get(), nullptr);
1145 __p = _M_target->target(&typeid(_Executor));
1148 return static_cast<_Executor*>(__p);
1151 template<typename _Executor>
1153 target() const noexcept
1155 const void* __p = nullptr;
1158 if (_M_target->_M_func == &_Tgt1<remove_cv_t<_Executor>>::_S_func)
1159 return (_Executor*)_M_target->_M_func(_M_target.get(), nullptr);
1162 __p = _M_target->target(&typeid(_Executor));
1165 return static_cast<const _Executor*>(__p);
1171 virtual void on_work_started() const noexcept = 0;
1172 virtual void on_work_finished() const noexcept = 0;
1173 virtual execution_context& context() const noexcept = 0;
1174 virtual void dispatch(std::function<void()>) const = 0;
1175 virtual void post(std::function<void()>) const = 0;
1176 virtual void defer(std::function<void()>) const = 0;
1177 virtual const void* target_type() const noexcept = 0;
1178 virtual void* target(const void*) noexcept = 0;
1179 virtual bool _M_equals(_Tgt*) const noexcept = 0;
1181 using _Func = void* (_Tgt*, const _Tgt*);
1182 _Func* _M_func; // Provides access to target without RTTI
1185 template<typename _Ex>
1190 : _M_ex(std::move(__ex))
1191 { this->_M_func = &_S_func; }
1194 on_work_started() const noexcept override
1195 { _M_ex.on_work_started(); }
1198 on_work_finished() const noexcept override
1199 { _M_ex.on_work_finished(); }
1202 context() const noexcept override
1203 { return _M_ex.context(); }
1206 dispatch(std::function<void()> __f) const override
1207 { _M_ex.dispatch(std::move(__f), allocator<void>()); }
1210 post(std::function<void()> __f) const override
1211 { _M_ex.post(std::move(__f), allocator<void>()); }
1214 defer(std::function<void()> __f) const override
1215 { _M_ex.defer(std::move(__f), allocator<void>()); }
1218 target_type() const noexcept override
1221 return &typeid(_Ex);
1228 target(const void* __ti) noexcept override
1231 if (*static_cast<const type_info*>(__ti) == typeid(_Ex))
1232 return std::__addressof(_M_ex);
1238 _M_equals(_Tgt* __tgt) const noexcept override
1241 if (const void* __p = __tgt->target(&typeid(_Ex)))
1242 return *static_cast<const _Ex*>(__p) == _M_ex;
1247 _Ex _M_ex [[__no_unique_address__]];
1250 _S_func(_Tgt* __p, const _Tgt* __q) noexcept
1252 auto& __ex = static_cast<_Tgt1*>(__p)->_M_ex;
1255 if (__ex == static_cast<const _Tgt1*>(__q)->_M_ex)
1261 return std::__addressof(__ex);
1265 template<typename _Ex, typename _Alloc>
1266 struct _Tgt2 : _Tgt1<_Ex>
1269 _Tgt2(_Ex&& __ex, const _Alloc& __a)
1270 : _Tgt1<_Ex>(std::move(__ex)), _M_alloc(__a) { }
1273 dispatch(std::function<void()> __f) const override
1274 { this->_M_ex.dispatch(std::move(__f), _M_alloc); }
1277 post(std::function<void()> __f) const override
1278 { this->_M_ex.post(std::move(__f), _M_alloc); }
1281 defer(std::function<void()> __f) const override
1282 { this->_M_ex.defer(std::move(__f), _M_alloc); }
1284 _Alloc _M_alloc [[__no_unique_address__]];
1287 // Partial specialization for std::allocator<T>.
1288 // Don't store the allocator.
1289 template<typename _Ex, typename _Tp>
1290 struct _Tgt2<_Ex, std::allocator<_Tp>> : _Tgt1<_Ex>
1294 operator==(const executor& __a, const executor& __b) noexcept
1296 _Tgt* __ta = __a._M_target.get();
1297 _Tgt* __tb = __b._M_target.get();
1302 if (__ta->_M_func == __tb->_M_func)
1303 return __ta->_M_func(__ta, __tb);
1304 return __ta->_M_equals(__tb);
1307 shared_ptr<_Tgt> _M_target;
1310 template<> struct is_executor<executor> : true_type { };
1312 /// executor comparisons
1314 operator==(const executor& __e, nullptr_t) noexcept
1318 operator==(nullptr_t, const executor& __e) noexcept
1322 operator!=(const executor& __a, const executor& __b) noexcept
1323 { return !(__a == __b); }
1326 operator!=(const executor& __e, nullptr_t) noexcept
1327 { return (bool)__e; }
1330 operator!=(nullptr_t, const executor& __e) noexcept
1331 { return (bool)__e; }
1333 /// Swap two executor objects.
1334 inline void swap(executor& __a, executor& __b) noexcept { __a.swap(__b); }
1337 template<typename _CompletionHandler>
1341 __dispatcher(_CompletionHandler& __h)
1342 : _M_h(std::move(__h)), _M_w(net::make_work_guard(_M_h))
1347 auto __alloc = net::get_associated_allocator(_M_h);
1348 _M_w.get_executor().dispatch(std::move(_M_h), __alloc);
1352 _CompletionHandler _M_h;
1353 decltype(net::make_work_guard(_M_h)) _M_w;
1356 template<typename _CompletionHandler>
1357 inline __dispatcher<_CompletionHandler>
1358 __make_dispatcher(_CompletionHandler& __h)
1359 { return __dispatcher<_CompletionHandler>{__h}; }
1365 template<typename _CompletionToken>
1366 inline __deduced_t<_CompletionToken, void()>
1367 dispatch(_CompletionToken&& __token)
1369 async_completion<_CompletionToken, void()> __cmpl{__token};
1370 auto __ex = net::get_associated_executor(__cmpl.completion_handler);
1371 auto __alloc = net::get_associated_allocator(__cmpl.completion_handler);
1372 __ex.dispatch(std::move(__cmpl.completion_handler), __alloc);
1373 return __cmpl.result.get();
1376 template<typename _Executor, typename _CompletionToken>
1378 enable_if_t<is_executor<_Executor>::value,
1379 __deduced_t<_CompletionToken, void()>>
1380 dispatch(const _Executor& __ex, _CompletionToken&& __token)
1382 async_completion<_CompletionToken, void()> __cmpl{__token};
1383 auto __alloc = net::get_associated_allocator(__cmpl.completion_handler);
1384 __ex.dispatch(net::__make_dispatcher(__cmpl.completion_handler),
1386 return __cmpl.result.get();
1389 template<typename _ExecutionContext, typename _CompletionToken>
1391 enable_if_t<__is_exec_context<_ExecutionContext>::value,
1392 __deduced_t<_CompletionToken, void()>>
1393 dispatch(_ExecutionContext& __ctx, _CompletionToken&& __token)
1395 return net::dispatch(__ctx.get_executor(),
1396 forward<_CompletionToken>(__token));
1401 template<typename _CompletionToken>
1402 inline __deduced_t<_CompletionToken, void()>
1403 post(_CompletionToken&& __token)
1405 async_completion<_CompletionToken, void()> __cmpl{__token};
1406 auto __ex = net::get_associated_executor(__cmpl.completion_handler);
1407 auto __alloc = net::get_associated_allocator(__cmpl.completion_handler);
1408 __ex.post(std::move(__cmpl.completion_handler), __alloc);
1409 return __cmpl.result.get();
1412 template<typename _Executor, typename _CompletionToken>
1414 enable_if_t<is_executor<_Executor>::value,
1415 __deduced_t<_CompletionToken, void()>>
1416 post(const _Executor& __ex, _CompletionToken&& __token)
1418 async_completion<_CompletionToken, void()> __cmpl{__token};
1419 auto __alloc = net::get_associated_allocator(__cmpl.completion_handler);
1420 __ex.post(net::__make_dispatcher(__cmpl.completion_handler), __alloc);
1421 return __cmpl.result.get();
1424 template<typename _ExecutionContext, typename _CompletionToken>
1426 enable_if_t<__is_exec_context<_ExecutionContext>::value,
1427 __deduced_t<_CompletionToken, void()>>
1428 post(_ExecutionContext& __ctx, _CompletionToken&& __token)
1430 return net::post(__ctx.get_executor(),
1431 forward<_CompletionToken>(__token));
1436 template<typename _CompletionToken>
1437 inline __deduced_t<_CompletionToken, void()>
1438 defer(_CompletionToken&& __token)
1440 async_completion<_CompletionToken, void()> __cmpl{__token};
1441 auto __ex = net::get_associated_executor(__cmpl.completion_handler);
1442 auto __alloc = net::get_associated_allocator(__cmpl.completion_handler);
1443 __ex.defer(std::move(__cmpl.completion_handler), __alloc);
1444 return __cmpl.result.get();
1447 template<typename _Executor, typename _CompletionToken>
1449 enable_if_t<is_executor<_Executor>::value,
1450 __deduced_t<_CompletionToken, void()>>
1451 defer(const _Executor& __ex, _CompletionToken&& __token)
1453 async_completion<_CompletionToken, void()> __cmpl{__token};
1454 auto __alloc = net::get_associated_allocator(__cmpl.completion_handler);
1455 __ex.defer(net::__make_dispatcher(__cmpl.completion_handler), __alloc);
1456 return __cmpl.result.get();
1459 template<typename _ExecutionContext, typename _CompletionToken>
1461 enable_if_t<__is_exec_context<_ExecutionContext>::value,
1462 __deduced_t<_CompletionToken, void()>>
1463 defer(_ExecutionContext& __ctx, _CompletionToken&& __token)
1465 return net::defer(__ctx.get_executor(),
1466 forward<_CompletionToken>(__token));
1470 template<typename _Executor>
1476 using inner_executor_type = _Executor;
1478 // construct / copy / destroy:
1480 strand(); // TODO make state
1482 explicit strand(_Executor __ex) : _M_inner_ex(__ex) { } // TODO make state
1484 template<typename _Alloc>
1485 strand(allocator_arg_t, const _Alloc& __a, _Executor __ex)
1486 : _M_inner_ex(__ex) { } // TODO make state
1488 strand(const strand& __other) noexcept
1489 : _M_state(__other._M_state), _M_inner_ex(__other._M_inner_ex) { }
1491 strand(strand&& __other) noexcept
1492 : _M_state(std::move(__other._M_state)),
1493 _M_inner_ex(std::move(__other._M_inner_ex)) { }
1495 template<typename _OtherExecutor>
1496 strand(const strand<_OtherExecutor>& __other) noexcept
1497 : _M_state(__other._M_state), _M_inner_ex(__other._M_inner_ex) { }
1499 template<typename _OtherExecutor>
1500 strand(strand<_OtherExecutor>&& __other) noexcept
1501 : _M_state(std::move(__other._M_state)),
1502 _M_inner_ex(std::move(__other._M_inner_ex)) { }
1505 operator=(const strand& __other) noexcept
1507 static_assert(is_copy_assignable<_Executor>::value,
1508 "inner executor type must be CopyAssignable");
1510 // TODO lock __other
1512 _M_inner_ex = __other._M_inner_ex;
1517 operator=(strand&& __other) noexcept
1519 static_assert(is_move_assignable<_Executor>::value,
1520 "inner executor type must be MoveAssignable");
1523 _M_inner_ex = std::move(__other._M_inner_ex);
1527 template<typename _OtherExecutor>
1529 operator=(const strand<_OtherExecutor>& __other) noexcept
1531 static_assert(is_convertible<_OtherExecutor, _Executor>::value,
1532 "inner executor type must be compatible");
1534 // TODO lock __other
1536 _M_inner_ex = __other._M_inner_ex;
1540 template<typename _OtherExecutor>
1542 operator=(strand<_OtherExecutor>&& __other) noexcept
1544 static_assert(is_convertible<_OtherExecutor, _Executor>::value,
1545 "inner executor type must be compatible");
1548 _M_inner_ex = std::move(__other._M_inner_ex);
1554 // the task queue outlives this object if non-empty
1555 // TODO create circular ref in queue?
1558 // strand operations:
1561 get_inner_executor() const noexcept
1562 { return _M_inner_ex; }
1565 running_in_this_thread() const noexcept
1566 { return _M_state->running_in_this_thread(); }
1569 context() const noexcept
1570 { return _M_inner_ex.context(); }
1572 void on_work_started() const noexcept { _M_inner_ex.on_work_started(); }
1573 void on_work_finished() const noexcept { _M_inner_ex.on_work_finished(); }
1575 template<typename _Func, typename _Alloc>
1577 dispatch(_Func&& __f, const _Alloc& __a) const
1579 if (running_in_this_thread())
1580 decay_t<_Func>{std::forward<_Func>(__f)}();
1582 post(std::forward<_Func>(__f), __a);
1585 template<typename _Func, typename _Alloc>
1587 post(_Func&& __f, const _Alloc& __a) const; // TODO
1589 template<typename _Func, typename _Alloc>
1591 defer(_Func&& __f, const _Alloc& __a) const
1592 { post(std::forward<_Func>(__f), __a); }
1596 operator==(const strand& __a, const strand& __b)
1597 { return __a._M_state == __b._M_state; }
1599 // TODO add synchronised queue
1602 #if defined(_GLIBCXX_HAS_GTHREADS)
1604 running_in_this_thread() const noexcept
1605 { return std::this_thread::get_id() == _M_running_on; }
1607 std::thread::id _M_running_on;
1609 bool running_in_this_thread() const { return true; }
1612 shared_ptr<_State> _M_state;
1613 _Executor _M_inner_ex;
1616 #if defined(_GLIBCXX_HAS_GTHREADS)
1618 // Completion token for asynchronous operations initiated with use_future.
1619 template<typename _Func, typename _Alloc>
1620 struct __use_future_ct
1622 std::tuple<_Func, _Alloc> _M_t;
1625 template<typename _Func, typename _Tp>
1626 struct __use_future_ct<_Func, std::allocator<_Tp>>
1631 template<typename _ProtoAllocator = allocator<void>>
1635 // use_future_t types:
1636 using allocator_type = _ProtoAllocator;
1638 // use_future_t members:
1641 noexcept(is_nothrow_default_constructible<_ProtoAllocator>::value)
1645 use_future_t(const _ProtoAllocator& __a) noexcept : _M_alloc(__a) { }
1647 template<typename _OtherAllocator>
1648 use_future_t<_OtherAllocator>
1649 rebind(const _OtherAllocator& __a) const noexcept
1650 { return use_future_t<_OtherAllocator>(__a); }
1652 allocator_type get_allocator() const noexcept { return _M_alloc; }
1654 template<typename _Func>
1656 operator()(_Func&& __f) const
1658 using _Token = __use_future_ct<decay_t<_Func>, _ProtoAllocator>;
1659 return _Token{ {std::forward<_Func>(__f), _M_alloc} };
1663 _ProtoAllocator _M_alloc;
1666 template<typename _Tp>
1667 class use_future_t<std::allocator<_Tp>>
1670 // use_future_t types:
1671 using allocator_type = std::allocator<_Tp>;
1673 // use_future_t members:
1674 constexpr use_future_t() noexcept = default;
1677 use_future_t(const allocator_type& __a) noexcept { }
1680 use_future_t<std::allocator<_Up>>
1681 rebind(const std::allocator<_Up>& __a) const noexcept
1682 { return use_future_t<std::allocator<_Up>>(__a); }
1684 allocator_type get_allocator() const noexcept { return {}; }
1686 template<typename _Func>
1688 operator()(_Func&& __f) const
1690 using _Token = __use_future_ct<decay_t<_Func>, allocator_type>;
1691 return _Token{std::forward<_Func>(__f)};
1695 constexpr use_future_t<> use_future = use_future_t<>();
1697 template<typename _Func, typename _Alloc, typename _Res, typename... _Args>
1698 class async_result<__use_future_ct<_Func, _Alloc>, _Res(_Args...)>;
1700 template<typename _Result, typename _Executor>
1701 struct __use_future_ex;
1703 // Completion handler for asynchronous operations initiated with use_future.
1704 template<typename _Func, typename... _Args>
1705 struct __use_future_ch
1707 template<typename _Alloc>
1709 __use_future_ch(__use_future_ct<_Func, _Alloc>&& __token)
1710 : _M_f{ std::move(std::get<0>(__token._M_t)) },
1711 _M_promise{ std::get<1>(__token._M_t) }
1714 template<typename _Tp>
1716 __use_future_ch(__use_future_ct<_Func, std::allocator<_Tp>>&& __token)
1717 : _M_f{ std::move(__token._M_f) }
1721 operator()(_Args&&... __args)
1725 _M_promise.set_value(_M_f(std::forward<_Args>(__args)...));
1727 __catch(__cxxabiv1::__forced_unwind&)
1729 __throw_exception_again;
1733 _M_promise.set_exception(std::current_exception());
1737 using __result = result_of_t<_Func(decay_t<_Args>...)>;
1739 future<__result> get_future() { return _M_promise.get_future(); }
1742 template<typename _Result, typename _Executor>
1743 friend struct __use_future_ex;
1746 mutable promise<__result> _M_promise;
1749 // Specialization of async_result for operations initiated with use_future.
1750 template<typename _Func, typename _Alloc, typename _Res, typename... _Args>
1751 class async_result<__use_future_ct<_Func, _Alloc>, _Res(_Args...)>
1754 using completion_handler_type = __use_future_ch<_Func, _Args...>;
1755 using return_type = future<typename completion_handler_type::__result>;
1758 async_result(completion_handler_type& __h)
1759 : _M_future(__h.get_future())
1762 async_result(const async_result&) = delete;
1763 async_result& operator=(const async_result&) = delete;
1765 return_type get() { return std::move(_M_future); }
1768 return_type _M_future;
1771 template<typename _Result, typename _Executor>
1772 struct __use_future_ex
1774 template<typename _Handler>
1775 __use_future_ex(const _Handler& __h, _Executor __ex)
1776 : _M_t(__h._M_promise, __ex)
1779 template<typename _Fn, typename _Alloc>
1781 dispatch(_Fn&& __fn)
1785 std::get<1>(_M_t).dispatch(std::forward<_Fn>(__fn));
1787 __catch(__cxxabiv1::__forced_unwind&)
1789 __throw_exception_again;
1793 std::get<0>(_M_t).set_exception(std::current_exception());
1797 template<typename _Fn, typename _Alloc>
1803 std::get<1>(_M_t).post(std::forward<_Fn>(__fn));
1805 __catch(__cxxabiv1::__forced_unwind&)
1807 __throw_exception_again;
1811 std::get<0>(_M_t).set_exception(std::current_exception());
1815 template<typename _Fn, typename _Alloc>
1821 std::get<1>(_M_t).defer(std::forward<_Fn>(__fn));
1823 __catch(__cxxabiv1::__forced_unwind&)
1825 __throw_exception_again;
1829 std::get<0>(_M_t).set_exception(std::current_exception());
1834 tuple<promise<_Result>&, _Executor> _M_t;
1837 template<typename _Func, typename... _Args, typename _Executor>
1838 struct associated_executor<__use_future_ch<_Func, _Args...>, _Executor>
1841 using __handler = __use_future_ch<_Func, _Args...>;
1843 using type = __use_future_ex<typename __handler::__result, _Executor>;
1846 get(const __handler& __h, const _Executor& __ex)
1847 { return { __h, __ex }; }
1852 // [async.use.future.traits]
1853 template<typename _Allocator, typename _Ret, typename... _Args>
1854 class handler_type<use_future_t<_Allocator>, _Ret(_Args...)> // TODO uglify name
1856 template<typename... _Args>
1857 struct __is_error_result : false_type { };
1859 template<typename... _Args>
1860 struct __is_error_result<error_code, _Args...> : true_type { };
1862 template<typename... _Args>
1863 struct __is_error_result<exception_ptr, _Args...> : true_type { };
1865 static exception_ptr
1866 _S_exptr(exception_ptr& __ex)
1867 { return std::move(__ex); }
1869 static exception_ptr
1870 _S_exptr(const error_code& __ec)
1871 { return make_exception_ptr(system_error(__ec)); }
1873 template<bool _IsError, typename... _UArgs>
1877 template<bool _IsError>
1878 struct _Type<_IsError>
1880 std::promise<void> _M_promise;
1885 _M_promise.set_value();
1889 // N == 1, U0 is error_code or exception_ptr
1890 template<typename _UArg0>
1891 struct _Type<true, _UArg0>
1893 std::promise<void> _M_promise;
1895 template<typename _Arg0>
1897 operator()(_Arg0&& __a0)
1900 _M_promise.set_exception(_S_exptr(__a0));
1902 _M_promise.set_value();
1906 // N == 1, U0 is not error_code or exception_ptr
1907 template<typename _UArg0>
1908 struct _Type<false, _UArg0>
1910 std::promise<_UArg0> _M_promise;
1912 template<typename _Arg0>
1914 operator()(_Arg0&& __a0)
1916 _M_promise.set_value(std::forward<_Arg0>(__a0));
1920 // N == 2, U0 is error_code or exception_ptr
1921 template<typename _UArg0, typename _UArg1>
1922 struct _Type<true, _UArg0, _UArg1>
1924 std::promise<_UArg1> _M_promise;
1926 template<typename _Arg0, typename _Arg1>
1928 operator()(_Arg0&& __a0, _Arg1&& __a1)
1931 _M_promise.set_exception(_S_exptr(__a0));
1933 _M_promise.set_value(std::forward<_Arg1>(__a1));
1937 // N >= 2, U0 is not error_code or exception_ptr
1938 template<typename... _UArgs>
1939 struct _Type<false, _UArgs...>
1941 static_assert(sizeof...(_UArgs) > 1, "wrong partial specialization");
1943 std::promise<tuple<_UArgs...>> _M_promise;
1945 template<typename... _Args>
1947 operator()(_Args&&... __args)
1949 _M_promise.set_value(
1950 std::forward_as_tuple(std::forward<_Args>(__args)...));
1954 // N > 2, U0 is error_code or exception_ptr
1955 template<typename _UArg0, typename... _UArgs>
1956 struct _Type<true, _UArg0, _UArgs...>
1958 static_assert(sizeof...(_UArgs) > 1, "wrong partial specialization");
1960 std::promise<tuple<_UArgs...>> _M_promise;
1962 template<typename _Arg0, typename... _Args>
1964 operator()(_Arg0&& __a0, _Args&&... __args)
1967 _M_promise.set_exception(_S_exptr(__a0));
1969 _M_promise.set_value(
1970 std::forward_as_tuple(std::forward<_Args>(__args)...));
1976 _Type<__is_error_result<_Args...>::value, decay_t<_Args>...>;
1980 template<typename _Alloc, typename _Ret, typename... _Args>
1981 struct async_result<use_future_t<_Alloc>, _Ret(_Args...)>
1983 using completion_handler_type
1984 = typename handler_type<use_future_t<_Alloc>, _Ret(_Args...)>::type;
1986 using return_type = void; // XXX TODO ???;
1989 async_result(completion_handler_type& __h) : _M_handler(__h) { }
1991 auto get() { return _M_handler._M_provider.get_future(); }
1993 async_result(const async_result&) = delete;
1994 async_result& operator=(const async_result&) = delete;
1996 return_type get() { return _M_handler._M_promise.get_future(); }
1999 completion_handler_type& _M_handler;
2002 // TODO specialize associated_executor for
2003 // async_result<use_future_t<A>, Sig>::completion_handler_type
2004 // to use a __use_future_ex
2005 // (probably need to move _Type outside of handler_type so we don't have
2006 // a non-deduced context)
2010 // [async.packaged.task.specializations]
2011 template<typename _Ret, typename... _Args, typename _Signature>
2012 class async_result<packaged_task<_Ret(_Args...)>, _Signature>
2015 using completion_handler_type = packaged_task<_Ret(_Args...)>;
2016 using return_type = future<_Ret>;
2019 async_result(completion_handler_type& __h)
2020 : _M_future(__h.get_future()) { }
2022 async_result(const async_result&) = delete;
2023 async_result& operator=(const async_result&) = delete;
2025 return_type get() { return std::move(_M_future); }
2028 return_type _M_future;
2031 #endif // _GLIBCXX_HAS_GTHREADS
2037 } // namespace experimental
2039 template<typename _Alloc>
2040 struct uses_allocator<experimental::net::executor, _Alloc>
2043 _GLIBCXX_END_NAMESPACE_VERSION
2048 #endif // _GLIBCXX_EXPERIMENTAL_EXECUTOR