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

STEllAR-GROUP / hpx / #852

17 Dec 2022 04:43PM UTC coverage: 85.912% (-0.7%) from 86.568%
#852

push

StellarBot
Merge #6106

6106: Modernizing modules of levels 0 to 5 r=hkaiser a=hkaiser

- flyby: HPX_FORWARD/HPX_MOVE now expand to std::forward and std::move if those are implemented as builtin functions

working towards #5497

Co-authored-by: Hartmut Kaiser <hartmut.kaiser@gmail.com>

87 of 87 new or added lines in 24 files covered. (100.0%)

173152 of 201546 relevant lines covered (85.91%)

1910264.28 hits per line

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

48.1
/libs/core/execution/include/hpx/execution/algorithms/make_future.hpp
1
//  Copyright (c) 2021 ETH Zurich
2
//  Copyright (c) 2022 Hartmut Kaiser
3
//
4
//  SPDX-License-Identifier: BSL-1.0
5
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
6
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7

8
#pragma once
9

10
#include <hpx/allocator_support/allocator_deleter.hpp>
11
#include <hpx/allocator_support/internal_allocator.hpp>
12
#include <hpx/allocator_support/traits/is_allocator.hpp>
13
#include <hpx/errors/try_catch_exception_ptr.hpp>
14
#include <hpx/execution/algorithms/detail/inject_scheduler.hpp>
15
#include <hpx/execution/algorithms/detail/partial_algorithm.hpp>
16
#include <hpx/execution/algorithms/detail/single_result.hpp>
17
#include <hpx/execution/algorithms/run_loop.hpp>
18
#include <hpx/execution_base/completion_signatures.hpp>
19
#include <hpx/execution_base/operation_state.hpp>
20
#include <hpx/execution_base/receiver.hpp>
21
#include <hpx/execution_base/sender.hpp>
22
#include <hpx/functional/detail/tag_priority_invoke.hpp>
23
#include <hpx/functional/invoke_result.hpp>
24
#include <hpx/futures/detail/future_data.hpp>
25
#include <hpx/futures/promise.hpp>
26
#include <hpx/modules/memory.hpp>
27
#include <hpx/type_support/meta.hpp>
28
#include <hpx/type_support/unused.hpp>
29

30
#include <exception>
31
#include <memory>
32
#include <type_traits>
33
#include <utility>
34

35
namespace hpx::execution::experimental { namespace detail {
36

37
    template <typename Data>
38
    struct future_receiver_base
918✔
39
    {
40
        hpx::intrusive_ptr<Data> data;
41

42
    protected:
43
        template <typename U>
44
        void set_value(U&& u) && noexcept
178✔
45
        {
46
            hpx::detail::try_catch_exception_ptr(
178✔
47
                [&]() { data->set_value(HPX_FORWARD(U, u)); },
356✔
48
                [&](std::exception_ptr ep) {
178✔
49
                    data->set_exception(HPX_MOVE(ep));
×
50
                });
×
51
            data.reset();
178✔
52
        }
178✔
53

54
    private:
55
        friend void tag_invoke(set_error_t, future_receiver_base&& r,
×
56
            std::exception_ptr ep) noexcept
57
        {
58
            r.data->set_exception(HPX_MOVE(ep));
×
59
            r.data.reset();
×
60
        }
×
61

62
        friend void tag_invoke(set_stopped_t, future_receiver_base&&) noexcept
×
63
        {
64
            std::terminate();
×
65
        }
66
    };
67

68
    template <typename T>
69
    struct future_receiver
83✔
70
      : future_receiver_base<hpx::lcos::detail::future_data_base<T>>
71
    {
72
    private:
73
        template <typename U>
74
        friend void tag_invoke(set_value_t, future_receiver&& r, U&& u) noexcept
15✔
75
        {
76
            HPX_MOVE(r).set_value(HPX_FORWARD(U, u));
15✔
77
        }
15✔
78
    };
79

80
    template <>
81
    struct future_receiver<void>
835✔
82
      : future_receiver_base<hpx::lcos::detail::future_data_base<void>>
83
    {
84
    private:
85
        friend void tag_invoke(set_value_t, future_receiver&& r) noexcept
163✔
86
        {
87
            HPX_MOVE(r).set_value(hpx::util::unused);
163✔
88
        }
163✔
89
    };
90

91
    template <typename T, typename Allocator, typename OperationState,
92
        typename Derived = void>
93
    struct future_data
178✔
94
      : hpx::lcos::detail::future_data_allocator<T, Allocator,
95
            std::conditional_t<std::is_void_v<Derived>,
96
                future_data<T, Allocator, OperationState, Derived>, Derived>>
97
    {
98
        HPX_NON_COPYABLE(future_data);
99

100
        using derived_type =
101
            std::conditional_t<std::is_void_v<Derived>, future_data, Derived>;
102
        using base_type = hpx::lcos::detail::future_data_allocator<T, Allocator,
103
            derived_type>;
104
        using operation_state_type = std::decay_t<OperationState>;
105
        using init_no_addref = typename base_type::init_no_addref;
106
        using other_allocator = typename std::allocator_traits<
107
            Allocator>::template rebind_alloc<future_data>;
108

109
        operation_state_type op_state;
110

111
        template <typename Sender>
112
        future_data(init_no_addref no_addref, other_allocator const& alloc,
178✔
113
            Sender&& sender)
114
          : base_type(no_addref, alloc)
178✔
115
          , op_state(hpx::execution::experimental::connect(
356✔
116
                HPX_FORWARD(Sender, sender),
178✔
117
                detail::future_receiver<T>{{this}}))
178✔
118
        {
356✔
119
            hpx::execution::experimental::start(op_state);
178✔
120
        }
178✔
121
    };
122

123
    template <typename T, typename Allocator, typename OperationState>
124
    struct future_data_with_run_loop
×
125
      : future_data<T, Allocator, OperationState,
126
            future_data_with_run_loop<T, Allocator, OperationState>>
127
    {
128
        hpx::execution::experimental::run_loop& loop;
129

130
        using base_type = future_data<T, Allocator, OperationState,
131
            future_data_with_run_loop>;
132
        using init_no_addref = typename base_type::init_no_addref;
133
        using other_allocator = typename base_type::other_allocator;
134

135
        template <typename Sender>
136
        future_data_with_run_loop(init_no_addref no_addref,
×
137
            other_allocator const& alloc,
138
            hpx::execution::experimental::run_loop_scheduler const& sched,
139
            Sender&& sender)
140
          : base_type(no_addref, alloc, HPX_FORWARD(Sender, sender))
×
141
          , loop(sched.get_run_loop())
×
142
        {
×
143
            this->set_on_completed([this]() { loop.finish(); });
×
144
        }
×
145

146
        hpx::util::unused_type* get_result_void(
×
147
            error_code& ec = throws) override
148
        {
149
            execute_deferred(ec);
×
150
            return this->base_type::get_result_void(ec);
×
151
        }
152

153
        void execute_deferred(error_code& = throws) override
×
154
        {
155
            loop.run();
×
156
        }
×
157
    };
158

159
    ///////////////////////////////////////////////////////////////////////
160
    template <typename Sender, typename Allocator>
161
    auto make_future(Sender&& sender, Allocator const& allocator)
178✔
162
    {
163
        using allocator_type = Allocator;
164

165
        using value_types =
166
            hpx::execution::experimental::value_types_of_t<std::decay_t<Sender>,
167
                hpx::execution::experimental::empty_env, meta::pack,
168
                meta::pack>;
169

170
        using result_type = std::decay_t<detail::single_result_t<value_types>>;
171
        using operation_state_type =
172
            hpx::util::invoke_result_t<hpx::execution::experimental::connect_t,
173
                Sender, detail::future_receiver<result_type>>;
174

175
        using shared_state =
176
            future_data<result_type, allocator_type, operation_state_type>;
177
        using init_no_addref = typename shared_state::init_no_addref;
178
        using other_allocator = typename std::allocator_traits<
179
            allocator_type>::template rebind_alloc<shared_state>;
180
        using allocator_traits = std::allocator_traits<other_allocator>;
181
        using unique_ptr = std::unique_ptr<shared_state,
182
            util::allocator_deleter<other_allocator>>;
183

184
        other_allocator alloc(allocator);
178✔
185
        unique_ptr p(allocator_traits::allocate(alloc, 1),
178✔
186
            hpx::util::allocator_deleter<other_allocator>{alloc});
178✔
187

188
        allocator_traits::construct(alloc, p.get(), init_no_addref{}, alloc,
356✔
189
            HPX_FORWARD(Sender, sender));
178✔
190

191
        return hpx::traits::future_access<future<result_type>>::create(
178✔
192
            p.release(), false);
178✔
193
    }
178✔
194

195
    ///////////////////////////////////////////////////////////////////////
196
    template <typename Sender, typename Allocator>
197
    auto make_future_with_run_loop(
×
198
        hpx::execution::experimental::run_loop_scheduler const& sched,
199
        Sender&& sender, Allocator const& allocator)
200
    {
201
        using allocator_type = Allocator;
202

203
        using value_types =
204
            hpx::execution::experimental::value_types_of_t<std::decay_t<Sender>,
205
                hpx::execution::experimental::empty_env, meta::pack,
206
                meta::pack>;
207

208
        using result_type = std::decay_t<detail::single_result_t<value_types>>;
209
        using operation_state_type =
210
            hpx::util::invoke_result_t<hpx::execution::experimental::connect_t,
211
                Sender, detail::future_receiver<result_type>>;
212

213
        using shared_state = future_data_with_run_loop<result_type,
214
            allocator_type, operation_state_type>;
215
        using init_no_addref = typename shared_state::init_no_addref;
216
        using other_allocator = typename std::allocator_traits<
217
            allocator_type>::template rebind_alloc<shared_state>;
218
        using allocator_traits = std::allocator_traits<other_allocator>;
219
        using unique_ptr = std::unique_ptr<shared_state,
220
            util::allocator_deleter<other_allocator>>;
221

222
        other_allocator alloc(allocator);
×
223
        unique_ptr p(allocator_traits::allocate(alloc, 1),
×
224
            hpx::util::allocator_deleter<other_allocator>{alloc});
×
225

226
        allocator_traits::construct(alloc, p.get(), init_no_addref{}, alloc,
×
227
            sched, HPX_FORWARD(Sender, sender));
×
228

229
        return hpx::traits::future_access<future<result_type>>::create(
×
230
            p.release(), false);
×
231
    }
×
232
}}    // namespace hpx::execution::experimental::detail
233

234
namespace hpx { namespace traits { namespace detail {
235

236
    template <typename T, typename Allocator, typename OperationState,
237
        typename NewAllocator>
238
    struct shared_state_allocator<hpx::execution::experimental::detail::
239
                                      future_data<T, Allocator, OperationState>,
240
        NewAllocator>
241
    {
242
        using type = hpx::execution::experimental::detail::future_data<T,
243
            NewAllocator, OperationState>;
244
    };
245

246
    template <typename T, typename Allocator, typename OperationState,
247
        typename NewAllocator>
248
    struct shared_state_allocator<
249
        hpx::execution::experimental::detail::future_data_with_run_loop<T,
250
            Allocator, OperationState>,
251
        NewAllocator>
252
    {
253
        using type =
254
            hpx::execution::experimental::detail::future_data_with_run_loop<T,
255
                NewAllocator, OperationState>;
256
    };
257
}}}    // namespace hpx::traits::detail
258

259
namespace hpx::execution::experimental {
260

261
    ///////////////////////////////////////////////////////////////////////////
262
    // execution::make_future is a sender consumer that submits the work
263
    // described by the provided sender for execution, similarly to
264
    // ensure_started, except that it returns a future that provides an optional
265
    // tuple of values that were sent by the provided sender on its completion
266
    // of work.
267
    //
268
    // Where 4.20.1 execution::schedule and 4.20.3 execution::transfer_just are
269
    // meant to enter the domain of senders, make_future is meant to exit the
270
    // domain of senders, retrieving the result of the task graph.
271
    //
272
    // If the provided sender sends an error instead of values, make_future
273
    // stores that error as an exception in the future, or the original
274
    // exception if the error is of type std::exception_ptr.
275
    //
276
    // If the provided sender sends the "stopped" signal instead of values,
277
    // make_future calls std::terminate.
278
    //
279
    inline constexpr struct make_future_t final
280
      : hpx::functional::detail::tag_priority<make_future_t>
281
    {
282
    private:
283
        // clang-format off
284
        template <typename Sender,
285
            typename Allocator = hpx::util::internal_allocator<>,
286
            HPX_CONCEPT_REQUIRES_(
287
                is_sender_v<Sender> &&
288
                hpx::traits::is_allocator_v<Allocator> &&
289
                experimental::detail::is_completion_scheduler_tag_invocable_v<
290
                    hpx::execution::experimental::set_value_t,
291
                    Sender, make_future_t, Allocator
292
                >
293
            )>
294
        // clang-format on
295
        friend constexpr HPX_FORCEINLINE auto tag_override_invoke(make_future_t,
×
296
            Sender&& sender, Allocator const& allocator = Allocator{})
297
        {
298
            auto scheduler =
299
                hpx::execution::experimental::get_completion_scheduler<
×
300
                    hpx::execution::experimental::set_value_t>(sender);
×
301

302
            return hpx::functional::tag_invoke(make_future_t{},
×
303
                HPX_MOVE(scheduler), HPX_FORWARD(Sender, sender), allocator);
×
304
        }
305

306
        // clang-format off
307
        template <typename Sender,
308
            typename Allocator = hpx::util::internal_allocator<>,
309
            HPX_CONCEPT_REQUIRES_(
310
                hpx::execution::experimental::is_sender_v<Sender>
311
            )>
312
        // clang-format on
313
        friend auto tag_invoke(make_future_t,
×
314
            hpx::execution::experimental::run_loop_scheduler const& sched,
315
            Sender&& sender, Allocator const& allocator = Allocator{})
316
        {
317
            return detail::make_future_with_run_loop(
×
318
                sched, HPX_FORWARD(Sender, sender), allocator);
×
319
        }
320

321
        // clang-format off
322
        template <typename Sender,
323
            typename Allocator = hpx::util::internal_allocator<>,
324
            HPX_CONCEPT_REQUIRES_(
325
                is_sender_v<Sender> &&
326
                hpx::traits::is_allocator_v<Allocator>
327
            )>
328
        // clang-format on
329
        friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke(make_future_t,
178✔
330
            Sender&& sender, Allocator const& allocator = Allocator{})
331
        {
332
            return detail::make_future(HPX_FORWARD(Sender, sender), allocator);
178✔
333
        }
334

335
        // clang-format off
336
        template <typename Scheduler,
337
            typename Allocator = hpx::util::internal_allocator<>,
338
            HPX_CONCEPT_REQUIRES_(
339
                hpx::execution::experimental::is_scheduler_v<Scheduler> &&
340
                hpx::traits::is_allocator_v<Allocator>
341
            )>
342
        // clang-format on
343
        friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke(make_future_t,
×
344
            Scheduler&& scheduler, Allocator const& allocator = Allocator{})
345
        {
346
            return hpx::execution::experimental::detail::inject_scheduler<
×
347
                make_future_t, Scheduler, Allocator>{
348
                HPX_FORWARD(Scheduler, scheduler), allocator};
×
349
        }
350

351
        // clang-format off
352
        template <typename Allocator = hpx::util::internal_allocator<>,
353
            HPX_CONCEPT_REQUIRES_(
354
                hpx::traits::is_allocator_v<Allocator>
355
            )>
356
        // clang-format on
357
        friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke(
2✔
358
            make_future_t, Allocator const& allocator = Allocator{})
359
        {
360
            return detail::partial_algorithm<make_future_t, Allocator>{
2✔
361
                allocator};
2✔
362
        }
363
    } make_future{};
364
}    // namespace hpx::execution::experimental
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