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

STEllAR-GROUP / hpx / #872

24 Jan 2023 07:29PM CUT coverage: 85.694% (-0.9%) from 86.624%
#872

push

StellarBot
Merge #6148

6148: Investigate the failure of the LCI parcelport. r=hkaiser a=JiakunYan



Co-authored-by: Jiakun Yan <jiakunyan1998@gmail.com>

173076 of 201969 relevant lines covered (85.69%)

2110584.0 hits per line

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

71.08
/libs/core/execution/include/hpx/execution/algorithms/let_value.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/config.hpp>
11
#include <hpx/assert.hpp>
12
#include <hpx/concepts/concepts.hpp>
13
#include <hpx/datastructures/tuple.hpp>
14
#include <hpx/datastructures/variant.hpp>
15
#include <hpx/errors/try_catch_exception_ptr.hpp>
16
#include <hpx/execution/algorithms/detail/inject_scheduler.hpp>
17
#include <hpx/execution/algorithms/detail/partial_algorithm.hpp>
18
#include <hpx/execution/algorithms/run_loop.hpp>
19
#include <hpx/execution_base/completion_scheduler.hpp>
20
#include <hpx/execution_base/completion_signatures.hpp>
21
#include <hpx/execution_base/get_env.hpp>
22
#include <hpx/execution_base/receiver.hpp>
23
#include <hpx/execution_base/sender.hpp>
24
#include <hpx/functional/detail/tag_priority_invoke.hpp>
25
#include <hpx/functional/invoke_fused.hpp>
26
#include <hpx/functional/invoke_result.hpp>
27
#include <hpx/type_support/detail/with_result_of.hpp>
28
#include <hpx/type_support/meta.hpp>
29
#include <hpx/type_support/pack.hpp>
30

31
#include <exception>
32
#include <tuple>
33
#include <type_traits>
34
#include <utility>
35

36
namespace hpx::execution::experimental {
37

38
    namespace detail {
39

40
        template <typename PredecessorSender, typename F,
41
            typename Scheduler = no_scheduler>
42
        struct let_value_sender
43
        {
44
            using predecessor_sender_t = std::decay_t<PredecessorSender>;
45

46
            HPX_NO_UNIQUE_ADDRESS predecessor_sender_t predecessor_sender;
47
            HPX_NO_UNIQUE_ADDRESS std::decay_t<F> f;
48
            HPX_NO_UNIQUE_ADDRESS std::decay_t<Scheduler> scheduler;
49

50
            template <typename Env = empty_env>
51
            struct generate_completion_signatures
52
            {
53
                template <typename Pack, typename Enable = void>
54
                struct successor_sender_types_helper;
55

56
                template <typename... Ts>
57
                struct successor_sender_types_helper<meta::pack<Ts...>,
58
                    std::enable_if_t<hpx::is_invocable_v<F,
59
                        std::add_lvalue_reference_t<std::decay_t<Ts>>...>>>
60
                {
61
                    using type = hpx::util::invoke_result_t<F,
62
                        std::add_lvalue_reference_t<std::decay_t<Ts>>...>;
63

64
                    static_assert(is_sender_v<type>,
65
                        "let_value expects the invocable sender factory to "
66
                        "return a sender");
67
                };
68

69
                template <typename... Ts>
70
                using successor_sender_types = meta::type<
71
                    successor_sender_types_helper<meta::pack<Ts...>>>;
72

73
                // Type of the potential values returned from the predecessor
74
                // sender
75
                template <template <typename...> class Tuple = meta::pack,
76
                    template <typename...> class Variant = meta::pack>
77
                using predecessor_value_types =
78
                    value_types_of_t<predecessor_sender_t, Env, Tuple, Variant>;
79

80
                // Types of the potential senders returned from the sender
81
                // factory F
82

83
                // clang-format off
84
                template <template <typename...> typename Tuple = meta::pack,
85
                    template <typename...> typename Variant = meta::pack>
86
                using sender_types =
87
                    meta::apply<
88
                        meta::transform<meta::uncurry<
89
                            meta::func<successor_sender_types>>,
90
                            meta::func<Variant>>,
91
                        predecessor_value_types<Tuple>>;
92

93
                template <template <typename...> typename Tuple,
94
                    template <typename...> typename Variant>
95
                using value_types =
96
                    meta::apply<
97
                        meta::transform<
98
                            meta::bind_back<
99
                                meta::defer<detail::value_types_of>, Env,
100
                                meta::func<Tuple>>,
101
                            meta::uncurry<meta::unique<meta::func<Variant>>>>,
102
                        sender_types<>>;
103

104
                template <template <typename...> typename Variant>
105
                using error_types =
106
                    meta::invoke<
107
                        meta::push_back<meta::uncurry<
108
                            meta::unique<meta::func<Variant>>>>,
109
                        meta::apply<
110
                            meta::transform<meta::bind_back<
111
                                meta::defer<detail::error_types_of>, Env>>,
112
                            sender_types<>>,
113
                        meta::pack<std::exception_ptr>>;
114

115
                static constexpr bool sends_stopped = meta::value<
116
                    meta::apply<
117
                        meta::transform<
118
                            meta::bind_back1_func<
119
                                detail::sends_stopped_of, Env>,
120
                            meta::right_fold<
121
                                detail::sends_stopped_of<
122
                                    predecessor_sender_t, Env>,
123
                                meta::func2<meta::or_>>>,
124
                        sender_types<>>>;
125
                // clang-format on
126
            };
127

128
            template <typename Env>
129
            friend auto tag_invoke(get_completion_signatures_t,
130
                let_value_sender const&, Env) noexcept
131
                -> generate_completion_signatures<Env>;
132

133
            // clang-format off
134
            template <typename CPO, typename Scheduler_ = Scheduler,
135
                HPX_CONCEPT_REQUIRES_(
136
                   !hpx::execution::experimental::is_scheduler_v<Scheduler_> &&
137
                    hpx::execution::experimental::detail::is_receiver_cpo_v<CPO> &&
138
                    hpx::execution::experimental::detail::has_completion_scheduler_v<
139
                        CPO, predecessor_sender_t>
140
                )>
141
            // clang-format on
142
            friend constexpr auto tag_invoke(
143
                hpx::execution::experimental::get_completion_scheduler_t<CPO>
144
                    tag,
145
                let_value_sender const& sender)
146
            {
147
                return tag(sender.predecessor_sender);
148
            }
149

150
            // clang-format off
151
            template <typename CPO, typename Scheduler_ = Scheduler,
152
                HPX_CONCEPT_REQUIRES_(
153
                    hpx::execution::experimental::is_scheduler_v<Scheduler_> &&
154
                    hpx::execution::experimental::detail::is_receiver_cpo_v<CPO>
155
                )>
156
            // clang-format on
157
            friend constexpr auto tag_invoke(
×
158
                hpx::execution::experimental::get_completion_scheduler_t<CPO>,
159
                let_value_sender const& sender)
160
            {
161
                return sender.scheduler;
×
162
            }
163

164
            // TODO: add forwarding_sender_query
165

166
            template <typename Receiver>
167
            struct operation_state
37✔
168
            {
169
                struct let_value_predecessor_receiver;
170

171
                // Type of the operation state returned when connecting the
172
                // predecessor sender to the let_value_predecessor_receiver
173
                using predecessor_operation_state_type =
174
                    std::decay_t<connect_result_t<PredecessorSender&&,
175
                        let_value_predecessor_receiver>>;
176

177
                // Type of the potential operation states returned when
178
                // connecting a sender in successor_sender_types to the receiver
179
                // connected to the let_value_sender
180
                template <typename Sender>
181
                struct successor_operation_state_types_helper
182
                {
183
                    using type = connect_result_t<Sender, Receiver>;
184
                };
185

186
                template <template <typename...> class Tuple,
187
                    template <typename...> class Variant>
188
                using successor_operation_state_types =
189
                    hpx::util::detail::transform_t<
190
                        typename generate_completion_signatures<>::
191
                            template sender_types<Tuple, Variant>,
192
                        successor_operation_state_types_helper>;
193

194
                // Operation state from connecting predecessor sender to
195
                // let_value_predecessor_receiver
196
                predecessor_operation_state_type predecessor_op_state;
197

198
                using predecessor_ts_type = hpx::util::detail::prepend_t<
199
                    typename generate_completion_signatures<>::
200
                        template predecessor_value_types<decayed_tuple,
201
                            hpx::variant>,
202
                    hpx::monostate>;
203

204
                // Potential values returned from the predecessor sender
205
                predecessor_ts_type predecessor_ts;
206

207
                // Potential operation states returned when connecting a sender
208
                // in successor_sender_types to the receiver connected to the
209
                // let_value_sender
210
                hpx::util::detail::prepend_t<
211
                    successor_operation_state_types<hpx::tuple, hpx::variant>,
212
                    hpx::monostate>
213
                    successor_op_state;
214

215
                struct let_value_predecessor_receiver
216
                {
217
                    HPX_NO_UNIQUE_ADDRESS std::decay_t<Receiver> receiver;
218
                    HPX_NO_UNIQUE_ADDRESS std::decay_t<F> f;
219
                    operation_state& op_state;
220

221
                    template <typename Receiver_, typename F_>
222
                    let_value_predecessor_receiver(
95✔
223
                        Receiver_&& receiver, F_&& f, operation_state& op_state)
224
                      : receiver(HPX_FORWARD(Receiver_, receiver))
95✔
225
                      , f(HPX_FORWARD(F_, f))
95✔
226
                      , op_state(op_state)
95✔
227
                    {
228
                    }
95✔
229

230
                    template <typename Error>
231
                    friend void tag_invoke(set_error_t,
3✔
232
                        let_value_predecessor_receiver&& r,
233
                        Error&& error) noexcept
234
                    {
235
                        hpx::execution::experimental::set_error(
3✔
236
                            HPX_MOVE(r.receiver), HPX_FORWARD(Error, error));
3✔
237
                    }
3✔
238

239
                    friend void tag_invoke(set_stopped_t,
×
240
                        let_value_predecessor_receiver&& r) noexcept
241
                    {
242
                        hpx::execution::experimental::set_stopped(
×
243
                            HPX_MOVE(r.receiver));
×
244
                    };
×
245

246
                    struct start_visitor
247
                    {
248
                        [[noreturn]] void operator()(hpx::monostate) const
×
249
                        {
250
                            HPX_UNREACHABLE;
×
251
                        }
×
252

253
                        template <typename OperationState_,
254
                            typename = std::enable_if_t<!std::is_same_v<
255
                                std::decay_t<OperationState_>, hpx::monostate>>>
256
                        void operator()(OperationState_& op_state) const
92✔
257
                        {
258
                            hpx::execution::experimental::start(op_state);
92✔
259
                        }
92✔
260
                    };
261

262
                    struct set_value_visitor
263
                    {
264
                        HPX_NO_UNIQUE_ADDRESS std::decay_t<Receiver> receiver;
265
                        HPX_NO_UNIQUE_ADDRESS std::decay_t<F> f;
266
                        operation_state& op_state;
267

268
                        [[noreturn]] void operator()(hpx::monostate) const
×
269
                        {
270
                            HPX_UNREACHABLE;
×
271
                        }
×
272

273
                        template <typename T,
274
                            typename = std::enable_if_t<!std::is_same_v<
275
                                std::decay_t<T>, hpx::monostate>>>
276
                        void operator()(T& t)
92✔
277
                        {
278
                            using operation_state_type =
279
                                decltype(hpx::execution::experimental::connect(
280
                                    hpx::invoke_fused(HPX_MOVE(f), t),
281
                                    std::declval<Receiver>()));
282

283
#if defined(HPX_HAVE_CXX17_COPY_ELISION)
284
                            // with_result_of is used to emplace the operation state
285
                            // returned from connect without any intermediate copy
286
                            // construction (the operation state is not required to be
287
                            // copyable nor movable).
288
                            op_state.successor_op_state
92✔
289
                                .template emplace<operation_state_type>(
92✔
290
                                    hpx::util::detail::with_result_of([&]() {
184✔
291
                                        return hpx::execution::experimental::
92✔
292
                                            connect(hpx::invoke_fused(
92✔
293
                                                        HPX_MOVE(f), t),
92✔
294
                                                HPX_MOVE(receiver));
92✔
295
                                    }));
×
296
#else
297
                            // earlier versions of MSVC don't get copy elision
298
                            // quite right, the operation state must be
299
                            // constructed explicitly directly in place
300
                            op_state.successor_op_state
301
                                .template emplace_f<operation_state_type>(
302
                                    hpx::execution::experimental::connect,
303
                                    hpx::invoke_fused(HPX_MOVE(f), t),
304
                                    HPX_MOVE(receiver));
305
#endif
306
                            hpx::visit(
92✔
307
                                start_visitor{}, op_state.successor_op_state);
92✔
308
                        }
92✔
309
                    };
310

311
                    // This typedef is duplicated from the parent struct. The
312
                    // parent typedef is not instantiated early enough for use
313
                    // here.
314
                    using predecessor_ts_type = hpx::util::detail::prepend_t<
315
                        typename generate_completion_signatures<>::
316
                            template predecessor_value_types<decayed_tuple,
317
                                hpx::variant>,
318
                        hpx::monostate>;
319

320
                    template <typename... Ts>
321
                    void set_value(Ts&&... ts)
92✔
322
                    {
323
                        hpx::detail::try_catch_exception_ptr(
92✔
324
                            [&]() {
184✔
325
                                op_state.predecessor_ts
92✔
326
                                    .template emplace<hpx::tuple<Ts...>>(
92✔
327
                                        HPX_FORWARD(Ts, ts)...);
87✔
328
                                hpx::visit(set_value_visitor{HPX_MOVE(receiver),
368✔
329
                                               HPX_MOVE(f), op_state},
184✔
330
                                    op_state.predecessor_ts);
92✔
331
                            },
92✔
332
                            [&](std::exception_ptr ep) {
92✔
333
                                hpx::execution::experimental::set_error(
×
334
                                    HPX_MOVE(receiver), HPX_MOVE(ep));
×
335
                            });
×
336
                    }
92✔
337

338
                    template <typename... Ts>
339
                    friend auto tag_invoke(set_value_t,
92✔
340
                        let_value_predecessor_receiver&& r, Ts&&... ts) noexcept
341
                        -> decltype(std::declval<predecessor_ts_type>()
342
                                        .template emplace<
343
                                            hpx::tuple<std::decay_t<Ts>...>>(
344
                                            HPX_FORWARD(Ts, ts)...),
345
                            void())
346
                    {
347
                        // set_value is in a member function only because of a
348
                        // compiler bug in GCC 7. When the body of set_value is
349
                        // inlined here compilation fails with an internal
350
                        // compiler error.
351
                        r.set_value(HPX_FORWARD(Ts, ts)...);
92✔
352
                    }
92✔
353

354
                    // Pass through the get_env receiver query
355
                    friend auto tag_invoke(
×
356
                        get_env_t, let_value_predecessor_receiver const& r)
357
                        -> env_of_t<std::decay_t<Receiver>>
358
                    {
359
                        return hpx::execution::experimental::get_env(
×
360
                            r.receiver);
×
361
                    }
362
                };
363

364
                template <typename PredecessorSender_, typename Receiver_,
365
                    typename F_>
366
                operation_state(PredecessorSender_&& predecessor_sender,
95✔
367
                    Receiver_&& receiver, F_&& f)
368
                  : predecessor_op_state{hpx::execution::experimental::connect(
190✔
369
                        HPX_FORWARD(PredecessorSender_, predecessor_sender),
95✔
370
                        let_value_predecessor_receiver(
95✔
371
                            HPX_FORWARD(Receiver_, receiver),
95✔
372
                            HPX_FORWARD(F_, f), *this))}
95✔
373
                {
374
                }
95✔
375

376
                operation_state(operation_state&&) = delete;
377
                operation_state& operator=(operation_state&&) = delete;
378
                operation_state(operation_state const&) = delete;
379
                operation_state& operator=(operation_state const&) = delete;
380

381
                friend void tag_invoke(start_t, operation_state& os) noexcept
95✔
382
                {
383
                    hpx::execution::experimental::start(
95✔
384
                        os.predecessor_op_state);
95✔
385
                }
95✔
386
            };
387

388
            template <typename Receiver>
389
            friend auto tag_invoke(
95✔
390
                connect_t, let_value_sender&& s, Receiver&& receiver)
391
            {
392
                return operation_state<Receiver>(HPX_MOVE(s.predecessor_sender),
190✔
393
                    HPX_FORWARD(Receiver, receiver), HPX_MOVE(s.f));
95✔
394
            }
395
        };
396
    }    // namespace detail
397

398
    // let_value is very similar to then: when it is started, it invokes the
399
    // provided function with the values sent by the input sender as arguments.
400
    // However, where the sender returned from then sends exactly what that
401
    // function ends up returning - let_value requires that the function return
402
    // a sender, and the sender returned by let_value sends the values sent by
403
    // the sender returned from the callback. This is similar to the notion of
404
    // "future unwrapping" in future/promise-based frameworks.
405
    //
406
    // let_value is guaranteed to not begin executing function until the
407
    // returned sender is started.
408
    inline constexpr struct let_value_t final
409
      : hpx::functional::detail::tag_priority<let_value_t>
410
    {
411
    private:
412
        // clang-format off
413
        template <typename PredecessorSender, typename F,
414
            HPX_CONCEPT_REQUIRES_(
415
                hpx::execution::experimental::is_sender_v<PredecessorSender> &&
416
                experimental::detail::is_completion_scheduler_tag_invocable_v<
417
                    hpx::execution::experimental::set_value_t,
418
                    PredecessorSender, let_value_t, F
419
                >
420
            )>
421
        // clang-format on
422
        friend constexpr HPX_FORCEINLINE auto tag_override_invoke(
423
            let_value_t, PredecessorSender&& predecessor_sender, F&& f)
424
        {
425
            auto scheduler =
426
                hpx::execution::experimental::get_completion_scheduler<
427
                    hpx::execution::experimental::set_value_t>(
428
                    predecessor_sender);
429

430
            return hpx::functional::tag_invoke(let_value_t{},
431
                HPX_MOVE(scheduler),
432
                HPX_FORWARD(PredecessorSender, predecessor_sender),
433
                HPX_FORWARD(F, f));
434
        }
435

436
        // clang-format off
437
        template <typename PredecessorSender, typename F,
438
            HPX_CONCEPT_REQUIRES_(
439
                hpx::execution::experimental::is_sender_v<PredecessorSender>
440
            )>
441
        // clang-format on
442
        friend constexpr HPX_FORCEINLINE auto tag_invoke(let_value_t,
×
443
            hpx::execution::experimental::run_loop_scheduler const& sched,
444
            PredecessorSender&& predecessor_sender, F&& f)
445
        {
446
            return detail::let_value_sender<PredecessorSender, F,
×
447
                hpx::execution::experimental::run_loop_scheduler>{
×
448
                HPX_FORWARD(PredecessorSender, predecessor_sender),
×
449
                HPX_FORWARD(F, f), sched};
×
450
        }
451

452
        // clang-format off
453
        template <typename PredecessorSender, typename F,
454
            HPX_CONCEPT_REQUIRES_(
455
                hpx::execution::experimental::is_sender_v<PredecessorSender>
456
            )>
457
        // clang-format on
458
        friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke(
95✔
459
            let_value_t, PredecessorSender&& predecessor_sender, F&& f)
460
        {
461
            return detail::let_value_sender<PredecessorSender, F>{
276✔
462
                HPX_FORWARD(PredecessorSender, predecessor_sender),
95✔
463
                HPX_FORWARD(F, f), detail::no_scheduler{}};
95✔
464
        }
465

466
        // clang-format off
467
        template <typename F, typename Scheduler,
468
            HPX_CONCEPT_REQUIRES_(
469
                hpx::execution::experimental::is_scheduler_v<Scheduler>
470
            )>
471
        // clang-format on
472
        friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke(
473
            let_value_t, Scheduler&& scheduler, F&& f)
474
        {
475
            return hpx::execution::experimental::detail::inject_scheduler<
476
                let_value_t, Scheduler, F>{
477
                HPX_FORWARD(Scheduler, scheduler), HPX_FORWARD(F, f)};
478
        }
479

480
        template <typename F>
481
        friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke(
13✔
482
            let_value_t, F&& f)
483
        {
484
            return detail::partial_algorithm<let_value_t, F>{HPX_FORWARD(F, f)};
13✔
485
        }
486
    } let_value{};
487
}    // 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