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

bemanproject / execution / 29958970710

22 Jul 2026 09:22PM UTC coverage: 93.566% (-0.2%) from 93.738%
29958970710

push

github

web-flow
Implement task and task_scheduler (#299)

* Fix issue where lvalue scope-tokens cannot be used as arguments for `spawn` and `spawn_future`

* Exception safety on `spawn` allocation failure

* WIP(exec.task)

* WIP(exec.task)

* Fix bugs

* Add test

* Add missing headers

* Format code

* Workaround failed template argument  deduction for optional::value_or

* Workaround for the bug of equality operator in gcc 15

303 of 348 new or added lines in 8 files covered. (87.07%)

1687 of 1803 relevant lines covered (93.57%)

264.88 hits per line

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

96.1
/include/beman/execution/detail/spawn_future.hpp
1
// include/beman/execution/detail/spawn_future.hpp                    -*-C++-*-
2
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3

4
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_SPAWN_FUTURE
5
#define INCLUDED_BEMAN_EXECUTION_DETAIL_SPAWN_FUTURE
6

7
#include <beman/execution/detail/common.hpp>
8
#ifdef BEMAN_HAS_IMPORT_STD
9
import std;
10
#else
11
#include <exception>
12
#include <memory>
13
#include <mutex>
14
#include <tuple>
15
#include <type_traits>
16
#include <utility>
17
#include <variant>
18
#endif
19
#ifdef BEMAN_HAS_MODULES
20
import beman.execution.detail.as_tuple;
21
import beman.execution.detail.basic_sender;
22
import beman.execution.detail.completion_signatures;
23
import beman.execution.detail.completion_signatures_for;
24
import beman.execution.detail.completion_signatures_of_t;
25
import beman.execution.detail.connect_result_t;
26
import beman.execution.detail.decayed_tuple;
27
import beman.execution.detail.default_impls;
28
import beman.execution.detail.env;
29
import beman.execution.detail.get_allocator;
30
import beman.execution.detail.get_env;
31
import beman.execution.detail.impls_for;
32
import beman.execution.detail.inplace_stop_source;
33
import beman.execution.detail.join_env;
34
import beman.execution.detail.make_sender;
35
import beman.execution.detail.meta.combine;
36
import beman.execution.detail.meta.prepend;
37
import beman.execution.detail.meta.unique;
38
import beman.execution.detail.prop;
39
import beman.execution.detail.queryable;
40
import beman.execution.detail.receiver;
41
import beman.execution.detail.scope_token;
42
import beman.execution.detail.sender;
43
import beman.execution.detail.set_error;
44
import beman.execution.detail.set_stopped;
45
import beman.execution.detail.set_value;
46
import beman.execution.detail.spawn_get_allocator;
47
import beman.execution.detail.start;
48
import beman.execution.detail.stop_when;
49
import beman.execution.detail.write_env;
50
#else
51
#include <beman/execution/detail/as_tuple.hpp>
52
#include <beman/execution/detail/completion_signatures_of_t.hpp>
53
#include <beman/execution/detail/connect_result_t.hpp>
54
#include <beman/execution/detail/default_impls.hpp>
55
#include <beman/execution/detail/env.hpp>
56
#include <beman/execution/detail/get_allocator.hpp>
57
#include <beman/execution/detail/get_env.hpp>
58
#include <beman/execution/detail/impls_for.hpp>
59
#include <beman/execution/detail/inplace_stop_source.hpp>
60
#include <beman/execution/detail/join_env.hpp>
61
#include <beman/execution/detail/make_sender.hpp>
62
#include <beman/execution/detail/meta_combine.hpp>
63
#include <beman/execution/detail/meta_unique.hpp>
64
#include <beman/execution/detail/prop.hpp>
65
#include <beman/execution/detail/queryable.hpp>
66
#include <beman/execution/detail/receiver.hpp>
67
#include <beman/execution/detail/scope_token.hpp>
68
#include <beman/execution/detail/sender.hpp>
69
#include <beman/execution/detail/set_error.hpp>
70
#include <beman/execution/detail/set_stopped.hpp>
71
#include <beman/execution/detail/set_value.hpp>
72
#include <beman/execution/detail/spawn_get_allocator.hpp>
73
#include <beman/execution/detail/start.hpp>
74
#include <beman/execution/detail/stop_when.hpp>
75
#include <beman/execution/detail/write_env.hpp>
76
#endif
77

78
// ----------------------------------------------------------------------------
79

80
namespace beman::execution::detail {
81
template <typename>
82
struct non_throwing_args_copy;
83
template <typename Rc, typename... A>
84
struct non_throwing_args_copy<Rc(A...)> {
85
    static constexpr bool value = (true && ... && ::std::is_nothrow_constructible_v<::std::decay_t<A>, A>);
86
};
87
template <typename S>
88
inline constexpr bool non_throwing_args_copy_v{non_throwing_args_copy<S>::value};
89

90
template <typename Completions>
91
struct spawn_future_state_base;
92
template <typename... Sigs>
93
struct spawn_future_state_base<::beman::execution::completion_signatures<Sigs...>> {
94
    static constexpr bool has_non_throwing_args_copy = (true && ... && non_throwing_args_copy_v<Sigs>);
95
    using result_t                                   = ::beman::execution::detail::meta::unique<
96
        ::std::conditional_t<has_non_throwing_args_copy,
97
                             ::std::variant<::std::monostate, ::beman::execution::detail::as_tuple_t<Sigs>...>,
98
                             ::std::variant<::std::monostate,
99
                                            ::std::tuple<::beman::execution::set_error_t, ::std::exception_ptr>,
100
                                            ::beman::execution::detail::as_tuple_t<Sigs>...>>>;
101

102
    result_t result{};
103
    virtual ~spawn_future_state_base()       = default;
14✔
104
    virtual auto complete() noexcept -> void = 0;
105
};
106

107
template <typename Completions>
108
struct spawn_future_receiver {
109
    using receiver_concept = ::beman::execution::receiver_tag;
110
    using state_t          = ::beman::execution::detail::spawn_future_state_base<Completions>;
111

112
    state_t* state{};
113

114
    template <typename... A>
115
    auto set_value(A&&... a) && noexcept -> void {
6✔
116
        this->set_complete<::beman::execution::set_value_t>(::std::forward<A>(a)...);
10✔
117
    }
6✔
118
    template <typename E>
119
    auto set_error(E&& e) && noexcept -> void {
1✔
120
        this->set_complete<::beman::execution::set_error_t>(::std::forward<E>(e));
1✔
121
    }
1✔
122
    auto set_stopped() && noexcept -> void { this->set_complete<::beman::execution::set_stopped_t>(); }
1✔
123

124
    template <typename Tag, typename... T>
125
    auto set_complete(T&&... t) noexcept {
8✔
126
        try {
127
            this->state->result.template emplace<::beman::execution::detail::decayed_tuple<Tag, T...>>(
19✔
128
                Tag(), ::std::forward<T>(t)...);
8✔
129
        } catch (...) {
1✔
130
            if constexpr (!state_t::has_non_throwing_args_copy) {
131
                this->state->result
132
                    .template emplace<::std::tuple<::beman::execution::set_error_t, ::std::exception_ptr>>(
133
                        ::beman::execution::set_error_t{}, ::std::current_exception());
134
            }
135
        }
136
        this->state->complete();
8✔
137
    }
8✔
138
};
1✔
139

1✔
140
template <::beman::execution::sender Sndr, typename Env>
2✔
141
using future_spawned_sender = decltype(::beman::execution::write_env(
142
    ::beman::execution::detail::stop_when(::std::declval<Sndr>(),
143
                                          ::std::declval<::beman::execution::inplace_stop_token>()),
144
    ::std::declval<Env>()));
145

146
template <::beman::execution::sender Sndr, typename Env>
147
using spawn_future_sigs = ::beman::execution::detail::meta::unique<::beman::execution::detail::meta::prepend<
148
    ::beman::execution::set_stopped_t(),
149
    ::beman::execution::completion_signatures_of_t<::beman::execution::detail::future_spawned_sender<Sndr, Env>>>>;
150

151
template <typename Allocator, ::beman::execution::scope_token Token, ::beman::execution::sender Sndr, typename Env>
152
struct spawn_future_state
153
    : ::beman::execution::detail::spawn_future_state_base<::beman::execution::detail::spawn_future_sigs<Sndr, Env>> {
154
    using alloc_t          = typename ::std::allocator_traits<Allocator>::template rebind_alloc<spawn_future_state>;
155
    using assoc_t          = ::std::remove_cvref_t<decltype(::std::declval<const Token&>().try_associate())>;
156
    using traits_t         = ::std::allocator_traits<alloc_t>;
157
    using spawned_sender_t = ::beman::execution::detail::future_spawned_sender<Sndr, Env>;
158
    using sigs_t           = ::beman::execution::detail::spawn_future_sigs<Sndr, Env>;
159
    using receiver_t       = ::beman::execution::detail::spawn_future_receiver<sigs_t>;
160
    static_assert(::beman::execution::sender<spawned_sender_t>);
161
    static_assert(::beman::execution::receiver<receiver_t>);
162
    using op_t = ::beman::execution::connect_result_t<spawned_sender_t, receiver_t>;
163

164
    template <::beman::execution::sender S>
165
    spawn_future_state(auto a, S&& s, const Token& tok, Env env)
4✔
166
        : alloc(::std::move(a)),
8✔
167
          op(::beman::execution::write_env(
4✔
168
                 ::beman::execution::detail::stop_when(::std::forward<S>(s), source.get_token()), env),
8✔
169
             receiver_t{this}),
170
          assoc(tok.try_associate()) {
12✔
171
        if (this->assoc) {
4✔
172
            ::beman::execution::start(this->op);
4✔
173
        } else {
NEW
174
            ::beman::execution::set_stopped(receiver_t{this});
×
175
        }
176
    }
4✔
177
    auto complete() noexcept -> void override {
4✔
178
        {
179
            ::std::lock_guard cerberos(this->gate);
4✔
180
            if (this->fun == nullptr) {
4✔
181
                this->receiver = this;
2✔
182
                return;
2✔
183
            }
184
        }
4✔
185
        this->fun(this->receiver, *this);
2✔
186
    }
187
    auto abandon() noexcept -> void {
4✔
188
        bool ready{[&] {
12✔
189
            ::std::lock_guard cerberos(this->gate);
190
            if (this->receiver == nullptr) {
191
                this->receiver = this;
192
                this->fun      = [](void*, spawn_future_state& state) noexcept { state.destroy(); };
193
                return false;
194
            }
195
            return true;
196
        }()};
8✔
197
        if (ready) {
4✔
198
            this->destroy();
3✔
199
        } else {
200
            this->source.request_stop();
1✔
201
        }
202
    }
4✔
203
    template <::beman::execution::receiver Rcvr>
4✔
204
    static auto complete_receiver(Rcvr& rcvr, typename spawn_future_state::result_t& res) noexcept {
4✔
205
        std::visit(
1✔
206
            [&rcvr]<typename Tuplish>(Tuplish&& tuplish) noexcept {
2✔
207
                if constexpr (!::std::same_as<::std::remove_cvref_t<decltype(tuplish)>, ::std::monostate>) {
1✔
208
                    ::std::apply(
3✔
209
                        [&rcvr]<typename... Args>(auto cpo, Args&&... args) {
2✔
210
                            cpo(::std::move(rcvr), ::std::forward<Args>(args)...);
2✔
211
                        },
6✔
212
                        ::std::forward<Tuplish>(tuplish));
213
                }
214
            },
215
            ::std::move(res));
2✔
216
    }
2✔
217
    template <::beman::execution::receiver Rcvr>
2✔
218
    auto consume(Rcvr& rcvr) noexcept -> void {
4✔
219
        {
4✔
220
            ::std::lock_guard cerberos(this->gate);
2✔
221
            if (this->receiver == nullptr) {
2✔
222
                this->receiver = &rcvr;
2✔
223
                this->fun      = [](void* ptr, spawn_future_state& state) noexcept {
1✔
224
                    spawn_future_state::complete_receiver(*static_cast<Rcvr*>(ptr), state.result);
3✔
225
                };
226
                return;
1✔
227
            }
228
        }
2✔
229
        spawn_future_state::complete_receiver(rcvr, this->result);
1✔
230
    }
1✔
231
    auto destroy() noexcept -> void {
4✔
232
        assoc_t _ = ::std::move(this->assoc);
8✔
233
        alloc_t a{this->alloc};
4✔
234
        traits_t::destroy(a, this);
235
        traits_t::deallocate(a, this, 1u);
236
    }
4✔
237

238
    ::std::mutex                            gate{};
239
    alloc_t                                 alloc;
240
    ::beman::execution::inplace_stop_source source{};
241
    op_t                                    op;
242
    assoc_t                                 assoc;
243
    void*                                   receiver{};
244
    auto (*fun)(void*, spawn_future_state&) noexcept -> void = nullptr;
245
};
246

247
class spawn_future_t {
248
  public:
249
    template <::beman::execution::sender Sndr, ::beman::execution::scope_token Tok, typename Ev>
250
        requires ::beman::execution::detail::queryable<::std::remove_cvref_t<Ev>>
251
    auto operator()(Sndr&& sndr, const Tok& tok, Ev&& ev) const {
4✔
252
        //-dk:TODO why decltype(auto) instead of auto?
253
        auto make{[&]() -> decltype(auto) { return tok.wrap(::std::forward<Sndr>(sndr)); }};
12✔
254
        using sndr_t = decltype(make());
255
        static_assert(::beman::execution::sender<Sndr>);
256

257
        auto [alloc, senv] = spawn_get_allocator(sndr, ev);
4✔
258
        using state_t = ::beman::execution::detail::spawn_future_state<decltype(alloc), Tok, sndr_t, decltype(senv)>;
259
        using state_alloc_t  = typename ::std::allocator_traits<decltype(alloc)>::template rebind_alloc<state_t>;
260
        using state_traits_t = ::std::allocator_traits<state_alloc_t>;
261
        state_alloc_t state_alloc(alloc);
262
        state_t*      op{state_traits_t::allocate(state_alloc, 1u)};
4✔
263
        try {
264
            state_traits_t::construct(state_alloc, op, alloc, make(), tok, senv);
4✔
265
        } catch (...) {
×
266
            state_traits_t::deallocate(state_alloc, op, 1u);
267
            throw;
×
268
        }
269

270
        using deleter = decltype([](state_t* p) noexcept { p->abandon(); });
271
        return ::beman::execution::detail::make_sender(*this, ::std::unique_ptr<state_t, deleter>{op});
8✔
272
    }
4✔
273
    template <::beman::execution::sender Sndr, ::beman::execution::scope_token Tok>
274
    auto operator()(Sndr&& sndr, const Tok& tok) const {
275
        return (*this)(::std::forward<Sndr>(sndr), tok, ::beman::execution::env<>{});
276
    }
277

278
  private:
279
    template <typename, typename>
280
    struct get_signatures;
281
    template <typename State, typename Deleter, typename Env>
282
    struct get_signatures<::beman::execution::detail::basic_sender<::beman::execution::detail::spawn_future_t,
283
                                                                   ::std::unique_ptr<State, Deleter>>,
284
                          Env> {
285
        using type = typename State::sigs_t;
286
    };
287

288
  public:
289
    template <typename Sender, typename... Env>
290
    static consteval auto get_completion_signatures() {
291
        return typename get_signatures<std::remove_cvref_t<Sender>, Env...>::type{};
292
    }
293
    struct impls_for : ::beman::execution::detail::default_impls {
294
        struct start_impl {
295
            auto operator()(auto& state, auto& rcvr) const noexcept -> void { state->consume(rcvr); }
2✔
296
        };
297
        static constexpr auto start{start_impl{}};
298
    };
299
};
300

301
} // namespace beman::execution::detail
302

303
namespace beman::execution {
304
using spawn_future_t = ::beman::execution::detail::spawn_future_t;
305
inline constexpr spawn_future_t spawn_future{};
306
} // namespace beman::execution
307

308
// ----------------------------------------------------------------------------
309

310
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_SPAWN_FUTURE
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc