]>
Commit | Line | Data |
---|---|---|
e5989e71 JW |
1 | // <experimental/executor> -*- C++ -*- |
2 | ||
7adcbafe | 3 | // Copyright (C) 2015-2022 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. | |
3084625d | 27 | * @ingroup networking-ts |
e5989e71 JW |
28 | */ |
29 | ||
30 | #ifndef _GLIBCXX_EXPERIMENTAL_EXECUTOR | |
31 | #define _GLIBCXX_EXPERIMENTAL_EXECUTOR 1 | |
32 | ||
33 | #pragma GCC system_header | |
34 | ||
35 | #if __cplusplus >= 201402L | |
36 | ||
37 | #include <algorithm> | |
82a0f2fd | 38 | #include <condition_variable> |
e5989e71 JW |
39 | #include <functional> |
40 | #include <future> | |
41 | #include <list> | |
e5989e71 JW |
42 | #include <queue> |
43 | #include <thread> | |
44 | #include <tuple> | |
45 | #include <unordered_map> | |
e5989e71 JW |
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 | 59 | |
3084625d JW |
60 | /** @addtogroup networking-ts |
61 | * @{ | |
e5989e71 JW |
62 | */ |
63 | ||
64 | /// Customization point for asynchronous operations. | |
65 | template<typename _CompletionToken, typename _Signature, typename = void> | |
66 | class async_result; | |
67 | ||
68 | /// Convenience utility to help implement asynchronous operations. | |
69 | template<typename _CompletionToken, typename _Signature> | |
70 | class async_completion; | |
71 | ||
72 | template<typename _Tp, typename _ProtoAlloc, typename = __void_t<>> | |
73 | struct __associated_allocator_impl | |
74 | { | |
75 | using type = _ProtoAlloc; | |
76 | ||
77 | static type | |
78 | _S_get(const _Tp&, const _ProtoAlloc& __a) noexcept { return __a; } | |
79 | }; | |
80 | ||
81 | template<typename _Tp, typename _ProtoAlloc> | |
82 | struct __associated_allocator_impl<_Tp, _ProtoAlloc, | |
83 | __void_t<typename _Tp::allocator_type>> | |
84 | { | |
85 | using type = typename _Tp::allocator_type; | |
86 | ||
87 | static type | |
88 | _S_get(const _Tp& __t, const _ProtoAlloc&) noexcept | |
89 | { return __t.get_allocator(); } | |
90 | }; | |
91 | ||
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> | |
96 | { | |
97 | static auto | |
98 | get(const _Tp& __t, | |
99 | const _ProtoAllocator& __a = _ProtoAllocator()) noexcept | |
100 | { | |
101 | using _Impl = __associated_allocator_impl<_Tp, _ProtoAllocator>; | |
102 | return _Impl::_S_get(__t, __a); | |
103 | } | |
104 | }; | |
105 | ||
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; | |
110 | ||
111 | // get_associated_allocator: | |
112 | ||
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); } | |
117 | ||
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); } | |
123 | ||
124 | enum class fork_event { prepare, parent, child }; | |
125 | ||
126 | /// An extensible, type-safe, polymorphic set of services. | |
127 | class execution_context; | |
128 | ||
c62f5e6e JW |
129 | class service_already_exists : public logic_error |
130 | { | |
00082ff8 JW |
131 | public: |
132 | // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
133 | // 3414. service_already_exists has no usable constructors | |
c62f5e6e JW |
134 | service_already_exists() : logic_error("service already exists") { } |
135 | }; | |
e5989e71 JW |
136 | |
137 | template<typename _Tp> struct is_executor; | |
138 | ||
139 | struct executor_arg_t { }; | |
140 | ||
141 | constexpr executor_arg_t executor_arg = executor_arg_t(); | |
142 | ||
143 | /// Trait for determining whether to construct an object with an executor. | |
144 | template<typename _Tp, typename _Executor> struct uses_executor; | |
145 | ||
146 | template<typename _Tp, typename _Executor, typename = __void_t<>> | |
147 | struct __associated_executor_impl | |
148 | { | |
149 | using type = _Executor; | |
150 | ||
151 | static type | |
152 | _S_get(const _Tp&, const _Executor& __e) noexcept { return __e; } | |
153 | }; | |
154 | ||
155 | template<typename _Tp, typename _Executor> | |
156 | struct __associated_executor_impl<_Tp, _Executor, | |
157 | __void_t<typename _Tp::executor_type>> | |
158 | { | |
159 | using type = typename _Tp::executor_type; | |
160 | ||
161 | static type | |
162 | _S_get(const _Tp& __t, const _Executor&) noexcept | |
163 | { return __t.get_executor(); } | |
164 | }; | |
165 | ||
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> | |
170 | { | |
171 | static auto | |
172 | get(const _Tp& __t, const _Executor& __e = _Executor()) noexcept | |
173 | { return __associated_executor_impl<_Tp, _Executor>::_S_get(__t, __e); } | |
174 | }; | |
175 | ||
176 | ||
177 | template<typename _Tp, typename _Executor = system_executor> | |
178 | using associated_executor_t | |
179 | = typename associated_executor<_Tp, _Executor>::type; | |
180 | ||
181 | template<typename _ExecutionContext> | |
182 | using __is_exec_context | |
183 | = is_convertible<_ExecutionContext&, execution_context&>; | |
184 | ||
185 | template<typename _Tp> | |
186 | using __executor_t = typename _Tp::executor_type; | |
187 | ||
188 | // get_associated_executor: | |
189 | ||
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); } | |
194 | ||
195 | template<typename _Tp, typename _Executor> | |
196 | inline | |
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); } | |
201 | ||
202 | template<typename _Tp, typename _ExecutionContext> | |
203 | inline | |
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()); } | |
208 | ||
209 | ||
210 | /// Helper to bind an executor to an object or function. | |
211 | template<typename _Tp, typename _Executor> | |
212 | class executor_binder; | |
213 | ||
214 | template<typename _Tp, typename _Executor, typename _Signature> | |
215 | class async_result<executor_binder<_Tp, _Executor>, _Signature>; | |
216 | ||
217 | template<typename _Tp, typename _Executor, typename _ProtoAllocator> | |
218 | struct associated_allocator<executor_binder<_Tp, _Executor>, | |
219 | _ProtoAllocator>; | |
220 | ||
221 | template<typename _Tp, typename _Executor, typename _Executor1> | |
222 | struct associated_executor<executor_binder<_Tp, _Executor>, _Executor1>; | |
223 | ||
224 | // bind_executor: | |
225 | ||
226 | template<typename _Executor, typename _Tp> | |
227 | inline | |
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 }; } | |
232 | ||
233 | template<typename _ExecutionContext, typename _Tp> | |
234 | inline | |
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) }; } | |
239 | ||
240 | ||
241 | /// A scope-guard type to record when work is started and finished. | |
242 | template<typename _Executor> | |
243 | class executor_work_guard; | |
244 | ||
245 | // make_work_guard: | |
246 | ||
247 | template<typename _Executor> | |
248 | inline | |
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); } | |
252 | ||
253 | template<typename _ExecutionContext> | |
254 | inline | |
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()); } | |
259 | ||
260 | template<typename _Tp> | |
261 | inline | |
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); } | |
266 | ||
267 | template<typename _Tp, typename _Up> | |
268 | auto | |
269 | make_work_guard(const _Tp& __t, _Up&& __u) | |
270 | -> decltype(net::make_work_guard( | |
271 | net::get_associated_executor(__t, forward<_Up>(__u)))) | |
272 | { | |
273 | return net::make_work_guard( | |
274 | net::get_associated_executor(__t, forward<_Up>(__u))); | |
275 | } | |
276 | ||
277 | /// Allows function objects to execute on any thread. | |
278 | class system_executor; | |
279 | ||
280 | /// The execution context associated with system_executor objects. | |
281 | class system_context; | |
282 | ||
283 | inline bool | |
284 | operator==(const system_executor&, const system_executor&) { return true; } | |
285 | ||
286 | inline bool | |
287 | operator!=(const system_executor&, const system_executor&) { return false; } | |
288 | ||
289 | /// Exception thrown by empty executors. | |
290 | class bad_executor; | |
291 | ||
292 | /// Polymorphic wrapper for types satisfying the Executor requirements. | |
293 | class executor; | |
294 | ||
295 | bool | |
98f29f56 | 296 | operator==(const executor&, const executor&) noexcept; |
e5989e71 JW |
297 | |
298 | bool | |
98f29f56 | 299 | operator==(const executor&, nullptr_t) noexcept; |
e5989e71 | 300 | |
98f29f56 JW |
301 | bool |
302 | operator==(nullptr_t, const executor&) noexcept; | |
e5989e71 | 303 | |
98f29f56 JW |
304 | bool |
305 | operator!=(const executor&, const executor&) noexcept; | |
e5989e71 | 306 | |
98f29f56 JW |
307 | bool |
308 | operator!=(const executor&, nullptr_t) noexcept; | |
e5989e71 | 309 | |
98f29f56 JW |
310 | bool |
311 | operator!=(nullptr_t, const executor&) noexcept; | |
e5989e71 JW |
312 | |
313 | void swap(executor&, executor&) noexcept; | |
314 | ||
315 | // dispatch: | |
316 | ||
317 | template<typename _CompletionToken> | |
318 | __deduced_t<_CompletionToken, void()> | |
319 | dispatch(_CompletionToken&& __token); | |
320 | ||
321 | template<typename _Executor, typename _CompletionToken> | |
322 | __deduced_t<_CompletionToken, void()> | |
323 | dispatch(const _Executor& __ex, _CompletionToken&& __token); | |
324 | ||
325 | template<typename _ExecutionContext, typename _CompletionToken> | |
326 | __deduced_t<_CompletionToken, void()> | |
327 | dispatch(_ExecutionContext& __ctx, _CompletionToken&& __token); | |
328 | ||
329 | // post: | |
330 | ||
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); | |
342 | ||
343 | // defer: | |
344 | ||
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); | |
354 | ||
355 | template<typename _Executor> | |
356 | class strand; | |
357 | ||
358 | template<typename _Executor> | |
359 | bool | |
360 | operator==(const strand<_Executor>& __a, const strand<_Executor>& __b); | |
361 | ||
362 | template<typename _Executor> | |
363 | bool | |
364 | operator!=(const strand<_Executor>& __a, const strand<_Executor>& __b) | |
365 | { return !(__a == __b); } | |
366 | ||
367 | template<typename _CompletionToken, typename _Signature, typename> | |
368 | class async_result | |
369 | { | |
370 | public: | |
f26e72d8 JW |
371 | using completion_handler_type = _CompletionToken; |
372 | using return_type = void; | |
e5989e71 JW |
373 | |
374 | explicit async_result(completion_handler_type&) {} | |
375 | async_result(const async_result&) = delete; | |
376 | async_result& operator=(const async_result&) = delete; | |
377 | ||
378 | return_type get() {} | |
379 | }; | |
380 | ||
381 | template<typename _CompletionToken, typename _Signature> | |
382 | class async_completion | |
383 | { | |
384 | using __result_type | |
385 | = async_result<decay_t<_CompletionToken>, _Signature>; | |
386 | ||
387 | public: | |
388 | using completion_handler_type | |
389 | = typename __result_type::completion_handler_type; | |
390 | ||
391 | private: | |
a09bb4a8 | 392 | using __handler_type = __conditional_t< |
e5989e71 JW |
393 | is_same<_CompletionToken, completion_handler_type>::value, |
394 | completion_handler_type&, | |
395 | completion_handler_type>; | |
396 | ||
397 | public: | |
398 | explicit | |
399 | async_completion(_CompletionToken& __t) | |
400 | : completion_handler(std::forward<__handler_type>(__t)), | |
401 | result(completion_handler) | |
402 | { } | |
403 | ||
404 | async_completion(const async_completion&) = delete; | |
405 | async_completion& operator=(const async_completion&) = delete; | |
406 | ||
407 | __handler_type completion_handler; | |
408 | __result_type result; | |
409 | }; | |
410 | ||
411 | ||
412 | class execution_context | |
413 | { | |
414 | public: | |
415 | class service | |
416 | { | |
417 | protected: | |
418 | // construct / copy / destroy: | |
419 | ||
420 | explicit | |
421 | service(execution_context& __owner) : _M_context(__owner) { } | |
422 | ||
423 | service(const service&) = delete; | |
424 | service& operator=(const service&) = delete; | |
425 | ||
426 | virtual ~service() { } // TODO should not be inline | |
427 | ||
428 | // service observers: | |
429 | ||
430 | execution_context& context() const noexcept { return _M_context; } | |
431 | ||
432 | private: | |
433 | // service operations: | |
434 | ||
435 | virtual void shutdown() noexcept = 0; | |
436 | virtual void notify_fork(fork_event) { } | |
437 | ||
438 | friend class execution_context; | |
439 | execution_context& _M_context; | |
440 | }; | |
441 | ||
442 | // construct / copy / destroy: | |
443 | ||
444 | execution_context() { } | |
445 | ||
446 | execution_context(const execution_context&) = delete; | |
447 | execution_context& operator=(const execution_context&) = delete; | |
448 | ||
449 | virtual ~execution_context() | |
450 | { | |
451 | shutdown(); | |
452 | destroy(); | |
453 | } | |
454 | ||
455 | // execution context operations: | |
456 | ||
457 | void | |
458 | notify_fork(fork_event __e) | |
459 | { | |
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); | |
463 | else | |
464 | std::for_each(_M_services.begin(), _M_services.end(), __l); | |
465 | } | |
466 | ||
467 | protected: | |
468 | // execution context protected operations: | |
469 | ||
470 | void | |
471 | shutdown() | |
472 | { | |
473 | std::for_each(_M_services.rbegin(), _M_services.rend(), | |
474 | [=](auto& __svc) { | |
475 | if (__svc._M_active) | |
476 | { | |
477 | __svc._M_ptr->shutdown(); | |
478 | __svc._M_active = false; | |
479 | } | |
480 | }); | |
481 | } | |
482 | ||
483 | void | |
484 | destroy() | |
485 | { | |
486 | while (_M_services.size()) | |
487 | _M_services.pop_back(); | |
488 | _M_keys.clear(); | |
489 | } | |
490 | ||
491 | protected: | |
492 | ||
493 | template<typename _Service> | |
494 | static void | |
495 | _S_deleter(service* __svc) { delete static_cast<_Service*>(__svc); } | |
496 | ||
497 | struct _ServicePtr | |
498 | { | |
499 | template<typename _Service> | |
500 | explicit | |
501 | _ServicePtr(_Service* __svc) | |
502 | : _M_ptr(__svc, &_S_deleter<_Service>), _M_active(true) { } | |
503 | ||
504 | std::unique_ptr<service, void(*)(service*)> _M_ptr; | |
505 | bool _M_active; | |
506 | }; | |
507 | ||
18095be1 JW |
508 | #if defined(_GLIBCXX_HAS_GTHREADS) |
509 | using mutex_type = std::mutex; | |
510 | #else | |
511 | struct mutex_type | |
512 | { | |
513 | void lock() const { } | |
514 | void unlock() const { } | |
515 | }; | |
516 | #endif | |
517 | mutable mutex_type _M_mutex; | |
e5989e71 JW |
518 | |
519 | // Sorted in order of beginning of service object lifetime. | |
520 | std::list<_ServicePtr> _M_services; | |
521 | ||
522 | template<typename _Service, typename... _Args> | |
523 | service* | |
524 | _M_add_svc(_Args&&... __args) | |
525 | { | |
526 | _M_services.push_back( | |
527 | _ServicePtr{new _Service{*this, std::forward<_Args>(__args)...}} ); | |
528 | return _M_services.back()._M_ptr.get(); | |
529 | } | |
530 | ||
531 | using __key_type = void(*)(); | |
532 | ||
533 | template<typename _Key> | |
534 | static __key_type | |
535 | _S_key() { return reinterpret_cast<__key_type>(&_S_key<_Key>); } | |
536 | ||
537 | std::unordered_map<__key_type, service*> _M_keys; | |
538 | ||
539 | template<typename _Service> | |
540 | friend typename _Service::key_type& | |
541 | use_service(execution_context&); | |
542 | ||
543 | template<typename _Service, typename... _Args> | |
544 | friend _Service& | |
545 | make_service(execution_context&, _Args&&...); | |
546 | ||
547 | template<typename _Service> | |
548 | friend bool | |
549 | has_service(const execution_context&) noexcept; | |
550 | }; | |
551 | ||
552 | // service access: | |
553 | ||
554 | template<typename _Service> | |
555 | typename _Service::key_type& | |
556 | use_service(execution_context& __ctx) | |
557 | { | |
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>(); | |
18095be1 | 564 | lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex); |
e5989e71 JW |
565 | auto& __svc = __ctx._M_keys[__key]; |
566 | if (__svc == nullptr) | |
567 | { | |
568 | __try { | |
569 | __svc = __ctx._M_add_svc<_Service>(); | |
570 | } __catch(...) { | |
571 | __ctx._M_keys.erase(__key); | |
572 | __throw_exception_again; | |
573 | } | |
574 | } | |
575 | return static_cast<_Key&>(*__svc); | |
576 | } | |
577 | ||
578 | template<typename _Service, typename... _Args> | |
579 | _Service& | |
580 | make_service(execution_context& __ctx, _Args&&... __args) | |
581 | { | |
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>(); | |
18095be1 | 588 | lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex); |
e5989e71 JW |
589 | auto& __svc = __ctx._M_keys[__key]; |
590 | if (__svc != nullptr) | |
591 | throw service_already_exists(); | |
592 | __try { | |
593 | __svc = __ctx._M_add_svc<_Service>(std::forward<_Args>(__args)...); | |
594 | } __catch(...) { | |
595 | __ctx._M_keys.erase(__key); | |
596 | __throw_exception_again; | |
597 | } | |
598 | return static_cast<_Service&>(*__svc); | |
599 | } | |
600 | ||
601 | template<typename _Service> | |
602 | inline bool | |
603 | has_service(const execution_context& __ctx) noexcept | |
604 | { | |
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"); | |
18095be1 | 610 | lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex); |
e5989e71 JW |
611 | return __ctx._M_keys.count(execution_context::_S_key<_Key>()); |
612 | } | |
613 | ||
614 | template<typename _Tp, typename = __void_t<>> | |
615 | struct __is_executor_impl : false_type | |
616 | { }; | |
617 | ||
618 | // Check Executor requirements. | |
619 | template<typename _Tp, typename _Up = remove_const_t<_Tp>> | |
620 | auto | |
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)) | |
632 | >>; | |
633 | ||
634 | template<typename _Tp> | |
635 | struct __is_executor_impl<_Tp, decltype(__executor_reqs<_Tp>())> | |
636 | : true_type | |
637 | { }; | |
638 | ||
639 | template<typename _Tp> | |
640 | struct is_executor : __is_executor_impl<_Tp> | |
641 | { }; | |
642 | ||
643 | template<typename _Tp> | |
644 | constexpr bool is_executor_v = is_executor<_Tp>::value; | |
645 | ||
646 | template<typename _Tp, typename _Executor, typename = __void_t<>> | |
647 | struct __uses_executor_impl : false_type | |
648 | { }; | |
649 | ||
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> | |
654 | { }; | |
655 | ||
656 | template<typename _Tp, typename _Executor> | |
657 | struct uses_executor : __uses_executor_impl<_Tp, _Executor>::type | |
658 | { }; | |
659 | ||
660 | template<typename _Tp, typename _Executor> | |
661 | constexpr bool uses_executor_v = uses_executor<_Tp, _Executor>::value; | |
662 | ||
663 | template<typename _Tp, typename _Executor> | |
664 | class executor_binder | |
665 | { | |
666 | struct __use_exec { }; | |
667 | ||
668 | public: | |
669 | // types: | |
670 | ||
f26e72d8 JW |
671 | using target_type = _Tp; |
672 | using executor_type = _Executor; | |
e5989e71 JW |
673 | |
674 | // construct / copy / destroy: | |
675 | ||
676 | executor_binder(_Tp __t, const _Executor& __ex) | |
677 | : executor_binder(__use_exec{}, std::move(__t), __ex) | |
678 | { } | |
679 | ||
680 | executor_binder(const executor_binder&) = default; | |
681 | executor_binder(executor_binder&&) = default; | |
682 | ||
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()) | |
686 | { } | |
687 | ||
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()) | |
692 | { } | |
693 | ||
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) | |
698 | { } | |
699 | ||
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) | |
704 | { } | |
705 | ||
706 | ~executor_binder(); | |
707 | ||
708 | // executor binder access: | |
709 | ||
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; } | |
713 | ||
714 | // executor binder invocation: | |
715 | ||
716 | template<class... _Args> | |
717 | result_of_t<_Tp&(_Args&&...)> | |
718 | operator()(_Args&&... __args) | |
719 | { return std::__invoke(get(), std::forward<_Args>(__args)...); } | |
720 | ||
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)...); } | |
725 | ||
726 | private: | |
727 | template<typename _Up> | |
728 | using __use_exec_cond | |
729 | = __and_<uses_executor<_Tp, _Executor>, | |
730 | is_constructible<_Tp, executor_arg_t, _Executor, _Up>>; | |
731 | ||
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)) | |
737 | { } | |
738 | ||
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)) | |
744 | { } | |
745 | ||
746 | _Executor _M_ex; | |
747 | _Tp _M_target; | |
748 | }; | |
749 | ||
750 | template<typename _Tp, typename _Executor, typename _Signature> | |
751 | class async_result<executor_binder<_Tp, _Executor>, _Signature> | |
752 | { | |
753 | using __inner = async_result<_Tp, _Signature>; | |
754 | ||
755 | public: | |
756 | using completion_handler_type = | |
757 | executor_binder<typename __inner::completion_handler_type, _Executor>; | |
758 | ||
759 | using return_type = typename __inner::return_type; | |
760 | ||
761 | explicit | |
762 | async_result(completion_handler_type& __h) | |
763 | : _M_target(__h.get()) { } | |
764 | ||
765 | async_result(const async_result&) = delete; | |
766 | async_result& operator=(const async_result&) = delete; | |
767 | ||
768 | return_type get() { return _M_target.get(); } | |
769 | ||
770 | private: | |
771 | __inner _M_target; | |
772 | }; | |
773 | ||
774 | template<typename _Tp, typename _Executor, typename _ProtoAlloc> | |
775 | struct associated_allocator<executor_binder<_Tp, _Executor>, _ProtoAlloc> | |
776 | { | |
f26e72d8 | 777 | using type = associated_allocator_t<_Tp, _ProtoAlloc>; |
e5989e71 JW |
778 | |
779 | static type | |
780 | get(const executor_binder<_Tp, _Executor>& __b, | |
781 | const _ProtoAlloc& __a = _ProtoAlloc()) noexcept | |
782 | { return associated_allocator<_Tp, _ProtoAlloc>::get(__b.get(), __a); } | |
783 | }; | |
784 | ||
785 | template<typename _Tp, typename _Executor, typename _Executor1> | |
786 | struct associated_executor<executor_binder<_Tp, _Executor>, _Executor1> | |
787 | { | |
f26e72d8 | 788 | using type = _Executor; |
e5989e71 JW |
789 | |
790 | static type | |
791 | get(const executor_binder<_Tp, _Executor>& __b, | |
792 | const _Executor1& = _Executor1()) noexcept | |
793 | { return __b.get_executor(); } | |
794 | }; | |
795 | ||
796 | template<typename _Executor> | |
797 | class executor_work_guard | |
798 | { | |
799 | public: | |
800 | // types: | |
801 | ||
f26e72d8 | 802 | using executor_type = _Executor; |
e5989e71 JW |
803 | |
804 | // construct / copy / destroy: | |
805 | ||
806 | explicit | |
807 | executor_work_guard(const executor_type& __ex) noexcept | |
808 | : _M_ex(__ex), _M_owns(true) | |
809 | { _M_ex.on_work_started(); } | |
810 | ||
811 | executor_work_guard(const executor_work_guard& __other) noexcept | |
812 | : _M_ex(__other._M_ex), _M_owns(__other._M_owns) | |
813 | { | |
814 | if (_M_owns) | |
815 | _M_ex.on_work_started(); | |
816 | } | |
817 | ||
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; } | |
821 | ||
822 | executor_work_guard& operator=(const executor_work_guard&) = delete; | |
823 | ||
824 | ~executor_work_guard() | |
825 | { | |
826 | if (_M_owns) | |
827 | _M_ex.on_work_finished(); | |
828 | } | |
829 | ||
830 | // executor work guard observers: | |
831 | ||
832 | executor_type get_executor() const noexcept { return _M_ex; } | |
833 | ||
834 | bool owns_work() const noexcept { return _M_owns; } | |
835 | ||
836 | // executor work guard modifiers: | |
837 | ||
838 | void reset() noexcept | |
839 | { | |
840 | if (_M_owns) | |
841 | _M_ex.on_work_finished(); | |
842 | _M_owns = false; | |
843 | } | |
844 | ||
845 | private: | |
846 | _Executor _M_ex; | |
847 | bool _M_owns; | |
848 | }; | |
849 | ||
850 | ||
851 | class system_context : public execution_context | |
852 | { | |
853 | public: | |
854 | // types: | |
855 | ||
f26e72d8 | 856 | using executor_type = system_executor; |
e5989e71 JW |
857 | |
858 | // construct / copy / destroy: | |
859 | ||
8c9d69ba | 860 | system_context() = delete; |
e5989e71 JW |
861 | system_context(const system_context&) = delete; |
862 | system_context& operator=(const system_context&) = delete; | |
863 | ||
864 | ~system_context() | |
865 | { | |
866 | stop(); | |
867 | join(); | |
868 | } | |
869 | ||
870 | // system_context operations: | |
871 | ||
872 | executor_type get_executor() noexcept; | |
873 | ||
874 | void stop() | |
875 | { | |
18095be1 | 876 | lock_guard<mutex_type> __lock(_M_mtx); |
e5989e71 JW |
877 | _M_stopped = true; |
878 | _M_cv.notify_all(); | |
879 | } | |
880 | ||
881 | bool stopped() const noexcept | |
882 | { | |
18095be1 | 883 | lock_guard<mutex_type> __lock(_M_mtx); |
e5989e71 JW |
884 | return _M_stopped; |
885 | } | |
886 | ||
887 | void join() | |
888 | { | |
18095be1 JW |
889 | if (_M_thread.joinable()) |
890 | _M_thread.join(); | |
e5989e71 JW |
891 | } |
892 | ||
893 | private: | |
894 | friend system_executor; | |
895 | ||
2a6918e4 | 896 | struct __tag { explicit __tag() = default; }; |
e5989e71 JW |
897 | system_context(__tag) { } |
898 | ||
18095be1 JW |
899 | #ifndef _GLIBCXX_HAS_GTHREADS |
900 | struct thread | |
901 | { | |
902 | bool joinable() const { return false; } | |
903 | void join() { } | |
904 | }; | |
905 | struct condition_variable | |
906 | { | |
907 | void notify_all() { } | |
908 | }; | |
909 | #endif | |
910 | ||
e5989e71 | 911 | thread _M_thread; |
18095be1 | 912 | mutable mutex_type _M_mtx; // XXX can we reuse base's _M_mutex? |
e5989e71 JW |
913 | condition_variable _M_cv; |
914 | queue<function<void()>> _M_tasks; | |
915 | bool _M_stopped = false; | |
916 | ||
18095be1 | 917 | #ifdef _GLIBCXX_HAS_GTHREADS |
e5989e71 JW |
918 | void |
919 | _M_run() | |
920 | { | |
921 | while (true) | |
922 | { | |
923 | function<void()> __f; | |
924 | { | |
18095be1 | 925 | unique_lock<mutex_type> __lock(_M_mtx); |
e5989e71 | 926 | _M_cv.wait(__lock, |
61759518 | 927 | [this]{ return _M_stopped || !_M_tasks.empty(); }); |
e5989e71 JW |
928 | if (_M_stopped) |
929 | return; | |
930 | __f = std::move(_M_tasks.front()); | |
931 | _M_tasks.pop(); | |
932 | } | |
933 | __f(); | |
934 | } | |
935 | } | |
18095be1 | 936 | #endif |
e5989e71 JW |
937 | |
938 | void | |
18095be1 | 939 | _M_post(std::function<void()> __f __attribute__((__unused__))) |
e5989e71 | 940 | { |
18095be1 | 941 | lock_guard<mutex_type> __lock(_M_mtx); |
e5989e71 JW |
942 | if (_M_stopped) |
943 | return; | |
18095be1 | 944 | #ifdef _GLIBCXX_HAS_GTHREADS |
e5989e71 JW |
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 | |
948 | _M_cv.notify_one(); | |
18095be1 JW |
949 | #else |
950 | __throw_system_error(EOPNOTSUPP); | |
951 | #endif | |
e5989e71 JW |
952 | } |
953 | ||
954 | static system_context& | |
955 | _S_get() noexcept | |
956 | { | |
957 | static system_context __sc(__tag{}); | |
958 | return __sc; | |
959 | } | |
960 | }; | |
961 | ||
962 | class system_executor | |
963 | { | |
964 | public: | |
965 | // executor operations: | |
966 | ||
967 | system_executor() { } | |
968 | ||
969 | system_context& | |
970 | context() const noexcept { return system_context::_S_get(); } | |
971 | ||
972 | void on_work_started() const noexcept { } | |
973 | void on_work_finished() const noexcept { } | |
974 | ||
975 | template<typename _Func, typename _ProtoAlloc> | |
976 | void | |
977 | dispatch(_Func&& __f, const _ProtoAlloc& __a) const | |
978 | { decay_t<_Func>{std::forward<_Func>(__f)}(); } | |
979 | ||
980 | template<typename _Func, typename _ProtoAlloc> | |
981 | void | |
982 | post(_Func&& __f, const _ProtoAlloc&) const // XXX allocator not used | |
983 | { | |
984 | system_context::_S_get()._M_post(std::forward<_Func>(__f)); | |
985 | } | |
986 | ||
987 | template<typename _Func, typename _ProtoAlloc> | |
988 | void | |
989 | defer(_Func&& __f, const _ProtoAlloc& __a) const | |
990 | { post(std::forward<_Func>(__f), __a); } | |
991 | }; | |
992 | ||
993 | inline system_executor | |
994 | system_context::get_executor() noexcept | |
995 | { return {}; } | |
996 | ||
997 | class bad_executor : public std::exception | |
998 | { | |
999 | virtual const char* what() const noexcept { return "bad executor"; } | |
1000 | }; | |
1001 | ||
1002 | inline void __throw_bad_executor() // TODO make non-inline | |
1003 | { | |
1004 | #if __cpp_exceptions | |
1005 | throw bad_executor(); | |
1006 | #else | |
1007 | __builtin_abort(); | |
1008 | #endif | |
1009 | } | |
1010 | ||
1011 | class executor | |
1012 | { | |
1013 | public: | |
1014 | // construct / copy / destroy: | |
1015 | ||
1016 | executor() noexcept = default; | |
1017 | ||
1018 | executor(nullptr_t) noexcept { } | |
1019 | executor(const executor&) noexcept = default; | |
1020 | executor(executor&&) noexcept = default; | |
1021 | ||
1022 | template<typename _Executor> | |
1023 | executor(_Executor __e) | |
98f29f56 | 1024 | : _M_target(make_shared<_Tgt1<_Executor>>(std::move(__e))) |
e5989e71 JW |
1025 | { } |
1026 | ||
1027 | template<typename _Executor, typename _ProtoAlloc> | |
1028 | executor(allocator_arg_t, const _ProtoAlloc& __a, _Executor __e) | |
98f29f56 JW |
1029 | : _M_target(allocate_shared<_Tgt2<_Executor, _ProtoAlloc>>(__a, |
1030 | std::move(__e), __a)) | |
e5989e71 JW |
1031 | { } |
1032 | ||
1033 | executor& operator=(const executor&) noexcept = default; | |
1034 | executor& operator=(executor&&) noexcept = default; | |
1035 | ||
1036 | executor& | |
1037 | operator=(nullptr_t) noexcept | |
1038 | { | |
1039 | _M_target = nullptr; | |
1040 | return *this; | |
1041 | } | |
1042 | ||
1043 | template<typename _Executor> | |
1044 | executor& | |
1045 | operator=(_Executor __e) | |
1046 | { | |
1047 | executor(std::move(__e)).swap(*this); | |
1048 | return *this; | |
1049 | } | |
1050 | ||
1051 | ~executor() = default; | |
1052 | ||
1053 | // executor modifiers: | |
1054 | ||
1055 | void | |
1056 | swap(executor& __other) noexcept | |
1057 | { _M_target.swap(__other._M_target); } | |
1058 | ||
1059 | template<typename _Executor, typename _Alloc> | |
1060 | void | |
1061 | assign(_Executor __e, const _Alloc& __a) | |
1062 | { executor(allocator_arg, __a, std::move(__e)).swap(*this); } | |
1063 | ||
1064 | // executor operations: | |
1065 | ||
1066 | execution_context& | |
1067 | context() const noexcept | |
1068 | { | |
1069 | __glibcxx_assert( _M_target ); | |
1070 | return _M_target->context(); | |
1071 | } | |
1072 | ||
1073 | void | |
1074 | on_work_started() const noexcept | |
1075 | { | |
1076 | __glibcxx_assert( _M_target ); | |
1077 | return _M_target->on_work_started(); | |
1078 | } | |
1079 | ||
1080 | void | |
1081 | on_work_finished() const noexcept | |
1082 | { | |
1083 | __glibcxx_assert( _M_target ); | |
1084 | return _M_target->on_work_finished(); | |
1085 | } | |
1086 | ||
1087 | template<typename _Func, typename _Alloc> | |
1088 | void | |
1089 | dispatch(_Func&& __f, const _Alloc& __a) const | |
1090 | { | |
1091 | if (!_M_target) | |
1092 | __throw_bad_executor(); | |
1093 | // _M_target->dispatch({allocator_arg, __a, std::forward<_Func>(__f)}); | |
1094 | _M_target->dispatch(std::forward<_Func>(__f)); | |
1095 | } | |
1096 | ||
1097 | template<typename _Func, typename _Alloc> | |
1098 | void | |
1099 | post(_Func&& __f, const _Alloc& __a) const | |
1100 | { | |
1101 | if (!_M_target) | |
1102 | __throw_bad_executor(); | |
1103 | // _M_target->post({allocator_arg, __a, std::forward<_Func>(__f)}); | |
1104 | _M_target->post(std::forward<_Func>(__f)); | |
1105 | } | |
1106 | ||
1107 | template<typename _Func, typename _Alloc> | |
1108 | void | |
1109 | defer(_Func&& __f, const _Alloc& __a) const | |
1110 | { | |
1111 | if (!_M_target) | |
1112 | __throw_bad_executor(); | |
1113 | // _M_target->defer({allocator_arg, __a, std::forward<_Func>(__f)}); | |
1114 | _M_target->defer(std::forward<_Func>(__f)); | |
1115 | } | |
1116 | ||
1117 | // executor capacity: | |
1118 | ||
1119 | explicit operator bool() const noexcept | |
1120 | { return static_cast<bool>(_M_target); } | |
1121 | ||
1122 | // executor target access: | |
1123 | ||
1124 | #if __cpp_rtti | |
1125 | const type_info& | |
1126 | target_type() const noexcept | |
98f29f56 JW |
1127 | { |
1128 | if (_M_target) | |
1129 | return *static_cast<const type_info*>(_M_target->target_type()); | |
1130 | return typeid(void); | |
1131 | } | |
1132 | #endif | |
e5989e71 JW |
1133 | |
1134 | template<typename _Executor> | |
1135 | _Executor* | |
1136 | target() noexcept | |
1137 | { | |
98f29f56 | 1138 | void* __p = nullptr; |
e5989e71 | 1139 | if (_M_target) |
98f29f56 JW |
1140 | { |
1141 | if (_M_target->_M_func == &_Tgt1<remove_cv_t<_Executor>>::_S_func) | |
1142 | __p = _M_target->_M_func(_M_target.get(), nullptr); | |
1143 | #if __cpp_rtti | |
1144 | else | |
1145 | __p = _M_target->target(&typeid(_Executor)); | |
1146 | #endif | |
1147 | } | |
1148 | return static_cast<_Executor*>(__p); | |
e5989e71 JW |
1149 | } |
1150 | ||
1151 | template<typename _Executor> | |
1152 | const _Executor* | |
1153 | target() const noexcept | |
1154 | { | |
98f29f56 | 1155 | const void* __p = nullptr; |
e5989e71 | 1156 | if (_M_target) |
98f29f56 JW |
1157 | { |
1158 | if (_M_target->_M_func == &_Tgt1<remove_cv_t<_Executor>>::_S_func) | |
1159 | return (_Executor*)_M_target->_M_func(_M_target.get(), nullptr); | |
1160 | #if __cpp_rtti | |
1161 | else | |
1162 | __p = _M_target->target(&typeid(_Executor)); | |
e5989e71 | 1163 | #endif |
98f29f56 JW |
1164 | } |
1165 | return static_cast<const _Executor*>(__p); | |
1166 | } | |
e5989e71 JW |
1167 | |
1168 | private: | |
1169 | struct _Tgt | |
1170 | { | |
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; | |
98f29f56 JW |
1177 | virtual const void* target_type() const noexcept = 0; |
1178 | virtual void* target(const void*) noexcept = 0; | |
e5989e71 | 1179 | virtual bool _M_equals(_Tgt*) const noexcept = 0; |
98f29f56 JW |
1180 | |
1181 | using _Func = void* (_Tgt*, const _Tgt*); | |
1182 | _Func* _M_func; // Provides access to target without RTTI | |
e5989e71 JW |
1183 | }; |
1184 | ||
98f29f56 JW |
1185 | template<typename _Ex> |
1186 | struct _Tgt1 : _Tgt | |
e5989e71 JW |
1187 | { |
1188 | explicit | |
98f29f56 JW |
1189 | _Tgt1(_Ex&& __ex) |
1190 | : _M_ex(std::move(__ex)) | |
1191 | { this->_M_func = &_S_func; } | |
e5989e71 | 1192 | |
e5989e71 | 1193 | void |
98f29f56 JW |
1194 | on_work_started() const noexcept override |
1195 | { _M_ex.on_work_started(); } | |
1196 | ||
e5989e71 | 1197 | void |
98f29f56 JW |
1198 | on_work_finished() const noexcept override |
1199 | { _M_ex.on_work_finished(); } | |
1200 | ||
1201 | execution_context& | |
1202 | context() const noexcept override | |
1203 | { return _M_ex.context(); } | |
1204 | ||
e5989e71 | 1205 | void |
98f29f56 JW |
1206 | dispatch(std::function<void()> __f) const override |
1207 | { _M_ex.dispatch(std::move(__f), allocator<void>()); } | |
e5989e71 | 1208 | |
98f29f56 JW |
1209 | void |
1210 | post(std::function<void()> __f) const override | |
1211 | { _M_ex.post(std::move(__f), allocator<void>()); } | |
1212 | ||
1213 | void | |
1214 | defer(std::function<void()> __f) const override | |
1215 | { _M_ex.defer(std::move(__f), allocator<void>()); } | |
1216 | ||
1217 | const void* | |
1218 | target_type() const noexcept override | |
1219 | { | |
e5989e71 | 1220 | #if __cpp_rtti |
98f29f56 JW |
1221 | return &typeid(_Ex); |
1222 | #else | |
1223 | return nullptr; | |
1224 | #endif | |
1225 | } | |
e5989e71 | 1226 | |
98f29f56 JW |
1227 | void* |
1228 | target(const void* __ti) noexcept override | |
e5989e71 | 1229 | { |
98f29f56 JW |
1230 | #if __cpp_rtti |
1231 | if (*static_cast<const type_info*>(__ti) == typeid(_Ex)) | |
1232 | return std::__addressof(_M_ex); | |
1233 | #endif | |
e5989e71 JW |
1234 | return nullptr; |
1235 | } | |
1236 | ||
98f29f56 JW |
1237 | bool |
1238 | _M_equals(_Tgt* __tgt) const noexcept override | |
e5989e71 | 1239 | { |
98f29f56 JW |
1240 | #if __cpp_rtti |
1241 | if (const void* __p = __tgt->target(&typeid(_Ex))) | |
1242 | return *static_cast<const _Ex*>(__p) == _M_ex; | |
1243 | #endif | |
e5989e71 JW |
1244 | return false; |
1245 | } | |
1246 | ||
98f29f56 | 1247 | _Ex _M_ex [[__no_unique_address__]]; |
e5989e71 | 1248 | |
98f29f56 JW |
1249 | static void* |
1250 | _S_func(_Tgt* __p, const _Tgt* __q) noexcept | |
1251 | { | |
1252 | auto& __ex = static_cast<_Tgt1*>(__p)->_M_ex; | |
1253 | if (__q) | |
1254 | { | |
1255 | if (__ex == static_cast<const _Tgt1*>(__q)->_M_ex) | |
1256 | return __p; | |
1257 | else | |
1258 | return nullptr; | |
1259 | } | |
1260 | else | |
1261 | return std::__addressof(__ex); | |
1262 | } | |
e5989e71 JW |
1263 | }; |
1264 | ||
98f29f56 JW |
1265 | template<typename _Ex, typename _Alloc> |
1266 | struct _Tgt2 : _Tgt1<_Ex> | |
e5989e71 | 1267 | { |
98f29f56 JW |
1268 | explicit |
1269 | _Tgt2(_Ex&& __ex, const _Alloc& __a) | |
1270 | : _Tgt1<_Ex>(std::move(__ex)), _M_alloc(__a) { } | |
1271 | ||
1272 | void | |
1273 | dispatch(std::function<void()> __f) const override | |
1274 | { this->_M_ex.dispatch(std::move(__f), _M_alloc); } | |
1275 | ||
1276 | void | |
1277 | post(std::function<void()> __f) const override | |
1278 | { this->_M_ex.post(std::move(__f), _M_alloc); } | |
1279 | ||
1280 | void | |
1281 | defer(std::function<void()> __f) const override | |
1282 | { this->_M_ex.defer(std::move(__f), _M_alloc); } | |
1283 | ||
1284 | _Alloc _M_alloc [[__no_unique_address__]]; | |
1285 | }; | |
1286 | ||
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> | |
1291 | { }; | |
e5989e71 JW |
1292 | |
1293 | friend bool | |
1294 | operator==(const executor& __a, const executor& __b) noexcept | |
1295 | { | |
98f29f56 JW |
1296 | _Tgt* __ta = __a._M_target.get(); |
1297 | _Tgt* __tb = __b._M_target.get(); | |
1298 | if (__ta == __tb) | |
e5989e71 | 1299 | return true; |
98f29f56 | 1300 | if (!__ta || !__tb) |
e5989e71 | 1301 | return false; |
98f29f56 JW |
1302 | if (__ta->_M_func == __tb->_M_func) |
1303 | return __ta->_M_func(__ta, __tb); | |
1304 | return __ta->_M_equals(__tb); | |
e5989e71 JW |
1305 | } |
1306 | ||
1307 | shared_ptr<_Tgt> _M_target; | |
1308 | }; | |
1309 | ||
1310 | template<> struct is_executor<executor> : true_type { }; | |
1311 | ||
1312 | /// executor comparisons | |
1313 | inline bool | |
1314 | operator==(const executor& __e, nullptr_t) noexcept | |
1315 | { return !__e; } | |
1316 | ||
98f29f56 JW |
1317 | inline bool |
1318 | operator==(nullptr_t, const executor& __e) noexcept | |
1319 | { return !__e; } | |
1320 | ||
1321 | inline bool | |
1322 | operator!=(const executor& __a, const executor& __b) noexcept | |
1323 | { return !(__a == __b); } | |
1324 | ||
1325 | inline bool | |
1326 | operator!=(const executor& __e, nullptr_t) noexcept | |
1327 | { return (bool)__e; } | |
1328 | ||
1329 | inline bool | |
1330 | operator!=(nullptr_t, const executor& __e) noexcept | |
1331 | { return (bool)__e; } | |
1332 | ||
e5989e71 JW |
1333 | /// Swap two executor objects. |
1334 | inline void swap(executor& __a, executor& __b) noexcept { __a.swap(__b); } | |
1335 | ||
1336 | ||
1337 | template<typename _CompletionHandler> | |
1338 | struct __dispatcher | |
1339 | { | |
1340 | explicit | |
1341 | __dispatcher(_CompletionHandler& __h) | |
1342 | : _M_h(std::move(__h)), _M_w(net::make_work_guard(_M_h)) | |
1343 | { } | |
1344 | ||
1345 | void operator()() | |
1346 | { | |
1347 | auto __alloc = net::get_associated_allocator(_M_h); | |
1348 | _M_w.get_executor().dispatch(std::move(_M_h), __alloc); | |
1349 | _M_w.reset(); | |
1350 | } | |
1351 | ||
1352 | _CompletionHandler _M_h; | |
1353 | decltype(net::make_work_guard(_M_h)) _M_w; | |
1354 | }; | |
1355 | ||
1356 | template<typename _CompletionHandler> | |
1357 | inline __dispatcher<_CompletionHandler> | |
1358 | __make_dispatcher(_CompletionHandler& __h) | |
1359 | { return __dispatcher<_CompletionHandler>{__h}; } | |
1360 | ||
1361 | ||
1362 | ||
1363 | // dispatch: | |
1364 | ||
1365 | template<typename _CompletionToken> | |
1366 | inline __deduced_t<_CompletionToken, void()> | |
1367 | dispatch(_CompletionToken&& __token) | |
1368 | { | |
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(); | |
1374 | } | |
1375 | ||
1376 | template<typename _Executor, typename _CompletionToken> | |
1377 | inline | |
1378 | enable_if_t<is_executor<_Executor>::value, | |
1379 | __deduced_t<_CompletionToken, void()>> | |
1380 | dispatch(const _Executor& __ex, _CompletionToken&& __token) | |
1381 | { | |
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), | |
1385 | __alloc); | |
1386 | return __cmpl.result.get(); | |
1387 | } | |
1388 | ||
1389 | template<typename _ExecutionContext, typename _CompletionToken> | |
1390 | inline | |
1391 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
1392 | __deduced_t<_CompletionToken, void()>> | |
1393 | dispatch(_ExecutionContext& __ctx, _CompletionToken&& __token) | |
1394 | { | |
1395 | return net::dispatch(__ctx.get_executor(), | |
1396 | forward<_CompletionToken>(__token)); | |
1397 | } | |
1398 | ||
1399 | // post: | |
1400 | ||
1401 | template<typename _CompletionToken> | |
1402 | inline __deduced_t<_CompletionToken, void()> | |
1403 | post(_CompletionToken&& __token) | |
1404 | { | |
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(); | |
1410 | } | |
1411 | ||
1412 | template<typename _Executor, typename _CompletionToken> | |
1413 | inline | |
1414 | enable_if_t<is_executor<_Executor>::value, | |
1415 | __deduced_t<_CompletionToken, void()>> | |
1416 | post(const _Executor& __ex, _CompletionToken&& __token) | |
1417 | { | |
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(); | |
1422 | } | |
1423 | ||
1424 | template<typename _ExecutionContext, typename _CompletionToken> | |
1425 | inline | |
1426 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
1427 | __deduced_t<_CompletionToken, void()>> | |
1428 | post(_ExecutionContext& __ctx, _CompletionToken&& __token) | |
1429 | { | |
1430 | return net::post(__ctx.get_executor(), | |
1431 | forward<_CompletionToken>(__token)); | |
1432 | } | |
1433 | ||
1434 | // defer: | |
1435 | ||
1436 | template<typename _CompletionToken> | |
1437 | inline __deduced_t<_CompletionToken, void()> | |
1438 | defer(_CompletionToken&& __token) | |
1439 | { | |
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(); | |
1445 | } | |
1446 | ||
1447 | template<typename _Executor, typename _CompletionToken> | |
1448 | inline | |
1449 | enable_if_t<is_executor<_Executor>::value, | |
1450 | __deduced_t<_CompletionToken, void()>> | |
1451 | defer(const _Executor& __ex, _CompletionToken&& __token) | |
1452 | { | |
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(); | |
1457 | } | |
1458 | ||
1459 | template<typename _ExecutionContext, typename _CompletionToken> | |
1460 | inline | |
1461 | enable_if_t<__is_exec_context<_ExecutionContext>::value, | |
1462 | __deduced_t<_CompletionToken, void()>> | |
1463 | defer(_ExecutionContext& __ctx, _CompletionToken&& __token) | |
1464 | { | |
1465 | return net::defer(__ctx.get_executor(), | |
1466 | forward<_CompletionToken>(__token)); | |
1467 | } | |
1468 | ||
1469 | ||
1470 | template<typename _Executor> | |
1471 | class strand | |
1472 | { | |
1473 | public: | |
1474 | // types: | |
1475 | ||
f26e72d8 | 1476 | using inner_executor_type = _Executor; |
e5989e71 JW |
1477 | |
1478 | // construct / copy / destroy: | |
1479 | ||
1480 | strand(); // TODO make state | |
1481 | ||
1482 | explicit strand(_Executor __ex) : _M_inner_ex(__ex) { } // TODO make state | |
1483 | ||
1484 | template<typename _Alloc> | |
1485 | strand(allocator_arg_t, const _Alloc& __a, _Executor __ex) | |
1486 | : _M_inner_ex(__ex) { } // TODO make state | |
1487 | ||
1488 | strand(const strand& __other) noexcept | |
1489 | : _M_state(__other._M_state), _M_inner_ex(__other._M_inner_ex) { } | |
1490 | ||
1491 | strand(strand&& __other) noexcept | |
1492 | : _M_state(std::move(__other._M_state)), | |
1493 | _M_inner_ex(std::move(__other._M_inner_ex)) { } | |
1494 | ||
1495 | template<typename _OtherExecutor> | |
1496 | strand(const strand<_OtherExecutor>& __other) noexcept | |
1497 | : _M_state(__other._M_state), _M_inner_ex(__other._M_inner_ex) { } | |
1498 | ||
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)) { } | |
1503 | ||
1504 | strand& | |
1505 | operator=(const strand& __other) noexcept | |
1506 | { | |
1507 | static_assert(is_copy_assignable<_Executor>::value, | |
1508 | "inner executor type must be CopyAssignable"); | |
1509 | ||
1510 | // TODO lock __other | |
1511 | // TODO copy state | |
1512 | _M_inner_ex = __other._M_inner_ex; | |
1513 | return *this; | |
1514 | } | |
1515 | ||
1516 | strand& | |
1517 | operator=(strand&& __other) noexcept | |
1518 | { | |
1519 | static_assert(is_move_assignable<_Executor>::value, | |
1520 | "inner executor type must be MoveAssignable"); | |
1521 | ||
1522 | // TODO move state | |
1523 | _M_inner_ex = std::move(__other._M_inner_ex); | |
1524 | return *this; | |
1525 | } | |
1526 | ||
1527 | template<typename _OtherExecutor> | |
1528 | strand& | |
1529 | operator=(const strand<_OtherExecutor>& __other) noexcept | |
1530 | { | |
1531 | static_assert(is_convertible<_OtherExecutor, _Executor>::value, | |
1532 | "inner executor type must be compatible"); | |
1533 | ||
1534 | // TODO lock __other | |
1535 | // TODO copy state | |
1536 | _M_inner_ex = __other._M_inner_ex; | |
1537 | return *this; | |
1538 | } | |
1539 | ||
1540 | template<typename _OtherExecutor> | |
1541 | strand& | |
1542 | operator=(strand<_OtherExecutor>&& __other) noexcept | |
1543 | { | |
1544 | static_assert(is_convertible<_OtherExecutor, _Executor>::value, | |
1545 | "inner executor type must be compatible"); | |
1546 | ||
1547 | // TODO move state | |
1548 | _M_inner_ex = std::move(__other._M_inner_ex); | |
1549 | return *this; | |
1550 | } | |
1551 | ||
1552 | ~strand() | |
1553 | { | |
1554 | // the task queue outlives this object if non-empty | |
1555 | // TODO create circular ref in queue? | |
1556 | } | |
1557 | ||
1558 | // strand operations: | |
1559 | ||
1560 | inner_executor_type | |
1561 | get_inner_executor() const noexcept | |
1562 | { return _M_inner_ex; } | |
1563 | ||
1564 | bool | |
1565 | running_in_this_thread() const noexcept | |
18095be1 | 1566 | { return _M_state->running_in_this_thread(); } |
e5989e71 JW |
1567 | |
1568 | execution_context& | |
1569 | context() const noexcept | |
1570 | { return _M_inner_ex.context(); } | |
1571 | ||
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(); } | |
1574 | ||
1575 | template<typename _Func, typename _Alloc> | |
1576 | void | |
1577 | dispatch(_Func&& __f, const _Alloc& __a) const | |
1578 | { | |
1579 | if (running_in_this_thread()) | |
1580 | decay_t<_Func>{std::forward<_Func>(__f)}(); | |
1581 | else | |
1582 | post(std::forward<_Func>(__f), __a); | |
1583 | } | |
1584 | ||
1585 | template<typename _Func, typename _Alloc> | |
1586 | void | |
1587 | post(_Func&& __f, const _Alloc& __a) const; // TODO | |
1588 | ||
1589 | template<typename _Func, typename _Alloc> | |
1590 | void | |
1591 | defer(_Func&& __f, const _Alloc& __a) const | |
1592 | { post(std::forward<_Func>(__f), __a); } | |
1593 | ||
1594 | private: | |
1595 | friend bool | |
1596 | operator==(const strand& __a, const strand& __b) | |
1597 | { return __a._M_state == __b._M_state; } | |
1598 | ||
1599 | // TODO add synchronised queue | |
1600 | struct _State | |
1601 | { | |
18095be1 JW |
1602 | #if defined(_GLIBCXX_HAS_GTHREADS) |
1603 | bool | |
b784bbbe JW |
1604 | running_in_this_thread() const noexcept |
1605 | { return std::this_thread::get_id() == _M_running_on; } | |
18095be1 | 1606 | |
e5989e71 | 1607 | std::thread::id _M_running_on; |
18095be1 JW |
1608 | #else |
1609 | bool running_in_this_thread() const { return true; } | |
1610 | #endif | |
e5989e71 JW |
1611 | }; |
1612 | shared_ptr<_State> _M_state; | |
1613 | _Executor _M_inner_ex; | |
1614 | }; | |
1615 | ||
18095be1 | 1616 | #if defined(_GLIBCXX_HAS_GTHREADS) |
e5989e71 JW |
1617 | |
1618 | // Completion token for asynchronous operations initiated with use_future. | |
1619 | template<typename _Func, typename _Alloc> | |
1620 | struct __use_future_ct | |
1621 | { | |
1622 | std::tuple<_Func, _Alloc> _M_t; | |
1623 | }; | |
1624 | ||
5ce55f3f JW |
1625 | template<typename _Func, typename _Tp> |
1626 | struct __use_future_ct<_Func, std::allocator<_Tp>> | |
1627 | { | |
1628 | _Func _M_f; | |
1629 | }; | |
1630 | ||
e5989e71 JW |
1631 | template<typename _ProtoAllocator = allocator<void>> |
1632 | class use_future_t | |
1633 | { | |
1634 | public: | |
1635 | // use_future_t types: | |
5ce55f3f | 1636 | using allocator_type = _ProtoAllocator; |
e5989e71 JW |
1637 | |
1638 | // use_future_t members: | |
d9d34449 JW |
1639 | constexpr |
1640 | use_future_t() | |
1641 | noexcept(is_nothrow_default_constructible<_ProtoAllocator>::value) | |
1642 | : _M_alloc() { } | |
e5989e71 JW |
1643 | |
1644 | explicit | |
1645 | use_future_t(const _ProtoAllocator& __a) noexcept : _M_alloc(__a) { } | |
1646 | ||
5ce55f3f | 1647 | template<typename _OtherAllocator> |
e5989e71 JW |
1648 | use_future_t<_OtherAllocator> |
1649 | rebind(const _OtherAllocator& __a) const noexcept | |
1650 | { return use_future_t<_OtherAllocator>(__a); } | |
1651 | ||
1652 | allocator_type get_allocator() const noexcept { return _M_alloc; } | |
1653 | ||
1654 | template<typename _Func> | |
1655 | auto | |
1656 | operator()(_Func&& __f) const | |
1657 | { | |
1658 | using _Token = __use_future_ct<decay_t<_Func>, _ProtoAllocator>; | |
1659 | return _Token{ {std::forward<_Func>(__f), _M_alloc} }; | |
1660 | } | |
1661 | ||
1662 | private: | |
1663 | _ProtoAllocator _M_alloc; | |
1664 | }; | |
1665 | ||
5ce55f3f JW |
1666 | template<typename _Tp> |
1667 | class use_future_t<std::allocator<_Tp>> | |
1668 | { | |
1669 | public: | |
1670 | // use_future_t types: | |
1671 | using allocator_type = std::allocator<_Tp>; | |
1672 | ||
1673 | // use_future_t members: | |
1674 | constexpr use_future_t() noexcept = default; | |
1675 | ||
1676 | explicit | |
1677 | use_future_t(const allocator_type& __a) noexcept { } | |
1678 | ||
1679 | template<class _Up> | |
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); } | |
1683 | ||
1684 | allocator_type get_allocator() const noexcept { return {}; } | |
1685 | ||
1686 | template<typename _Func> | |
1687 | auto | |
1688 | operator()(_Func&& __f) const | |
1689 | { | |
1690 | using _Token = __use_future_ct<decay_t<_Func>, allocator_type>; | |
1691 | return _Token{std::forward<_Func>(__f)}; | |
1692 | } | |
1693 | }; | |
1694 | ||
e5989e71 JW |
1695 | constexpr use_future_t<> use_future = use_future_t<>(); |
1696 | ||
1697 | template<typename _Func, typename _Alloc, typename _Res, typename... _Args> | |
1698 | class async_result<__use_future_ct<_Func, _Alloc>, _Res(_Args...)>; | |
1699 | ||
1700 | template<typename _Result, typename _Executor> | |
1701 | struct __use_future_ex; | |
1702 | ||
1703 | // Completion handler for asynchronous operations initiated with use_future. | |
1704 | template<typename _Func, typename... _Args> | |
1705 | struct __use_future_ch | |
1706 | { | |
1707 | template<typename _Alloc> | |
1708 | explicit | |
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) } | |
1712 | { } | |
1713 | ||
5ce55f3f JW |
1714 | template<typename _Tp> |
1715 | explicit | |
1716 | __use_future_ch(__use_future_ct<_Func, std::allocator<_Tp>>&& __token) | |
1717 | : _M_f{ std::move(__token._M_f) } | |
1718 | { } | |
1719 | ||
e5989e71 JW |
1720 | void |
1721 | operator()(_Args&&... __args) | |
1722 | { | |
1723 | __try | |
1724 | { | |
1725 | _M_promise.set_value(_M_f(std::forward<_Args>(__args)...)); | |
1726 | } | |
1727 | __catch(__cxxabiv1::__forced_unwind&) | |
1728 | { | |
1729 | __throw_exception_again; | |
1730 | } | |
1731 | __catch(...) | |
1732 | { | |
1733 | _M_promise.set_exception(std::current_exception()); | |
1734 | } | |
1735 | } | |
1736 | ||
1737 | using __result = result_of_t<_Func(decay_t<_Args>...)>; | |
1738 | ||
1739 | future<__result> get_future() { return _M_promise.get_future(); } | |
1740 | ||
1741 | private: | |
1742 | template<typename _Result, typename _Executor> | |
1743 | friend struct __use_future_ex; | |
1744 | ||
1745 | _Func _M_f; | |
1746 | mutable promise<__result> _M_promise; | |
1747 | }; | |
1748 | ||
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...)> | |
1752 | { | |
1753 | public: | |
1754 | using completion_handler_type = __use_future_ch<_Func, _Args...>; | |
1755 | using return_type = future<typename completion_handler_type::__result>; | |
1756 | ||
1757 | explicit | |
1758 | async_result(completion_handler_type& __h) | |
1759 | : _M_future(__h.get_future()) | |
1760 | { } | |
1761 | ||
1762 | async_result(const async_result&) = delete; | |
1763 | async_result& operator=(const async_result&) = delete; | |
1764 | ||
1765 | return_type get() { return std::move(_M_future); } | |
1766 | ||
1767 | private: | |
1768 | return_type _M_future; | |
1769 | }; | |
1770 | ||
1771 | template<typename _Result, typename _Executor> | |
1772 | struct __use_future_ex | |
1773 | { | |
1774 | template<typename _Handler> | |
1775 | __use_future_ex(const _Handler& __h, _Executor __ex) | |
1776 | : _M_t(__h._M_promise, __ex) | |
1777 | { } | |
1778 | ||
1779 | template<typename _Fn, typename _Alloc> | |
1780 | void | |
1781 | dispatch(_Fn&& __fn) | |
1782 | { | |
1783 | __try | |
1784 | { | |
1785 | std::get<1>(_M_t).dispatch(std::forward<_Fn>(__fn)); | |
1786 | } | |
1787 | __catch(__cxxabiv1::__forced_unwind&) | |
1788 | { | |
1789 | __throw_exception_again; | |
1790 | } | |
1791 | __catch(...) | |
1792 | { | |
1793 | std::get<0>(_M_t).set_exception(std::current_exception()); | |
1794 | } | |
1795 | } | |
1796 | ||
1797 | template<typename _Fn, typename _Alloc> | |
1798 | void | |
1799 | post(_Fn&& __fn) | |
1800 | { | |
1801 | __try | |
1802 | { | |
1803 | std::get<1>(_M_t).post(std::forward<_Fn>(__fn)); | |
1804 | } | |
1805 | __catch(__cxxabiv1::__forced_unwind&) | |
1806 | { | |
1807 | __throw_exception_again; | |
1808 | } | |
1809 | __catch(...) | |
1810 | { | |
1811 | std::get<0>(_M_t).set_exception(std::current_exception()); | |
1812 | } | |
1813 | } | |
1814 | ||
1815 | template<typename _Fn, typename _Alloc> | |
1816 | void | |
1817 | defer(_Fn&& __fn) | |
1818 | { | |
1819 | __try | |
1820 | { | |
1821 | std::get<1>(_M_t).defer(std::forward<_Fn>(__fn)); | |
1822 | } | |
1823 | __catch(__cxxabiv1::__forced_unwind&) | |
1824 | { | |
1825 | __throw_exception_again; | |
1826 | } | |
1827 | __catch(...) | |
1828 | { | |
1829 | std::get<0>(_M_t).set_exception(std::current_exception()); | |
1830 | } | |
1831 | } | |
1832 | ||
1833 | private: | |
1834 | tuple<promise<_Result>&, _Executor> _M_t; | |
1835 | }; | |
1836 | ||
1837 | template<typename _Func, typename... _Args, typename _Executor> | |
1838 | struct associated_executor<__use_future_ch<_Func, _Args...>, _Executor> | |
1839 | { | |
1840 | private: | |
1841 | using __handler = __use_future_ch<_Func, _Args...>; | |
1842 | ||
1843 | using type = __use_future_ex<typename __handler::__result, _Executor>; | |
1844 | ||
1845 | static type | |
1846 | get(const __handler& __h, const _Executor& __ex) | |
1847 | { return { __h, __ex }; } | |
1848 | }; | |
1849 | ||
1850 | #if 0 | |
1851 | ||
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 | |
1855 | { | |
1856 | template<typename... _Args> | |
1857 | struct __is_error_result : false_type { }; | |
1858 | ||
1859 | template<typename... _Args> | |
1860 | struct __is_error_result<error_code, _Args...> : true_type { }; | |
1861 | ||
1862 | template<typename... _Args> | |
1863 | struct __is_error_result<exception_ptr, _Args...> : true_type { }; | |
1864 | ||
1865 | static exception_ptr | |
1866 | _S_exptr(exception_ptr& __ex) | |
1867 | { return std::move(__ex); } | |
1868 | ||
1869 | static exception_ptr | |
1870 | _S_exptr(const error_code& __ec) | |
1871 | { return make_exception_ptr(system_error(__ec)); } | |
1872 | ||
1873 | template<bool _IsError, typename... _UArgs> | |
1874 | struct _Type; | |
1875 | ||
1876 | // N == 0 | |
1877 | template<bool _IsError> | |
1878 | struct _Type<_IsError> | |
1879 | { | |
1880 | std::promise<void> _M_promise; | |
1881 | ||
1882 | void | |
1883 | operator()() | |
1884 | { | |
1885 | _M_promise.set_value(); | |
1886 | } | |
1887 | }; | |
1888 | ||
1889 | // N == 1, U0 is error_code or exception_ptr | |
1890 | template<typename _UArg0> | |
1891 | struct _Type<true, _UArg0> | |
1892 | { | |
1893 | std::promise<void> _M_promise; | |
1894 | ||
1895 | template<typename _Arg0> | |
1896 | void | |
1897 | operator()(_Arg0&& __a0) | |
1898 | { | |
1899 | if (__a0) | |
1900 | _M_promise.set_exception(_S_exptr(__a0)); | |
1901 | else | |
1902 | _M_promise.set_value(); | |
1903 | } | |
1904 | }; | |
1905 | ||
1906 | // N == 1, U0 is not error_code or exception_ptr | |
1907 | template<typename _UArg0> | |
1908 | struct _Type<false, _UArg0> | |
1909 | { | |
1910 | std::promise<_UArg0> _M_promise; | |
1911 | ||
1912 | template<typename _Arg0> | |
1913 | void | |
1914 | operator()(_Arg0&& __a0) | |
1915 | { | |
1916 | _M_promise.set_value(std::forward<_Arg0>(__a0)); | |
1917 | } | |
1918 | }; | |
1919 | ||
1920 | // N == 2, U0 is error_code or exception_ptr | |
1921 | template<typename _UArg0, typename _UArg1> | |
1922 | struct _Type<true, _UArg0, _UArg1> | |
1923 | { | |
1924 | std::promise<_UArg1> _M_promise; | |
1925 | ||
1926 | template<typename _Arg0, typename _Arg1> | |
1927 | void | |
1928 | operator()(_Arg0&& __a0, _Arg1&& __a1) | |
1929 | { | |
1930 | if (__a0) | |
1931 | _M_promise.set_exception(_S_exptr(__a0)); | |
1932 | else | |
1933 | _M_promise.set_value(std::forward<_Arg1>(__a1)); | |
1934 | } | |
1935 | }; | |
1936 | ||
1937 | // N >= 2, U0 is not error_code or exception_ptr | |
1938 | template<typename... _UArgs> | |
1939 | struct _Type<false, _UArgs...> | |
1940 | { | |
1941 | static_assert(sizeof...(_UArgs) > 1, "wrong partial specialization"); | |
1942 | ||
1943 | std::promise<tuple<_UArgs...>> _M_promise; | |
1944 | ||
1945 | template<typename... _Args> | |
1946 | void | |
1947 | operator()(_Args&&... __args) | |
1948 | { | |
1949 | _M_promise.set_value( | |
1950 | std::forward_as_tuple(std::forward<_Args>(__args)...)); | |
1951 | } | |
1952 | }; | |
1953 | ||
1954 | // N > 2, U0 is error_code or exception_ptr | |
1955 | template<typename _UArg0, typename... _UArgs> | |
1956 | struct _Type<true, _UArg0, _UArgs...> | |
1957 | { | |
1958 | static_assert(sizeof...(_UArgs) > 1, "wrong partial specialization"); | |
1959 | ||
1960 | std::promise<tuple<_UArgs...>> _M_promise; | |
1961 | ||
1962 | template<typename _Arg0, typename... _Args> | |
1963 | void | |
1964 | operator()(_Arg0&& __a0, _Args&&... __args) | |
1965 | { | |
1966 | if (__a0) | |
1967 | _M_promise.set_exception(_S_exptr(__a0)); | |
1968 | else | |
1969 | _M_promise.set_value( | |
1970 | std::forward_as_tuple(std::forward<_Args>(__args)...)); | |
1971 | } | |
1972 | }; | |
1973 | ||
1974 | public: | |
1975 | using type = | |
1976 | _Type<__is_error_result<_Args...>::value, decay_t<_Args>...>; | |
1977 | }; | |
1978 | ||
1979 | ||
1980 | template<typename _Alloc, typename _Ret, typename... _Args> | |
1981 | struct async_result<use_future_t<_Alloc>, _Ret(_Args...)> | |
1982 | { | |
1983 | using completion_handler_type | |
1984 | = typename handler_type<use_future_t<_Alloc>, _Ret(_Args...)>::type; | |
1985 | ||
1986 | using return_type = void; // XXX TODO ???; | |
1987 | ||
1988 | explicit | |
1989 | async_result(completion_handler_type& __h) : _M_handler(__h) { } | |
1990 | ||
1991 | auto get() { return _M_handler._M_provider.get_future(); } | |
1992 | ||
1993 | async_result(const async_result&) = delete; | |
1994 | async_result& operator=(const async_result&) = delete; | |
1995 | ||
1996 | return_type get() { return _M_handler._M_promise.get_future(); } | |
1997 | ||
1998 | private: | |
1999 | completion_handler_type& _M_handler; | |
2000 | }; | |
2001 | ||
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) | |
2007 | ||
e5989e71 JW |
2008 | #endif |
2009 | ||
2010 | // [async.packaged.task.specializations] | |
2011 | template<typename _Ret, typename... _Args, typename _Signature> | |
2012 | class async_result<packaged_task<_Ret(_Args...)>, _Signature> | |
2013 | { | |
2014 | public: | |
2015 | using completion_handler_type = packaged_task<_Ret(_Args...)>; | |
2016 | using return_type = future<_Ret>; | |
2017 | ||
2018 | explicit | |
2019 | async_result(completion_handler_type& __h) | |
2020 | : _M_future(__h.get_future()) { } | |
2021 | ||
2022 | async_result(const async_result&) = delete; | |
2023 | async_result& operator=(const async_result&) = delete; | |
2024 | ||
2025 | return_type get() { return std::move(_M_future); } | |
2026 | ||
2027 | private: | |
2028 | return_type _M_future; | |
2029 | }; | |
2030 | ||
18095be1 | 2031 | #endif // _GLIBCXX_HAS_GTHREADS |
e5989e71 JW |
2032 | |
2033 | /// @} | |
2034 | ||
e5989e71 JW |
2035 | } // namespace v1 |
2036 | } // namespace net | |
2037 | } // namespace experimental | |
2038 | ||
e5989e71 JW |
2039 | template<typename _Alloc> |
2040 | struct uses_allocator<experimental::net::executor, _Alloc> | |
2041 | : true_type {}; | |
2042 | ||
2043 | _GLIBCXX_END_NAMESPACE_VERSION | |
2044 | } // namespace std | |
2045 | ||
2046 | #endif // C++14 | |
2047 | ||
2048 | #endif // _GLIBCXX_EXPERIMENTAL_EXECUTOR |