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