1/*
2 * Copyright 2014-present 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 <thread>
21
22#include <folly/executors/InlineExecutor.h>
23#include <folly/futures/detail/Core.h>
24
25namespace folly {
26
27namespace futures {
28namespace detail {
29template <typename T>
30void coreDetachPromiseMaybeWithResult(Core<T>& core) {
31 if (!core.hasResult()) {
32 core.setResult(Try<T>(exception_wrapper(BrokenPromise(typeid(T).name()))));
33 }
34 core.detachPromise();
35}
36} // namespace detail
37} // namespace futures
38
39template <class T>
40Promise<T> Promise<T>::makeEmpty() noexcept {
41 return Promise<T>(futures::detail::EmptyConstruct{});
42}
43
44template <class T>
45Promise<T>::Promise() : retrieved_(false), core_(Core::make()) {}
46
47template <class T>
48Promise<T>::Promise(Promise<T>&& other) noexcept
49 : retrieved_(exchange(other.retrieved_, false)),
50 core_(exchange(other.core_, nullptr)) {}
51
52template <class T>
53Promise<T>& Promise<T>::operator=(Promise<T>&& other) noexcept {
54 detach();
55 retrieved_ = exchange(other.retrieved_, false);
56 core_ = exchange(other.core_, nullptr);
57 return *this;
58}
59
60template <class T>
61void Promise<T>::throwIfFulfilled() const {
62 if (getCore().hasResult()) {
63 throw_exception<PromiseAlreadySatisfied>();
64 }
65}
66
67template <class T>
68Promise<T>::Promise(futures::detail::EmptyConstruct) noexcept
69 : retrieved_(false), core_(nullptr) {}
70
71template <class T>
72Promise<T>::~Promise() {
73 detach();
74}
75
76template <class T>
77void Promise<T>::detach() {
78 if (core_) {
79 if (!retrieved_) {
80 core_->detachFuture();
81 }
82 futures::detail::coreDetachPromiseMaybeWithResult(*core_);
83 core_ = nullptr;
84 }
85}
86
87template <class T>
88SemiFuture<T> Promise<T>::getSemiFuture() {
89 if (retrieved_) {
90 throw_exception<FutureAlreadyRetrieved>();
91 }
92 retrieved_ = true;
93 return SemiFuture<T>(&getCore());
94}
95
96template <class T>
97Future<T> Promise<T>::getFuture() {
98 // An InlineExecutor approximates the old behaviour of continuations
99 // running inine on setting the value of the promise.
100 return getSemiFuture().via(&InlineExecutor::instance());
101}
102
103template <class T>
104template <class E>
105typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
106Promise<T>::setException(E const& e) {
107 setException(make_exception_wrapper<E>(e));
108}
109
110template <class T>
111void Promise<T>::setException(exception_wrapper ew) {
112 setTry(Try<T>(std::move(ew)));
113}
114
115template <class T>
116template <typename F>
117void Promise<T>::setInterruptHandler(F&& fn) {
118 getCore().setInterruptHandler(std::forward<F>(fn));
119}
120
121template <class T>
122void Promise<T>::setTry(Try<T>&& t) {
123 throwIfFulfilled();
124 core_->setResult(std::move(t));
125}
126
127template <class T>
128template <class M>
129void Promise<T>::setValue(M&& v) {
130 static_assert(!std::is_same<T, void>::value, "Use setValue() instead");
131
132 setTry(Try<T>(std::forward<M>(v)));
133}
134
135template <class T>
136template <class F>
137void Promise<T>::setWith(F&& func) {
138 throwIfFulfilled();
139 setTry(makeTryWith(std::forward<F>(func)));
140}
141
142template <class T>
143bool Promise<T>::isFulfilled() const noexcept {
144 if (core_) {
145 return core_->hasResult();
146 }
147 return true;
148}
149
150} // namespace folly
151