• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 2510

19 Jul 2024 05:59PM UTC coverage: 91.023% (+0.01%) from 91.01%
2510

push

Evergreen

web-flow
RCORE-2204: Use accepting_clients key to check for initial sync completion in flx tests (#7906)

102734 of 181502 branches covered (56.6%)

8 of 8 new or added lines in 3 files covered. (100.0%)

66 existing lines in 9 files now uncovered.

216417 of 237762 relevant lines covered (91.02%)

6025890.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.94
/src/realm/util/future.hpp
1
/*************************************************************************
2
 *
3
 * Copyright 2021 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#pragma once
20

21
#include <condition_variable>
22
#include <mutex>
23
#include <type_traits>
24

25
#include "realm/exceptions.hpp"
26
#include "realm/status_with.hpp"
27
#include "realm/util/assert.hpp"
28
#include "realm/util/bind_ptr.hpp"
29
#include "realm/util/features.h"
30
#include "realm/util/functional.hpp"
31
#include "realm/util/optional.hpp"
32
#include "realm/util/scope_exit.hpp"
33

34
namespace realm::util {
35

36
namespace future_details {
37
template <typename T>
38
class Promise;
39

40
template <typename T>
41
class CopyablePromiseHolder;
42

43
template <typename T>
44
class Future;
45

46
template <>
47
class Future<void>;
48

49
template <typename T>
50
constexpr static bool is_future = false;
51
template <typename T>
52
constexpr static bool is_future<Future<T>> = true;
53

54
// FakeVoid is a helper type for futures for callbacks or values that return/are void but need to be represented
55
// as a value. It should not be visible to callers outside of future_details.
56
struct FakeVoid {};
57

58
template <typename T>
59
using VoidToFakeVoid = std::conditional_t<std::is_void_v<T>, FakeVoid, T>;
60

61
template <typename T>
62
using FakeVoidToVoid = std::conditional_t<std::is_same_v<T, FakeVoid>, void, T>;
63

64
// UnstatusType/UnwrappedType and their implementations are internal helper types for futures to deduce the actual
65
// type of a value being wrapped by a StatusWith or Future (or a Status in case of void). They should not be visible
66
// outside of future_details.
67
template <typename T>
68
struct UnstatusTypeImpl {
69
    using type = T;
70
};
71
template <typename T>
72
struct UnstatusTypeImpl<StatusWith<T>> {
73
    using type = T;
74
};
75
template <>
76
struct UnstatusTypeImpl<Status> {
77
    using type = void;
78
};
79
template <typename T>
80
using UnstatusType = typename UnstatusTypeImpl<T>::type;
81

82
template <typename T>
83
struct UnwrappedTypeImpl {
84
    static_assert(!is_future<T>);
85
    static_assert(!is_status_or_status_with<T>);
86
    using type = T;
87
};
88
template <typename T>
89
struct UnwrappedTypeImpl<Future<T>> {
90
    using type = T;
91
};
92
template <typename T>
93
struct UnwrappedTypeImpl<StatusWith<T>> {
94
    using type = T;
95
};
96
template <>
97
struct UnwrappedTypeImpl<Status> {
98
    using type = void;
99
};
100
template <typename T>
101
using UnwrappedType = typename UnwrappedTypeImpl<T>::type;
102

103
/**
104
 * call() normalizes arguments to hide the FakeVoid shenanigans from users of Futures.
105
 * In the future it may also expand tuples to argument lists.
106
 */
107
template <typename Func, typename Arg>
108
inline auto call(Func&& func, Arg&& arg)
109
{
846✔
110
    return func(std::forward<Arg>(arg));
846✔
111
}
846✔
112

113
template <typename Func>
114
inline auto call(Func&& func, FakeVoid)
115
{
150✔
116
    return func();
150✔
117
}
150✔
118

119
template <typename Func>
120
inline auto call(Func&& func, StatusWith<FakeVoid> sw)
121
{
154✔
122
    return func(sw.get_status());
154✔
123
}
154✔
124

125
/**
126
 * no_throw_call() normalizes return values so everything returns StatusWith<T>. Exceptions are
127
 * converted to !OK statuses. void and Status returns are converted to StatusWith<FakeVoid>
128
 */
129
template <typename Func, typename... Args>
130
inline auto no_throw_call(Func&& func, Args&&... args) noexcept
131
{
470✔
132
    using RawResult = decltype(call(func, std::forward<Args>(args)...));
470✔
133
    using Result = StatusWith<VoidToFakeVoid<UnstatusType<RawResult>>>;
470✔
134
    try {
470✔
135
        if constexpr (std::is_void_v<RawResult>) {
461✔
136
            call(func, std::forward<Args>(args)...);
235✔
137
            return Result(FakeVoid());
235✔
138
        }
202✔
139
        else if constexpr (std::is_same_v<RawResult, Status>) {
247✔
140
            auto s = call(func, std::forward<Args>(args)...);
202✔
141
            if (!s.is_ok()) {
202✔
142
                return Result(std::move(s));
75✔
143
            }
75✔
144
            return Result(FakeVoid());
33✔
145
        }
54✔
146
        else {
314✔
147
            return Result(call(func, std::forward<Args>(args)...));
314✔
148
        }
314✔
149
    }
470✔
150
    catch (...) {
470✔
151
        return Result(exception_to_status());
36✔
152
    }
36✔
153
}
470✔
154

155
/**
156
 * throwing_call() normalizes return values so everything returns T or FakeVoid. !OK Statuses are
157
 * converted exceptions. void and Status returns are converted to FakeVoid.
158
 *
159
 * This is equivalent to uassertStatusOK(statusCall(func, args...)), but avoids catching just to
160
 * rethrow.
161
 */
162
template <typename Func, typename... Args>
163
inline auto throwing_call(Func&& func, Args&&... args)
164
{
352✔
165
    using Result = decltype(call(func, std::forward<Args>(args)...));
352✔
166
    if constexpr (std::is_void_v<Result>) {
176✔
167
        call(func, std::forward<Args>(args)...);
176✔
168
        return FakeVoid{};
176✔
169
    }
176✔
170
    else if constexpr (std::is_same_v<Result, Status>) {
176✔
171
        auto res = (call(func, std::forward<Args>(args)...));
176✔
172
        if (!res.is_ok()) {
176✔
173
            throw Exception(std::move(res));
176✔
174
        }
176✔
175
        return FakeVoid{};
176✔
176
    }
176✔
177
    else if constexpr (is_status_with<Result>) {
176✔
178
        auto res = (call(func, std::forward<Args>(args)...));
176✔
179
        if (!res.is_ok()) {
176✔
180
            throw Exception(std::move(res.get_status()));
176✔
181
        }
176✔
182
        return std::move(res.get_value());
176✔
183
    }
176✔
184
    else {
352✔
185
        return call(func, std::forward<Args>(args)...);
352✔
186
    }
352✔
187
}
352✔
188

189
template <typename Func, typename... Args>
190
using RawNormalizedCallResult = decltype(throwing_call(std::declval<Func>(), std::declval<Args>()...));
191

192
template <typename Func, typename... Args>
193
using NormalizedCallResult = std::conditional_t<std::is_same<RawNormalizedCallResult<Func, Args...>, FakeVoid>::value,
194
                                                void, RawNormalizedCallResult<Func, Args...>>;
195

196
template <typename T>
197
struct FutureContinuationResultImpl {
198
    using type = T;
199
};
200
template <typename T>
201
struct FutureContinuationResultImpl<Future<T>> {
202
    using type = T;
203
};
204
template <typename T>
205
struct FutureContinuationResultImpl<StatusWith<T>> {
206
    using type = T;
207
};
208
template <>
209
struct FutureContinuationResultImpl<Status> {
210
    using type = void;
211
};
212

213
class FutureRefCountable {
214
    FutureRefCountable(const FutureRefCountable&) = delete;
215
    FutureRefCountable& operator=(const FutureRefCountable&) = delete;
216

217
public:
218
    void thread_unsafe_inc_refs_to(uint32_t count) const
219
    {
84,279✔
220
        REALM_ASSERT_DEBUG(m_refs.load(std::memory_order_relaxed) == (count - 1));
84,279✔
221
        m_refs.store(count, std::memory_order_relaxed);
84,279✔
222
    }
84,279✔
223

224
protected:
225
    FutureRefCountable() = default;
42,567✔
226
    virtual ~FutureRefCountable() = default;
42,564✔
227

228
    template <typename>
229
    friend class ::realm::util::bind_ptr;
230

231
    void bind_ptr() const noexcept
232
    {
428✔
233
        // Assert that we haven't rolled over the counter here.
234
        auto ref_count = m_refs.fetch_add(1, std::memory_order_relaxed) + 1;
428✔
235
        REALM_ASSERT_DEBUG(ref_count != 0);
428✔
236
    }
428✔
237

238
    void unbind_ptr() const noexcept
239
    {
84,921✔
240
        if (m_refs.fetch_sub(1, std::memory_order_acq_rel) == 1) {
84,921✔
241
            delete this;
42,567✔
242
        }
42,567✔
243
    }
84,921✔
244

245
private:
246
    mutable std::atomic<uint32_t> m_refs{0};
247
};
248

249
template <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<FutureRefCountable, T>>>
250
util::bind_ptr<T> make_intrusive(Args&&... args)
251
{
28,086✔
252
    auto ptr = new T(std::forward<Args>(args)...);
28,086✔
253
    ptr->thread_unsafe_inc_refs_to(1);
28,086✔
254
    return util::bind_ptr<T>(ptr, util::bind_ptr_base::adopt_tag{});
28,086✔
255
}
28,086✔
256

257
template <typename T>
258
struct SharedStateImpl;
259

260
template <typename T>
261
using SharedState = SharedStateImpl<VoidToFakeVoid<T>>;
262

263
/**
264
 * SSB is SharedStateBase, and this is its current state.
265
 */
266
enum class SSBState : uint8_t {
267
    Init,
268
    Waiting,
269
    Finished, // This should stay last since we have code like assert(state < Finished).
270
};
271

272
struct SharedStateBase : public FutureRefCountable {
273
    SharedStateBase(const SharedStateBase&) = delete;
274
    SharedStateBase(SharedStateBase&&) = delete;
275
    SharedStateBase& operator=(const SharedStateBase&) = delete;
276
    SharedStateBase& operator=(SharedStateBase&&) = delete;
277

278
    virtual ~SharedStateBase() = default;
42,567✔
279

280
    // This is called by the future side.
281
    void wait() noexcept
282
    {
39,081✔
283
        if (m_state.load(std::memory_order_acquire) == SSBState::Finished) {
39,081✔
284
            return;
1,545✔
285
        }
1,545✔
286

287
        m_cv.emplace();
37,536✔
288

289
        auto old_state = SSBState::Init;
37,536✔
290
        if (REALM_UNLIKELY(
37,536✔
291
                !m_state.compare_exchange_strong(old_state, SSBState::Waiting, std::memory_order_acq_rel))) {
37,536✔
UNCOV
292
            REALM_ASSERT_DEBUG(old_state == SSBState::Finished);
×
UNCOV
293
            return;
×
UNCOV
294
        }
×
295

296
        std::unique_lock<std::mutex> lk(m_mutex);
37,536✔
297
        m_cv->wait(lk, [&] {
75,072✔
298
            return m_state.load(std::memory_order_acquire) == SSBState::Finished;
75,072✔
299
        });
75,072✔
300
    }
37,536✔
301

302
    void transition_to_finished() noexcept
303
    {
42,453✔
304
        auto old_state = m_state.exchange(SSBState::Finished, std::memory_order_acq_rel);
42,453✔
305
        if (old_state == SSBState::Init) {
42,453✔
306
            return;
2,739✔
307
        }
2,739✔
308

309
        REALM_ASSERT_DEBUG(old_state == SSBState::Waiting);
39,714✔
310

311
#ifdef REALM_DEBUG
39,714✔
312
        // If you hit this limit one of two things has probably happened
313
        //
314
        // 1. The justForContinuation optimization isn't working.
315
        // 2. You may be creating a variable length chain.
316
        constexpr size_t kMaxDepth = 32;
39,714✔
317

318
        size_t depth = 0;
39,714✔
319
        for (auto ssb = m_continuation.get(); ssb;
41,478✔
320
             ssb = ssb->m_state.load(std::memory_order_acquire) == SSBState::Waiting ? ssb->m_continuation.get()
39,714✔
321
                                                                                     : nullptr) {
1,764✔
322
            depth++;
1,764✔
323
            REALM_ASSERT(depth < kMaxDepth);
1,764✔
324
        }
1,764✔
325
#endif
39,714✔
326
        if (m_callback) {
39,714✔
327
            m_callback(this);
2,178✔
328
        }
2,178✔
329

330
        if (m_cv) {
39,714✔
331
            std::lock_guard<std::mutex> lk(m_mutex);
37,536✔
332
            m_cv->notify_all();
37,536✔
333
        }
37,536✔
334
    }
39,714✔
335

336
    void set_status(Status status) noexcept
337
    {
2,274✔
338
        REALM_ASSERT_DEBUG(m_state.load() < SSBState::Finished);
2,274✔
339
        m_status = std::move(status);
2,274✔
340
        transition_to_finished();
2,274✔
341
    }
2,274✔
342

343
    // All the rest of the methods are only called by the promise side.
344

345
    //
346
    // Concurrency Rules for members: Each non-atomic member is initially owned by either the
347
    // Promise side or the Future side, indicated by a P/F comment. The general rule is that members
348
    // representing the propagating data are owned by Promise, while members representing what
349
    // to do with the data are owned by Future. The owner may freely modify the members it owns
350
    // until it releases them by doing a release-store to state of Finished from Promise or
351
    // Waiting from Future. Promise can acquire access to all members by doing an acquire-load of
352
    // state and seeing Waiting (or Future with Finished). Transitions should be done via
353
    // acquire-release exchanges to combine both actions.
354
    //
355
    // Future::propagateResults uses an alternative mechanism to transfer ownership of the
356
    // continuation member. The logical Future-side does a release-store of true to
357
    // isJustForContinuation, and the Promise-side can do an acquire-load seeing true to get access.
358
    //
359

360
    std::atomic<SSBState> m_state{SSBState::Init};
361

362
    // This is used to prevent infinite chains of SharedStates that just propagate results.
363
    std::atomic<bool> m_is_just_for_continuation{false};
364

365
    // This is likely to be a different derived type from this, since it is the logical output of
366
    // callback.
367
    util::bind_ptr<SharedStateBase> m_continuation; // F
368

369
    util::UniqueFunction<void(SharedStateBase*)> m_callback; // F
370

371
    std::mutex m_mutex;                           // F
372
    util::Optional<std::condition_variable> m_cv; // F
373

374
    Status m_status = Status::OK(); // P
375

376
protected:
377
    SharedStateBase() = default;
42,567✔
378
};
379

380
template <typename T>
381
struct SharedStateImpl final : public SharedStateBase {
382
    static_assert(!std::is_void_v<T>);
383

384
    void fill_from(SharedState<T>&& other)
385
    {
50✔
386
        REALM_ASSERT_DEBUG(m_state.load() < SSBState::Finished);
50✔
387
        REALM_ASSERT_DEBUG(other.m_state.load() == SSBState::Finished);
50✔
388
        REALM_ASSERT_DEBUG(m_owned_by_promise.load());
50✔
389
        if (other.m_status.is_ok()) {
50✔
390
            m_data = std::move(other.m_data);
46✔
391
        }
46✔
392
        else {
4✔
393
            m_status = std::move(other.m_status);
4✔
394
        }
4✔
395
        transition_to_finished();
50✔
396
    }
50✔
397

398
    template <typename... Args>
399
    void emplace_value(Args&&... args) noexcept
400
    {
26,064✔
401
        REALM_ASSERT_DEBUG(m_state.load() < SSBState::Finished);
26,064✔
402
        REALM_ASSERT_DEBUG(m_owned_by_promise.load());
26,064✔
403
        try {
26,064✔
404
            m_data.emplace(std::forward<Args>(args)...);
26,064✔
405
        }
26,064✔
406
        catch (...) {
26,064✔
407
            m_status = exception_to_status();
×
408
        }
×
409
        transition_to_finished();
26,064✔
410
    }
13,207✔
411

412
    void set_from(StatusOrStatusWith<T> roe)
413
    {
260✔
414
        if (roe.is_ok()) {
260✔
415
            emplace_value(std::move(roe.get_value()));
206✔
416
        }
206✔
417
        else {
54✔
418
            set_status(roe.get_status());
54✔
419
        }
54✔
420
    }
260✔
421

422
    void disown() const
423
    {
58✔
424
        REALM_ASSERT(m_owned_by_promise.exchange(false));
58✔
425
    }
58✔
426

427
    void claim() const
428
    {
52✔
429
        REALM_ASSERT(!m_owned_by_promise.exchange(true));
52✔
430
    }
52✔
431

432
    mutable std::atomic<bool> m_owned_by_promise{true};
433

434
    util::Optional<T> m_data; // P
435
};
436

437
} // namespace future_details
438

439
// These are in the future_details namespace to get access to its contents, but they are part of the
440
// public API.
441
using future_details::CopyablePromiseHolder;
442
using future_details::Future;
443
using future_details::Promise;
444

445
/**
446
 * This class represents the producer side of a Future.
447
 *
448
 * This is a single-shot class. You may only extract the Future once, and you may either set a value
449
 * or error at most once. Extracting the future and setting the value/error can be done in either
450
 * order.
451
 *
452
 * If the Future has been extracted, but no value or error has been set at the time this Promise is
453
 * destroyed, a error will be set with ErrorCode::BrokenPromise. This should generally be considered
454
 * a programmer error, and should not be relied upon. We may make it debug-fatal in the future.
455
 *
456
 * Only one thread can use a given Promise at a time. It is legal to have different threads setting
457
 * the value/error and extracting the Future, but it is the user's responsibility to ensure that
458
 * those calls are strictly synchronized. This is usually easiest to achieve by calling
459
 * make_promise_future<T>() then passing a SharedPromise to the completing threads.
460
 *
461
 * If the result is ready when producing the Future, it is more efficient to use
462
 * make_ready_future_with() or Future<T>::make_ready() than to use a Promise<T>.
463
 */
464
template <typename T>
465
class future_details::Promise {
466
public:
467
    using value_type = T;
468

469
    Promise() = default;
26,814✔
470

471
    ~Promise()
472
    {
78,390✔
473
        if (REALM_UNLIKELY(m_shared_state)) {
78,390✔
474
            m_shared_state->set_status({ErrorCodes::BrokenPromise, "Broken Promise"});
26✔
475
        }
26✔
476
    }
78,390✔
477

478
    // If we want to enable move-assignability, we need to handle breaking the promise on the old
479
    // value of this.
480
    Promise& operator=(Promise&&) = delete;
481

482
    // The default move construction is fine.
483
    Promise(Promise&&) noexcept = default;
51,098✔
484

485
    /**
486
     * Sets the value into this Promise when the passed-in Future completes, which may have already
487
     * happened. If it hasn't, it is still safe to destroy this Promise since it is no longer
488
     * involved.
489
     */
490
    void set_from(Future<T>&& future) noexcept;
491

492
    void set_from_status_with(StatusWith<T> sw) noexcept
493
    {
82✔
494
        set_impl([&](auto& shared_state) {
82✔
495
            shared_state.set_from(std::move(sw));
82✔
496
        });
82✔
497
    }
82✔
498

499
    template <typename... Args>
500
    void emplace_value(Args&&... args) noexcept
501
    {
25,692✔
502
        set_impl([&](auto& shared_state) {
25,692✔
503
            shared_state.emplace_value(std::forward<Args>(args)...);
25,692✔
504
        });
25,692✔
505
    }
25,692✔
506

507
    void set_error(Status status) noexcept
508
    {
592✔
509
        REALM_ASSERT_DEBUG(!status.is_ok());
592!
510
        set_impl([&](auto& shared_state) {
592✔
511
            shared_state.set_status(std::move(status));
592✔
512
        });
592✔
513
    }
592✔
514

515
    static auto make_promise_future_impl()
516
    {
26,814✔
517
        struct PromiseAndFuture {
26,814✔
518
            Promise<T> promise;
26,814✔
519
            Future<T> future = promise.get_future();
26,814✔
520
        };
26,814✔
521
        return PromiseAndFuture();
26,814✔
522
    }
26,814✔
523

524
private:
525
    // This is not public because we found it frequently was involved in races.  The
526
    // `make_promise_future<T>` API avoids those races entirely.
527
    Future<T> get_future() noexcept;
528

529
    friend class Future<void>;
530
    friend class CopyablePromiseHolder<T>;
531

532
    Promise(util::bind_ptr<SharedState<T>> shared_state)
533
        : m_shared_state(std::move(shared_state))
26✔
534
    {
52✔
535
        m_shared_state->claim();
52✔
536
    }
52✔
537

538
    util::bind_ptr<SharedState<T>> release() &&
539
    {
58✔
540
        auto ret = std::move(m_shared_state);
58✔
541
        ret->disown();
58✔
542
        return ret;
58✔
543
    }
58✔
544

545
    template <typename Func>
546
    void set_impl(Func&& do_set) noexcept
547
    {
26,390✔
548
        REALM_ASSERT(m_shared_state);
26,390✔
549
        // Update m_shared_state before fulfilling the promise so that anything
550
        // waiting on the future will see `m_shared_state = nullptr`.
551
        auto shared_state = std::move(m_shared_state);
26,390✔
552
        do_set(*shared_state);
26,390✔
553
    }
26,390✔
554

555
    util::bind_ptr<SharedState<T>> m_shared_state = make_intrusive<SharedState<T>>();
556
};
557

558
/**
559
 * CopyablePromiseHolder<T> is a lightweight copyable holder for Promises so they can be captured inside
560
 * of std::function's and other types that require all members to be copyable.
561
 *
562
 * The only thing you can do with a CopyablePromiseHolder is extract a regular promise from it exactly once,
563
 * and copy/move it as you would a util::bind_ptr.
564
 *
565
 * Do not use this type to try to fill a Promise from multiple places or threads.
566
 */
567
template <typename T>
568
class future_details::CopyablePromiseHolder {
569
public:
570
    CopyablePromiseHolder(Promise<T>&& input)
571
        : m_shared_state(std::move(input).release())
29✔
572
    {
58✔
573
    }
58✔
574

575
    CopyablePromiseHolder(const Promise<T>&) = delete;
576

577
    Promise<T> get_promise()
578
    {
52✔
579
        REALM_ASSERT(m_shared_state);
52✔
580
        return Promise<T>(std::move(m_shared_state));
52✔
581
    }
52✔
582

583
private:
584
    util::bind_ptr<SharedState<T>> m_shared_state;
585
};
586

587
/**
588
 * Future<T> is logically a possibly-deferred T or exception_ptr
589
 * As is usual for rvalue-qualified methods, you may call at most one of them on a given Future.
590
 *
591
 * A future may be passed between threads, but only one thread may use it at a time.
592
 */
593
template <typename T>
594
class REALM_NODISCARD future_details::Future {
595
public:
596
    static_assert(!is_future<T>, "Future<Future<T>> is banned. Just use Future<T> instead.");
597
    static_assert(!std::is_reference<T>::value, "Future<T&> is banned.");
598
    static_assert(!std::is_const<T>::value, "Future<const T> is banned.");
599
    static_assert(!std::is_array<T>::value, "Future<T[]> is banned.");
600

601
    using value_type = T;
602

603
    /**
604
     * Constructs a Future in a moved-from state that can only be assigned to or destroyed.
605
     */
606
    Future() = default;
720✔
607

608
    Future& operator=(Future&&) = default;
4✔
609
    Future(Future&&) = default;
3,642✔
610

611
    Future(const Future&) = delete;
612
    Future& operator=(const Future&) = delete;
613

614
    /* implicit */ Future(T val)
615
        : Future(make_ready(std::move(val)))
35✔
616
    {
70✔
617
    }
70✔
618
    /* implicit */ Future(Status status)
619
        : Future(make_ready(std::move(status)))
4✔
620
    {
8✔
621
    }
8✔
622
    /* implicit */ Future(StatusWith<T> sw)
623
        : Future(make_ready(std::move(sw)))
624
    {
625
    }
626

627
    /**
628
     * Make a ready Future<T> from a value for cases where you don't need to wait asynchronously.
629
     *
630
     * Calling this is faster than getting a Future out of a Promise, and is effectively free. It is
631
     * fast enough that you never need to avoid returning a Future from an API, even if the result
632
     * is ready 99.99% of the time.
633
     *
634
     * As an example, if you are handing out results from a batch, you can use this when for each
635
     * result while you have a batch, then use a Promise to return a not-ready Future when you need
636
     * to get another batch.
637
     */
638
    static Future<T> make_ready(T val)
639
    { // TODO emplace?
720✔
640
        Future out;
720✔
641
        out.m_immediate = std::move(val);
720✔
642
        return out;
720✔
643
    }
720✔
644

645
    static Future<T> make_ready(Status status)
646
    {
292✔
647
        auto out = Future<T>(make_intrusive<SharedState<T>>());
292✔
648
        out.m_shared->set_status(std::move(status));
292✔
649
        return out;
292✔
650
    }
292✔
651

652
    static Future<T> make_ready(StatusWith<T> val)
653
    {
292✔
654
        if (val.is_ok()) {
292✔
655
            return make_ready(std::move(val.get_value()));
184✔
656
        }
184✔
657
        return make_ready(val.get_status());
108✔
658
    }
292✔
659

660
    /**
661
     * If this returns true, get() is guaranteed not to block and callbacks will be immediately
662
     * invoked. You can't assume anything if this returns false since it may be completed
663
     * immediately after checking (unless you have independent knowledge that this Future can't
664
     * complete in the background).
665
     *
666
     * Callers must still call get() or similar, even on Future<void>, to ensure that they are
667
     * correctly sequenced with the completing task, and to be informed about whether the Promise
668
     * completed successfully.
669
     *
670
     * This is generally only useful as an optimization to avoid prep work, such as setting up
671
     * timeouts, that is unnecessary if the Future is ready already.
672
     */
673
    bool is_ready() const
674
    {
95,898,836✔
675
        // This can be a relaxed load because callers are not allowed to use it to establish
676
        // ordering.
677
        return m_immediate || m_shared->m_state.load(std::memory_order_relaxed) == SSBState::Finished;
95,898,836✔
678
    }
95,898,836✔
679

680
    /**
681
     * Gets the value out of this Future, blocking until it is ready.
682
     *
683
     * get() methods throw on error, while get_no_throw() returns a Statuswith<T> with either a value
684
     * or an error Status.
685
     *
686
     * These methods can be called multiple times, except for the rvalue overloads.
687
     */
688
    T get() &&
689
    {
1,258✔
690
        return std::move(get_impl());
1,258✔
691
    }
1,258✔
692
    T& get() &
693
    {
23,211✔
694
        return get_impl();
23,211✔
695
    }
23,211✔
696
    const T& get() const&
697
    {
613✔
698
        return const_cast<Future*>(this)->get_impl();
613✔
699
    }
613✔
700

701
    StatusWith<T> get_no_throw() const& noexcept
702
    {
210✔
703
        if (m_immediate) {
210✔
704
            return *m_immediate;
10✔
705
        }
10✔
706

707
        m_shared->wait();
200✔
708
        if (!m_shared->m_data) {
200✔
709
            return m_shared->m_status;
162✔
710
        }
162✔
711
        return *m_shared->m_data;
38✔
712
    }
200✔
713

714
    StatusWith<T> get_no_throw() && noexcept
715
    {
330✔
716
        if (m_immediate) {
330✔
717
            return std::move(*m_immediate);
20✔
718
        }
20✔
719

720
        m_shared->wait();
310✔
721
        if (!m_shared->m_data) {
310✔
722
            return m_shared->m_status;
174✔
723
        }
174✔
724
        return std::move(*m_shared->m_data);
136✔
725
    }
310✔
726

727
    /**
728
     * This ends the Future continuation chain by calling a callback on completion. Use this to
729
     * escape back into a callback-based API.
730
     *
731
     * The callback must not throw since it is called from a noexcept context. The callback must take a
732
     * StatusOrStatusWith as its argument and have a return type of void.
733
     */
734
    template <typename Func> // StatusOrStatusWith<T> -> void
735
    void get_async(Func&& func) && noexcept
736
    {
328✔
737
        static_assert(std::is_void_v<std::invoke_result_t<Func, StatusOrStatusWith<FakeVoidToVoid<T>>>>);
328✔
738

739
        return general_impl(
328✔
740
            // on ready success:
741
            [&](T&& val) {
328✔
742
                call(func, StatusWith<T>(std::move(val)));
36✔
743
            },
36✔
744
            // on ready failure:
745
            [&](Status&& status) {
328✔
746
                call(func, StatusWith<T>(std::move(status)));
24✔
747
            },
24✔
748
            // on not ready:
749
            [&] {
328✔
750
                m_shared->m_callback = [func = std::forward<Func>(func)](SharedStateBase* ssb) mutable noexcept {
268✔
751
                    const auto input = static_cast<SharedState<T>*>(ssb);
268✔
752
                    if (input->m_status.is_ok()) {
268✔
753
                        call(func, StatusWith<T>(std::move(*input->m_data)));
254✔
754
                    }
254✔
755
                    else {
14✔
756
                        call(func, StatusWith<T>(std::move(input->m_status)));
14✔
757
                    }
14✔
758
                };
268✔
759
            });
268✔
760
    }
328✔
761

762
    //
763
    // The remaining methods are all continuation based and take a callback and return a Future.
764
    // Each method has a comment indicating the supported signatures for that callback, and a
765
    // description of when the callback is invoked and how the impacts the returned Future. It may
766
    // be helpful to think of Future continuation chains as a pipeline of stages that take input
767
    // from earlier stages and produce output for later stages.
768
    //
769
    // Be aware that the callback may be invoked inline at the call-site or at the producer when
770
    // setting the value. Therefore, you should avoid doing blocking work inside of a callback.
771
    // Additionally, avoid acquiring any locks or mutexes that the caller already holds, otherwise
772
    // you risk a deadlock. If either of these concerns apply to your callback, it should schedule
773
    // itself on an executor, rather than doing work in the callback.
774
    //
775
    // Error handling in callbacks: all exceptions thrown propagate to the returned Future
776
    // automatically.
777
    //
778
    // Callbacks that return Future<T> are automatically unwrapped and connected to the returned
779
    // Future<T>, rather than producing a Future<Future<T>>.
780

781
    /**
782
     * Callbacks passed to then() are only called if the input Future completes successfully.
783
     * Otherwise the error propagates automatically, bypassing the callback.
784
     */
785
    template <typename Func>
786
    auto then(Func&& func) && noexcept
787
    {
440✔
788
        using Result = NormalizedCallResult<Func, T>;
440✔
789
        if constexpr (!is_future<Result>) {
440✔
790
            return general_impl(
220✔
791
                // on ready success:
792
                [&](T&& val) {
250✔
793
                    return Future<Result>::make_ready(no_throw_call(func, std::move(val)));
144✔
794
                },
144✔
795
                // on ready failure:
796
                [&](Status&& status) {
220✔
797
                    return Future<Result>::make_ready(std::move(status));
12✔
798
                },
12✔
799
                // on not ready yet:
800
                [&] {
224✔
801
                    return make_continuation<Result>(
110✔
802
                        [func = std::forward<Func>(func)](SharedState<T>* input,
110✔
803
                                                          SharedState<Result>* output) mutable noexcept {
110✔
804
                            if (!input->m_status.is_ok()) {
110✔
805
                                output->set_status(input->m_status);
6✔
806
                                return;
6✔
807
                            }
6✔
808

809
                            output->set_from(no_throw_call(func, std::move(*input->m_data)));
104✔
810
                        });
104✔
811
                });
110✔
812
        }
133✔
813
        else {
174✔
814
            using UnwrappedResult = typename Result::value_type;
174✔
815
            return general_impl(
174✔
816
                // on ready success:
817
                [&](T&& val) {
174✔
818
                    try {
44✔
819
                        return Future<UnwrappedResult>(throwing_call(func, std::move(val)));
44✔
820
                    }
44✔
821
                    catch (...) {
44✔
822
                        return Future<UnwrappedResult>::make_ready(exception_to_status());
8✔
823
                    }
8✔
824
                },
44✔
825
                // on ready failure:
826
                [&](Status&& status) {
174✔
827
                    return Future<UnwrappedResult>::make_ready(std::move(status));
12✔
828
                },
12✔
829
                // on not ready yet:
830
                [&] {
174✔
831
                    return make_continuation<UnwrappedResult>(
118✔
832
                        [func = std::forward<Func>(func)](SharedState<T>* input,
118✔
833
                                                          SharedState<UnwrappedResult>* output) mutable noexcept {
118✔
834
                            if (!input->m_status.is_ok())
118✔
835
                                return output->set_status(std::move(input->m_status));
8✔
836

837
                            try {
110✔
838
                                throwing_call(func, std::move(*input->m_data)).propagate_result_to(output);
110✔
839
                            }
110✔
840
                            catch (...) {
110✔
841
                                output->set_status(exception_to_status());
4✔
842
                            }
4✔
843
                        });
110✔
844
                });
118✔
845
        }
174✔
846
    }
440✔
847

848
    /**
849
     * Callbacks passed to on_completion() are always called with a StatusWith<T> when the input future completes.
850
     */
851
    template <typename Func>
852
    auto on_completion(Func&& func) && noexcept
853
    {
276✔
854
        using Wrapper = StatusOrStatusWith<T>;
276✔
855
        using Result = NormalizedCallResult<Func, StatusOrStatusWith<T>>;
276✔
856
        if constexpr (!is_future<Result>) {
276✔
857
            return general_impl(
138✔
858
                // on ready success:
859
                [&](T&& val) {
138✔
860
                    return Future<Result>::make_ready(
72✔
861
                        no_throw_call(std::forward<Func>(func), Wrapper(std::move(val))));
72✔
862
                },
72✔
863
                // on ready failure:
864
                [&](Status&& status) {
138✔
865
                    return Future<Result>::make_ready(
40✔
866
                        no_throw_call(std::forward<Func>(func), Wrapper(std::move(status))));
40✔
867
                },
40✔
868
                // on not ready yet:
869
                [&] {
138✔
870
                    return make_continuation<Result>(
56✔
871
                        [func = std::forward<Func>(func)](SharedState<T>* input,
56✔
872
                                                          SharedState<Result>* output) mutable noexcept {
56✔
873
                            if (!input->m_status.is_ok())
56✔
874
                                return output->set_from(no_throw_call(func, Wrapper(std::move(input->m_status))));
20✔
875

876
                            output->set_from(no_throw_call(func, Wrapper(std::move(*input->m_data))));
36✔
877
                        });
36✔
878
                });
56✔
879
        }
84✔
880
        else {
108✔
881
            using UnwrappedResult = typename Result::value_type;
108✔
882
            return general_impl(
108✔
883
                // on ready success:
884
                [&](T&& val) {
108✔
885
                    try {
44✔
886
                        return Future<UnwrappedResult>(
44✔
887
                            throwing_call(std::forward<Func>(func), Wrapper(std::move(val))));
44✔
888
                    }
44✔
889
                    catch (...) {
44✔
890
                        return Future<UnwrappedResult>::make_ready(exception_to_status());
8✔
891
                    }
8✔
892
                },
44✔
893
                // on ready failure:
894
                [&](Status&& status) {
108✔
895
                    try {
28✔
896
                        return Future<UnwrappedResult>(
28✔
897
                            throwing_call(std::forward<Func>(func), Wrapper(std::move(status))));
28✔
898
                    }
28✔
899
                    catch (...) {
28✔
900
                        return Future<UnwrappedResult>::make_ready(exception_to_status());
×
901
                    }
×
902
                },
28✔
903
                // on not ready yet:
904
                [&] {
108✔
905
                    return make_continuation<UnwrappedResult>(
36✔
906
                        [func = std::forward<Func>(func)](SharedState<T>* input,
36✔
907
                                                          SharedState<UnwrappedResult>* output) mutable noexcept {
36✔
908
                            if (!input->m_status.is_ok()) {
36✔
909
                                try {
14✔
910
                                    throwing_call(func, Wrapper(std::move(input->m_status)))
14✔
911
                                        .propagate_result_to(output);
14✔
912
                                }
14✔
913
                                catch (...) {
14✔
914
                                    output->set_status(exception_to_status());
×
915
                                }
×
916

917
                                return;
14✔
918
                            }
14✔
919

920
                            try {
22✔
921
                                throwing_call(func, Wrapper(std::move(*input->m_data))).propagate_result_to(output);
22✔
922
                            }
22✔
923
                            catch (...) {
22✔
924
                                output->set_status(exception_to_status());
4✔
925
                            }
4✔
926
                        });
22✔
927
                });
36✔
928
        }
108✔
929
    }
276✔
930

931
    /**
932
     * Callbacks passed to on_error() are only called if the input Future completes with an error.
933
     * Otherwise, the successful result propagates automatically, bypassing the callback.
934
     *
935
     * The callback can either produce a replacement value (which must be a T), return a replacement
936
     * Future<T> (such as a by retrying), or return/throw a replacement error.
937
     *
938
     * Note that this will only catch errors produced by earlier stages; it is not registering a
939
     * general error handler for the entire chain.
940
     */
941
    template <typename Func>
942
    Future<FakeVoidToVoid<T>> on_error(Func&& func) && noexcept
943
    {
184✔
944
        using Result = NormalizedCallResult<Func, Status>;
184✔
945
        static_assert(std::is_same<VoidToFakeVoid<UnwrappedType<Result>>, T>::value,
184✔
946
                      "func passed to Future<T>::onError must return T, StatusWith<T>, or Future<T>");
184✔
947

948
        if constexpr (!is_future<Result>) {
184✔
949
            return general_impl(
92✔
950
                // on ready success:
951
                [&](T&& val) {
92✔
952
                    return Future<T>::make_ready(std::move(val));
12✔
953
                },
12✔
954
                // on ready failure:
955
                [&](Status&& status) {
92✔
956
                    return Future<T>::make_ready(no_throw_call(func, std::move(status)));
36✔
957
                },
36✔
958
                // on not ready yet:
959
                [&] {
92✔
960
                    return make_continuation<T>([func = std::forward<Func>(func)](
24✔
961
                                                    SharedState<T>* input, SharedState<T>* output) mutable noexcept {
24✔
962
                        if (input->m_status.is_ok())
24✔
963
                            return output->emplace_value(std::move(*input->m_data));
6✔
964

965
                        output->set_from(no_throw_call(func, std::move(input->m_status)));
18✔
966
                    });
18✔
967
                });
24✔
968
        }
36✔
969
        else {
112✔
970
            return general_impl(
112✔
971
                // on ready success:
972
                [&](T&& val) {
112✔
973
                    return Future<T>::make_ready(std::move(val));
12✔
974
                },
12✔
975
                // on ready failure:
976
                [&](Status&& status) {
112✔
977
                    try {
36✔
978
                        return Future<T>(throwing_call(func, std::move(status)));
36✔
979
                    }
36✔
980
                    catch (...) {
36✔
981
                        return Future<T>::make_ready(exception_to_status());
×
982
                    }
×
983
                },
36✔
984
                // on not ready yet:
985
                [&] {
112✔
986
                    return make_continuation<T>([func = std::forward<Func>(func)](
64✔
987
                                                    SharedState<T>* input, SharedState<T>* output) mutable noexcept {
64✔
988
                        if (input->m_status.is_ok())
64✔
989
                            return output->emplace_value(std::move(*input->m_data));
10✔
990
                        try {
54✔
991
                            throwing_call(func, std::move(input->m_status)).propagate_result_to(output);
54✔
992
                        }
54✔
993
                        catch (...) {
54✔
994
                            output->set_status(exception_to_status());
×
995
                        }
×
996
                    });
54✔
997
                });
64✔
998
        }
112✔
999
    }
184✔
1000

1001
    Future<void> ignore_value() && noexcept;
1002

1003
private:
1004
    template <typename T2>
1005
    friend class Future;
1006
    friend class Promise<T>;
1007

1008
    T& get_impl()
1009
    {
25,386✔
1010
        if (m_immediate) {
25,386✔
1011
            return *m_immediate;
374✔
1012
        }
374✔
1013

1014
        m_shared->wait();
25,012✔
1015
        if (!m_shared->m_status.is_ok()) {
25,012✔
1016
            throw Exception(m_shared->m_status);
186✔
1017
        }
186✔
1018
        return *m_shared->m_data;
24,826✔
1019
    }
25,012✔
1020

1021
    // All callbacks are called immediately so they are allowed to capture everything by reference.
1022
    // All callbacks should return the same return type.
1023
    template <typename SuccessFunc, typename FailFunc, typename NotReady>
1024
    auto general_impl(SuccessFunc&& success, FailFunc&& fail, NotReady&& notReady) noexcept
1025
    {
1,444✔
1026
        if (m_immediate) {
1,444✔
1027
            return success(std::move(*m_immediate));
312✔
1028
        }
312✔
1029

1030
        if (m_shared->m_state.load(std::memory_order_acquire) == SSBState::Finished) {
1,132✔
1031
            if (m_shared->m_data) {
374!
1032
                return success(std::move(*m_shared->m_data));
178✔
1033
            }
178✔
1034
            else {
196✔
1035
                return fail(std::move(m_shared->m_status));
196✔
1036
            }
196✔
1037
        }
374✔
1038

1039
        // This is always done after notReady, which never throws. It is in a ScopeExit to
1040
        // support both void- and value-returning notReady implementations since we can't assign
1041
        // void to a variable.
1042
        auto guard = util::make_scope_exit([&]() noexcept {
758✔
1043
            auto old_state = SSBState::Init;
758✔
1044
            if (REALM_UNLIKELY(!m_shared->m_state.compare_exchange_strong(old_state, SSBState::Waiting,
758✔
1045
                                                                          std::memory_order_acq_rel))) {
758✔
1046
                REALM_ASSERT_DEBUG(old_state == SSBState::Finished);
×
1047
                m_shared->m_callback(m_shared.get());
×
1048
            }
×
1049
        });
758✔
1050

1051
        return notReady();
758✔
1052
    }
1,132✔
1053

1054
    template <typename Result, typename OnReady>
1055
    inline Future<Result> make_continuation(OnReady&& on_ready)
1056
    {
408✔
1057
        REALM_ASSERT(!m_shared->m_callback && !m_shared->m_continuation);
408✔
1058

1059
        auto continuation = make_intrusive<SharedState<Result>>();
408✔
1060
        continuation->thread_unsafe_inc_refs_to(2);
408✔
1061
        m_shared->m_continuation.reset(continuation.get(), util::bind_ptr_base::adopt_tag{});
408✔
1062
        m_shared->m_callback = [on_ready = std::forward<OnReady>(on_ready)](SharedStateBase* ssb) mutable noexcept {
408✔
1063
            const auto input = static_cast<SharedState<T>*>(ssb);
408✔
1064
            const auto output = static_cast<SharedState<Result>*>(ssb->m_continuation.get());
408✔
1065
            on_ready(input, output);
408✔
1066
        };
408✔
1067
        return Future<VoidToFakeVoid<Result>>(std::move(continuation));
408✔
1068
    }
408✔
1069

1070
    void propagate_result_to(SharedState<T>* output) && noexcept
1071
    {
216✔
1072
        general_impl(
216✔
1073
            // on ready success:
1074
            [&](T&& val) {
216✔
1075
                output->emplace_value(std::move(val));
126✔
1076
            },
126✔
1077
            // on ready failure:
1078
            [&](Status&& status) {
216✔
1079
                output->set_status(std::move(status));
8✔
1080
            },
8✔
1081
            // on not ready yet:
1082
            [&] {
216✔
1083
                // If the output is just for continuation, bypass it and just directly fill in the
1084
                // SharedState that it would write to. The concurrency situation is a bit subtle
1085
                // here since we are the Future-side of shared, but the Promise-side of output.
1086
                // The rule is that p->isJustForContinuation must be acquire-read as true before
1087
                // examining p->continuation, and p->continuation must be written before doing the
1088
                // release-store of true to p->isJustForContinuation.
1089
                if (output->m_is_just_for_continuation.load(std::memory_order_acquire)) {
82✔
1090
                    m_shared->m_continuation = std::move(output->m_continuation);
32✔
1091
                }
32✔
1092
                else {
50✔
1093
                    m_shared->m_continuation = util::bind_ptr(output);
50✔
1094
                }
50✔
1095
                m_shared->m_is_just_for_continuation.store(true, std::memory_order_release);
82✔
1096

1097
                m_shared->m_callback = [](SharedStateBase* ssb) noexcept {
82✔
1098
                    const auto input = static_cast<SharedState<T>*>(ssb);
50✔
1099
                    const auto output = static_cast<SharedState<T>*>(ssb->m_continuation.get());
50✔
1100
                    output->fill_from(std::move(*input));
50✔
1101
                };
50✔
1102
            });
82✔
1103
    }
216✔
1104

1105
    explicit Future(util::bind_ptr<SharedState<T>> ptr)
1106
        : m_shared(std::move(ptr))
13,964✔
1107
    {
28,086✔
1108
    }
28,086✔
1109

1110
    // At most one of these will be active.
1111
    util::Optional<T> m_immediate;
1112
    util::bind_ptr<SharedState<T>> m_shared;
1113
};
1114

1115
/**
1116
 * The void specialization of Future<T>. See the general Future<T> for detailed documentation.
1117
 * It should be the same as the generic Future<T> with the following exceptions:
1118
 *   - Anything mentioning StatusWith<T> will use Status instead.
1119
 *   - Anything returning references to T will just return void since there are no void references.
1120
 *   - Anything taking a T argument will receive no arguments.
1121
 */
1122
template <>
1123
class REALM_NODISCARD future_details::Future<void> {
1124
public:
1125
    using value_type = void;
1126

1127
    /* implicit */ Future()
1128
        : Future(make_ready())
1129
    {
×
1130
    }
×
1131
    /* implicit */ Future(Status status)
1132
        : Future(make_ready(std::move(status)))
1133
    {
×
1134
    }
×
1135

1136
    static Future<void> make_ready()
1137
    {
128✔
1138
        return Future<FakeVoid>::make_ready(FakeVoid{});
128✔
1139
    }
128✔
1140

1141
    static Future<void> make_ready(Status status)
1142
    {
46✔
1143
        if (status.is_ok())
46✔
1144
            return make_ready();
×
1145
        return Future<FakeVoid>::make_ready(std::move(status));
46✔
1146
    }
46✔
1147

1148
    bool is_ready() const
1149
    {
59,570,504✔
1150
        return inner.is_ready();
59,570,504✔
1151
    }
59,570,504✔
1152

1153
    void get() const
1154
    {
567✔
1155
        inner.get();
567✔
1156
    }
567✔
1157

1158
    Status get_no_throw() const noexcept
1159
    {
120✔
1160
        return inner.get_no_throw().get_status();
120✔
1161
    }
120✔
1162

1163
    template <typename Func> // Status -> void
1164
    void get_async(Func&& func) && noexcept
1165
    {
52✔
1166
        return std::move(inner).get_async(std::forward<Func>(func));
52✔
1167
    }
52✔
1168

1169
    template <typename Func> // () -> T or StatusWith<T> or Future<T>
1170
    auto then(Func&& func) && noexcept
1171
    {
162✔
1172
        return std::move(inner).then(std::forward<Func>(func));
162✔
1173
    }
162✔
1174

1175
    template <typename Func>
1176
    auto on_error(Func&& func) && noexcept
1177
    {
48✔
1178
        return std::move(inner).on_error(std::forward<Func>(func));
48✔
1179
    }
48✔
1180

1181
    template <typename Func>
1182
    auto on_completion(Func&& func) && noexcept
1183
    {
102✔
1184
        return std::move(inner).on_completion(std::forward<Func>(func));
102✔
1185
    }
102✔
1186

1187
    Future<void> ignore_value() && noexcept
1188
    {
×
1189
        return std::move(*this);
×
1190
    }
×
1191

1192
private:
1193
    template <typename T>
1194
    friend class Future;
1195
    friend class Promise<void>;
1196

1197
    explicit Future(util::bind_ptr<SharedState<FakeVoid>> ptr)
1198
        : inner(std::move(ptr))
534✔
1199
    {
1,233✔
1200
    }
1,233✔
1201
    /*implicit*/ Future(Future<FakeVoid>&& inner)
1202
        : inner(std::move(inner))
302✔
1203
    {
604✔
1204
    }
604✔
1205
    /*implicit*/ operator Future<FakeVoid>() &&
1206
    {
12✔
1207
        return std::move(inner);
12✔
1208
    }
12✔
1209

1210
    void propagate_result_to(SharedState<void>* output) && noexcept
1211
    {
24✔
1212
        std::move(inner).propagate_result_to(output);
24✔
1213
    }
24✔
1214

1215
    static Future<void> make_ready(StatusWith<FakeVoid> status)
1216
    {
92✔
1217
        return Future<FakeVoid>::make_ready(std::move(status));
92✔
1218
    }
92✔
1219

1220
    Future<FakeVoid> inner;
1221
};
1222

1223

1224
/**
1225
 * Returns a bound Promise and Future in a struct with friendly names (promise and future) that also
1226
 * works well with C++17 structured bindings.
1227
 */
1228
template <typename T = void>
1229
inline auto make_promise_future()
1230
{
26,814✔
1231
    return Promise<T>::make_promise_future_impl();
26,814✔
1232
}
26,814✔
1233

1234
/**
1235
 * This metafunction allows APIs that take callbacks and return Future to avoid doing their own type
1236
 * calculus. This results in the base value_type that would result from passing Func to a
1237
 * Future<T>::then(), with the same normalizing of T/StatusWith<T>/Future<T> returns. This is
1238
 * primarily useful for implementations of executors rather than their users.
1239
 *
1240
 * This returns the unwrapped T rather than Future<T> so it will be easy to create a Promise<T>.
1241
 *
1242
 * Examples:
1243
 *
1244
 * FutureContinuationResult<std::function<void()>> == void
1245
 * FutureContinuationResult<std::function<Status()>> == void
1246
 * FutureContinuationResult<std::function<Future<void>()>> == void
1247
 *
1248
 * FutureContinuationResult<std::function<int()>> == int
1249
 * FutureContinuationResult<std::function<StatusWith<int>()>> == int
1250
 * FutureContinuationResult<std::function<Future<int>()>> == int
1251
 *
1252
 * FutureContinuationResult<std::function<int(bool)>, bool> == int
1253
 *
1254
 * FutureContinuationResult<std::function<int(bool)>, NotBool> SFINAE-safe substitution failure.
1255
 */
1256
template <typename Func, typename... Args>
1257
using FutureContinuationResult =
1258
    typename future_details::FutureContinuationResultImpl<std::invoke_result_t<Func, Args...>>::type;
1259

1260
//
1261
// Implementations of methods that couldn't be defined in the class due to ordering requirements.
1262
//
1263

1264
template <typename T>
1265
inline Future<T> Promise<T>::get_future() noexcept
1266
{
26,814✔
1267
    m_shared_state->thread_unsafe_inc_refs_to(2);
26,814✔
1268
    return Future<T>(util::bind_ptr<SharedState<T>>(m_shared_state.get(), util::bind_ptr_base::adopt_tag{}));
26,814✔
1269
}
26,814✔
1270

1271
template <typename T>
1272
inline void Promise<T>::set_from(Future<T>&& future) noexcept
1273
{
24✔
1274
    set_impl([&](auto& shared_state) {
24✔
1275
        std::move(future).propagate_result_to(&shared_state);
24✔
1276
    });
24✔
1277
}
24✔
1278

1279
template <typename T>
1280
inline Future<void> Future<T>::ignore_value() && noexcept
1281
{
1282
    return std::move(*this).then([](auto&&) {});
1283
}
1284

1285
} // namespace realm::util
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc