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

STEllAR-GROUP / hpx / #853

19 Dec 2022 01:01AM UTC coverage: 86.287% (+0.4%) from 85.912%
#853

push

StellarBot
Merge #6109

6109: Modernize serialization module r=hkaiser a=hkaiser

- flyby separate serialization of Boost types

working towards https://github.com/STEllAR-GROUP/hpx/issues/5497

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

53 of 53 new or added lines in 6 files covered. (100.0%)

173939 of 201582 relevant lines covered (86.29%)

1931657.12 hits per line

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

93.16
/libs/core/async_combinators/include/hpx/async_combinators/when_some.hpp
1
//  Copyright (c) 2007-2022 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 when_some.hpp
9

10
#pragma once
11

12
#if defined(DOXYGEN)
13
namespace hpx {
14

15
    ///////////////////////////////////////////////////////////////////////////
16
    /// Result type for \a when_some, contains a sequence of futures and
17
    /// indices pointing to ready futures.
18
    template <typename Sequence>
19
    struct when_some_result
20
    {
21
        /// List of indices of futures that have become ready
22
        std::vector<std::size_t> indices;
23

24
        /// The sequence of futures as passed to \a hpx::when_some
25
        Sequence futures;
26
    };
27

28
    /// The function \a when_some is an operator allowing to join on the result
29
    /// of all given futures. It AND-composes all future objects given and
30
    /// returns a new future object representing the same list of futures
31
    /// after n of them finished executing.
32
    ///
33
    /// \param n        [in] The number of futures out of the arguments which
34
    ///                 have to become ready in order for the returned future
35
    ///                 to get ready.
36
    /// \param first    [in] The iterator pointing to the first element of a
37
    ///                 sequence of \a future or \a shared_future objects for
38
    ///                 which \a when_all should wait.
39
    /// \param last     [in] The iterator pointing to the last element of a
40
    ///                 sequence of \a future or \a shared_future objects for
41
    ///                 which \a when_all should wait.
42
    ///
43
    /// \note The future returned by the function \a when_some becomes ready
44
    ///       when at least \a n argument futures have become ready.
45
    ///
46
    /// \return   Returns a when_some_result holding the same list of futures
47
    ///           as has been passed to when_some and indices pointing to
48
    ///           ready futures.
49
    ///           - future<when_some_result<Container<future<R>>>>: If the input
50
    ///             cardinality is unknown at compile time and the futures
51
    ///             are all of the same type. The order of the futures in the
52
    ///             output container will be the same as given by the input
53
    ///             iterator.
54
    ///
55
    /// \note Calling this version of \a when_some where first == last, returns
56
    ///       a future with an empty container that is immediately ready.
57
    ///       Each future and shared_future is waited upon and then copied into
58
    ///       the collection of the output (returned) future, maintaining the
59
    ///       order of the futures in the input collection.
60
    ///       The future returned by \a when_some will not throw an exception,
61
    ///       but the futures held in the output collection may.
62
    ///
63
    template <typename InputIter,
64
        typename Container = vector<
65
            future<typename std::iterator_traits<InputIter>::value_type>>>
66
    future<when_some_result<Container>> when_some(
67
        std::size_t n, Iterator first, Iterator last);
68

69
    /// The function \a when_some is an operator allowing to join on the result
70
    /// of all given futures. It AND-composes all future objects given and
71
    /// returns a new future object representing the same list of futures
72
    /// after n of them finished executing.
73
    ///
74
    /// \param n        [in] The number of futures out of the arguments which
75
    ///                 have to become ready in order for the returned future
76
    ///                 to get ready.
77
    /// \param futures  [in] A container holding an arbitrary amount of \a future
78
    ///                 or \a shared_future objects for which \a when_some
79
    ///                 should wait.
80
    ///
81
    /// \note The future returned by the function \a when_some becomes ready
82
    ///       when at least \a n argument futures have become ready.
83
    ///
84
    /// \return   Returns a when_some_result holding the same list of futures
85
    ///           as has been passed to when_some and indices pointing to
86
    ///           ready futures.
87
    ///           - future<when_some_result<Container<future<R>>>>: If the input
88
    ///             cardinality is unknown at compile time and the futures
89
    ///             are all of the same type. The order of the futures in the
90
    ///             output container will be the same as given by the input
91
    ///             iterator.
92
    ///
93
    /// \note Each future and shared_future is waited upon and then copied into
94
    ///       the collection of the output (returned) future, maintaining the
95
    ///       order of the futures in the input collection.
96
    ///       The future returned by \a when_some will not throw an exception,
97
    ///       but the futures held in the output collection may.
98
    ///
99
    template <typename Range>
100
    future<when_some_result<Range>> when_some(std::size_t n, Range&& futures);
101

102
    /// The function \a when_some is an operator allowing to join on the result
103
    /// of all given futures. It AND-composes all future objects given and
104
    /// returns a new future object representing the same list of futures
105
    /// after n of them finished executing.
106
    ///
107
    /// \param n        [in] The number of futures out of the arguments which
108
    ///                 have to become ready in order for the returned future
109
    ///                 to get ready.
110
    /// \param futures  [in] An arbitrary number of \a future or \a shared_future
111
    ///                 objects, possibly holding different types for which
112
    ///                 \a when_some should wait.
113
    ///
114
    /// \note The future returned by the function \a when_some becomes ready
115
    ///       when at least \a n argument futures have become ready.
116
    ///
117
    /// \return   Returns a when_some_result holding the same list of futures
118
    ///           as has been passed to when_some and an index pointing to a
119
    ///           ready future..
120
    ///           - future<when_some_result<tuple<future<T0>, future<T1>...>>>:
121
    ///             If inputs are fixed in number and are of heterogeneous
122
    ///             types. The inputs can be any arbitrary number of future
123
    ///             objects.
124
    ///           - future<when_some_result<tuple<>>> if \a when_some is
125
    ///             called with zero arguments.
126
    ///             The returned future will be initially ready.
127
    ///
128
    /// \note Each future and shared_future is waited upon and then copied into
129
    ///       the collection of the output (returned) future, maintaining the
130
    ///       order of the futures in the input collection.
131
    ///       The future returned by \a when_some will not throw an exception,
132
    ///       but the futures held in the output collection may.
133
    ///
134
    template <typename... Ts>
135
    future<when_some_result<tuple<future<T>...>>> when_some(
136
        std::size_t n, Ts&&... futures);
137

138
    /// The function \a when_some_n is an operator allowing to join on the result
139
    /// of all given futures. It AND-composes all future objects given and
140
    /// returns a new future object representing the same list of futures
141
    /// after n of them finished executing.
142
    ///
143
    /// \param n        [in] The number of futures out of the arguments which
144
    ///                 have to become ready in order for the returned future
145
    ///                 to get ready.
146
    /// \param first    [in] The iterator pointing to the first element of a
147
    ///                 sequence of \a future or \a shared_future objects for
148
    ///                 which \a when_all should wait.
149
    /// \param count    [in] The number of elements in the sequence starting at
150
    ///                 \a first.
151
    ///
152
    /// \note The future returned by the function \a when_some_n becomes ready
153
    ///       when at least \a n argument futures have become ready.
154
    ///
155
    /// \return   Returns a when_some_result holding the same list of futures
156
    ///           as has been passed to when_some and indices pointing to
157
    ///           ready futures.
158
    ///           - future<when_some_result<Container<future<R>>>>: If the input
159
    ///             cardinality is unknown at compile time and the futures
160
    ///             are all of the same type. The order of the futures in the
161
    ///             output container will be the same as given by the input
162
    ///             iterator.
163
    ///
164
    /// \note Calling this version of \a when_some_n where count == 0, returns
165
    ///       a future with the same elements as the arguments that is
166
    ///       immediately ready. Possibly none of the futures in that container
167
    ///       are ready.
168
    ///       Each future and shared_future is waited upon and then copied into
169
    ///       the collection of the output (returned) future, maintaining the
170
    ///       order of the futures in the input collection.
171
    ///       The future returned by \a when_some_n will not throw an exception,
172
    ///       but the futures held in the output collection may.
173
    ///
174
    template <typename InputIter,
175
        typename Container = vector<
176
            future<typename std::iterator_traits<InputIter>::value_type>>>
177
    future<when_some_result<Container>> when_some_n(
178
        std::size_t n, Iterator first, std::size_t count);
179
}    // namespace hpx
180

181
#else    // DOXYGEN
182

183
#include <hpx/config.hpp>
184
#include <hpx/assert.hpp>
185
#include <hpx/datastructures/tuple.hpp>
186
#include <hpx/functional/deferred_call.hpp>
187
#include <hpx/functional/tag_invoke.hpp>
188
#include <hpx/futures/future.hpp>
189
#include <hpx/futures/futures_factory.hpp>
190
#include <hpx/futures/traits/acquire_future.hpp>
191
#include <hpx/futures/traits/acquire_shared_state.hpp>
192
#include <hpx/futures/traits/detail/future_traits.hpp>
193
#include <hpx/futures/traits/future_access.hpp>
194
#include <hpx/futures/traits/is_future.hpp>
195
#include <hpx/futures/traits/is_future_range.hpp>
196
#include <hpx/modules/errors.hpp>
197
#include <hpx/type_support/pack.hpp>
198
#include <hpx/util/detail/reserve.hpp>
199

200
#include <algorithm>
201
#include <atomic>
202
#include <cstddef>
203
#include <iterator>
204
#include <memory>
205
#include <mutex>
206
#include <type_traits>
207
#include <utility>
208
#include <vector>
209

210
///////////////////////////////////////////////////////////////////////////////
211
namespace hpx {
212

213
    template <typename Sequence>
214
    struct when_some_result
84✔
215
    {
216
        when_some_result() = default;
×
217

218
        explicit when_some_result(Sequence&& futures) noexcept
12✔
219
          : indices()
12✔
220
          , futures(HPX_MOVE(futures))
12✔
221
        {
222
        }
12✔
223

224
        std::vector<std::size_t> indices;
225
        Sequence futures;
226
    };
227
}    // namespace hpx
228

229
namespace hpx::lcos::detail {
230

231
    ///////////////////////////////////////////////////////////////////////
232
    template <typename Sequence>
233
    struct when_some;
234

235
    template <typename Sequence>
236
    struct set_when_some_callback_impl
237
    {
238
        explicit set_when_some_callback_impl(when_some<Sequence>& when) noexcept
12✔
239
          : when_(when)
12✔
240
          , idx_(0)
12✔
241
        {
242
        }
12✔
243

244
        template <typename Future>
245
        std::enable_if_t<traits::is_future_v<Future>> operator()(
48✔
246
            Future& future) const
247
        {
248
            std::size_t counter = when_.count_.load(std::memory_order_seq_cst);
48✔
249
            if (counter < when_.needed_count_)
48✔
250
            {
251
                // handle future only if not enough futures are ready
252
                // yet also, do not touch any futures which are already
253
                // ready
254

255
                auto shared_state = traits::detail::get_shared_state(future);
40✔
256

257
                if (shared_state &&
40✔
258
                    !shared_state->is_ready(std::memory_order_relaxed))
40✔
259
                {
260
                    shared_state->execute_deferred();
21✔
261

262
                    // execute_deferred might have made the future ready
263
                    if (!shared_state->is_ready(std::memory_order_relaxed))
21✔
264
                    {
265
                        shared_state->set_on_completed(util::deferred_call(
38✔
266
                            &detail::when_some<Sequence>::on_future_ready,
19✔
267
                            when_.shared_from_this(), idx_,
19✔
268
                            hpx::execution_base::this_thread::agent()));
19✔
269
                        ++idx_;
19✔
270

271
                        return;
19✔
272
                    }
273
                }
2✔
274

275
                {
276
                    using mutex_type =
277
                        typename detail::when_some<Sequence>::mutex_type;
278
                    std::lock_guard<mutex_type> l(when_.mtx_);
21✔
279
                    when_.values_.indices.push_back(idx_);
21✔
280
                }
21✔
281

282
                if (when_.count_.fetch_add(1) + 1 == when_.needed_count_)
21✔
283
                {
284
                    when_.goal_reached_on_calling_thread_.store(
11✔
285
                        true, std::memory_order_release);
286
                }
11✔
287
            }
40✔
288
            ++idx_;
29✔
289
        }
48✔
290

291
        template <typename Sequence_>
292
        HPX_FORCEINLINE std::enable_if_t<traits::is_future_range_v<Sequence_>>
293
        operator()(Sequence_& sequence) const
2✔
294
        {
295
            apply(sequence);
2✔
296
        }
2✔
297

298
        template <typename Tuple, std::size_t... Is>
299
        HPX_FORCEINLINE void apply(Tuple& tuple, util::index_pack<Is...>) const
7✔
300
        {
301
            ((*this)(hpx::get<Is>(tuple)), ...);
7✔
302
        }
7✔
303

304
        template <typename... Ts>
305
        HPX_FORCEINLINE void apply(hpx::tuple<Ts...>& sequence) const
7✔
306
        {
307
            apply(sequence, util::make_index_pack_t<sizeof...(Ts)>());
7✔
308
        }
7✔
309

310
        template <typename Sequence_>
311
        HPX_FORCEINLINE void apply(Sequence_& sequence) const
7✔
312
        {
313
            std::for_each(sequence.begin(), sequence.end(), *this);
7✔
314
        }
7✔
315

316
        detail::when_some<Sequence>& when_;
317
        mutable std::size_t idx_;
318
    };
319

320
    template <typename Sequence>
321
    HPX_FORCEINLINE void set_on_completed_callback(
12✔
322
        detail::when_some<Sequence>& when)
323
    {
324
        set_when_some_callback_impl<Sequence> callback(when);
12✔
325
        callback.apply(when.values_.futures);
12✔
326
    }
12✔
327

328
    template <typename Sequence>
329
    struct when_some
12✔
330
      : std::enable_shared_from_this<when_some<Sequence>>    //-V690
331
    {
332
        using mutex_type = hpx::spinlock;
333

334
    public:
335
        void on_future_ready(
19✔
336
            std::size_t idx, hpx::execution_base::agent_ref ctx)
337
        {
338
            std::size_t const new_count = count_.fetch_add(1) + 1;
19✔
339
            if (new_count <= needed_count_)
19✔
340
            {
341
                {
342
                    std::lock_guard<mutex_type> l(this->mtx_);
2✔
343
                    values_.indices.push_back(idx);
2✔
344
                }
2✔
345

346
                if (new_count == needed_count_)
2✔
347
                {
348
                    if (ctx != hpx::execution_base::this_thread::agent())
1✔
349
                    {
350
                        ctx.resume();
1✔
351
                    }
1✔
352
                    else
353
                    {
354
                        goal_reached_on_calling_thread_.store(
×
355
                            true, std::memory_order_release);
356
                    }
357
                }
1✔
358
            }
2✔
359
        }
19✔
360

361
    private:
362
        when_some(when_some const&) = delete;
363
        when_some(when_some&&) = delete;
364

365
        when_some& operator=(when_some const&) = delete;
366
        when_some& operator=(when_some&&) = delete;
367

368
    public:
369
        using argument_type = Sequence;
370

371
        when_some(argument_type&& values, std::size_t n)
12✔
372
          : values_(HPX_MOVE(values))
12✔
373
          , count_(0)
12✔
374
          , needed_count_(n)
12✔
375
          , goal_reached_on_calling_thread_(false)
12✔
376
        {
12✔
377
        }
12✔
378

379
        when_some_result<Sequence> operator()()
12✔
380
        {
381
            // set callback functions to executed when future is ready
382
            set_on_completed_callback(*this);
12✔
383

384
            // if all of the requested futures are already set, our
385
            // callback above has already been called often enough, otherwise
386
            // we suspend ourselves
387
            if (!goal_reached_on_calling_thread_.load(
12✔
388
                    std::memory_order_acquire))
389
            {
390
                // wait for any of the futures to return to become ready
391
                hpx::execution_base::this_thread::suspend(
1✔
392
                    "hpx::lcos::detail::when_some::operator()");
393
            }
1✔
394

395
            // at least N futures should be ready
396
            HPX_ASSERT(count_.load(std::memory_order_acquire) >= needed_count_);
12✔
397

398
            return HPX_MOVE(values_);
12✔
399
        }
400

401
        mutable mutex_type mtx_;
402
        when_some_result<Sequence> values_;
403
        std::atomic<std::size_t> count_;
404
        std::size_t needed_count_;
405
        std::atomic<bool> goal_reached_on_calling_thread_;
406
    };
407
}    // namespace hpx::lcos::detail
408

409
namespace hpx {
410

411
    ///////////////////////////////////////////////////////////////////////////
412
    inline constexpr struct when_some_t final
413
      : hpx::functional::tag<when_some_t>
414
    {
415
    private:
416
        template <typename Range,
417
            typename Enable =
418
                std::enable_if_t<traits::is_future_range_v<Range>>>
419
        friend auto tag_invoke(when_some_t, std::size_t n, Range&& lazy_values)
5✔
420
        {
421
            using result_type = std::decay_t<Range>;
422

423
            if (n == 0)
5✔
424
            {
425
                return hpx::make_ready_future(when_some_result<result_type>());
×
426
            }
427

428
            result_type values =
429
                traits::acquire_future<result_type>()(lazy_values);
5✔
430

431
            if (n > values.size())
5✔
432
            {
433
                return hpx::make_exceptional_future<
×
434
                    when_some_result<result_type>>(HPX_GET_EXCEPTION(
×
435
                    hpx::error::bad_parameter, "hpx::when_some",
436
                    "number of results to wait for is out of bounds"));
437
            }
438

439
            auto f = std::make_shared<lcos::detail::when_some<result_type>>(
5✔
440
                HPX_MOVE(values), n);
441

442
            lcos::local::futures_factory<when_some_result<result_type>()> p(
5✔
443
                [f = HPX_MOVE(f)]() -> when_some_result<result_type> {
25✔
444
                    return (*f)();
5✔
445
                });
446

447
            auto result = p.get_future();
5✔
448
            p.post();
5✔
449

450
            return result;
5✔
451
        }
5✔
452

453
        template <typename Iterator,
454
            typename Enable =
455
                std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
456
        friend decltype(auto) tag_invoke(
1✔
457
            when_some_t, std::size_t n, Iterator begin, Iterator end)
458
        {
459
            using value_type =
460
                typename lcos::detail::future_iterator_traits<Iterator>::type;
461

462
            std::vector<value_type> values;
1✔
463
            traits::detail::reserve_if_random_access_by_range(
1✔
464
                values, begin, end);
1✔
465

466
            std::transform(begin, end, std::back_inserter(values),
1✔
467
                traits::acquire_future_disp());
468

469
            return tag_invoke(when_some_t{}, n, HPX_MOVE(values));
1✔
470
        }
1✔
471

472
        friend decltype(auto) tag_invoke(when_some_t, std::size_t n)
473
        {
474
            using result_type = hpx::tuple<>;
475

476
            if (n == 0)
477
            {
478
                return hpx::make_ready_future(when_some_result<result_type>());
479
            }
480

481
            return hpx::make_exceptional_future<when_some_result<result_type>>(
482
                HPX_GET_EXCEPTION(hpx::error::bad_parameter, "hpx::when_some",
483
                    "number of results to wait for is out of bounds"));
484
        }
485

486
        ///////////////////////////////////////////////////////////////////////////
487
        template <typename T, typename... Ts,
488
            typename Enable = std::enable_if_t<!(
489
                traits::is_future_range_v<T> && sizeof...(Ts) == 0)>>
490
        friend auto tag_invoke(when_some_t, std::size_t n, T&& t, Ts&&... ts)
7✔
491
        {
492
            using result_type = hpx::tuple<traits::acquire_future_t<T>,
493
                traits::acquire_future_t<Ts>...>;
494

495
            if (n == 0)
7✔
496
            {
497
                return hpx::make_ready_future(when_some_result<result_type>());
×
498
            }
499

500
            if (n > 1 + sizeof...(Ts))
7✔
501
            {
502
                return hpx::make_exceptional_future<
×
503
                    when_some_result<result_type>>(HPX_GET_EXCEPTION(
×
504
                    hpx::error::bad_parameter, "hpx::when_some",
505
                    "number of results to wait for is out of bounds"));
506
            }
507

508
            traits::acquire_future_disp func;
509
            result_type values(
7✔
510
                func(HPX_FORWARD(T, t)), func(HPX_FORWARD(Ts, ts))...);
7✔
511

512
            auto f = std::make_shared<lcos::detail::when_some<result_type>>(
7✔
513
                HPX_MOVE(values), n);
514

515
            lcos::local::futures_factory<when_some_result<result_type>()> p(
7✔
516
                [f = HPX_MOVE(f)]() -> when_some_result<result_type> {
35✔
517
                    return (*f)();
7✔
518
                });
519

520
            auto result = p.get_future();
7✔
521
            p.post();
7✔
522

523
            return result;
7✔
524
        }
7✔
525
    } when_some{};
526

527
    ///////////////////////////////////////////////////////////////////////////
528
    inline constexpr struct when_some_n_t final
529
      : hpx::functional::tag<when_some_n_t>
530
    {
531
    private:
532
        template <typename Iterator,
533
            typename Enable =
534
                std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
535
        friend decltype(auto) tag_invoke(
536
            when_some_n_t, std::size_t n, Iterator begin, std::size_t count)
537
        {
538
            using value_type =
539
                typename lcos::detail::future_iterator_traits<Iterator>::type;
540

541
            std::vector<value_type> values;
542
            values.reserve(count);
543

544
            traits::acquire_future_disp func;
545
            for (std::size_t i = 0; i != count; ++i)
546
            {
547
                values.push_back(func(*begin++));
548
            }
549

550
            return hpx::when_some(n, HPX_MOVE(values));
551
        }
552
    } when_some_n{};
553
}    // namespace hpx
554

555
namespace hpx::lcos {
556

557
    template <typename Range>
558
    HPX_DEPRECATED_V(
559
        1, 8, "hpx::lcos::when_some is deprecated. Use hpx::when_some instead.")
560
    std::enable_if_t<traits::is_future_range_v<Range>,
561
        hpx::future<
562
            when_some_result<std::decay_t<Range>>>> when_some(std::size_t n,
563
        Range&& values, error_code& = throws)
564
    {
565
        return hpx::when_some(n, HPX_FORWARD(Range, values));
566
    }
567

568
    template <typename Iterator,
569
        typename Container = std::vector<
570
            typename lcos::detail::future_iterator_traits<Iterator>::type>,
571
        typename Enable =
572
            std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
573
    HPX_DEPRECATED_V(
574
        1, 8, "hpx::lcos::when_some is deprecated. Use hpx::when_some instead.")
575
    hpx::future<when_some_result<Container>> when_some(
576
        std::size_t n, Iterator begin, Iterator end, error_code& = throws)
577
    {
578
        return hpx::when_some(n, begin, end);
579
    }
580

581
    template <typename Iterator,
582
        typename Container = std::vector<
583
            typename lcos::detail::future_iterator_traits<Iterator>::type>,
584
        typename Enable =
585
            std::enable_if_t<hpx::traits::is_iterator_v<Iterator>>>
586
    HPX_DEPRECATED_V(1, 8,
587
        "hpx::lcos::when_some_n is deprecated. Use hpx::when_some_n instead.")
588
    hpx::future<when_some_result<Container>> when_some_n(
589
        std::size_t n, Iterator begin, std::size_t count, error_code& = throws)
590
    {
591
        return hpx::when_some(n, begin, count);
592
    }
593

594
    HPX_DEPRECATED_V(
595
        1, 8, "hpx::lcos::when_some is deprecated. Use hpx::when_some instead.")
596
    inline hpx::future<when_some_result<hpx::tuple<>>> when_some(
597
        std::size_t n, error_code& = throws)
598
    {
599
        return hpx::when_some(n);
600
    }
601

602
    template <typename T, typename... Ts>
603
    HPX_DEPRECATED_V(
604
        1, 8, "hpx::lcos::when_some is deprecated. Use hpx::when_some instead.")
605
    std::enable_if_t<!(traits::is_future_range_v<T> && sizeof...(Ts) == 0),
606
        hpx::future<when_some_result<hpx::tuple<traits::acquire_future_t<T>,
607
            traits::acquire_future_t<Ts>...>>>> when_some(std::size_t n, T&& t,
608
        Ts&&... ts)
609
    {
610
        return hpx::when_some(n, HPX_FORWARD(T, t), HPX_FORWARD(Ts, ts)...);
611
    }
612

613
    template <typename T, typename... Ts>
614
    HPX_DEPRECATED_V(
615
        1, 8, "hpx::lcos::when_some is deprecated. Use hpx::when_some instead.")
616
    std::enable_if_t<!(traits::is_future_range_v<T> && sizeof...(Ts) == 0),
617
        hpx::future<when_some_result<hpx::tuple<traits::acquire_future_t<T>,
618
            traits::acquire_future_t<Ts>...>>>> when_some(std::size_t n,
619
        error_code&, T&& t, Ts&&... ts)
620
    {
621
        return hpx::when_some(n, HPX_FORWARD(T, t), HPX_FORWARD(Ts, ts)...);
622
    }
623

624
    template <typename Container>
625
    using when_some_result HPX_DEPRECATED_V(1, 8,
626
        "hpx::lcos::when_some_result is deprecated. Use hpx::when_some_result "
627
        "instead.") = hpx::when_some_result<Container>;
628
}    // namespace hpx::lcos
629

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