(Wangle) Actually support the universal reference used in whenAll
[folly.git] / folly / wangle / detail.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <folly/Optional.h>
20 #include <stdexcept>
21 #include <atomic>
22
23 #include "Try.h"
24 #include "Promise.h"
25 #include "Future.h"
26
27 namespace folly { namespace wangle { namespace detail {
28
29 /** The shared state object for Future and Promise. */
30 template<typename T>
31 class FutureObject {
32  public:
33   FutureObject() = default;
34
35   // not copyable
36   FutureObject(FutureObject const&) = delete;
37   FutureObject& operator=(FutureObject const&) = delete;
38
39   // not movable (see comment in the implementation of Future::then)
40   FutureObject(FutureObject&&) = delete;
41   FutureObject& operator=(FutureObject&&) = delete;
42
43   Try<T>& getTry() {
44     return *value_;
45   }
46
47   template <typename F>
48   void setContinuation(F func) {
49     if (continuation_) {
50       throw std::logic_error("setContinuation called twice");
51     }
52
53     if (value_.hasValue()) {
54       func(std::move(*value_));
55       delete this;
56     } else {
57       continuation_ = std::move(func);
58     }
59   }
60
61   void fulfil(Try<T>&& t) {
62     if (value_.hasValue()) {
63       throw std::logic_error("fulfil called twice");
64     }
65
66     if (continuation_) {
67       continuation_(std::move(t));
68       delete this;
69     } else {
70       value_ = std::move(t);
71     }
72   }
73
74   void setException(std::exception_ptr const& e) {
75     fulfil(Try<T>(e));
76   }
77
78   template <class E> void setException(E const& e) {
79     fulfil(Try<T>(std::make_exception_ptr<E>(e)));
80   }
81
82   bool ready() const {
83     return value_.hasValue();
84   }
85
86   typename std::add_lvalue_reference<T>::type value() {
87     return value_->value();
88   }
89
90  private:
91   folly::Optional<Try<T>> value_;
92   std::function<void(Try<T>&&)> continuation_;
93 };
94
95 template <typename... Ts>
96 struct VariadicContext {
97   VariadicContext() : total(0), count(0) {}
98   Promise<std::tuple<Try<Ts>... > > p;
99   std::tuple<Try<Ts>... > results;
100   size_t total;
101   std::atomic<size_t> count;
102   typedef Future<std::tuple<Try<Ts>...>> type;
103 };
104
105 template <typename... Ts, typename THead, typename... Fs>
106 typename std::enable_if<sizeof...(Fs) == 0, void>::type
107 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
108   head.setContinuation([ctx](Try<typename THead::value_type>&& t) {
109     std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
110     if (++ctx->count == ctx->total) {
111       ctx->p.setValue(std::move(ctx->results));
112       delete ctx;
113     }
114   });
115 }
116
117 template <typename... Ts, typename THead, typename... Fs>
118 typename std::enable_if<sizeof...(Fs) != 0, void>::type
119 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
120   head.setContinuation([ctx](Try<typename THead::value_type>&& t) {
121     std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
122     if (++ctx->count == ctx->total) {
123       ctx->p.setValue(std::move(ctx->results));
124       delete ctx;
125     }
126   });
127   // template tail-recursion
128   whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
129 }
130
131 template <typename T>
132 struct WhenAllContext {
133   explicit WhenAllContext() : count(0), total(0) {}
134   Promise<std::vector<Try<T> > > p;
135   std::vector<Try<T> > results;
136   std::atomic<size_t> count;
137   size_t total;
138 };
139
140 template <typename T>
141 struct WhenAnyContext {
142   explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
143   Promise<std::pair<size_t, Try<T>>> p;
144   std::atomic<bool> done;
145   std::atomic<size_t> ref_count;
146   void decref() {
147     if (--ref_count == 0) {
148       delete this;
149     }
150   }
151 };
152
153 }}} // namespace