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

realm / realm-core / github_pull_request_278228

04 Oct 2023 10:15PM UTC coverage: 91.582% (+0.007%) from 91.575%
github_pull_request_278228

Pull #7029

Evergreen

tgoyne
Use UNITTEST_LOG_LEVEL in objectstore tests

For historical reasons core and sync tests use the UNITTEST_LOG_LEVEL
environment variable to determine the test log level, while object store tests
used a build time setting. This brings them into alignment on using the env
variable, and applies it via setting the default log level on startup in a
single place.
Pull Request #7029: Use UNITTEST_LOG_LEVEL in objectstore tests

94218 of 173442 branches covered (0.0%)

46 of 54 new or added lines in 5 files covered. (85.19%)

51 existing lines in 12 files now uncovered.

230351 of 251523 relevant lines covered (91.58%)

6704577.96 hits per line

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

95.81
/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

225
protected:
226
    FutureRefCountable() = default;
6,576✔
227
    virtual ~FutureRefCountable() = default;
6,576✔
228

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

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

239
    void unbind_ptr() const noexcept
240
    {
13,083✔
241
        if (m_refs.fetch_sub(1, std::memory_order_acq_rel) == 1) {
13,083✔
242
            delete this;
6,576✔
243
        }
6,576✔
244
    }
13,083✔
245

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

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

258
template <typename T>
259
struct SharedStateImpl;
260

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

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

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

279
    virtual ~SharedStateBase() = default;
6,576✔
280

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

1,023✔
288
        m_cv.emplace();
2,208✔
289

1,023✔
290
        auto old_state = SSBState::Init;
2,208✔
291
        if (REALM_UNLIKELY(
2,208✔
292
                !m_state.compare_exchange_strong(old_state, SSBState::Waiting, std::memory_order_acq_rel))) {
1,023✔
293
            REALM_ASSERT_DEBUG(old_state == SSBState::Finished);
×
294
            return;
×
295
        }
×
296

1,023✔
297
        std::unique_lock<std::mutex> lk(m_mutex);
2,208✔
298
        m_cv->wait(lk, [&] {
4,416✔
299
            return m_state.load(std::memory_order_acquire) == SSBState::Finished;
4,416✔
300
        });
4,416✔
301
    }
2,208✔
302

303
    void transition_to_finished() noexcept
304
    {
6,468✔
305
        auto old_state = m_state.exchange(SSBState::Finished, std::memory_order_acq_rel);
6,468✔
306
        if (old_state == SSBState::Init) {
6,468✔
307
            return;
2,556✔
308
        }
2,556✔
309

1,875✔
310
        REALM_ASSERT_DEBUG(old_state == SSBState::Waiting);
3,912✔
311

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

1,875✔
319
        size_t depth = 0;
3,912✔
320
        for (auto ssb = m_continuation.get(); ssb;
5,490✔
321
             ssb = ssb->m_state.load(std::memory_order_acquire) == SSBState::Waiting ? ssb->m_continuation.get()
2,664✔
322
                                                                                     : nullptr) {
1,578✔
323
            depth++;
1,578✔
324
            REALM_ASSERT(depth < kMaxDepth);
1,578✔
325
        }
1,578✔
326
#endif
3,912✔
327
        if (m_callback) {
3,912✔
328
            m_callback(this);
1,704✔
329
        }
1,704✔
330

1,875✔
331
        if (m_cv) {
3,912✔
332
            std::lock_guard<std::mutex> lk(m_mutex);
2,208✔
333
            m_cv->notify_all();
2,208✔
334
        }
2,208✔
335
    }
3,912✔
336

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

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

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

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

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

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

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

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

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

377
protected:
378
    SharedStateBase() = default;
6,576✔
379
};
380

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

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

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

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

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

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

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

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

438
} // namespace future_details
439

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

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

470
    Promise() = default;
2,718✔
471

472
    ~Promise()
473
    {
7,256✔
474
        if (REALM_UNLIKELY(m_shared_state)) {
7,256✔
475
            m_shared_state->set_status({ErrorCodes::BrokenPromise, "Broken Promise"});
6✔
476
        }
6✔
477
    }
7,256✔
478

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

483
    // The default move construction is fine.
484
    Promise(Promise&&) noexcept = default;
4,364✔
485

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

493
    void set_from_status_with(StatusWith<T> sw) noexcept
494
    {
64✔
495
        set_impl([&] {
64✔
496
            m_shared_state->set_from(std::move(sw));
64✔
497
        });
64✔
498
    }
64✔
499

500
    template <typename... Args>
501
    void emplace_value(Args&&... args) noexcept
502
    {
2,034✔
503
        set_impl([&] {
2,034✔
504
            m_shared_state->emplace_value(std::forward<Args>(args)...);
2,034✔
505
        });
2,034✔
506
    }
2,034✔
507

508
    void set_error(Status status) noexcept
509
    {
538✔
510
        REALM_ASSERT_DEBUG(!status.is_ok());
538!
511
        set_impl([&] {
538✔
512
            m_shared_state->set_status(std::move(status));
538✔
513
        });
538✔
514
    }
538✔
515

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

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

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

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

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

546
    template <typename Func>
547
    void set_impl(Func&& do_set) noexcept
548
    {
2,660✔
549
        REALM_ASSERT(m_shared_state);
2,660!
550
        do_set();
2,660✔
551
        m_shared_state.reset();
2,660✔
552
    }
2,660✔
553

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

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

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

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

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

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

600
    using value_type = T;
601

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

607
    Future& operator=(Future&&) = default;
×
608
    Future(Future&&) = default;
2,744✔
609

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

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

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

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

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

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

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

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

92✔
706
        m_shared->wait();
184✔
707
        if (!m_shared->m_data) {
184✔
708
            return m_shared->m_status;
152✔
709
        }
152✔
710
        return *m_shared->m_data;
32✔
711
    }
32✔
712

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

76✔
719
        m_shared->wait();
152✔
720
        if (!m_shared->m_data) {
152✔
721
            return m_shared->m_status;
128✔
722
        }
128✔
723
        return std::move(*m_shared->m_data);
24✔
724
    }
24✔
725

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

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

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

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

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

42✔
836
                            try {
84✔
837
                                throwing_call(func, std::move(*input->m_data)).propagate_result_to(output);
84✔
838
                            }
84✔
839
                            catch (...) {
44✔
840
                                output->set_status(exception_to_status());
4✔
841
                            }
4✔
842
                        });
84✔
843
                });
92✔
844
        }
148✔
845
    }
396✔
846

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

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

7✔
916
                                return;
14✔
917
                            }
22✔
918

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

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

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

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

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

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

1007
    T& get_impl()
1008
    {
1,778✔
1009
        if (m_immediate) {
1,778✔
1010
            return *m_immediate;
274✔
1011
        }
274✔
1012

668✔
1013
        m_shared->wait();
1,504✔
1014
        if (!m_shared->m_status.is_ok()) {
1,504✔
1015
            throw Exception(m_shared->m_status);
162✔
1016
        }
162✔
1017
        return *m_shared->m_data;
1,342✔
1018
    }
1,342✔
1019

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

489✔
1029
        if (m_shared->m_state.load(std::memory_order_acquire) == SSBState::Finished) {
978✔
1030
            if (m_shared->m_data) {
378!
1031
                return success(std::move(*m_shared->m_data));
182✔
1032
            }
182✔
1033
            else {
196✔
1034
                return fail(std::move(m_shared->m_status));
196✔
1035
            }
196✔
1036
        }
600✔
1037

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

300✔
1050
        return notReady();
600✔
1051
    }
600✔
1052

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

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

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

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

1104
    explicit Future(util::bind_ptr<SharedState<T>> ptr)
1105
        : m_shared(std::move(ptr))
1106
    {
3,766✔
1107
    }
3,766✔
1108

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

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

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

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

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

1147
    bool is_ready() const
1148
    {
54,414,098✔
1149
        return inner.is_ready();
54,414,098✔
1150
    }
54,414,098✔
1151

1152
    void get() const
1153
    {
516✔
1154
        inner.get();
516✔
1155
    }
516✔
1156

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

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

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

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

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

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

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

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

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

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

1219
    Future<FakeVoid> inner;
1220
};
1221

1222

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

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

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

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

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

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

1284
} // 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