/src/pistache/include/pistache/async.h
Line | Count | Source |
1 | | /* |
2 | | * SPDX-FileCopyrightText: 2015 Mathieu Stefani |
3 | | * |
4 | | * SPDX-License-Identifier: Apache-2.0 |
5 | | */ |
6 | | |
7 | | /* async.h |
8 | | Mathieu Stefani, 05 novembre 2015 |
9 | | |
10 | | This header brings a Promise<T> class inspired by the Promises/A+ |
11 | | specification for asynchronous operations |
12 | | */ |
13 | | |
14 | | #pragma once |
15 | | |
16 | | #include <pistache/typeid.h> |
17 | | |
18 | | #include <atomic> |
19 | | #include <condition_variable> |
20 | | #include <functional> |
21 | | #include <memory> |
22 | | #include <mutex> |
23 | | #include <stdexcept> |
24 | | #include <type_traits> |
25 | | #include <typeinfo> |
26 | | #include <vector> |
27 | | |
28 | | namespace Pistache::Async |
29 | | { |
30 | | |
31 | | class Error : public std::runtime_error |
32 | | { |
33 | | public: |
34 | | explicit Error(const char* what) |
35 | 0 | : std::runtime_error(what) |
36 | 0 | { } |
37 | | explicit Error(const std::string& what) |
38 | | : std::runtime_error(what) |
39 | 0 | { } |
40 | | }; |
41 | | |
42 | | class BadType : public Error |
43 | | { |
44 | | public: |
45 | | explicit BadType(TypeId id) |
46 | 0 | : Error("Argument type can not be used to resolve the promise " |
47 | 0 | " (TypeId does not match)") |
48 | 0 | , id_(std::move(id)) |
49 | 0 | { } |
50 | | |
51 | 0 | TypeId typeId() const { return id_; } |
52 | | |
53 | | private: |
54 | | TypeId id_; |
55 | | }; |
56 | | |
57 | | class BadAnyCast : public std::bad_cast |
58 | | { |
59 | | public: |
60 | 0 | const char* what() const noexcept override { return "Bad any cast"; } |
61 | | ~BadAnyCast() override = default; |
62 | | }; |
63 | | |
64 | | enum class State { Pending, |
65 | | Fulfilled, |
66 | | Rejected }; |
67 | | |
68 | | template <typename T> |
69 | | class Promise; |
70 | | |
71 | | class PromiseBase |
72 | | { |
73 | | public: |
74 | 0 | virtual ~PromiseBase() = default; |
75 | | virtual bool isPending() const = 0; |
76 | | virtual bool isFulfilled() const = 0; |
77 | | virtual bool isRejected() const = 0; |
78 | | |
79 | 0 | bool isSettled() const { return isFulfilled() || isRejected(); } |
80 | | }; |
81 | | |
82 | | namespace detail |
83 | | { |
84 | | template <typename Func, typename T> |
85 | | struct IsCallable |
86 | | { |
87 | | |
88 | | template <typename U> |
89 | | static auto test(U*) |
90 | | -> decltype(std::declval<Func>()(std::declval<U>()), std::true_type()); |
91 | | |
92 | | template <typename U> |
93 | | static auto test(...) -> std::false_type; |
94 | | |
95 | | static constexpr bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value; |
96 | | }; |
97 | | |
98 | | template <typename Func> |
99 | | struct IsMoveCallable : public IsMoveCallable<decltype(&Func::operator())> |
100 | | { }; |
101 | | |
102 | | template <typename R, typename Class, typename Arg> |
103 | | struct IsMoveCallable<R (Class::*)(Arg) const> |
104 | | : public std::is_rvalue_reference<Arg> |
105 | | { }; |
106 | | |
107 | | template <typename Func, typename Arg> |
108 | | typename std::conditional<IsMoveCallable<Func>::value, Arg&&, |
109 | | const Arg&>::type |
110 | | tryMove(Arg& arg) |
111 | 0 | { |
112 | 0 | return std::move(arg); |
113 | 0 | } Unexecuted instantiation: _ZN8Pistache5Async6detail7tryMoveINSt3__18functionIFNS0_7PromiseIlEElEEElEENS3_11conditionalIXsr14IsMoveCallableIT_EE5valueEOT0_RKSB_E4typeERSB_ Unexecuted instantiation: http.cc:_ZN8Pistache5Async6detail7tryMoveIZNS_4Http9serveFileERNS3_14ResponseWriterERKNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEERKNS3_4Mime9MediaTypeEE3$_1lEENS6_11conditionalIXsr14IsMoveCallableIT_EE5valueEOT0_RKSM_E4typeERSM_ |
114 | | |
115 | | template <typename Func> |
116 | | struct FunctionTrait : public FunctionTrait<decltype(&Func::operator())> |
117 | | { }; |
118 | | |
119 | | template <typename R, typename Class, typename... Args> |
120 | | struct FunctionTrait<R (Class::*)(Args...) const> |
121 | | { |
122 | | typedef R ReturnType; |
123 | | |
124 | | static constexpr size_t ArgsCount = sizeof...(Args); |
125 | | }; |
126 | | |
127 | | template <typename R, typename Class, typename... Args> |
128 | | struct FunctionTrait<R (Class::*)(Args...)> |
129 | | { |
130 | | typedef R ReturnType; |
131 | | |
132 | | static constexpr size_t ArgsCount = sizeof...(Args); |
133 | | }; |
134 | | |
135 | | template <typename T> |
136 | | struct RemovePromise |
137 | | { |
138 | | typedef T Type; |
139 | | }; |
140 | | |
141 | | template <typename T> |
142 | | struct RemovePromise<Promise<T>> |
143 | | { |
144 | | typedef T Type; |
145 | | }; |
146 | | |
147 | | template <size_t N, typename... T> |
148 | | struct nth_element; |
149 | | |
150 | | template <typename Head, typename... Tail> |
151 | | struct nth_element<0, Head, Tail...> |
152 | | { |
153 | | typedef Head type; |
154 | | }; |
155 | | |
156 | | template <size_t N, typename Head, typename... Tail> |
157 | | struct nth_element<N, Head, Tail...> |
158 | | { |
159 | | typedef typename nth_element<N - 1, Tail...>::type type; |
160 | | }; |
161 | | |
162 | | } // namespace detail |
163 | | |
164 | | namespace Private |
165 | | { |
166 | | |
167 | | struct InternalRethrow |
168 | | { |
169 | | explicit InternalRethrow(std::exception_ptr _exc) |
170 | 0 | : exc(std::move(_exc)) |
171 | 0 | { } |
172 | | |
173 | | std::exception_ptr exc; |
174 | | }; |
175 | | |
176 | | struct IgnoreException |
177 | | { |
178 | 0 | void operator()(std::exception_ptr) const { } |
179 | | }; |
180 | | |
181 | | struct NoExcept |
182 | | { |
183 | 0 | void operator()(std::exception_ptr) const { std::terminate(); } |
184 | | }; |
185 | | |
186 | | struct Throw |
187 | | { |
188 | | [[noreturn]] void operator()(std::exception_ptr exc) const |
189 | 0 | { |
190 | 0 | throw InternalRethrow(std::move(exc)); |
191 | 0 | } |
192 | | }; |
193 | | |
194 | | struct Core; |
195 | | |
196 | | class Request |
197 | | { |
198 | | public: |
199 | | virtual void resolve(const std::shared_ptr<Core>& core) = 0; |
200 | | virtual void reject(const std::shared_ptr<Core>& core) = 0; |
201 | 0 | virtual ~Request() = default; |
202 | | }; |
203 | | |
204 | | struct Core |
205 | | { |
206 | | Core(State _state, TypeId _id) |
207 | 0 | : allocated(false) |
208 | 0 | , state(_state) |
209 | 0 | , exc() |
210 | 0 | , mtx() |
211 | 0 | , requests() |
212 | 0 | , id(_id) |
213 | 0 | { } |
214 | | |
215 | | bool allocated; |
216 | | std::atomic<State> state; |
217 | | std::exception_ptr exc; |
218 | | |
219 | | /* |
220 | | * We need this lock because a Promise might be resolved or rejected from a |
221 | | * thread A while a continuation to the same Promise (Core) might be attached |
222 | | * at the same from a thread B. If that's the case, then we need to serialize |
223 | | * operations so that we avoid a race-condition. |
224 | | * |
225 | | * Since we have a lock, we have a blocking progress guarantee but I don't |
226 | | * expect this to be a major bottleneck as I don't expect major contention on |
227 | | * the lock If it ends up being a bottlenick, try @improving it by |
228 | | * experimenting with a lock-free scheme |
229 | | */ |
230 | | std::mutex mtx; |
231 | | std::vector<std::shared_ptr<Request>> requests; |
232 | | TypeId id; |
233 | | |
234 | | virtual void* memory() = 0; |
235 | | |
236 | | virtual bool isVoid() const = 0; |
237 | | |
238 | | template <typename T, typename... Args> |
239 | | void construct(Args&&... args) |
240 | 0 | { |
241 | 0 | if (isVoid()) |
242 | 0 | throw Error("Can not construct a void core"); |
243 | | |
244 | 0 | if (id != TypeId::of<T>()) |
245 | 0 | { |
246 | 0 | throw BadType(id); |
247 | 0 | } |
248 | | |
249 | 0 | void* mem = memory(); |
250 | |
|
251 | 0 | if (allocated) |
252 | 0 | { |
253 | 0 | std::destroy_at(reinterpret_cast<T*>(mem)); |
254 | 0 | allocated = false; |
255 | 0 | } |
256 | |
|
257 | 0 | new (mem) T(std::forward<Args>(args)...); |
258 | 0 | allocated = true; |
259 | 0 | state = State::Fulfilled; |
260 | 0 | } Unexecuted instantiation: void Pistache::Async::Private::Core::construct<long, long const&>(long const&) Unexecuted instantiation: void Pistache::Async::Private::Core::construct<long, long&>(long&) Unexecuted instantiation: void Pistache::Async::Private::Core::construct<long, long>(long&&) Unexecuted instantiation: void Pistache::Async::Private::Core::construct<rusage, rusage&>(rusage&) Unexecuted instantiation: void Pistache::Async::Private::Core::construct<unsigned long, unsigned long&>(unsigned long&) |
261 | | |
262 | 0 | virtual ~Core() = default; |
263 | | }; |
264 | | |
265 | | template <typename T> |
266 | | struct CoreT : public Core |
267 | | { |
268 | | CoreT() |
269 | 0 | : Core(State::Pending, TypeId::of<T>()) |
270 | | , storage() |
271 | 0 | { } |
272 | | |
273 | | ~CoreT() override |
274 | 0 | { |
275 | 0 | if (allocated) |
276 | 0 | { |
277 | 0 | std::destroy_at(reinterpret_cast<T*>(storage)); |
278 | 0 | allocated = false; |
279 | 0 | } |
280 | 0 | } |
281 | | |
282 | | template <class Other> |
283 | | struct Rebind |
284 | | { |
285 | | typedef CoreT<Other> Type; |
286 | | }; |
287 | | |
288 | | T& value() |
289 | 0 | { |
290 | 0 | if (state != State::Fulfilled) |
291 | 0 | throw Error("Attempted to take the value of a not fulfilled promise"); |
292 | | |
293 | 0 | return *reinterpret_cast<T*>(&storage); |
294 | 0 | } |
295 | | |
296 | 0 | bool isVoid() const override { return false; } Unexecuted instantiation: Pistache::Async::Private::CoreT<rusage>::isVoid() const Unexecuted instantiation: Pistache::Async::Private::CoreT<long>::isVoid() const |
297 | | |
298 | | protected: |
299 | 0 | void* memory() override { return &storage; } Unexecuted instantiation: Pistache::Async::Private::CoreT<rusage>::memory() Unexecuted instantiation: Pistache::Async::Private::CoreT<long>::memory() |
300 | | |
301 | | private: |
302 | | alignas(T) std::byte storage[sizeof(T)]; |
303 | | }; |
304 | | |
305 | | template <> |
306 | | struct CoreT<void> : public Core |
307 | | { |
308 | | CoreT() |
309 | 0 | : Core(State::Pending, TypeId::of<void>()) |
310 | 0 | { } |
311 | | |
312 | 0 | bool isVoid() const override { return true; } |
313 | | |
314 | | protected: |
315 | 0 | void* memory() override { return nullptr; } |
316 | | }; |
317 | | |
318 | | template <typename T> |
319 | | struct Continuable : public Request |
320 | | { |
321 | | explicit Continuable(const std::shared_ptr<Core>& chain) |
322 | 0 | : resolveCount_(0) |
323 | 0 | , rejectCount_(0) |
324 | 0 | , chain_(chain) |
325 | 0 | { } |
326 | | |
327 | | void resolve(const std::shared_ptr<Core>& core) override |
328 | 0 | { |
329 | 0 | if (resolveCount_ >= 1) |
330 | 0 | return; // TODO is this the right thing? |
331 | | // throw Error("Resolve must not be called more than once"); |
332 | | |
333 | 0 | ++resolveCount_; |
334 | 0 | doResolve(coreCast(core)); |
335 | 0 | } |
336 | | |
337 | | void reject(const std::shared_ptr<Core>& core) override |
338 | 0 | { |
339 | 0 | if (rejectCount_ >= 1) |
340 | 0 | return; // TODO is this the right thing? |
341 | | // throw Error("Reject must not be called more than once"); |
342 | | |
343 | 0 | ++rejectCount_; |
344 | 0 | try |
345 | 0 | { |
346 | 0 | doReject(coreCast(core)); |
347 | 0 | } |
348 | 0 | catch (const InternalRethrow& e) |
349 | 0 | { |
350 | 0 | chain_->exc = e.exc; |
351 | 0 | chain_->state = State::Rejected; |
352 | 0 | for (const auto& req : chain_->requests) |
353 | 0 | { |
354 | 0 | if (req) |
355 | 0 | req->reject(chain_); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | std::shared_ptr<CoreT<T>> coreCast(const std::shared_ptr<Core>& core) const |
361 | 0 | { |
362 | 0 | return std::static_pointer_cast<CoreT<T>>(core); |
363 | 0 | } |
364 | | |
365 | | virtual void doResolve(const std::shared_ptr<CoreT<T>>& core) = 0; |
366 | | virtual void doReject(const std::shared_ptr<CoreT<T>>& core) = 0; |
367 | | |
368 | 0 | ~Continuable() override = default; |
369 | | |
370 | | size_t resolveCount_; |
371 | | size_t rejectCount_; |
372 | | std::shared_ptr<Core> chain_; |
373 | | }; |
374 | | |
375 | | namespace impl |
376 | | { |
377 | | |
378 | | template <typename T, typename Resolve, typename Reject, typename U> |
379 | | struct Continuation; |
380 | | |
381 | | template <typename T, typename Resolve, typename Reject, typename Res, |
382 | | typename Cls, typename... Args> |
383 | | struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const> |
384 | | : public Continuation<T, Resolve, Reject, Res(Args...)> |
385 | | { |
386 | | typedef Continuation<T, Resolve, Reject, Res(Args...)> Base; |
387 | | |
388 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
389 | | Reject reject) |
390 | 0 | : Base(chain, std::move(resolve), std::move(reject)) |
391 | 0 | { } |
392 | | }; |
393 | | |
394 | | template <typename T, typename Resolve, typename Reject, typename Res, |
395 | | typename Cls, typename... Args> |
396 | | struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)> |
397 | | : public Continuation<T, Resolve, Reject, Res(Args...)> |
398 | | { |
399 | | typedef Continuation<T, Resolve, Reject, Res(Args...)> Base; |
400 | | |
401 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
402 | | Reject reject) |
403 | 0 | : Base(chain, std::move(resolve), std::move(reject)) |
404 | 0 | { } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>::*)(long const&)>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, {lambda(std::exception_ptr)#1}) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>::*)(long const&)>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, {lambda(std::exception_ptr)#1}) |
405 | | }; |
406 | | |
407 | | // General specialization |
408 | | template <typename T, typename Resolve, typename Reject, typename Res, |
409 | | typename... Args> |
410 | | struct Continuation<T, Resolve, Reject, Res(Args...)> : public Continuable<T> |
411 | | { |
412 | | |
413 | | static_assert(sizeof...(Args) == 1, |
414 | | "A continuation should only take one single argument"); |
415 | | |
416 | | typedef typename detail::nth_element<0, Args...>::type Arg; |
417 | | |
418 | | static_assert(std::is_same<T, Arg>::value || std::is_convertible<T, Arg>::value, |
419 | | "Incompatible types detected"); |
420 | | |
421 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
422 | | Reject reject) |
423 | | : Continuable<T>(chain) |
424 | | , resolve_(resolve) |
425 | | , reject_(reject) |
426 | | { } |
427 | | |
428 | | void doResolve(const std::shared_ptr<CoreT<T>>& core) override |
429 | | { |
430 | | finishResolve(resolve_(detail::tryMove<Resolve>(core->value()))); |
431 | | } |
432 | | |
433 | | void doReject(const std::shared_ptr<CoreT<T>>& core) override |
434 | | { |
435 | | reject_(core->exc); |
436 | | /* |
437 | | * reject_ is guaranteed to throw ("[[noreturn]]") so |
438 | | * doing this "for" loop is pointless |
439 | | * |
440 | | for (const auto& req : this->chain_->requests) |
441 | | { |
442 | | if (req) |
443 | | req->reject(this->chain_); |
444 | | } |
445 | | */ |
446 | | } |
447 | | |
448 | | template <typename Ret> |
449 | | void finishResolve(Ret&& ret) const |
450 | | { |
451 | | typedef typename std::decay<Ret>::type CleanRet; |
452 | | this->chain_->template construct<CleanRet>(std::forward<Ret>(ret)); |
453 | | for (const auto& req : this->chain_->requests) |
454 | | { |
455 | | if (req) |
456 | | req->resolve(this->chain_); |
457 | | } |
458 | | } |
459 | | |
460 | | Resolve resolve_; |
461 | | Reject reject_; |
462 | | }; |
463 | | |
464 | | // Specialization for a void-Promise |
465 | | template <typename Resolve, typename Reject, typename Res, typename... Args> |
466 | | struct Continuation<void, Resolve, Reject, Res(Args...)> |
467 | | : public Continuable<void> |
468 | | { |
469 | | |
470 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
471 | | Reject reject) |
472 | | : Continuable<void>(chain) |
473 | | , resolve_(resolve) |
474 | | , reject_(reject) |
475 | | { } |
476 | | |
477 | | static_assert(sizeof...(Args) == 0, |
478 | | "Can not attach a non-void continuation to a void-Promise"); |
479 | | |
480 | | void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) override |
481 | | { |
482 | | finishResolve(resolve_()); |
483 | | } |
484 | | |
485 | | void doReject(const std::shared_ptr<CoreT<void>>& core) override |
486 | | { |
487 | | reject_(core->exc); |
488 | | for (const auto& req : this->chain_->requests) |
489 | | { |
490 | | if (req) |
491 | | req->reject(this->chain_); |
492 | | } |
493 | | } |
494 | | |
495 | | template <typename Ret> |
496 | | void finishResolve(Ret&& ret) const |
497 | | { |
498 | | typedef typename std::remove_reference<Ret>::type CleanRet; |
499 | | this->chain_->template construct<CleanRet>(std::forward<Ret>(ret)); |
500 | | for (const auto& req : this->chain_->requests) |
501 | | { |
502 | | if (req) |
503 | | req->resolve(this->chain_); |
504 | | } |
505 | | } |
506 | | |
507 | | Resolve resolve_; |
508 | | Reject reject_; |
509 | | }; |
510 | | |
511 | | // Specialization for a callback returning void |
512 | | template <typename T, typename Resolve, typename Reject, typename... Args> |
513 | | struct Continuation<T, Resolve, Reject, void(Args...)> : public Continuable<T> |
514 | | { |
515 | | |
516 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
517 | | Reject reject) |
518 | 0 | : Continuable<T>(chain) |
519 | 0 | , resolve_(resolve) |
520 | 0 | , reject_(reject) |
521 | 0 | { } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (long const&)>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, {lambda(std::exception_ptr)#1}) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (long const&)>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, {lambda(std::exception_ptr)#1}) |
522 | | |
523 | | static_assert(sizeof...(Args) == 1, |
524 | | "A continuation should only take one single argument"); |
525 | | |
526 | | typedef typename detail::nth_element<0, Args...>::type Arg; |
527 | | |
528 | | static_assert(std::is_same<T, Arg>::value || std::is_convertible<T, Arg>::value, |
529 | | "Incompatible types detected"); |
530 | | |
531 | | void doResolve(const std::shared_ptr<CoreT<T>>& core) override |
532 | 0 | { |
533 | 0 | resolve_(core->value()); |
534 | 0 | } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (long const&)>::doResolve(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (long const&)>::doResolve(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) |
535 | | |
536 | | void doReject(const std::shared_ptr<CoreT<T>>& core) override |
537 | 0 | { |
538 | 0 | reject_(core->exc); |
539 | 0 | } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (long const&)>::doReject(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, void (long const&)>::doReject(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) |
540 | | |
541 | | Resolve resolve_; |
542 | | Reject reject_; |
543 | | }; |
544 | | |
545 | | // Specialization for a void-Promise on a callback returning void |
546 | | template <typename Resolve, typename Reject, typename... Args> |
547 | | struct Continuation<void, Resolve, Reject, void(Args...)> |
548 | | : public Continuable<void> |
549 | | { |
550 | | |
551 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
552 | | Reject reject) |
553 | | : Continuable<void>(chain) |
554 | | , resolve_(resolve) |
555 | | , reject_(reject) |
556 | | { } |
557 | | |
558 | | static_assert(sizeof...(Args) == 0, |
559 | | "Can not attach a non-void continuation to a void-Promise"); |
560 | | |
561 | | void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) override |
562 | | { |
563 | | resolve_(); |
564 | | } |
565 | | |
566 | | void doReject(const std::shared_ptr<CoreT<void>>& core) override |
567 | | { |
568 | | reject_(core->exc); |
569 | | } |
570 | | |
571 | | Resolve resolve_; |
572 | | Reject reject_; |
573 | | }; |
574 | | |
575 | | // Specialization for a callback returning a Promise |
576 | | template <typename T, typename Resolve, typename Reject, typename U, |
577 | | typename... Args> |
578 | | struct Continuation<T, Resolve, Reject, Promise<U>(Args...)> |
579 | | : public Continuable<T> |
580 | | { |
581 | | |
582 | | static_assert(sizeof...(Args) == 1, |
583 | | "A continuation should only take one single argument"); |
584 | | |
585 | | typedef typename detail::nth_element<0, Args...>::type Arg; |
586 | | |
587 | | static_assert(std::is_same<T, Arg>::value || std::is_convertible<T, Arg>::value, |
588 | | "Incompatible types detected"); |
589 | | |
590 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
591 | | Reject reject) |
592 | 0 | : Continuable<T>(chain) |
593 | 0 | , resolve_(resolve) |
594 | 0 | , reject_(reject) |
595 | 0 | { } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw) |
596 | | |
597 | | void doResolve(const std::shared_ptr<CoreT<T>>& core) override |
598 | 0 | { |
599 | 0 | auto promise = resolve_(detail::tryMove<Resolve>(core->value())); |
600 | 0 | finishResolve(promise); |
601 | 0 | } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::doResolve(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::doResolve(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) |
602 | | |
603 | | void doReject(const std::shared_ptr<CoreT<T>>& core) override |
604 | 0 | { |
605 | 0 | reject_(core->exc); |
606 | | /* |
607 | | * reject_ is guaranteed to throw ("[[noreturn]]") so |
608 | | * doing this "for" loop is pointless |
609 | | * |
610 | | for (const auto& req : core->requests) |
611 | | { |
612 | | if (req) |
613 | | req->reject(core); |
614 | | } |
615 | | */ |
616 | 0 | } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::doReject(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::doReject(std::__1::shared_ptr<Pistache::Async::Private::CoreT<long> > const&) |
617 | | |
618 | | template <typename PromiseType> |
619 | | struct Chainer |
620 | | { |
621 | | explicit Chainer(const std::shared_ptr<Private::Core>& core) |
622 | 0 | : chainCore(core) |
623 | 0 | { } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>::Chainer(std::__1::shared_ptr<Pistache::Async::Private::Core> const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>::Chainer(std::__1::shared_ptr<Pistache::Async::Private::Core> const&) |
624 | | |
625 | | void operator()(const PromiseType& val) |
626 | 0 | { |
627 | 0 | chainCore->construct<PromiseType>(val); |
628 | 0 | for (const auto& req : chainCore->requests) |
629 | 0 | { |
630 | 0 | if (req) |
631 | 0 | req->resolve(chainCore); |
632 | 0 | } |
633 | 0 | } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>::operator()(long const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>::operator()(long const&) |
634 | | |
635 | | std::shared_ptr<Core> chainCore; |
636 | | }; |
637 | | |
638 | | template <typename Promise, |
639 | | typename Type = typename detail::RemovePromise<Promise>::Type> |
640 | | Chainer<Type> makeChainer(const Promise&) |
641 | 0 | { |
642 | 0 | return Chainer<Type>(this->chain_); |
643 | 0 | } Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long> Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::makeChainer<Pistache::Async::Promise<long>, long>(Pistache::Async::Promise<long> const&) Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long> Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::makeChainer<Pistache::Async::Promise<long>, long>(Pistache::Async::Promise<long> const&) |
644 | | |
645 | | template <typename P> |
646 | | void finishResolve(P& promise) |
647 | 0 | { |
648 | 0 | auto chainer = makeChainer(promise); |
649 | 0 | std::weak_ptr<Core> weakPtr = this->chain_; |
650 | 0 | promise.then(std::move(chainer), [weakPtr](std::exception_ptr exc) { |
651 | 0 | if (auto core = weakPtr.lock()) |
652 | 0 | { |
653 | 0 | core->exc = std::move(exc); |
654 | 0 | core->state = State::Rejected; |
655 | |
|
656 | 0 | for (const auto& req : core->requests) |
657 | 0 | { |
658 | 0 | if (req) |
659 | 0 | req->reject(core); |
660 | 0 | } |
661 | 0 | } |
662 | 0 | }); Unexecuted instantiation: Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}::operator()(std::exception_ptr) const Unexecuted instantiation: http.cc:Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}::operator()(std::exception_ptr) const |
663 | 0 | } Unexecuted instantiation: void Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&) Unexecuted instantiation: http.cc:void Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&) |
664 | | |
665 | | Resolve resolve_; |
666 | | Reject reject_; |
667 | | }; |
668 | | |
669 | | // Specialization for a void callback returning a Promise |
670 | | template <typename Resolve, typename Reject, typename U, typename... Args> |
671 | | struct Continuation<void, Resolve, Reject, Promise<U>(Args...)> |
672 | | : public Continuable<void> |
673 | | { |
674 | | |
675 | | static_assert(sizeof...(Args) == 0, |
676 | | "Can not attach a non-void continuation to a void-Promise"); |
677 | | |
678 | | Continuation(const std::shared_ptr<Core>& chain, Resolve resolve, |
679 | | Reject reject) |
680 | | : Continuable<void>(chain) |
681 | | , resolve_(resolve) |
682 | | , reject_(reject) |
683 | | { } |
684 | | |
685 | | void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) override |
686 | | { |
687 | | auto promise = resolve_(); |
688 | | finishResolve(promise); |
689 | | } |
690 | | |
691 | | void doReject(const std::shared_ptr<CoreT<void>>& core) override |
692 | | { |
693 | | reject_(core->exc); |
694 | | for (const auto& req : core->requests) |
695 | | { |
696 | | if (req) |
697 | | req->reject(core); |
698 | | } |
699 | | } |
700 | | |
701 | | template <typename PromiseType, typename Dummy = void> |
702 | | struct Chainer |
703 | | { |
704 | | explicit Chainer(const std::shared_ptr<Private::Core>& core) |
705 | | : chainCore(core) |
706 | | { } |
707 | | |
708 | | void operator()(const PromiseType& val) |
709 | | { |
710 | | chainCore->construct<PromiseType>(val); |
711 | | for (const auto& req : chainCore->requests) |
712 | | { |
713 | | if (req) |
714 | | req->resolve(chainCore); |
715 | | } |
716 | | } |
717 | | |
718 | | std::shared_ptr<Core> chainCore; |
719 | | }; |
720 | | |
721 | | template <typename Dummy> |
722 | | struct Chainer<void, Dummy> |
723 | | { |
724 | | explicit Chainer(const std::shared_ptr<Private::Core>& core) |
725 | | : chainCore(core) |
726 | | { } |
727 | | |
728 | | void operator()() |
729 | | { |
730 | | chainCore->state = State::Fulfilled; |
731 | | |
732 | | for (const auto& req : chainCore->requests) |
733 | | { |
734 | | if (req) |
735 | | req->resolve(chainCore); |
736 | | } |
737 | | } |
738 | | |
739 | | std::shared_ptr<Core> chainCore; |
740 | | }; |
741 | | |
742 | | template <typename Promise, |
743 | | typename Type = typename detail::RemovePromise<Promise>::Type> |
744 | | Chainer<Type> makeChainer(const Promise&) |
745 | | { |
746 | | return Chainer<Type>(this->chain_); |
747 | | } |
748 | | |
749 | | template <typename P> |
750 | | void finishResolve(P& promise) |
751 | | { |
752 | | auto chainer = makeChainer(promise); |
753 | | promise.then(std::move(chainer), [this](std::exception_ptr exc) { |
754 | | auto core = this->chain_; |
755 | | core->exc = std::move(exc); |
756 | | core->state = State::Rejected; |
757 | | |
758 | | for (const auto& req : core->requests) |
759 | | { |
760 | | if (req) |
761 | | req->reject(core); |
762 | | } |
763 | | }); |
764 | | } |
765 | | |
766 | | Resolve resolve_; |
767 | | Reject reject_; |
768 | | }; |
769 | | |
770 | | } // namespace impl |
771 | | |
772 | | template <typename T, typename Resolve, typename Reject, typename Sig> |
773 | | struct Continuation : public impl::Continuation<T, Resolve, Reject, |
774 | | decltype(&Sig::operator())> |
775 | | { |
776 | | |
777 | | typedef impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())> |
778 | | Base; |
779 | | |
780 | | Continuation(const std::shared_ptr<Core>& core, Resolve resolve, |
781 | | Reject reject) |
782 | 0 | : Base(core, std::move(resolve), std::move(reject)) |
783 | 0 | { } Unexecuted instantiation: Pistache::Async::Private::Continuation<long, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long> >::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, {lambda(std::exception_ptr)#1}) Unexecuted instantiation: http.cc:Pistache::Async::Private::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1>::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw) Unexecuted instantiation: http.cc:Pistache::Async::Private::Continuation<long, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long> >::Continuation(std::__1::shared_ptr<Pistache::Async::Private::Core> const&, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, {lambda(std::exception_ptr)#1}) |
784 | | }; |
785 | | |
786 | | template <typename T, typename Resolve, typename Reject, typename Res, |
787 | | typename... Args> |
788 | | struct Continuation<T, Resolve, Reject, Res (*)(Args...)> |
789 | | : public impl::Continuation<T, Resolve, Reject, Res(Args...)> |
790 | | { |
791 | | typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base; |
792 | | |
793 | | Continuation(const std::shared_ptr<Core>& core, Resolve resolve, |
794 | | Reject reject) |
795 | | : Base(core, std::move(resolve), std::move(reject)) |
796 | | { } |
797 | | }; |
798 | | |
799 | | template <typename T, typename Resolve, typename Reject, typename Res, |
800 | | typename Cls, typename... Args> |
801 | | struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)> |
802 | | : public impl::Continuation<T, Resolve, Reject, Res(Args...)> |
803 | | { |
804 | | typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base; |
805 | | |
806 | | Continuation(const std::shared_ptr<Core>& core, Resolve resolve, |
807 | | Reject reject) |
808 | | : Base(core, std::move(resolve), std::move(reject)) |
809 | | { } |
810 | | }; |
811 | | |
812 | | template <typename T, typename Resolve, typename Reject, typename Res, |
813 | | typename Cls, typename... Args> |
814 | | struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const> |
815 | | : public impl::Continuation<T, Resolve, Reject, Res(Args...)> |
816 | | { |
817 | | typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base; |
818 | | |
819 | | Continuation(const std::shared_ptr<Core>& core, Resolve resolve, |
820 | | Reject reject) |
821 | | : Base(core, std::move(resolve), std::move(reject)) |
822 | | { } |
823 | | }; |
824 | | |
825 | | template <typename T, typename Resolve, typename Reject, typename Res, |
826 | | typename... Args> |
827 | | struct Continuation<T, Resolve, Reject, std::function<Res(Args...)>> |
828 | | : public impl::Continuation<T, Resolve, Reject, Res(Args...)> |
829 | | { |
830 | | typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base; |
831 | | |
832 | | Continuation(const std::shared_ptr<Core>& core, Resolve resolve, |
833 | | Reject reject) |
834 | 0 | : Base(core, std::move(resolve), std::move(reject)) |
835 | 0 | { } |
836 | | }; |
837 | | } // namespace Private |
838 | | |
839 | | class Resolver |
840 | | { |
841 | | public: |
842 | | explicit Resolver(const std::shared_ptr<Private::Core>& core) |
843 | 0 | : core_(core) |
844 | 0 | { } |
845 | | |
846 | | Resolver(const Resolver& other) = delete; |
847 | | Resolver& operator=(const Resolver& other) = delete; |
848 | | |
849 | 0 | Resolver(Resolver&& other) = default; |
850 | | Resolver& operator=(Resolver&& other) = default; |
851 | | |
852 | | template <typename Arg> |
853 | | bool operator()(Arg&& arg) const |
854 | 0 | { |
855 | 0 | if (!core_) |
856 | 0 | return false; |
857 | | |
858 | 0 | typedef typename std::remove_reference<Arg>::type Type; |
859 | |
|
860 | 0 | if (core_->state != State::Pending) |
861 | 0 | throw Error("Attempt to resolve a fulfilled promise"); |
862 | | |
863 | | /* In a ideal world, this should be checked at compile-time rather |
864 | | * than runtime. However, since types are erased, this looks like |
865 | | * a difficult task |
866 | | */ |
867 | 0 | if (core_->isVoid()) |
868 | 0 | { |
869 | 0 | throw Error("Attempt to resolve a void promise with arguments"); |
870 | 0 | } |
871 | | |
872 | 0 | std::unique_lock<std::mutex> guard(core_->mtx); |
873 | 0 | core_->construct<Type>(std::forward<Arg>(arg)); |
874 | |
|
875 | 0 | for (const auto& req : core_->requests) |
876 | 0 | { |
877 | 0 | if (req) |
878 | 0 | req->resolve(core_); |
879 | 0 | } |
880 | |
|
881 | 0 | return true; |
882 | 0 | } Unexecuted instantiation: bool Pistache::Async::Resolver::operator()<long>(long&&) const Unexecuted instantiation: bool Pistache::Async::Resolver::operator()<rusage&>(rusage&) const Unexecuted instantiation: bool Pistache::Async::Resolver::operator()<unsigned long&>(unsigned long&) const |
883 | | |
884 | | bool operator()() const |
885 | 0 | { |
886 | 0 | if (!core_) |
887 | 0 | return false; |
888 | 0 |
|
889 | 0 | if (core_->state != State::Pending) |
890 | 0 | throw Error("Attempt to resolve a fulfilled promise"); |
891 | 0 |
|
892 | 0 | if (!core_->isVoid()) |
893 | 0 | throw Error("Attempt ro resolve a non-void promise with no argument"); |
894 | 0 |
|
895 | 0 | std::unique_lock<std::mutex> guard(core_->mtx); |
896 | 0 | core_->state = State::Fulfilled; |
897 | 0 | for (const auto& req : core_->requests) |
898 | 0 | { |
899 | 0 | if (req) |
900 | 0 | req->resolve(core_); |
901 | 0 | } |
902 | 0 |
|
903 | 0 | return true; |
904 | 0 | } |
905 | | |
906 | 0 | void clear() { core_ = nullptr; } |
907 | | |
908 | 0 | Resolver clone() { return Resolver(core_); } |
909 | | |
910 | | private: |
911 | | std::shared_ptr<Private::Core> core_; |
912 | | }; |
913 | | |
914 | | class Rejection |
915 | | { |
916 | | public: |
917 | | explicit Rejection(const std::shared_ptr<Private::Core>& core) |
918 | 0 | : core_(core) |
919 | 0 | { } |
920 | | |
921 | | Rejection(const Rejection& other) = delete; |
922 | | Rejection& operator=(const Rejection& other) = delete; |
923 | | |
924 | 0 | Rejection(Rejection&& other) = default; |
925 | | Rejection& operator=(Rejection&& other) = default; |
926 | | |
927 | | template <typename Exc> |
928 | | bool operator()(Exc exc) const |
929 | 0 | { |
930 | 0 | if (!core_) |
931 | 0 | return false; |
932 | | |
933 | 0 | if (core_->state != State::Pending) |
934 | 0 | throw Error("Attempt to reject a fulfilled promise"); |
935 | | |
936 | 0 | std::unique_lock<std::mutex> guard(core_->mtx); |
937 | 0 | core_->exc = std::make_exception_ptr(exc); |
938 | 0 | core_->state = State::Rejected; |
939 | 0 | for (const auto& req : core_->requests) |
940 | 0 | { |
941 | 0 | if (req) |
942 | 0 | req->reject(core_); |
943 | 0 | } |
944 | |
|
945 | 0 | return true; |
946 | 0 | } Unexecuted instantiation: bool Pistache::Async::Rejection::operator()<Pistache::Error>(Pistache::Error) const Unexecuted instantiation: bool Pistache::Async::Rejection::operator()<std::runtime_error>(std::runtime_error) const |
947 | | |
948 | 0 | void clear() { core_ = nullptr; } |
949 | | |
950 | 0 | Rejection clone() { return Rejection(core_); } |
951 | | |
952 | | private: |
953 | | std::shared_ptr<Private::Core> core_; |
954 | | }; |
955 | | |
956 | | template <typename T> |
957 | | class Deferred |
958 | | { |
959 | | public: |
960 | | Deferred() |
961 | 0 | : resolver(nullptr) |
962 | 0 | , rejection(nullptr) |
963 | 0 | { } |
964 | | |
965 | | Deferred(const Deferred& other) = delete; |
966 | | Deferred& operator=(const Deferred& other) = delete; |
967 | | |
968 | 0 | Deferred(Deferred&& other) = default; Unexecuted instantiation: Pistache::Async::Deferred<long>::Deferred(Pistache::Async::Deferred<long>&&) Unexecuted instantiation: Pistache::Async::Deferred<unsigned long>::Deferred(Pistache::Async::Deferred<unsigned long>&&) |
969 | | Deferred& operator=(Deferred&& other) = default; |
970 | | |
971 | | Deferred(Resolver _resolver, Rejection _reject) |
972 | 0 | : resolver(std::move(_resolver)) |
973 | 0 | , rejection(std::move(_reject)) |
974 | 0 | { } |
975 | | |
976 | | template <typename U> |
977 | | bool resolve(U&& arg) |
978 | 0 | { |
979 | 0 | typedef typename std::remove_reference<U>::type CleanU; |
980 | |
|
981 | 0 | static_assert(std::is_same<T, CleanU>::value || std::is_convertible<U, T>::value, |
982 | 0 | "Types mismatch"); |
983 | |
|
984 | 0 | return resolver(std::forward<U>(arg)); |
985 | 0 | } Unexecuted instantiation: bool Pistache::Async::Deferred<long>::resolve<long>(long&&) Unexecuted instantiation: bool Pistache::Async::Deferred<rusage>::resolve<rusage&>(rusage&) Unexecuted instantiation: bool Pistache::Async::Deferred<unsigned long>::resolve<unsigned long&>(unsigned long&) |
986 | | |
987 | | template <typename... Args> |
988 | | void emplaceResolve(Args&&...) { } |
989 | | |
990 | | template <typename Exc> |
991 | | bool reject(Exc exc) |
992 | 0 | { |
993 | 0 | return rejection(std::move(exc)); |
994 | 0 | } Unexecuted instantiation: bool Pistache::Async::Deferred<long>::reject<Pistache::Error>(Pistache::Error) Unexecuted instantiation: bool Pistache::Async::Deferred<unsigned long>::reject<std::runtime_error>(std::runtime_error) Unexecuted instantiation: bool Pistache::Async::Deferred<unsigned long>::reject<Pistache::Error>(Pistache::Error) Unexecuted instantiation: bool Pistache::Async::Deferred<rusage>::reject<std::runtime_error>(std::runtime_error) |
995 | | |
996 | | void clear() |
997 | 0 | { |
998 | 0 | resolver.clear(); |
999 | 0 | rejection.clear(); |
1000 | 0 | } |
1001 | | |
1002 | | private: |
1003 | | Resolver resolver; |
1004 | | Rejection rejection; |
1005 | | }; |
1006 | | |
1007 | | template <> |
1008 | | class Deferred<void> |
1009 | | { |
1010 | | public: |
1011 | | Deferred() |
1012 | | : resolver(nullptr) |
1013 | | , rejection(nullptr) |
1014 | 0 | { } |
1015 | | |
1016 | | Deferred(const Deferred& other) = delete; |
1017 | | Deferred& operator=(const Deferred& other) = delete; |
1018 | | |
1019 | | Deferred(Deferred&& other) = default; |
1020 | | Deferred& operator=(Deferred&& other) = default; |
1021 | | |
1022 | | Deferred(Resolver _resolver, Rejection _reject) |
1023 | | : resolver(std::move(_resolver)) |
1024 | | , rejection(std::move(_reject)) |
1025 | 0 | { } |
1026 | | |
1027 | 0 | void resolve() { resolver(); } |
1028 | | |
1029 | | template <typename Exc> |
1030 | | void reject(Exc _exc) { rejection(std::move(_exc)); } |
1031 | | |
1032 | | private: |
1033 | | Resolver resolver; |
1034 | | Rejection rejection; |
1035 | | }; |
1036 | | |
1037 | | static constexpr Private::IgnoreException IgnoreException {}; |
1038 | | static constexpr Private::NoExcept NoExcept {}; |
1039 | | static constexpr Private::Throw Throw {}; |
1040 | | |
1041 | | namespace details |
1042 | | { |
1043 | | |
1044 | | /* |
1045 | | * Note that we could use std::result_of to SFINAE-out and dispatch to the right |
1046 | | * call However, gcc 4.7 does not correctly support std::result_of for SFINAE |
1047 | | * purposes, so we use a decltype SFINAE-expression instead. |
1048 | | * |
1049 | | * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3462.html and |
1050 | | * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56283 for reference |
1051 | | */ |
1052 | | template <typename T, typename Func> |
1053 | | auto callAsync(Func func, Resolver& resolver, Rejection& rejection) |
1054 | | -> decltype(std::declval<Func>()(resolver, rejection), void()) |
1055 | | { |
1056 | | func(resolver, rejection); |
1057 | | } |
1058 | | |
1059 | | template <typename T, typename Func> |
1060 | | auto callAsync(Func func, Resolver& resolver, Rejection& rejection) |
1061 | | -> decltype(std::declval<Func>()(Deferred<T>()), void()) |
1062 | 0 | { |
1063 | 0 | func(Deferred<T>(std::move(resolver), std::move(rejection))); |
1064 | 0 | } Unexecuted instantiation: _ZN8Pistache5Async7details9callAsyncI6rusageZNS_3Tcp9Transport4loadEvEUlNS0_8DeferredIS3_EEE_EEDTcmclclsr3stdE7declvalIT0_EEcvNS6_IT_EE_EEcvv_EES9_RNS0_8ResolverERNS0_9RejectionE Unexecuted instantiation: _ZN8Pistache5Async7details9callAsyncIlZNS_3Tcp9Transport10asyncWriteINS_9RawBufferEEENS0_7PromiseIlEEiRKT_iEUlNS0_8DeferredIlEEE_EEDTcmclclsr3stdE7declvalIT0_EEcvNSC_IS9_EE_EEcvv_EESF_RNS0_8ResolverERNS0_9RejectionE Unexecuted instantiation: _ZN8Pistache5Async7details9callAsyncIlZNS_3Tcp9Transport10asyncWriteINS_10FileBufferEEENS0_7PromiseIlEEiRKT_iEUlNS0_8DeferredIlEEE_EEDTcmclclsr3stdE7declvalIT0_EEcvNSC_IS9_EE_EEcvv_EESF_RNS0_8ResolverERNS0_9RejectionE |
1065 | | } // namespace details |
1066 | | |
1067 | | template <typename T> |
1068 | | class Promise : public PromiseBase |
1069 | | { |
1070 | | public: |
1071 | | template <typename U> |
1072 | | friend class Promise; |
1073 | | |
1074 | | typedef Private::CoreT<T> Core; |
1075 | | |
1076 | | template <typename Func> |
1077 | | explicit Promise(Func func) |
1078 | 0 | : core_(std::make_shared<Core>()) |
1079 | 0 | , resolver_(core_) |
1080 | 0 | , rejection_(core_) |
1081 | 0 | { |
1082 | 0 | details::callAsync<T>(func, resolver_, rejection_); |
1083 | 0 | } Unexecuted instantiation: Pistache::Async::Promise<long>::Promise<Pistache::Tcp::Transport::asyncWrite<Pistache::RawBuffer>(int, Pistache::RawBuffer const&, int)::{lambda(Pistache::Async::Deferred<long>)#1}>(Pistache::Tcp::Transport::asyncWrite<Pistache::RawBuffer>(int, Pistache::RawBuffer const&, int)::{lambda(Pistache::Async::Deferred<long>)#1}) Unexecuted instantiation: Pistache::Async::Promise<long>::Promise<Pistache::Tcp::Transport::asyncWrite<Pistache::FileBuffer>(int, Pistache::FileBuffer const&, int)::{lambda(Pistache::Async::Deferred<long>)#1}>(Pistache::Tcp::Transport::asyncWrite<Pistache::FileBuffer>(int, Pistache::FileBuffer const&, int)::{lambda(Pistache::Async::Deferred<long>)#1}) |
1084 | | |
1085 | | Promise(const Promise<T>& other) = delete; |
1086 | | Promise& operator=(const Promise<T>& other) = delete; |
1087 | | |
1088 | | Promise(Promise<T>&& other) = default; |
1089 | | Promise& operator=(Promise<T>&& other) = default; |
1090 | | |
1091 | 0 | ~Promise() override = default; Unexecuted instantiation: Pistache::Async::Promise<long>::~Promise() Unexecuted instantiation: Pistache::Async::Promise<void>::~Promise() |
1092 | | |
1093 | | template <typename U> |
1094 | | static Promise<T> resolved(U&& value) |
1095 | 0 | { |
1096 | 0 | static_assert(!std::is_void<T>::value, |
1097 | 0 | "Can not resolve a void promise with parameters"); |
1098 | 0 | static_assert(std::is_same<T, U>::value || std::is_convertible<U, T>::value, |
1099 | 0 | "Incompatible value type"); |
1100 | |
|
1101 | 0 | auto core = std::make_shared<Core>(); |
1102 | 0 | core->template construct<T>(std::forward<U>(value)); |
1103 | 0 | return Promise<T>(std::move(core)); |
1104 | 0 | } |
1105 | | |
1106 | | static Promise<void> resolved() |
1107 | | { |
1108 | | static_assert(std::is_void<T>::value, |
1109 | | "Resolving a non-void promise requires parameters"); |
1110 | | |
1111 | | auto core = std::make_shared<Core>(); |
1112 | | core->state = State::Fulfilled; |
1113 | | return Promise<T>(std::move(core)); |
1114 | | } |
1115 | | |
1116 | | template <typename Exc> |
1117 | | static Promise<T> rejected(Exc exc) |
1118 | 0 | { |
1119 | 0 | auto core = std::make_shared<Core>(); |
1120 | 0 | core->exc = std::make_exception_ptr(exc); |
1121 | 0 | core->state = State::Rejected; |
1122 | 0 | return Promise<T>(std::move(core)); |
1123 | 0 | } Unexecuted instantiation: Pistache::Async::Promise<long> Pistache::Async::Promise<long>::rejected<Pistache::Error>(Pistache::Error) Unexecuted instantiation: Pistache::Async::Promise<long> Pistache::Async::Promise<long>::rejected<std::exception_ptr>(std::exception_ptr) Unexecuted instantiation: Pistache::Async::Promise<long> Pistache::Async::Promise<long>::rejected<std::runtime_error>(std::runtime_error) |
1124 | | |
1125 | 0 | bool isPending() const override { return core_->state == State::Pending; } Unexecuted instantiation: Pistache::Async::Promise<rusage>::isPending() const Unexecuted instantiation: Pistache::Async::Promise<long>::isPending() const Unexecuted instantiation: Pistache::Async::Promise<void>::isPending() const |
1126 | 0 | bool isFulfilled() const override { return core_->state == State::Fulfilled; } Unexecuted instantiation: Pistache::Async::Promise<rusage>::isFulfilled() const Unexecuted instantiation: Pistache::Async::Promise<long>::isFulfilled() const Unexecuted instantiation: Pistache::Async::Promise<void>::isFulfilled() const |
1127 | 0 | bool isRejected() const override { return core_->state == State::Rejected; } Unexecuted instantiation: Pistache::Async::Promise<rusage>::isRejected() const Unexecuted instantiation: Pistache::Async::Promise<long>::isRejected() const Unexecuted instantiation: Pistache::Async::Promise<void>::isRejected() const |
1128 | | |
1129 | | template <typename ResolveFunc, typename RejectFunc> |
1130 | | auto then(ResolveFunc resolveFunc, RejectFunc rejectFunc) |
1131 | | -> Promise<typename detail::RemovePromise< |
1132 | | typename detail::FunctionTrait<ResolveFunc>::ReturnType>::Type> |
1133 | 0 | { |
1134 | |
|
1135 | 0 | typedef typename detail::RemovePromise< |
1136 | 0 | typename detail::FunctionTrait<ResolveFunc>::ReturnType>::Type RetType; |
1137 | |
|
1138 | 0 | Promise<RetType> promise; |
1139 | |
|
1140 | 0 | typedef Private::Continuation<T, ResolveFunc, RejectFunc, ResolveFunc> |
1141 | 0 | Continuation; |
1142 | 0 | std::shared_ptr<Private::Request> req = std::make_shared<Continuation>(promise.core_, resolveFunc, rejectFunc); |
1143 | |
|
1144 | 0 | std::unique_lock<std::mutex> guard(core_->mtx); |
1145 | 0 | if (isFulfilled()) |
1146 | 0 | { |
1147 | 0 | if (req) |
1148 | 0 | req->resolve(core_); |
1149 | 0 | } |
1150 | 0 | else if (isRejected()) |
1151 | 0 | { |
1152 | 0 | if (req) |
1153 | 0 | req->reject(core_); |
1154 | 0 | } |
1155 | |
|
1156 | 0 | core_->requests.push_back(req); |
1157 | |
|
1158 | 0 | return promise; |
1159 | 0 | } Unexecuted instantiation: Pistache::Async::Promise<Pistache::Async::detail::RemovePromise<Pistache::Async::detail::FunctionTrait<std::__1::function<Pistache::Async::Promise<long> (long)> >::ReturnType>::Type> Pistache::Async::Promise<long>::then<std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)> >(std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>) Unexecuted instantiation: Pistache::Async::Promise<Pistache::Async::detail::RemovePromise<Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}::FunctionTrait<Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long> >::ReturnType>::Type> Pistache::Async::Promise<long>::then<Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}>(Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, std::__1::function<Pistache::Async::Promise<long> (long)>, std::__1::function<void (std::exception_ptr&)>, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}) Unexecuted instantiation: http.cc:Pistache::Async::Promise<Pistache::Async::detail::RemovePromise<Pistache::Async::detail::FunctionTrait<Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1>::ReturnType>::Type> Pistache::Async::Promise<long>::then<Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw>(Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw) Unexecuted instantiation: http.cc:Pistache::Async::Promise<Pistache::Async::detail::RemovePromise<Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}::FunctionTrait<Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long> >::ReturnType>::Type> Pistache::Async::Promise<long>::then<Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}>(Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::Chainer<long>, Pistache::Async::Private::impl::Continuation<long, Pistache::Http::serveFile(Pistache::Http::ResponseWriter&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Pistache::Http::Mime::MediaType const&)::$_1, Pistache::Async::Private::Throw, Pistache::Async::Promise<long> (long)>::finishResolve<Pistache::Async::Promise<long> >(Pistache::Async::Promise<long>&)::{lambda(std::exception_ptr)#1}) |
1160 | | |
1161 | | private: |
1162 | | Promise() |
1163 | 0 | : core_(std::make_shared<Core>()) |
1164 | 0 | , resolver_(core_) |
1165 | 0 | , rejection_(core_) |
1166 | 0 | { } Unexecuted instantiation: Pistache::Async::Promise<long>::Promise() Unexecuted instantiation: Pistache::Async::Promise<void>::Promise() |
1167 | | |
1168 | | explicit Promise(std::shared_ptr<Core>&& core) |
1169 | 0 | : core_(core) |
1170 | 0 | , resolver_(core_) |
1171 | 0 | , rejection_(core_) |
1172 | 0 | { } |
1173 | | |
1174 | | std::shared_ptr<Core> core_; |
1175 | | Resolver resolver_; |
1176 | | Rejection rejection_; |
1177 | | }; |
1178 | | |
1179 | | template <typename T> |
1180 | | class Barrier |
1181 | | { |
1182 | | public: |
1183 | | explicit Barrier(Promise<T>& promise) |
1184 | | : promise_(promise) |
1185 | | { } |
1186 | | |
1187 | | void wait() |
1188 | | { |
1189 | | if (promise_.isFulfilled() || promise_.isRejected()) |
1190 | | return; |
1191 | | |
1192 | | promise_.then( |
1193 | | [&](const T&) mutable { |
1194 | | std::unique_lock<std::mutex> guard(mtx); |
1195 | | cv.notify_one(); |
1196 | | }, |
1197 | | [&](std::exception_ptr) mutable { |
1198 | | std::unique_lock<std::mutex> guard(mtx); |
1199 | | cv.notify_one(); |
1200 | | }); |
1201 | | |
1202 | | std::unique_lock<std::mutex> guard(mtx); |
1203 | | cv.wait(guard, |
1204 | | [&] { return promise_.isFulfilled() || promise_.isRejected(); }); |
1205 | | } |
1206 | | |
1207 | | template <class Rep, class Period> |
1208 | | std::cv_status wait_for(const std::chrono::duration<Rep, Period>& period) |
1209 | | { |
1210 | | if (promise_.isFulfilled() || promise_.isRejected()) |
1211 | | return std::cv_status::no_timeout; |
1212 | | |
1213 | | promise_.then( |
1214 | | [&](const T&) mutable { |
1215 | | std::unique_lock<std::mutex> guard(mtx); |
1216 | | cv.notify_one(); |
1217 | | }, |
1218 | | [&](std::exception_ptr) mutable { |
1219 | | std::unique_lock<std::mutex> guard(mtx); |
1220 | | cv.notify_one(); |
1221 | | }); |
1222 | | |
1223 | | std::unique_lock<std::mutex> guard(mtx); |
1224 | | return cv.wait_for(guard, period); |
1225 | | } |
1226 | | |
1227 | | private: |
1228 | | Promise<T>& promise_; |
1229 | | mutable std::mutex mtx; |
1230 | | std::condition_variable cv; |
1231 | | }; |
1232 | | |
1233 | | namespace Impl |
1234 | | { |
1235 | | struct Any; |
1236 | | } |
1237 | | |
1238 | | class Any |
1239 | | { |
1240 | | public: |
1241 | | friend struct Impl::Any; |
1242 | | |
1243 | | Any(const Any& other) = default; |
1244 | | Any& operator=(const Any& other) = default; |
1245 | | |
1246 | | Any(Any&& other) = default; |
1247 | | Any& operator=(Any&& other) = default; |
1248 | | |
1249 | | template <typename T> |
1250 | | bool is() const { return core_->id == TypeId::of<T>(); } |
1251 | | |
1252 | | template <typename T> |
1253 | | T cast() const |
1254 | | { |
1255 | | if (!is<T>()) |
1256 | | throw BadAnyCast(); |
1257 | | |
1258 | | auto core = std::static_pointer_cast<Private::CoreT<T>>(core_); |
1259 | | return core->value(); |
1260 | | } |
1261 | | |
1262 | | private: |
1263 | | explicit Any(const std::shared_ptr<Private::Core>& core) |
1264 | | : core_(core) |
1265 | 0 | { } |
1266 | | std::shared_ptr<Private::Core> core_; |
1267 | | }; |
1268 | | |
1269 | | namespace Impl |
1270 | | { |
1271 | | |
1272 | | /* Instead of duplicating the code between whenAll and whenAny functions, the |
1273 | | * main implementation is in the When class below and we configure the class |
1274 | | * with a policy instead, depending if we are executing an "all" or "any" |
1275 | | * operation, how cool is that ? |
1276 | | */ |
1277 | | struct All |
1278 | | { |
1279 | | |
1280 | | struct Data |
1281 | | { |
1282 | | Data(const size_t _total, Resolver _resolver, Rejection _rejection) |
1283 | | : total(_total) |
1284 | | , resolved(0) |
1285 | | , rejected(false) |
1286 | | , mtx() |
1287 | | , resolve(std::move(_resolver)) |
1288 | | , reject(std::move(_rejection)) |
1289 | 0 | { } |
1290 | | |
1291 | | const size_t total; |
1292 | | size_t resolved; |
1293 | | bool rejected; |
1294 | | std::mutex mtx; |
1295 | | |
1296 | | Resolver resolve; |
1297 | | Rejection reject; |
1298 | | }; |
1299 | | |
1300 | | template <size_t Index, typename T, typename Data> |
1301 | | static void resolveT(const T& val, Data& data) |
1302 | | { |
1303 | | std::lock_guard<std::mutex> guard(data->mtx); |
1304 | | |
1305 | | if (data->rejected) |
1306 | | return; |
1307 | | |
1308 | | // @Check thread-safety of std::get ? |
1309 | | std::get<Index>(data->results) = val; |
1310 | | data->resolved++; |
1311 | | |
1312 | | if (data->resolved == data->total) |
1313 | | { |
1314 | | data->resolve(data->results); |
1315 | | } |
1316 | | } |
1317 | | |
1318 | | template <typename Data> |
1319 | | static void resolveVoid(Data& data) |
1320 | | { |
1321 | | std::lock_guard<std::mutex> guard(data->mtx); |
1322 | | |
1323 | | if (data->rejected) |
1324 | | return; |
1325 | | |
1326 | | data->resolved++; |
1327 | | |
1328 | | if (data->resolved == data->total) |
1329 | | { |
1330 | | data->resolve(data->results); |
1331 | | } |
1332 | | } |
1333 | | |
1334 | | template <typename Data> |
1335 | | static void reject(std::exception_ptr exc, Data& data) |
1336 | | { |
1337 | | std::lock_guard<std::mutex> guard(data->mtx); |
1338 | | |
1339 | | data->rejected = true; |
1340 | | data->reject(exc); |
1341 | | } |
1342 | | }; |
1343 | | |
1344 | | struct Any |
1345 | | { |
1346 | | |
1347 | | struct Data |
1348 | | { |
1349 | | Data(size_t, Resolver resolver, Rejection rejection) |
1350 | | : done(false) |
1351 | | , mtx() |
1352 | | , resolve(std::move(resolver)) |
1353 | | , reject(std::move(rejection)) |
1354 | 0 | { } |
1355 | | |
1356 | | bool done; |
1357 | | std::mutex mtx; |
1358 | | |
1359 | | Resolver resolve; |
1360 | | Rejection reject; |
1361 | | }; |
1362 | | |
1363 | | template <size_t Index, typename T, typename Data> |
1364 | | static void resolveT(const T& val, Data& data) |
1365 | | { |
1366 | | std::lock_guard<std::mutex> guard(data->mtx); |
1367 | | |
1368 | | if (data->done) |
1369 | | return; |
1370 | | |
1371 | | // Instead of allocating a new core, ideally we could share the same core as |
1372 | | // the relevant promise but we do not have access to the promise here is so |
1373 | | // meh |
1374 | | auto core = std::make_shared<Private::CoreT<T>>(); |
1375 | | core->template construct<T>(val); |
1376 | | data->resolve(Async::Any(core)); |
1377 | | |
1378 | | data->done = true; |
1379 | | } |
1380 | | |
1381 | | template <typename Data> |
1382 | | static void resolveVoid(Data& data) |
1383 | | { |
1384 | | std::lock_guard<std::mutex> guard(data->mtx); |
1385 | | |
1386 | | if (data->done) |
1387 | | return; |
1388 | | |
1389 | | auto core = std::make_shared<Private::CoreT<void>>(); |
1390 | | data->resolve(Async::Any(core)); |
1391 | | |
1392 | | data->done = true; |
1393 | | } |
1394 | | |
1395 | | template <typename Data> |
1396 | | static void reject(std::exception_ptr exc, Data& data) |
1397 | | { |
1398 | | std::lock_guard<std::mutex> guard(data->mtx); |
1399 | | |
1400 | | data->done = true; |
1401 | | data->reject(exc); |
1402 | | } |
1403 | | }; |
1404 | | |
1405 | | template <typename ContinuationPolicy> |
1406 | | struct When |
1407 | | { |
1408 | | When(Resolver resolver, Rejection rejection) |
1409 | | : resolve(std::move(resolver)) |
1410 | | , reject(std::move(rejection)) |
1411 | | { } |
1412 | | |
1413 | | template <typename... Args> |
1414 | | void operator()(Args&&... args) |
1415 | | { |
1416 | | whenArgs(std::forward<Args>(args)...); |
1417 | | } |
1418 | | |
1419 | | private: |
1420 | | template <typename T, size_t Index, typename Data> |
1421 | | struct WhenContinuation |
1422 | | { |
1423 | | explicit WhenContinuation(Data _data) |
1424 | | : data(std::move(_data)) |
1425 | | { } |
1426 | | |
1427 | | void operator()(const T& val) const |
1428 | | { |
1429 | | ContinuationPolicy::template resolveT<Index>(val, data); |
1430 | | } |
1431 | | |
1432 | | Data data; |
1433 | | }; |
1434 | | |
1435 | | template <size_t Index, typename Data> |
1436 | | struct WhenContinuation<void, Index, Data> |
1437 | | { |
1438 | | explicit WhenContinuation(Data _data) |
1439 | | : data(std::move(_data)) |
1440 | | { } |
1441 | | |
1442 | | void operator()() const { ContinuationPolicy::resolveVoid(data); } |
1443 | | |
1444 | | Data data; |
1445 | | }; |
1446 | | |
1447 | | template <typename T, size_t Index, typename Data> |
1448 | | WhenContinuation<T, Index, Data> makeContinuation(const Data& data) |
1449 | | { |
1450 | | return WhenContinuation<T, Index, Data>(data); |
1451 | | } |
1452 | | |
1453 | | template <size_t Index, typename Data, typename T> |
1454 | | void when(const Data& data, Promise<T>& promise) |
1455 | | { |
1456 | | promise.then(makeContinuation<T, Index>(data), [=](std::exception_ptr ptr) { |
1457 | | ContinuationPolicy::reject(std::move(ptr), data); |
1458 | | }); |
1459 | | } |
1460 | | |
1461 | | template <size_t Index, typename Data, typename T> |
1462 | | void when(const Data& data, T&& arg) |
1463 | | { |
1464 | | typedef typename std::remove_reference<T>::type CleanT; |
1465 | | auto promise = Promise<CleanT>::resolved(std::forward<T>(arg)); |
1466 | | when<Index>(data, promise); |
1467 | | } |
1468 | | |
1469 | | template <typename... Args> |
1470 | | void whenArgs(Args&&... args) |
1471 | | { |
1472 | | typedef std::tuple<typename detail::RemovePromise< |
1473 | | typename std::remove_reference<Args>::type>::Type...> |
1474 | | Results; |
1475 | | /* We need to keep the results alive until the last promise |
1476 | | * finishes its execution |
1477 | | */ |
1478 | | |
1479 | | /* See the trick here ? Basically, we only have access to the real type of |
1480 | | * the results in this function. The policy classes do not have access to |
1481 | | * the full type (std::tuple), but, instead, take a generic template data |
1482 | | * type as a parameter. They only need to know that results is a tuple, they |
1483 | | * do not need to know the real type of the results. |
1484 | | * |
1485 | | * This is some sort of compile-time template type-erasing, hue |
1486 | | */ |
1487 | | struct Data : public ContinuationPolicy::Data |
1488 | | { |
1489 | | Data(size_t total, Resolver resolver, Rejection rejection) |
1490 | | : ContinuationPolicy::Data(total, std::move(resolver), |
1491 | | std::move(rejection)) |
1492 | | { } |
1493 | | |
1494 | | Results results; |
1495 | | }; |
1496 | | |
1497 | | auto data = std::make_shared<Data>(sizeof...(Args), std::move(resolve), |
1498 | | std::move(reject)); |
1499 | | whenArgs<0>(data, std::forward<Args>(args)...); |
1500 | | } |
1501 | | |
1502 | | template <size_t Index, typename Data, typename Head, typename... Rest> |
1503 | | void whenArgs(const Data& data, Head&& head, Rest&&... rest) |
1504 | | { |
1505 | | when<Index>(data, std::forward<Head>(head)); |
1506 | | whenArgs<Index + 1>(data, std::forward<Rest>(rest)...); |
1507 | | } |
1508 | | |
1509 | | template <size_t Index, typename Data, typename Head> |
1510 | | void whenArgs(const Data& data, Head&& head) |
1511 | | { |
1512 | | when<Index>(data, std::forward<Head>(head)); |
1513 | | } |
1514 | | |
1515 | | Resolver resolve; |
1516 | | Rejection reject; |
1517 | | }; |
1518 | | |
1519 | | template <typename T, typename Results> |
1520 | | struct WhenAllRange |
1521 | | { |
1522 | | WhenAllRange(Resolver _resolve, Rejection _reject) |
1523 | | : resolve(std::move(_resolve)) |
1524 | | , reject(std::move(_reject)) |
1525 | | { } |
1526 | | |
1527 | | template <typename Iterator> |
1528 | | void operator()(Iterator first, Iterator last) |
1529 | | { |
1530 | | auto data = std::make_shared<DataT<T>>( |
1531 | | static_cast<size_t>(std::distance(first, last)), std::move(resolve), |
1532 | | std::move(reject)); |
1533 | | |
1534 | | size_t index = 0; |
1535 | | for (auto it = first; it != last; ++it) |
1536 | | { |
1537 | | |
1538 | | WhenContinuation<T> cont(data, index); |
1539 | | |
1540 | | it->then(std::move(cont), [=](std::exception_ptr ptr) { |
1541 | | std::lock_guard<std::mutex> guard(data->mtx); |
1542 | | |
1543 | | if (data->rejected) |
1544 | | return; |
1545 | | |
1546 | | data->rejected = true; |
1547 | | data->reject(std::move(ptr)); |
1548 | | }); |
1549 | | |
1550 | | ++index; |
1551 | | } |
1552 | | } |
1553 | | |
1554 | | private: |
1555 | | struct Data |
1556 | | { |
1557 | | Data(size_t _total, Resolver _resolver, Rejection _rejection) |
1558 | | : total(_total) |
1559 | | , resolved(0) |
1560 | | , rejected(false) |
1561 | | , mtx() |
1562 | | , resolve(std::move(_resolver)) |
1563 | | , reject(std::move(_rejection)) |
1564 | | { } |
1565 | | |
1566 | | const size_t total; |
1567 | | size_t resolved; |
1568 | | bool rejected; |
1569 | | std::mutex mtx; |
1570 | | |
1571 | | Resolver resolve; |
1572 | | Rejection reject; |
1573 | | }; |
1574 | | |
1575 | | /* Ok so apparently I can not fully specialize a template structure |
1576 | | * here, so you know what, compiler ? Take that Dummy type and leave |
1577 | | * me alone |
1578 | | */ |
1579 | | template <typename ValueType, typename Dummy = void> |
1580 | | struct DataT : public Data |
1581 | | { |
1582 | | DataT(size_t total, Resolver resolver, Rejection rejection) |
1583 | | : Data(total, std::move(resolver), std::move(rejection)) |
1584 | | { |
1585 | | results.resize(total); |
1586 | | } |
1587 | | |
1588 | | Results results; |
1589 | | }; |
1590 | | |
1591 | | /* For a vector of void promises, we do not have any results, that's |
1592 | | * why we need a distinct specialization for the void case |
1593 | | */ |
1594 | | template <typename Dummy> |
1595 | | struct DataT<void, Dummy> : public Data |
1596 | | { |
1597 | | DataT(size_t total, Resolver resolver, Rejection rejection) |
1598 | | : Data(total, std::move(resolver), std::move(rejection)) |
1599 | | { } |
1600 | | }; |
1601 | | |
1602 | | template <typename ValueType, typename Dummy = void> |
1603 | | struct WhenContinuation |
1604 | | { |
1605 | | using D = std::shared_ptr<DataT<ValueType>>; |
1606 | | |
1607 | | WhenContinuation(const D& _data, size_t _index) |
1608 | | : data(_data) |
1609 | | , index(_index) |
1610 | | { } |
1611 | | |
1612 | | void operator()(const ValueType& val) const |
1613 | | { |
1614 | | std::lock_guard<std::mutex> guard(data->mtx); |
1615 | | |
1616 | | if (data->rejected) |
1617 | | return; |
1618 | | |
1619 | | data->results[index] = val; |
1620 | | data->resolved++; |
1621 | | if (data->resolved == data->total) |
1622 | | { |
1623 | | data->resolve(data->results); |
1624 | | } |
1625 | | } |
1626 | | |
1627 | | D data; |
1628 | | size_t index; |
1629 | | }; |
1630 | | |
1631 | | template <typename Dummy> |
1632 | | struct WhenContinuation<void, Dummy> |
1633 | | { |
1634 | | using D = std::shared_ptr<DataT<void>>; |
1635 | | |
1636 | | WhenContinuation(const D& _data, size_t) |
1637 | | : data(_data) |
1638 | | { } |
1639 | | |
1640 | | void operator()() const |
1641 | | { |
1642 | | std::lock_guard<std::mutex> guard(data->mtx); |
1643 | | |
1644 | | if (data->rejected) |
1645 | | return; |
1646 | | |
1647 | | data->resolved++; |
1648 | | if (data->resolved == data->total) |
1649 | | { |
1650 | | data->resolve(); |
1651 | | } |
1652 | | } |
1653 | | |
1654 | | D data; |
1655 | | }; |
1656 | | |
1657 | | Resolver resolve; |
1658 | | Rejection reject; |
1659 | | }; |
1660 | | |
1661 | | } // namespace Impl |
1662 | | |
1663 | | template <typename... Args, |
1664 | | typename Results = std::tuple<typename detail::RemovePromise< |
1665 | | typename std::remove_reference<Args>::type>::Type...>> |
1666 | | Promise<Results> whenAll(Args&&... args) |
1667 | | { |
1668 | | // As ugly as it looks, this is needed to bypass a bug of gcc < 4.9 |
1669 | | // whereby template parameters pack inside a lambda expression are not |
1670 | | // captured correctly and can not be expanded inside the lambda. |
1671 | | Resolver* resolve; |
1672 | | Rejection* reject; |
1673 | | |
1674 | | Promise<Results> promise([&](Resolver& resolver, Rejection& rejection) { |
1675 | | resolve = &resolver; |
1676 | | reject = &rejection; |
1677 | | }); |
1678 | | |
1679 | | Impl::When<Impl::All> impl(std::move(*resolve), std::move(*reject)); |
1680 | | // So we capture everything we need inside the lambda and then call the |
1681 | | // implementation and expand the parameters pack here |
1682 | | impl(std::forward<Args>(args)...); |
1683 | | |
1684 | | return promise; |
1685 | | } |
1686 | | |
1687 | | template <typename... Args> |
1688 | | Promise<Any> whenAny(Args&&... args) |
1689 | | { |
1690 | | // Same trick as above; |
1691 | | Resolver* resolve; |
1692 | | Rejection* reject; |
1693 | | |
1694 | | Promise<Any> promise([&](Resolver& resolver, Rejection& rejection) { |
1695 | | resolve = &resolver; |
1696 | | reject = &rejection; |
1697 | | }); |
1698 | | |
1699 | | Impl::When<Impl::Any> impl(std::move(*resolve), std::move(*reject)); |
1700 | | impl(std::forward<Args>(args)...); |
1701 | | return promise; |
1702 | | } |
1703 | | |
1704 | | template <typename Iterator, |
1705 | | typename ValueType = typename detail::RemovePromise< |
1706 | | typename std::iterator_traits<Iterator>::value_type>::Type, |
1707 | | typename Results = |
1708 | | typename std::conditional<std::is_same<void, ValueType>::value, |
1709 | | void, std::vector<ValueType>>::type> |
1710 | | Promise<Results> whenAll(Iterator first, Iterator last) |
1711 | | { |
1712 | | /* @Security, assert that last >= first */ |
1713 | | |
1714 | | return Promise<Results>([=](Resolver& resolve, Rejection& rejection) { |
1715 | | Impl::WhenAllRange<ValueType, Results> impl(std::move(resolve), |
1716 | | std::move(rejection)); |
1717 | | |
1718 | | impl(first, last); |
1719 | | }); |
1720 | | } |
1721 | | |
1722 | | } // namespace Pistache::Async |