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

STEllAR-GROUP / hpx / #871

22 Jan 2023 11:22PM UTC coverage: 86.624% (+0.7%) from 85.97%
#871

push

StellarBot
Merge #6144

6144: General improvements to scheduling and related fixes r=hkaiser a=hkaiser

This is a collection of unrelated improvements applied to different parts of the code

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

152 of 152 new or added lines in 23 files covered. (100.0%)

174953 of 201969 relevant lines covered (86.62%)

1838882.76 hits per line

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

85.81
/libs/core/async_combinators/include/hpx/async_combinators/wait_all.hpp
1
//  Copyright (c) 2007-2023 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

10
#pragma once
11

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

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

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

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

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

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

143
#else    // DOXYGEN
144

145
#include <hpx/config.hpp>
146
#include <hpx/async_combinators/detail/throw_if_exceptional.hpp>
147
#include <hpx/datastructures/tuple.hpp>
148
#include <hpx/functional/tag_invoke.hpp>
149
#include <hpx/futures/detail/future_data.hpp>
150
#include <hpx/futures/traits/acquire_shared_state.hpp>
151
#include <hpx/futures/traits/detail/future_traits.hpp>
152
#include <hpx/futures/traits/future_access.hpp>
153
#include <hpx/futures/traits/is_future.hpp>
154
#include <hpx/iterator_support/range.hpp>
155
#include <hpx/iterator_support/traits/is_iterator.hpp>
156
#include <hpx/modules/memory.hpp>
157
#include <hpx/type_support/decay.hpp>
158
#include <hpx/type_support/unwrap_ref.hpp>
159

160
#include <algorithm>
161
#include <array>
162
#include <cstddef>
163
#include <functional>
164
#include <iterator>
165
#include <type_traits>
166
#include <utility>
167
#include <vector>
168

169
///////////////////////////////////////////////////////////////////////////////
170
#if !defined(HPX_INTEL_VERSION)
171
#define HPX_WAIT_ALL_FORCEINLINE HPX_FORCEINLINE
172
#else
173
#define HPX_WAIT_ALL_FORCEINLINE
174
#endif
175

176
namespace hpx::detail {
177

178
    ///////////////////////////////////////////////////////////////////////
179
    template <typename Future, typename Enable = void>
180
    struct is_future_or_shared_state : traits::is_future<Future>
181
    {
182
    };
183

184
    template <typename R>
185
    struct is_future_or_shared_state<
186
        hpx::intrusive_ptr<hpx::lcos::detail::future_data_base<R>>>
187
      : std::true_type
188
    {
189
    };
190

191
    template <typename R>
192
    struct is_future_or_shared_state<std::reference_wrapper<R>>
193
      : is_future_or_shared_state<R>
194
    {
195
    };
196

197
    template <typename R>
198
    inline constexpr bool is_future_or_shared_state_v =
199
        is_future_or_shared_state<R>::value;
200

201
    ///////////////////////////////////////////////////////////////////////
202
    template <typename Range, typename Enable = void>
203
    struct is_future_or_shared_state_range : std::false_type
204
    {
205
    };
206

207
    template <typename T>
208
    struct is_future_or_shared_state_range<std::vector<T>>
209
      : is_future_or_shared_state<T>
210
    {
211
    };
212

213
    template <typename T, std::size_t N>
214
    struct is_future_or_shared_state_range<std::array<T, N>>
215
      : is_future_or_shared_state<T>
216
    {
217
    };
218

219
    template <typename R>
220
    inline constexpr bool is_future_or_shared_state_range_v =
221
        is_future_or_shared_state_range<R>::value;
222

223
    ///////////////////////////////////////////////////////////////////////
224
    template <typename Future, typename Enable = void>
225
    struct future_or_shared_state_result;
226

227
    template <typename Future>
228
    struct future_or_shared_state_result<Future,
229
        std::enable_if_t<hpx::traits::is_future_v<Future>>>
230
      : hpx::traits::future_traits<Future>
231
    {
232
    };
233

234
    template <typename R>
235
    struct future_or_shared_state_result<
236
        hpx::intrusive_ptr<hpx::lcos::detail::future_data_base<R>>>
237
    {
238
        using type = R;
239
    };
240

241
    template <typename R>
242
    using future_or_shared_state_result_t =
243
        typename future_or_shared_state_result<R>::type;
244

245
    ///////////////////////////////////////////////////////////////////////
246
    template <typename Tuple>
247
    struct wait_all_frame    //-V690
629,248✔
248
      : hpx::lcos::detail::future_data<void>
249
    {
250
    private:
251
        using base_type = hpx::lcos::detail::future_data<void>;
252
        using init_no_addref = typename base_type::init_no_addref;
253

254
        wait_all_frame(wait_all_frame const&) = delete;
255
        wait_all_frame(wait_all_frame&&) = delete;
256

257
        wait_all_frame& operator=(wait_all_frame const&) = delete;
258
        wait_all_frame& operator=(wait_all_frame&&) = delete;
259

260
        template <std::size_t I>
261
        struct is_end
262
          : std::integral_constant<bool, hpx::tuple_size<Tuple>::value == I>
263
        {
264
        };
265

266
        template <std::size_t I>
267
        static constexpr bool is_end_v = is_end<I>::value;
268

269
    public:
270
        explicit wait_all_frame(Tuple const& t) noexcept
314,624✔
271
          : base_type(init_no_addref{})
314,624✔
272
          , t_(t)
314,624✔
273
        {
629,248✔
274
        }
314,624✔
275

276
    protected:
277
        // Current element is a range (vector or array) of futures
278
        template <std::size_t I, typename Iter>
279
        void await_range(Iter&& next, Iter&& end)
464,756✔
280
        {
281
            hpx::intrusive_ptr<wait_all_frame> this_(this);
464,756✔
282
            for (/**/; next != end; ++next)
1,046,851✔
283
            {
284
                auto next_future_data =
285
                    hpx::traits::detail::get_shared_state(*next);
904,191✔
286

287
                if (next_future_data)
904,193✔
288
                {
289
                    if (!next_future_data->is_ready(std::memory_order_relaxed))
904,191✔
290
                    {
291
                        next_future_data->execute_deferred();
322,512✔
292

293
                        // execute_deferred might have made the future ready
294
                        if (!next_future_data->is_ready(
322,512✔
295
                                std::memory_order_relaxed))
296
                        {
297
                            // Attach a continuation to this future which will
298
                            // re-evaluate it and continue to the next element
299
                            // in the sequence (if any).
300
                            next_future_data->set_on_completed(
322,096✔
301
                                [this_ = HPX_MOVE(this_),
2,254,672✔
302
                                    next = HPX_FORWARD(Iter, next),
322,096✔
303
                                    end = HPX_FORWARD(
322,096✔
304
                                        Iter, end)]() mutable -> void {
305
                                    this_->template await_range<I>(
644,192✔
306
                                        HPX_MOVE(next), HPX_MOVE(end));
322,096✔
307
                                });
322,096✔
308

309
                            // explicitly destruct iterators as those might
310
                            // become dangling after we make ourselves ready
311
                            next = std::decay_t<Iter>{};
322,096✔
312
                            end = std::decay_t<Iter>{};
322,096✔
313

314
                            return;
322,096✔
315
                        }
316
                    }
416✔
317

318
                    // check whether the current future is exceptional
319
                    if (!has_exceptional_results_ &&
582,095✔
320
                        next_future_data->has_exception())
578,836✔
321
                    {
322
                        has_exceptional_results_ = true;
385✔
323
                    }
385✔
324
                }
582,093✔
325
            }
904,189✔
326

327
            // explicitly destruct iterators as those might become dangling
328
            // after we make ourselves ready
329
            next = std::decay_t<Iter>{};
142,660✔
330
            end = std::decay_t<Iter>{};
142,660✔
331

332
            // All elements of the sequence are ready now, proceed to the next
333
            // argument.
334
            do_await<I + 1>();
142,660✔
335
        }
464,756✔
336

337
        template <std::size_t I>
338
        HPX_FORCEINLINE void await_range()
142,660✔
339
        {
340
            await_range<I>(
142,660✔
341
                hpx::util::begin(hpx::util::unwrap_ref(hpx::get<I>(t_))),
142,660✔
342
                hpx::util::end(hpx::util::unwrap_ref(hpx::get<I>(t_))));
142,660✔
343
        }
142,660✔
344

345
        // Current element is a simple future
346
        template <std::size_t I>
347
        HPX_FORCEINLINE void await_future()
346,268✔
348
        {
349
            hpx::intrusive_ptr<wait_all_frame> this_(this);
346,268✔
350
            auto next_future_data =
351
                hpx::traits::detail::get_shared_state(hpx::get<I>(t_));
346,268✔
352

353
            if (next_future_data)
346,268✔
354
            {
355
                if (!next_future_data->is_ready(std::memory_order_relaxed))
346,268✔
356
                {
357
                    next_future_data->execute_deferred();
172,856✔
358

359
                    // execute_deferred might have made the future ready
360
                    if (!next_future_data->is_ready(std::memory_order_relaxed))
172,856✔
361
                    {
362
                        // Attach a continuation to this future which will
363
                        // re-evaluate it and continue to the next argument (if
364
                        // any).
365
                        next_future_data->set_on_completed(
172,856✔
366
                            [this_ = HPX_MOVE(this_)]() -> void {
864,280✔
367
                                this_->template await_future<I>();
172,856✔
368
                            });
172,856✔
369

370
                        return;
172,856✔
371
                    }
372
                }
×
373

374
                // check whether the current future is exceptional
375
                if (!has_exceptional_results_ &&
173,412✔
376
                    next_future_data->has_exception())
173,412✔
377
                {
378
                    has_exceptional_results_ = true;
316✔
379
                }
316✔
380
            }
173,412✔
381

382
            do_await<I + 1>();
173,412✔
383
        }
346,268✔
384

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

399
                if constexpr (is_future_or_shared_state_v<future_type>)
400
                {
401
                    await_future<I>();
173,412✔
402
                }
403
                else
404
                {
405
                    static_assert(
406
                        is_future_or_shared_state_range_v<future_type>,
407
                        "element must be future or range of futures");
408
                    await_range<I>();
142,660✔
409
                }
410
            }
411
        }
630,696✔
412

413
    public:
414
        bool wait_all()
314,624✔
415
        {
416
            do_await<0>();
314,624✔
417

418
            // If there are still futures which are not ready, suspend
419
            // and wait.
420
            if (!this->is_ready(std::memory_order_relaxed))
314,624✔
421
            {
422
                this->wait();
312,494✔
423
            }
312,494✔
424

425
            // return whether at least one of the futures has become
426
            // exceptional
427
            return has_exceptional_results_;
314,624✔
428
        }
429

430
    private:
431
        Tuple const& t_;
432
        bool has_exceptional_results_ = false;
314,624✔
433
    };
434
}    // namespace hpx::detail
435

436
namespace hpx {
437

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

451
                result_type data(values);
140,079✔
452

453
                // frame is initialized with initial reference count
454
                hpx::intrusive_ptr<frame_type> frame(
140,079✔
455
                    new frame_type(data), false);
140,079✔
456
                return frame->wait_all();
140,079✔
457
            }
140,079✔
458
            return false;
364✔
459
        }
140,443✔
460

461
        template <typename Future>
462
        friend bool tag_invoke(
6,628✔
463
            wait_all_nothrow_t, std::vector<Future> const& values)
464
        {
465
            return wait_all_nothrow_t::wait_all_nothrow_impl(values);
6,628✔
466
        }
467

468
        template <typename Future>
469
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
133,815✔
470
            wait_all_nothrow_t, std::vector<Future>& values)
471
        {
472
            return wait_all_nothrow_t::wait_all_nothrow_impl(
133,815✔
473
                const_cast<std::vector<Future> const&>(values));
133,815✔
474
        }
475

476
        template <typename Future>
477
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
478
            wait_all_nothrow_t, std::vector<Future>&& values)
479
        {
480
            return wait_all_nothrow_t::wait_all_nothrow_impl(
481
                const_cast<std::vector<Future> const&>(values));
482
        }
483

484
        template <typename Future, std::size_t N>
485
        static bool wait_all_nothrow_impl(std::array<Future, N> const& values)
3✔
486
        {
487
            using result_type = hpx::tuple<std::array<Future, N> const&>;
488
            using frame_type = hpx::detail::wait_all_frame<result_type>;
489

490
            result_type data(values);
3✔
491

492
            // frame is initialized with initial reference count
493
            hpx::intrusive_ptr<frame_type> frame(new frame_type(data), false);
3✔
494
            return frame->wait_all();
3✔
495
        }
3✔
496

497
        template <typename Future, std::size_t N>
498
        friend bool tag_invoke(
1✔
499
            wait_all_nothrow_t, std::array<Future, N> const& values)
500
        {
501
            return wait_all_nothrow_t::wait_all_nothrow_impl(values);
1✔
502
        }
503

504
        template <typename Future, std::size_t N>
505
        friend HPX_WAIT_ALL_FORCEINLINE bool tag_invoke(
2✔
506
            wait_all_nothrow_t, std::array<Future, N>& values)
507
        {
508
            return wait_all_nothrow_t::wait_all_nothrow_impl(
2✔
509
                const_cast<std::array<Future, N> const&>(values));
2✔
510
        }
511

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

522
            auto values = traits::acquire_shared_state<Iterator>()(begin, end);
523
            return wait_all_nothrow_t::wait_all_nothrow_impl(values);
524
        }
525

526
        friend HPX_WAIT_ALL_FORCEINLINE constexpr bool tag_invoke(
527
            wait_all_nothrow_t) noexcept
528
        {
529
            return false;
530
        }
531

532
        template <typename... Ts>
533
        friend bool tag_invoke(wait_all_nothrow_t, Ts&&... ts)
174,542✔
534
        {
535
            if constexpr (sizeof...(Ts) != 0)
536
            {
537
                using result_type =
538
                    hpx::tuple<traits::detail::shared_state_ptr_for_t<Ts>...>;
539
                using frame_type = detail::wait_all_frame<result_type>;
540

541
                result_type values =
542
                    result_type(hpx::traits::detail::get_shared_state(ts)...);
174,542✔
543

544
                // frame is initialized with initial reference count
545
                hpx::intrusive_ptr<frame_type> frame(
174,542✔
546
                    new frame_type(values), false);
174,542✔
547
                return frame->wait_all();
174,542✔
548
            }
174,542✔
549
            return false;
550
        }
×
551

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

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

569
    ///////////////////////////////////////////////////////////////////////////
570
    inline constexpr struct wait_all_t final : 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(
6,626✔
585
            wait_all_t, std::vector<Future>& values)
586
        {
587
            if (hpx::wait_all_nothrow(
6,626✔
588
                    const_cast<std::vector<Future> const&>(values)))
6,626✔
589
            {
590
                hpx::detail::throw_if_exceptional(values);
×
591
            }
×
592
        }
6,625✔
593

594
        template <typename Future>
595
        friend HPX_WAIT_ALL_FORCEINLINE void tag_invoke(
2✔
596
            wait_all_t, std::vector<Future>&& values)
597
        {
598
            if (hpx::wait_all_nothrow(
2✔
599
                    const_cast<std::vector<Future> const&>(values)))
2✔
600
            {
601
                hpx::detail::throw_if_exceptional(values);
×
602
            }
×
603
        }
2✔
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(
1✔
617
            wait_all_t, std::array<Future, N>& values)
618
        {
619
            if (hpx::wait_all_nothrow(
1✔
620
                    const_cast<std::array<Future, N> const&>(values)))
1✔
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)
1,451✔
657
        {
658
            if (hpx::wait_all_nothrow(ts...))
1,451✔
659
            {
660
                hpx::detail::throw_if_exceptional(HPX_FORWARD(Ts, ts)...);
×
661
            }
×
662
        }
1,449✔
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
    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(
4✔
694
            wait_all_n_nothrow_t, Iterator begin, std::size_t count)
695
        {
696
            if (count == 0)
4✔
697
            {
698
                return false;
×
699
            }
700

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

707
    ///////////////////////////////////////////////////////////////////////////
708
    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)
2✔
716
        {
717
            if (count != 0)
2✔
718
            {
719
                auto values =
720
                    traits::acquire_shared_state<Iterator>()(begin, count);
2✔
721
                if (hpx::wait_all_nothrow(values))
2✔
722
                {
723
                    hpx::detail::throw_if_exceptional(values);
2✔
724
                }
×
725
            }
2✔
726
        }
2✔
727
    } wait_all_n{};
728
}    // namespace hpx
729

730
#undef HPX_WAIT_ALL_FORCEINLINE
731

732
namespace hpx::lcos {
733

734
    template <typename... Ts>
735
    HPX_DEPRECATED_V(
736
        1, 8, "hpx::lcos::wait_all is deprecated. Use hpx::wait_all instead.")
737
    void wait_all(Ts&&... ts)
738
    {
739
        hpx::wait_all(HPX_FORWARD(Ts, ts)...);
740
    }
741

742
    template <typename Iterator,
743
        typename Enable =
744
            std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
745
    HPX_DEPRECATED_V(1, 8,
746
        "hpx::lcos::wait_all_n is deprecated. Use hpx::wait_all_n instead.")
747
    void wait_all_n(Iterator begin, std::size_t count)
748
    {
749
        hpx::wait_all_n(begin, count);
750
    }
751
}    // namespace hpx::lcos
752

753
#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