Future::value() should throw when unset
[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 <atomic>
20 #include <folly/Optional.h>
21 #include <stdexcept>
22 #include <vector>
23
24 #include "Try.h"
25 #include "Promise.h"
26 #include "Future.h"
27
28 namespace folly { namespace wangle { namespace detail {
29
30 /** The shared state object for Future and Promise. */
31 template<typename T>
32 class FutureObject {
33  public:
34   FutureObject() = default;
35
36   // not copyable
37   FutureObject(FutureObject const&) = delete;
38   FutureObject& operator=(FutureObject const&) = delete;
39
40   // not movable (see comment in the implementation of Future::then)
41   FutureObject(FutureObject&&) noexcept = delete;
42   FutureObject& operator=(FutureObject&&) = delete;
43
44   Try<T>& getTry() {
45     return *value_;
46   }
47
48   template <typename F>
49   void setContinuation(F func) {
50     if (continuation_) {
51       throw std::logic_error("setContinuation called twice");
52     }
53
54     continuation_ = std::move(func);
55
56     if (shouldContinue_.test_and_set()) {
57       continuation_(std::move(*value_));
58       delete this;
59     }
60   }
61
62   void fulfil(Try<T>&& t) {
63     if (value_.hasValue()) {
64       throw std::logic_error("fulfil called twice");
65     }
66
67     value_ = std::move(t);
68
69     if (shouldContinue_.test_and_set()) {
70       continuation_(std::move(*value_));
71       delete this;
72     }
73   }
74
75   void setException(std::exception_ptr const& e) {
76     fulfil(Try<T>(e));
77   }
78
79   template <class E> void setException(E const& e) {
80     fulfil(Try<T>(std::make_exception_ptr<E>(e)));
81   }
82
83   bool ready() const {
84     return value_.hasValue();
85   }
86
87   typename std::add_lvalue_reference<T>::type value() {
88     if (ready()) {
89       return value_->value();
90     } else {
91       throw FutureNotReady();
92     }
93   }
94
95  private:
96   std::atomic_flag shouldContinue_ = ATOMIC_FLAG_INIT;
97   folly::Optional<Try<T>> value_;
98   std::function<void(Try<T>&&)> continuation_;
99 };
100
101 template <typename... Ts>
102 struct VariadicContext {
103   VariadicContext() : total(0), count(0) {}
104   Promise<std::tuple<Try<Ts>... > > p;
105   std::tuple<Try<Ts>... > results;
106   size_t total;
107   std::atomic<size_t> count;
108   typedef Future<std::tuple<Try<Ts>...>> type;
109 };
110
111 template <typename... Ts, typename THead, typename... Fs>
112 typename std::enable_if<sizeof...(Fs) == 0, void>::type
113 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
114   head.setContinuation([ctx](Try<typename THead::value_type>&& t) {
115     std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
116     if (++ctx->count == ctx->total) {
117       ctx->p.setValue(std::move(ctx->results));
118       delete ctx;
119     }
120   });
121 }
122
123 template <typename... Ts, typename THead, typename... Fs>
124 typename std::enable_if<sizeof...(Fs) != 0, void>::type
125 whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
126   head.setContinuation([ctx](Try<typename THead::value_type>&& t) {
127     std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
128     if (++ctx->count == ctx->total) {
129       ctx->p.setValue(std::move(ctx->results));
130       delete ctx;
131     }
132   });
133   // template tail-recursion
134   whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
135 }
136
137 template <typename T>
138 struct WhenAllContext {
139   explicit WhenAllContext() : count(0), total(0) {}
140   Promise<std::vector<Try<T> > > p;
141   std::vector<Try<T> > results;
142   std::atomic<size_t> count;
143   size_t total;
144 };
145
146 template <typename T>
147 struct WhenAnyContext {
148   explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
149   Promise<std::pair<size_t, Try<T>>> p;
150   std::atomic<bool> done;
151   std::atomic<size_t> ref_count;
152   void decref() {
153     if (--ref_count == 0) {
154       delete this;
155     }
156   }
157 };
158
159 }}} // namespace