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

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

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

90.38
/libs/core/async_combinators/include/hpx/async_combinators/wait_all.hpp
1
//  Copyright (c) 2007-2025 Hartmut Kaiser
2
//  Copyright (c) 2013 Agustin Berge
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
/// \file wait_all.hpp
9
/// \page hpx::wait_all
10
/// \headerfile hpx/future.hpp
11

12
#pragma once
13

14
#if defined(DOXYGEN)
15
namespace hpx {
16
    /// The function \a wait_all is an operator allowing to join on the result
17
    /// of all given futures. It AND-composes all future objects given and
18
    /// returns after they finished executing.
19
    ///
20
    /// \param first    The iterator pointing to the first element of a
21
    ///                 sequence of \a future or \a shared_future objects for
22
    ///                 which \a wait_all should wait.
23
    /// \param last     The iterator pointing to the last element of a
24
    ///                 sequence of \a future or \a shared_future objects for
25
    ///                 which \a wait_all should wait.
26
    ///
27
    /// \note The function \a wait_all returns after all futures have become
28
    ///       ready. All input futures are still valid after \a wait_all
29
    ///       returns.
30
    ///
31
    /// \note           The function wait_all will rethrow any exceptions
32
    ///                 captured by the futures while becoming ready. If this
33
    ///                 behavior is undesirable, use \a wait_all_nothrow
34
    ///                 instead.
35
    ///
36
    template <typename InputIter>
37
    void wait_all(InputIter first, InputIter last);
38

39
    /// The function \a wait_all is an operator allowing to join on the result
40
    /// of all given futures. It AND-composes all future objects given and
41
    /// returns after they finished executing.
42
    ///
43
    /// \param futures  A vector or array holding an arbitrary amount of
44
    ///                 \a future or \a shared_future objects for which
45
    ///                 \a wait_all should wait.
46
    ///
47
    /// \note The function \a wait_all returns after all futures have become
48
    ///       ready. All input futures are still valid after \a wait_all
49
    ///       returns.
50
    ///
51
    /// \note           The function wait_all will rethrow any exceptions
52
    ///                 captured by the futures while becoming ready. If this
53
    ///                 behavior is undesirable, use \a wait_all_nothrow
54
    ///                 instead.
55
    ///
56
    template <typename R>
57
    void wait_all(std::vector<future<R>>&& futures);
58

59
    /// The function \a wait_all is an operator allowing to join on the result
60
    /// of all given futures. It AND-composes all future objects given and
61
    /// returns after they finished executing.
62
    ///
63
    /// \param futures  A vector or array holding an arbitrary amount of
64
    ///                 \a future or \a shared_future objects for which
65
    ///                 \a wait_all should wait.
66
    ///
67
    /// \note The function \a wait_all returns after all futures have become
68
    ///       ready. All input futures are still valid after \a wait_all
69
    ///       returns.
70
    ///
71
    /// \note           The function wait_all will rethrow any exceptions
72
    ///                 captured by the futures while becoming ready. If this
73
    ///                 behavior is undesirable, use \a wait_all_nothrow
74
    ///                 instead.
75
    ///
76
    template <typename R, std::size_t N>
77
    void wait_all(std::array<future<R>, N>&& futures);
78

79
    /// The function \a wait_all is an operator allowing to join on the result
80
    /// of all given futures. It AND-composes all future objects given and
81
    /// returns after they finished executing.
82
    ///
83
    /// \param f        A \a future or \a shared_future for which
84
    ///                 \a wait_all should wait.
85
    ///
86
    /// \note The function \a wait_all returns after the future has become
87
    ///       ready. The input future is still valid after \a wait_all
88
    ///       returns.
89
    ///
90
    /// \note           The function wait_all will rethrow any exceptions
91
    ///                 captured by the future while becoming ready. If this
92
    ///                 behavior is undesirable, use \a wait_all_nothrow
93
    ///                 instead.
94
    ///
95
    template <typename T>
96
    void wait_all(hpx::future<T> const& f);
97

98
    /// The function \a wait_all is an operator allowing to join on the result
99
    /// of all given futures. It AND-composes all future objects given and
100
    /// returns after they finished executing.
101
    ///
102
    /// \param futures  An arbitrary number of \a future or \a shared_future
103
    ///                 objects, possibly holding different types for which
104
    ///                 \a wait_all should wait.
105
    ///
106
    /// \note The function \a wait_all returns after all futures have become
107
    ///       ready. All input futures are still valid after \a wait_all
108
    ///       returns.
109
    ///
110
    /// \note           The function wait_all will rethrow any exceptions
111
    ///                 captured by the futures while becoming ready. If this
112
    ///                 behavior is undesirable, use \a wait_all_nothrow
113
    ///                 instead.
114
    ///
115
    template <typename... T>
116
    void wait_all(T&&... futures);
117

118
    /// The function \a wait_all_n is an operator allowing to join on the result
119
    /// of all given futures. It AND-composes all future objects given and
120
    /// returns after they finished executing.
121
    ///
122
    /// \param begin    The iterator pointing to the first element of a
123
    ///                 sequence of \a future or \a shared_future objects for
124
    ///                 which \a wait_all_n should wait.
125
    /// \param count    The number of elements in the sequence starting at
126
    ///                 \a first.
127
    ///
128
    /// \return         The function \a wait_all_n will return an iterator
129
    ///                 referring to the first element in the input sequence
130
    ///                 after the last processed element.
131
    ///
132
    /// \note The function \a wait_all_n returns after all futures have become
133
    ///       ready. All input futures are still valid after \a wait_all_n
134
    ///       returns.
135
    ///
136
    /// \note           The function wait_all_n will rethrow any exceptions
137
    ///                 captured by the futures while becoming ready. If this
138
    ///                 behavior is undesirable, use \a wait_all_n_nothrow
139
    ///                 instead.
140
    ///
141
    template <typename InputIter>
142
    void wait_all_n(InputIter begin, std::size_t count);
143
}    // namespace hpx
144

145
#else    // DOXYGEN
146

147
#include <hpx/config.hpp>
148
#include <hpx/async_combinators/detail/throw_if_exceptional.hpp>
149
#include <hpx/modules/datastructures.hpp>
150
#include <hpx/modules/futures.hpp>
151
#include <hpx/modules/iterator_support.hpp>
152
#include <hpx/modules/memory.hpp>
153
#include <hpx/modules/tag_invoke.hpp>
154
#include <hpx/modules/type_support.hpp>
155

156
#include <algorithm>
157
#include <array>
158
#include <cstddef>
159
#include <functional>
160
#include <iterator>
161
#include <type_traits>
162
#include <utility>
163
#include <vector>
164

165
///////////////////////////////////////////////////////////////////////////////
166
#if !defined(HPX_INTEL_VERSION)
167
#define HPX_WAIT_ALL_FORCEINLINE HPX_FORCEINLINE
168
#else
169
#define HPX_WAIT_ALL_FORCEINLINE
170
#endif
171

172
namespace hpx::detail {
173

174
    ///////////////////////////////////////////////////////////////////////
175
    template <typename Future, typename Enable = void>
176
    struct is_future_or_shared_state : traits::is_future<Future>
177
    {
178
    };
179

180
    template <typename R>
181
    struct is_future_or_shared_state<
182
        hpx::intrusive_ptr<hpx::lcos::detail::future_data_base<R>>>
183
      : std::true_type
184
    {
185
    };
186

187
    template <typename R>
188
    struct is_future_or_shared_state<std::reference_wrapper<R>>
189
      : is_future_or_shared_state<R>
190
    {
191
    };
192

193
    template <typename R>
194
    inline constexpr bool is_future_or_shared_state_v =
195
        is_future_or_shared_state<R>::value;
196

197
    ///////////////////////////////////////////////////////////////////////
198
    template <typename Range, typename Enable = void>
199
    struct is_future_or_shared_state_range : std::false_type
200
    {
201
    };
202

203
    template <typename T>
204
    struct is_future_or_shared_state_range<std::vector<T>>
205
      : is_future_or_shared_state<T>
206
    {
207
    };
208

209
    template <typename T, std::size_t N>
210
    struct is_future_or_shared_state_range<std::array<T, N>>
211
      : is_future_or_shared_state<T>
212
    {
213
    };
214

215
    template <typename R>
216
    inline constexpr bool is_future_or_shared_state_range_v =
217
        is_future_or_shared_state_range<R>::value;
218

219
    ///////////////////////////////////////////////////////////////////////
220
    template <typename Future, typename Enable = void>
221
    struct future_or_shared_state_result;
222

223
    template <typename Future>
224
    struct future_or_shared_state_result<Future,
225
        std::enable_if_t<hpx::traits::is_future_v<Future>>>
226
      : hpx::traits::future_traits<Future>
227
    {
228
    };
229

230
    template <typename R>
231
    struct future_or_shared_state_result<
232
        hpx::intrusive_ptr<hpx::lcos::detail::future_data_base<R>>>
233
    {
234
        using type = R;
235
    };
236

237
    template <typename R>
238
    using future_or_shared_state_result_t =
239
        typename future_or_shared_state_result<R>::type;
240

241
    ///////////////////////////////////////////////////////////////////////
242
    template <typename Tuple>
243
    struct wait_all_frame    //-V690
244
      : hpx::lcos::detail::future_data<void>
245
    {
246
    private:
247
        using base_type = hpx::lcos::detail::future_data<void>;
248
        using init_no_addref = typename base_type::init_no_addref;
249

250
        wait_all_frame(wait_all_frame const&) = delete;
251
        wait_all_frame(wait_all_frame&&) = delete;
252

253
        wait_all_frame& operator=(wait_all_frame const&) = delete;
254
        wait_all_frame& operator=(wait_all_frame&&) = delete;
255

256
        template <std::size_t I>
257
        struct is_end
258
          : std::integral_constant<bool, hpx::tuple_size<Tuple>::value == I>
259
        {
260
        };
261

262
        template <std::size_t I>
263
        static constexpr bool is_end_v = is_end<I>::value;
264

265
    public:
266
        explicit wait_all_frame(Tuple const& t) noexcept
267
          : base_type(init_no_addref{})
268
          , t_(t)
269
        {
270
        }
118✔
271

272
    protected:
118✔
273
        // Current element is a range (vector or array) of futures
274
        template <std::size_t I, typename Iter>
275
        void await_range(Iter&& next, Iter&& end)
276
        {
277
            hpx::intrusive_ptr<wait_all_frame> this_(this);
278
            for (/**/; next != end; ++next)
279
            {
252✔
280
                auto next_future_data =
281
                    hpx::traits::detail::get_shared_state(*next);
282

426✔
283
                if (next_future_data)
284
                {
285
                    if (!next_future_data->is_ready(std::memory_order_relaxed))
286
                    {
287
                        next_future_data->execute_deferred();
318✔
288

289
                        // execute_deferred might have made the future ready
318✔
290
                        if (!next_future_data->is_ready(
291
                                std::memory_order_relaxed))
145✔
292
                        {
293
                            // Attach a continuation to this future which will
294
                            // re-evaluate it and continue to the next element
145✔
295
                            // in the sequence (if any).
296
                            next_future_data->set_on_completed(
297
                                [this_ = HPX_MOVE(this_),
298
                                    next = HPX_FORWARD(Iter, next),
299
                                    end = HPX_FORWARD(
300
                                        Iter, end)]() mutable -> void {
144✔
301
                                    this_->template await_range<I>(
410✔
302
                                        HPX_MOVE(next), HPX_MOVE(end));
303
                                });
304

305
                            // explicitly destruct iterators as those might
146✔
306
                            // become dangling after we make ourselves ready
146✔
307
                            next = std::decay_t<Iter>{};
308
                            end = std::decay_t<Iter>{};
309

310
                            return;
311
                        }
144✔
312
                    }
144✔
313

314
                    // check whether the current future is exceptional
315
                    if (!has_exceptional_results_ &&
316
                        next_future_data->has_exception())
317
                    {
318
                        has_exceptional_results_ = true;
319
                    }
174✔
320
                }
321
            }
322

×
323
            // explicitly destruct iterators as those might become dangling
324
            // after we make ourselves ready
325
            next = std::decay_t<Iter>{};
326
            end = std::decay_t<Iter>{};
327

328
            // All elements of the sequence are ready now, proceed to the next
329
            // argument.
108✔
330
            do_await<I + 1>();
108✔
331
        }
332

333
        template <std::size_t I>
334
        HPX_FORCEINLINE void await_range()
335
        {
336
            await_range<I>(
337
                hpx::util::begin(hpx::util::unwrap_ref(hpx::get<I>(t_))),
338
                hpx::util::end(hpx::util::unwrap_ref(hpx::get<I>(t_))));
339
        }
340

110✔
341
        // Current element is a simple future
110✔
342
        template <std::size_t I>
220✔
343
        HPX_FORCEINLINE void await_future()
344
        {
345
            hpx::intrusive_ptr<wait_all_frame> this_(this);
346
            auto next_future_data =
347
                hpx::traits::detail::get_shared_state(hpx::get<I>(t_));
348

349
            if (next_future_data)
350
            {
351
                if (!next_future_data->is_ready(std::memory_order_relaxed))
22✔
352
                {
353
                    next_future_data->execute_deferred();
22✔
354

355
                    // execute_deferred might have made the future ready
22✔
356
                    if (!next_future_data->is_ready(std::memory_order_relaxed))
357
                    {
10✔
358
                        // Attach a continuation to this future which will
359
                        // re-evaluate it and continue to the next argument (if
360
                        // any).
10✔
361
                        next_future_data->set_on_completed(
362
                            [this_ = HPX_MOVE(this_)]() -> void {
363
                                this_->template await_future<I>();
364
                            });
365

10✔
366
                        return;
16✔
367
                    }
368
                }
369

370
                // check whether the current future is exceptional
371
                if (!has_exceptional_results_ &&
372
                    next_future_data->has_exception())
373
                {
374
                    has_exceptional_results_ = true;
375
                }
12✔
376
            }
377

378
            do_await<I + 1>();
×
379
        }
380

381
        template <std::size_t I>
382
        HPX_FORCEINLINE void do_await()
383
        {
384
            // Check if end of the tuple is reached
385
            if constexpr (is_end_v<I>)
386
            {
387
                // simply make ourself ready
388
                this->set_data(util::unused);
389
            }
390
            else
391
            {
392
                using future_type = hpx::util::decay_unwrap_t<
118✔
393
                    typename hpx::tuple_element<I, Tuple>::type>;
394

395
                if constexpr (is_future_or_shared_state_v<future_type>)
396
                {
397
                    await_future<I>();
398
                }
399
                else
400
                {
401
                    static_assert(
402
                        is_future_or_shared_state_range_v<future_type>,
403
                        "element must be future or range of futures");
404
                    await_range<I>();
405
                }
406
            }
407
        }
408

409
    public:
410
        bool wait_all()
411
        {
118✔
412
            do_await<0>();
413

414
            // If there are still futures which are not ready, suspend
115✔
415
            // and wait.
416
            if (!this->is_ready(std::memory_order_relaxed))
417
            {
418
                this->wait();
419
            }
420

115✔
421
            // return whether at least one of the futures has become
422
            // exceptional
114✔
423
            return has_exceptional_results_;
424
        }
425

426
    private:
427
        Tuple const& t_;
115✔
428
        bool has_exceptional_results_ = false;
429
    };
430
}    // namespace hpx::detail
431

432
namespace hpx {
433

434
    ///////////////////////////////////////////////////////////////////////////
435
    HPX_CXX_EXPORT inline constexpr struct wait_all_nothrow_t final
436
      : hpx::functional::tag<wait_all_nothrow_t>
437
    {
438
    private:
439
        template <typename Future>
440
        static bool wait_all_nothrow_impl(std::vector<Future> const& values)
441
        {
442
            if (!values.empty())
443
            {
444
                using result_type = hpx::tuple<std::vector<Future> const&>;
209✔
445
                using frame_type = hpx::detail::wait_all_frame<result_type>;
446

209✔
447
                result_type data(values);
448

449
                // frame is initialized with initial reference count
450
                hpx::intrusive_ptr<frame_type> frame(
451
                    new frame_type(data), false);
452
                return frame->wait_all();
453
            }
454
            return false;
455
        }
108✔
456

108✔
457
        template <typename Future>
458
        friend bool tag_invoke(
459
            wait_all_nothrow_t, std::vector<Future> const& values)
460
        {
461
            return wait_all_nothrow_t::wait_all_nothrow_impl(values);
462
        }
93✔
463

464
        template <typename Future>
465
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
207✔
466
            wait_all_nothrow_t, std::vector<Future>& values)
467
        {
468
            return wait_all_nothrow_t::wait_all_nothrow_impl(
469
                const_cast<std::vector<Future> const&>(values));
470
        }
471

472
        template <typename Future>
3✔
473
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
474
            wait_all_nothrow_t, std::vector<Future>&& values)
475
        {
476
            return wait_all_nothrow_t::wait_all_nothrow_impl(
477
                const_cast<std::vector<Future> const&>(values));
478
        }
479

480
        template <typename Future, std::size_t N>
481
        static bool wait_all_nothrow_impl(std::array<Future, N> const& values)
482
        {
483
            using result_type = hpx::tuple<std::array<Future, N> const&>;
484
            using frame_type = hpx::detail::wait_all_frame<result_type>;
485

486
            result_type data(values);
487

488
            // frame is initialized with initial reference count
489
            hpx::intrusive_ptr<frame_type> frame(new frame_type(data), false);
490
            return frame->wait_all();
491
        }
492

493
        template <typename Future, std::size_t N>
494
        friend bool tag_invoke(
495
            wait_all_nothrow_t, std::array<Future, N> const& values)
496
        {
497
            return wait_all_nothrow_t::wait_all_nothrow_impl(values);
498
        }
499

500
        template <typename Future, std::size_t N>
501
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
502
            wait_all_nothrow_t, std::array<Future, N>& values)
503
        {
504
            return wait_all_nothrow_t::wait_all_nothrow_impl(
505
                const_cast<std::array<Future, N> const&>(values));
506
        }
507

508
        template <typename Iterator,
509
            typename Enable =
510
                std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
511
        friend bool tag_invoke(wait_all_nothrow_t, Iterator begin, Iterator end)
512
        {
513
            if (begin == end)
514
            {
515
                return false;
516
            }
517

518
            auto values = traits::acquire_shared_state<Iterator>()(begin, end);
519
            return wait_all_nothrow_t::wait_all_nothrow_impl(values);
520
        }
521

522
        friend HPX_WAIT_ALL_FORCEINLINE constexpr bool tag_invoke(
523
            wait_all_nothrow_t) noexcept
524
        {
525
            return false;
526
        }
527

528
        template <typename... Ts>
529
        friend bool tag_invoke(wait_all_nothrow_t, Ts&&... ts)
530
        {
531
            if constexpr (sizeof...(Ts) != 0)
532
            {
533
                using result_type =
8✔
534
                    hpx::tuple<traits::detail::shared_state_ptr_for_t<Ts>...>;
535
                using frame_type = detail::wait_all_frame<result_type>;
536

537
                result_type values =
538
                    result_type(hpx::traits::detail::get_shared_state(ts)...);
539

540
                // frame is initialized with initial reference count
541
                hpx::intrusive_ptr<frame_type> frame(
542
                    new frame_type(values), false);
543
                return frame->wait_all();
544
            }
545
            else
546
            {
8✔
547
                return false;
8✔
548
            }
549
        }
550

551
        template <typename T>
552
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
553
            wait_all_nothrow_t, hpx::future<T> const& f)
554
        {
555
            f.wait();
556
            return f.has_exception();
557
        }
558

559
        template <typename T>
560
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
561
            wait_all_nothrow_t, hpx::shared_future<T> const& f)
562
        {
563
            f.wait();
564
            return f.has_exception();
565
        }
566
    } wait_all_nothrow{};
567

568
    ///////////////////////////////////////////////////////////////////////////
569
    HPX_CXX_EXPORT inline constexpr struct wait_all_t final
570
      : hpx::functional::tag<wait_all_t>
571
    {
572
    private:
573
        template <typename Future>
574
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
575
            wait_all_t, std::vector<Future> const& values)
576
        {
577
            if (hpx::wait_all_nothrow(values))
578
            {
579
                hpx::detail::throw_if_exceptional(values);
580
            }
581
        }
582

583
        template <typename Future>
584
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
585
            wait_all_t, std::vector<Future>& values)
586
        {
587
            if (hpx::wait_all_nothrow(
207✔
588
                    const_cast<std::vector<Future> const&>(values)))
589
            {
590
                hpx::detail::throw_if_exceptional(values);
×
591
            }
592
        }
593

594
        template <typename Future>
595
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
596
            wait_all_t, std::vector<Future>&& values)
597
        {
598
            if (hpx::wait_all_nothrow(
1✔
599
                    const_cast<std::vector<Future> const&>(values)))
600
            {
601
                hpx::detail::throw_if_exceptional(values);
×
602
            }
603
        }
604

605
        template <typename Future, std::size_t N>
606
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
607
            wait_all_t, std::array<Future, N> const& values)
608
        {
609
            if (hpx::wait_all_nothrow(values))
610
            {
611
                hpx::detail::throw_if_exceptional(values);
612
            }
613
        }
614

615
        template <typename Future, std::size_t N>
616
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
617
            wait_all_t, std::array<Future, N>& values)
618
        {
619
            if (hpx::wait_all_nothrow(
620
                    const_cast<std::array<Future, N> const&>(values)))
621
            {
622
                hpx::detail::throw_if_exceptional(values);
623
            }
624
        }
625

626
        template <typename Future, std::size_t N>
627
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
628
            wait_all_t, std::array<Future, N>&& values)
629
        {
630
            if (hpx::wait_all_nothrow(
631
                    const_cast<std::array<Future, N> const&>(values)))
632
            {
633
                hpx::detail::throw_if_exceptional(values);
634
            }
635
        }
636

637
        template <typename Iterator,
638
            typename Enable =
639
                std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
640
        friend void tag_invoke(wait_all_t, Iterator begin, Iterator end)
641
        {
642
            if (begin != end)
643
            {
644
                auto values =
645
                    traits::acquire_shared_state<Iterator>()(begin, end);
646
                if (hpx::wait_all_nothrow(values))
647
                {
648
                    hpx::detail::throw_if_exceptional(values);
649
                }
650
            }
651
        }
652

653
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(wait_all_t) noexcept {}
654

655
        template <typename... Ts>
656
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(wait_all_t, Ts&&... ts)
657
        {
658
            if (hpx::wait_all_nothrow(ts...))
4✔
659
            {
660
                hpx::detail::throw_if_exceptional(HPX_FORWARD(Ts, ts)...);
×
661
            }
662
        }
663

664
        template <typename T>
665
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
666
            wait_all_t, hpx::future<T> const& f)
667
        {
668
            if (hpx::wait_all_nothrow(f))
669
            {
670
                hpx::detail::throw_if_exceptional(f);
671
            }
672
        }
673

674
        template <typename T>
675
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
676
            wait_all_t, hpx::shared_future<T> const& f)
677
        {
678
            if (hpx::wait_all_nothrow(f))
679
            {
680
                hpx::detail::throw_if_exceptional(f);
681
            }
682
        }
683
    } wait_all{};
684

685
    ///////////////////////////////////////////////////////////////////////////
686
    HPX_CXX_EXPORT inline constexpr struct wait_all_n_nothrow_t final
687
      : hpx::functional::tag<wait_all_n_nothrow_t>
688
    {
689
    private:
690
        template <typename Iterator,
691
            typename Enable =
692
                std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
693
        friend bool tag_invoke(
694
            wait_all_n_nothrow_t, Iterator begin, std::size_t count)
695
        {
696
            if (count == 0)
697
            {
698
                return false;
699
            }
700

701
            auto values =
702
                traits::acquire_shared_state<Iterator>()(begin, count);
703
            return hpx::wait_all_nothrow(values);
704
        }
705
    } wait_all_n_nothrow{};
706

707
    ///////////////////////////////////////////////////////////////////////////
708
    HPX_CXX_EXPORT inline constexpr struct wait_all_n_t final
709
      : hpx::functional::tag<wait_all_n_t>
710
    {
711
    private:
712
        template <typename Iterator,
713
            typename Enable =
714
                std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
715
        friend void tag_invoke(wait_all_n_t, Iterator begin, std::size_t count)
716
        {
717
            if (count != 0)
718
            {
719
                auto values =
720
                    traits::acquire_shared_state<Iterator>()(begin, count);
721
                if (hpx::wait_all_nothrow(values))
722
                {
723
                    hpx::detail::throw_if_exceptional(values);
724
                }
725
            }
726
        }
727
    } wait_all_n{};
728
}    // namespace hpx
729

730
#undef HPX_WAIT_ALL_FORCEINLINE
731

732
#endif    // DOXYGEN
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

© 2026 Coveralls, Inc