Coverage Report

Created: 2025-10-13 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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