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

STEllAR-GROUP / hpx / #863

11 Jan 2023 05:02PM UTC coverage: 86.533% (-0.05%) from 86.582%
#863

push

StellarBot
Merge #6126

6126: Deprecate hpx::parallel::task_block in favor of hpx::experimental::ta… r=hkaiser a=dimitraka



Co-authored-by: kadimitra <kadimitra@ece.auth.gr>

174614 of 201789 relevant lines covered (86.53%)

1954694.49 hits per line

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

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

7
/// \file parallel/executors/polymorphic_executor.hpp
8

9
#pragma once
10

11
#include <hpx/config.hpp>
12
#include <hpx/assert.hpp>
13
#include <hpx/execution/detail/future_exec.hpp>
14
#include <hpx/execution_base/execution.hpp>
15
#include <hpx/execution_base/traits/is_executor.hpp>
16
#include <hpx/functional/function.hpp>
17
#include <hpx/functional/move_only_function.hpp>
18
#include <hpx/futures/future.hpp>
19
#include <hpx/modules/thread_support.hpp>
20
#include <hpx/type_support/construct_at.hpp>
21

22
#include <algorithm>
23
#include <cstddef>
24
#include <memory>
25
#include <new>
26
#include <type_traits>
27
#include <utility>
28
#include <vector>
29

30
///////////////////////////////////////////////////////////////////////////////
31
namespace hpx::parallel::execution {
32

33
    namespace detail {
34

35
        struct shape_iter_impl_base
56✔
36
        {
37
            virtual ~shape_iter_impl_base() = default;
56✔
38
            virtual void copy(shape_iter_impl_base const&) = 0;
39
            virtual shape_iter_impl_base* clone() const = 0;
40
            virtual void increment() = 0;
41
            virtual std::size_t dereference() const = 0;
42
            virtual bool equal_to(shape_iter_impl_base const&) const = 0;
43
        };
44

45
        template <typename Iterator>
46
        struct shape_iter_impl : shape_iter_impl_base
112✔
47
        {
48
            explicit shape_iter_impl(Iterator it)
56✔
49
              : iterator_(it)
56✔
50
            {
112✔
51
            }
56✔
52

53
            void copy(shape_iter_impl_base const& from) override
×
54
            {
55
                iterator_ = static_cast<shape_iter_impl const&>(from).iterator_;
×
56
            }
×
57

58
            shape_iter_impl_base* clone() const override
32✔
59
            {
60
                return new shape_iter_impl(iterator_);
32✔
61
            }
×
62

63
            void increment() override
1,284✔
64
            {
65
                ++iterator_;
1,284✔
66
            }
1,284✔
67

68
            std::size_t dereference() const override
1,284✔
69
            {
70
                return *iterator_;
1,284✔
71
            }
72

73
            bool equal_to(shape_iter_impl_base const& other) const override
868✔
74
            {
75
                return iterator_ ==
1,736✔
76
                    static_cast<shape_iter_impl const&>(other).iterator_;
868✔
77
            }
78

79
        private:
80
            Iterator iterator_;
81
        };
82

83
        struct shape_iter
72✔
84
        {
85
            using iterator_category = std::input_iterator_tag;
86
            using value_type = std::size_t;
87
            using difference_type = std::ptrdiff_t;
88
            using pointer = std::size_t*;
89
            using reference = std::size_t&;
90

91
            template <typename Iterator>
92
            explicit shape_iter(Iterator it)
24✔
93
              : impl_(new shape_iter_impl<Iterator>(it))
24✔
94
            {
95
            }
24✔
96

97
            // we need to deep-copy the embedded iterator
98
            shape_iter(shape_iter const& rhs)
32✔
99
              : impl_(rhs.impl_->clone())
32✔
100
            {
101
            }
32✔
102

103
            shape_iter& operator=(shape_iter const& rhs)
104
            {
105
                impl_->copy(*rhs.impl_);
106
                return *this;
107
            }
108

109
            shape_iter(shape_iter&& rhs) = default;
16✔
110
            shape_iter& operator=(shape_iter&& rhs) = default;
111

112
            std::size_t operator*() const
1,284✔
113
            {
114
                return impl_->dereference();
1,284✔
115
            }
116

117
            shape_iter& operator++()
1,284✔
118
            {
119
                impl_->increment();
1,284✔
120
                return *this;
1,284✔
121
            }
122

123
            shape_iter operator++(int)
124
            {
125
                auto copy = *this;
126
                ++*this;
127
                return copy;
128
            }
129

130
            friend bool operator==(shape_iter const& lhs, shape_iter const& rhs)
868✔
131
            {
132
                return lhs.impl_->equal_to(*rhs.impl_);
868✔
133
            }
134

135
            friend bool operator!=(shape_iter const& lhs, shape_iter const& rhs)
864✔
136
            {
137
                return !(lhs == rhs);
864✔
138
            }
139

140
        protected:
141
            std::unique_ptr<shape_iter_impl_base> impl_;
142
        };
143

144
        struct range_proxy
36✔
145
        {
146
            using value_type = std::size_t;
147

148
            template <typename Shape>
149
            explicit range_proxy(Shape const& s)
12✔
150
              : begin_(s.begin())
12✔
151
              , end_(s.end())
12✔
152
              , size_(std::distance(s.begin(), s.end()))
12✔
153
            {
154
            }
12✔
155

156
            shape_iter begin() const
12✔
157
            {
158
                return begin_;
12✔
159
            }
160

161
            shape_iter end() const
12✔
162
            {
163
                return end_;
12✔
164
            }
165

166
            constexpr std::ptrdiff_t size() const noexcept
6✔
167
            {
168
                return size_;
6✔
169
            }
170

171
        private:
172
            shape_iter begin_;
173
            shape_iter end_;
174
            std::ptrdiff_t size_;
175
        };
176

177
        ///////////////////////////////////////////////////////////////////////
178
        template <typename T>
179
        struct construct_vtable
180
        {
181
        };
182

183
        ///////////////////////////////////////////////////////////////////////
184
        template <typename VTable, typename T>
185
        struct polymorphic_executor_vtables
186
        {
187
            static constexpr VTable instance =
188
                VTable(detail::construct_vtable<T>());
189
        };
190

191
        template <typename VTable, typename T>
192
        inline constexpr VTable
193
            polymorphic_executor_vtables<VTable, T>::instance;
194

195
        template <typename VTable, typename T>
196
        constexpr VTable const* get_polymorphic_executor_vtable() noexcept
2✔
197
        {
198
            static_assert(
199
                !std::is_reference_v<T>, "T shall have no ref-qualifiers");
200

201
            return &polymorphic_executor_vtables<VTable, T>::instance;
2✔
202
        }
203

204
        ///////////////////////////////////////////////////////////////////////
205
        struct empty_polymorphic_executor
206
        {
207
        };    // must be trivial and empty
208

209
        [[noreturn]] HPX_CORE_EXPORT void throw_bad_polymorphic_executor();
210

211
        template <typename R>
212
        [[noreturn]] inline R throw_bad_polymorphic_executor()
×
213
        {
214
            throw_bad_polymorphic_executor();
×
215
#if defined(HPX_INTEL_VERSION)
216
            return {};
217
#endif
218
        }
219

220
        ///////////////////////////////////////////////////////////////////////
221
        struct vtable_base
222
        {
223
            template <typename T>
224
            static T& get(void* obj) noexcept
20✔
225
            {
226
                return *reinterpret_cast<T*>(obj);
20✔
227
            }
228

229
            template <typename T>
230
            static T const& get(void const* obj) noexcept
×
231
            {
232
                return *reinterpret_cast<T const*>(obj);
×
233
            }
234

235
            template <typename T>
236
            static void* allocate(void* storage, std::size_t storage_size)
2✔
237
            {
238
                if (sizeof(T) > storage_size)
2✔
239
                {
240
                    using storage_t =
241
                        std::aligned_storage_t<sizeof(T), alignof(T)>;
242
                    return new storage_t;
1✔
243
                }
244
                return storage;
1✔
245
            }
2✔
246

247
            template <typename T>
248
            static void _deallocate(
2✔
249
                void* obj, std::size_t storage_size, bool destroy) noexcept
250
            {
251
                if (destroy)
2✔
252
                {
253
                    get<T>(obj).~T();
2✔
254
                }
2✔
255

256
                if (sizeof(T) > storage_size)
2✔
257
                {
258
                    using storage_t =
259
                        std::aligned_storage_t<sizeof(T), alignof(T)>;
260
                    delete static_cast<storage_t*>(obj);
1✔
261
                }
1✔
262
            }
2✔
263
            void (*deallocate)(void*, std::size_t storage_size, bool) noexcept;
264

265
            template <typename T>
266
            static void* _copy(void* storage, std::size_t storage_size,
×
267
                void const* src, bool destroy)
268
            {
269
                if (destroy)
×
270
                {
271
                    get<T>(storage).~T();
×
272
                }
×
273

274
                void* buffer = allocate<T>(storage, storage_size);
×
275
                return hpx::construct_at(static_cast<T*>(buffer), get<T>(src));
×
276
            }
277
            void* (*copy)(void*, std::size_t, void const*, bool);
278

279
            template <typename T>
280
            constexpr explicit vtable_base(construct_vtable<T>) noexcept
281
              : deallocate(&vtable_base::template _deallocate<T>)
282
              , copy(&vtable_base::template _copy<T>)
283
            {
284
            }
285
        };
286

287
        ///////////////////////////////////////////////////////////////////////
288
        // NonBlockingOneWayExecutor interface
289
        template <typename Sig>
290
        struct never_blocking_oneway_vtable;
291

292
        template <typename R, typename... Ts>
293
        struct never_blocking_oneway_vtable<R(Ts...)>
294
        {
295
            using post_function_type = hpx::move_only_function<R(Ts...)>;
296

297
            // post
298
            template <typename T>
299
            static void _post(void* exec, post_function_type&& f, Ts&&... ts)
×
300
            {
301
                execution::post(vtable_base::get<T>(exec), HPX_MOVE(f),
×
302
                    HPX_FORWARD(Ts, ts)...);
×
303
            }
×
304
            void (*post)(void*, post_function_type&&, Ts&&...);
305

306
            template <typename T>
307
            constexpr explicit never_blocking_oneway_vtable(
308
                construct_vtable<T>) noexcept
309
              : post(&never_blocking_oneway_vtable::template _post<T>)
310
            {
311
            }
312

313
            static void _empty_post(void*, post_function_type&&, Ts&&...)
×
314
            {
315
                throw_bad_polymorphic_executor();
×
316
            }
317

318
            constexpr explicit never_blocking_oneway_vtable(
319
                construct_vtable<empty_polymorphic_executor>) noexcept
320
              : post(&never_blocking_oneway_vtable::_empty_post)
321
            {
322
            }
323
        };
324

325
        ///////////////////////////////////////////////////////////////////////
326
        // OneWayExecutor interface
327
        template <typename Sig>
328
        struct oneway_vtable;
329

330
        template <typename R, typename... Ts>
331
        struct oneway_vtable<R(Ts...)>
332
        {
333
            using sync_execute_function_type =
334
                hpx::move_only_function<R(Ts...)>;
335

336
            // sync_execute
337
            template <typename T>
338
            static R _sync_execute(
2✔
339
                void* exec, sync_execute_function_type&& f, Ts&&... ts)
340
            {
341
                return execution::sync_execute(vtable_base::get<T>(exec),
4✔
342
                    HPX_MOVE(f), HPX_FORWARD(Ts, ts)...);
2✔
343
            }
344
            R (*sync_execute)(void*, sync_execute_function_type&&, Ts&&...);
345

346
            template <typename T>
347
            constexpr explicit oneway_vtable(construct_vtable<T>) noexcept
348
              : sync_execute(&oneway_vtable::template _sync_execute<T>)
349
            {
350
            }
351

352
            static R _empty_sync_execute(
×
353
                void*, sync_execute_function_type&&, Ts&&...)
354
            {
355
                throw_bad_polymorphic_executor<R>();
×
356
                return {};
357
            }
358

359
            constexpr explicit oneway_vtable(
360
                construct_vtable<empty_polymorphic_executor>) noexcept
361
              : sync_execute(&oneway_vtable::_empty_sync_execute)
362
            {
363
            }
364
        };
365

366
        ///////////////////////////////////////////////////////////////////////
367
        // TwoWayExecutor interface
368
        template <typename Sig>
369
        struct twoway_vtable;
370

371
        template <typename R, typename... Ts>
372
        struct twoway_vtable<R(Ts...)>
373
        {
374
            using async_execute_function_type =
375
                hpx::move_only_function<R(Ts...)>;
376
            using then_execute_function_type = hpx::move_only_function<R(
377
                hpx::shared_future<void> const&, Ts...)>;
378

379
            // async_execute
380
            template <typename T>
381
            static hpx::future<R> _async_execute(
2✔
382
                void* exec, async_execute_function_type&& f, Ts&&... ts)
383
            {
384
                return execution::async_execute(vtable_base::get<T>(exec),
4✔
385
                    HPX_MOVE(f), HPX_FORWARD(Ts, ts)...);
2✔
386
            }
387
            hpx::future<R> (*async_execute)(
388
                void*, async_execute_function_type&& f, Ts&&...);
389

390
            // then_execute
391
            template <typename T>
392
            static hpx::future<R> _then_execute(void* exec,
2✔
393
                then_execute_function_type&& f,
394
                hpx::shared_future<void> const& predecessor, Ts&&... ts)
395
            {
396
                return execution::then_execute(vtable_base::get<T>(exec),
4✔
397
                    HPX_MOVE(f), predecessor, HPX_FORWARD(Ts, ts)...);
2✔
398
            }
399
            hpx::future<R> (*then_execute)(void*,
400
                then_execute_function_type&& f, hpx::shared_future<void> const&,
401
                Ts&&...);
402

403
            template <typename T>
404
            constexpr explicit twoway_vtable(construct_vtable<T>) noexcept
405
              : async_execute(&twoway_vtable::template _async_execute<T>)
406
              , then_execute(&twoway_vtable::template _then_execute<T>)
407
            {
408
            }
409

410
            static hpx::future<R> _empty_async_execute(void* /* exec */,
×
411
                async_execute_function_type&& /* f */, Ts&&... /* ts */)
412
            {
413
                throw_bad_polymorphic_executor<R>();
×
414
                return {};
415
            }
416
            static hpx::future<R> _empty_then_execute(void*,
×
417
                then_execute_function_type&&, hpx::shared_future<void> const&,
418
                Ts&&...)
419
            {
420
                throw_bad_polymorphic_executor<R>();
×
421
                return {};
422
            }
423

424
            constexpr explicit twoway_vtable(
425
                construct_vtable<empty_polymorphic_executor>) noexcept
426
              : async_execute(&twoway_vtable::_empty_async_execute)
427
              , then_execute(&twoway_vtable::_empty_then_execute)
428
            {
429
            }
430
        };
431

432
        ///////////////////////////////////////////////////////////////////////
433
        // BulkOneWayExecutor interface
434
        template <typename Sig>
435
        struct bulk_oneway_vtable;
436

437
        template <typename R, typename... Ts>
438
        struct bulk_oneway_vtable<R(Ts...)>
439
        {
440
            using bulk_sync_execute_function_type =
441
                hpx::function<R(std::size_t, Ts...)>;
442

443
            // bulk_sync_execute
444
            template <typename T>
445
            static std::vector<R> _bulk_sync_execute(void* exec,
4✔
446
                bulk_sync_execute_function_type&& f, range_proxy const& shape,
447
                Ts&&... ts)
448
            {
449
                return execution::bulk_sync_execute(vtable_base::get<T>(exec),
8✔
450
                    HPX_MOVE(f), shape, HPX_FORWARD(Ts, ts)...);
4✔
451
            }
452
            std::vector<R> (*bulk_sync_execute)(void*,
453
                bulk_sync_execute_function_type&&, range_proxy const& shape,
454
                Ts&&...);
455

456
            template <typename T>
457
            constexpr explicit bulk_oneway_vtable(construct_vtable<T>) noexcept
458
              : bulk_sync_execute(
459
                    &bulk_oneway_vtable::template _bulk_sync_execute<T>)
460
            {
461
            }
462

463
            static std::vector<R> _empty_bulk_sync_execute(void*,
×
464
                bulk_sync_execute_function_type&&, range_proxy const&, Ts&&...)
465
            {
466
                throw_bad_polymorphic_executor<R>();
×
467
                return {};
468
            }
469

470
            constexpr explicit bulk_oneway_vtable(
471
                construct_vtable<empty_polymorphic_executor>) noexcept
472
              : bulk_sync_execute(&bulk_oneway_vtable::_empty_bulk_sync_execute)
473
            {
474
            }
475
        };
476

477
        ///////////////////////////////////////////////////////////////////////
478
        // BulkTwoWayExecutor interface
479
        template <typename Sig>
480
        struct bulk_twoway_vtable;
481

482
        template <typename R, typename... Ts>
483
        struct bulk_twoway_vtable<R(Ts...)>
484
        {
485
            using bulk_async_execute_function_type =
486
                hpx::function<R(std::size_t, Ts...)>;
487
            using bulk_then_execute_function_type = hpx::function<R(
488
                std::size_t, hpx::shared_future<void> const&, Ts...)>;
489

490
            // bulk_async_execute
491
            template <typename T>
492
            static std::vector<hpx::future<R>> _bulk_async_execute(void* exec,
4✔
493
                bulk_async_execute_function_type&& f, range_proxy const& shape,
494
                Ts&&... ts)
495
            {
496
                return execution::bulk_async_execute(vtable_base::get<T>(exec),
8✔
497
                    HPX_MOVE(f), shape, HPX_FORWARD(Ts, ts)...);
4✔
498
            }
499
            std::vector<hpx::future<R>> (*bulk_async_execute)(void*,
500
                bulk_async_execute_function_type&&, range_proxy const& shape,
501
                Ts&&...);
502

503
            // bulk_then_execute
504
            template <typename T>
505
            static hpx::future<std::vector<R>> _bulk_then_execute(void* exec,
4✔
506
                bulk_then_execute_function_type&& f, range_proxy const& shape,
507
                hpx::shared_future<void> const& predecessor, Ts&&... ts)
508
            {
509
                return execution::bulk_then_execute(vtable_base::get<T>(exec),
8✔
510
                    HPX_MOVE(f), shape, predecessor, HPX_FORWARD(Ts, ts)...);
4✔
511
            }
512
            hpx::future<std::vector<R>> (*bulk_then_execute)(void*,
513
                bulk_then_execute_function_type&&, range_proxy const& shape,
514
                hpx::shared_future<void> const&, Ts&&...);
515

516
            template <typename T>
517
            constexpr explicit bulk_twoway_vtable(construct_vtable<T>) noexcept
518
              : bulk_async_execute(
519
                    &bulk_twoway_vtable::template _bulk_async_execute<T>)
520
              , bulk_then_execute(
521
                    &bulk_twoway_vtable::template _bulk_then_execute<T>)
522
            {
523
            }
524

525
            static std::vector<hpx::future<R>> _empty_bulk_async_execute(void*,
×
526
                bulk_async_execute_function_type&&, range_proxy const&, Ts&&...)
527
            {
528
                throw_bad_polymorphic_executor<R>();
×
529
                return {};
530
            }
531

532
            static hpx::future<std::vector<R>> _empty_bulk_then_execute(void*,
×
533
                bulk_then_execute_function_type&&, range_proxy const&,
534
                hpx::shared_future<void> const&, Ts&&...)
535
            {
536
                throw_bad_polymorphic_executor<R>();
×
537
                return {};
538
            }
539

540
            constexpr explicit bulk_twoway_vtable(
541
                construct_vtable<empty_polymorphic_executor>) noexcept
542
              : bulk_async_execute(
543
                    &bulk_twoway_vtable::_empty_bulk_async_execute)
544
              , bulk_then_execute(&bulk_twoway_vtable::_empty_bulk_then_execute)
545
            {
546
            }
547
        };
548

549
        ///////////////////////////////////////////////////////////////////////
550
        template <typename Sig>
551
        struct polymorphic_executor_vtable
552
          : vtable_base
553
          , never_blocking_oneway_vtable<Sig>
554
          , oneway_vtable<Sig>
555
          , twoway_vtable<Sig>
556
          , bulk_oneway_vtable<Sig>
557
          , bulk_twoway_vtable<Sig>
558
        {
559
            template <typename T>
560
            constexpr explicit polymorphic_executor_vtable(
561
                construct_vtable<T>) noexcept
562
              : vtable_base(construct_vtable<T>())
563
              , never_blocking_oneway_vtable<Sig>(construct_vtable<T>())
564
              , oneway_vtable<Sig>(construct_vtable<T>())
565
              , twoway_vtable<Sig>(construct_vtable<T>())
566
              , bulk_oneway_vtable<Sig>(construct_vtable<T>())
567
              , bulk_twoway_vtable<Sig>(construct_vtable<T>())
568
            {
569
            }
570
        };
571

572
        ///////////////////////////////////////////////////////////////////////
573
        // make sure the empty table instance is initialized in time, even
574
        // during early startup
575
        template <typename Sig>
576
        struct polymorphic_executor_vtable;
577

578
        // NOTE: nvcc (at least CUDA 9.2 and 10.1) fails with an internal
579
        // compiler error ("there was an error in verifying the lgenfe output!")
580
        // with this enabled, so we explicitly use the fallback.
581
#if !defined(HPX_HAVE_GPU_SUPPORT)
582
        template <typename Sig>
583
        constexpr polymorphic_executor_vtable<Sig> const*
584
        get_empty_polymorphic_executor_vtable() noexcept
2✔
585
        {
586
            return &polymorphic_executor_vtables<
2✔
587
                polymorphic_executor_vtable<Sig>,
588
                empty_polymorphic_executor>::instance;
589
        }
590
#else
591
        template <typename Sig>
592
        polymorphic_executor_vtable<Sig> const*
593
        get_empty_polymorphic_executor_vtable() noexcept
594
        {
595
            static polymorphic_executor_vtable<Sig> const empty_vtable =
596
                polymorphic_executor_vtable<Sig>(
597
                    detail::construct_vtable<empty_polymorphic_executor>());
598
            return &empty_vtable;
599
        }
600
#endif
601

602
        ///////////////////////////////////////////////////////////////////////
603
        inline constexpr std::size_t polymorphic_executor_storage_size =
604
            3 * sizeof(void*);
605

606
        class HPX_CORE_EXPORT polymorphic_executor_base
607
        {
608
            using vtable = vtable_base;
609

610
        public:
611
            constexpr explicit polymorphic_executor_base(
2✔
612
                vtable const* empty_vptr) noexcept
613
              : vptr(empty_vptr)
2✔
614
              , object(nullptr)
2✔
615
              , storage_init()
2✔
616
            {
617
            }
2✔
618

619
            polymorphic_executor_base(polymorphic_executor_base const& other,
620
                vtable const* empty_vtable);
621
            polymorphic_executor_base(polymorphic_executor_base&& other,
622
                vtable const* empty_vtable) noexcept;
623
            ~polymorphic_executor_base();
624

625
            void op_assign(polymorphic_executor_base const& other,
626
                vtable const* empty_vtable);
627
            void op_assign(polymorphic_executor_base&& other,
628
                vtable const* empty_vtable) noexcept;
629

630
            void destroy() noexcept;
631
            void reset(vtable const* empty_vptr) noexcept;
632
            void swap(polymorphic_executor_base& exec) noexcept;
633

634
            bool empty() const noexcept
635
            {
636
                return object == nullptr;
637
            }
638

639
            explicit operator bool() const noexcept
640
            {
641
                return !empty();
642
            }
643

644
        protected:
645
            vtable const* vptr;
646
            void* object;
647
            union
648
            {
649
                char storage_init;
650
                mutable unsigned char
651
                    storage[polymorphic_executor_storage_size];
652
            };
653
        };
654

655
        ///////////////////////////////////////////////////////////////////////////
656
        inline bool is_empty_polymorphic_executor_impl(
657
            polymorphic_executor_base const* exec) noexcept
658
        {
659
            return exec->empty();
660
        }
661

662
        inline constexpr bool is_empty_polymorphic_executor_impl(...) noexcept
2✔
663
        {
664
            return false;
2✔
665
        }
666

667
        template <typename Exec>
668
        constexpr bool is_empty_polymorphic_executor(Exec const& exec) noexcept
2✔
669
        {
670
            return detail::is_empty_polymorphic_executor_impl(&exec);
2✔
671
        }
672
    }    // namespace detail
673

674
    ///////////////////////////////////////////////////////////////////////////
675
    template <typename Sig>
676
    class polymorphic_executor;
2✔
677

678
    template <typename R, typename... Ts>
679
    class polymorphic_executor<R(Ts...)> : detail::polymorphic_executor_base
680
    {
681
        using base_type = detail::polymorphic_executor_base;
682
        using vtable = detail::polymorphic_executor_vtable<R(Ts...)>;
683

684
    public:
685
        constexpr polymorphic_executor() noexcept
686
          : base_type(get_empty_vtable())
687
        {
688
        }
689

690
        polymorphic_executor(polymorphic_executor const& other)
691
          : base_type(other, get_empty_vtable())
692
        {
693
        }
694

695
        polymorphic_executor(polymorphic_executor&& other) noexcept
696
          : base_type(HPX_MOVE(other), get_empty_vtable())
697
        {
698
        }
699

700
        polymorphic_executor& operator=(polymorphic_executor const& other)
701
        {
702
            base_type::op_assign(other, get_empty_vtable());
703
            return *this;
704
        }
705

706
        polymorphic_executor& operator=(polymorphic_executor&& other) noexcept
707
        {
708
            base_type::op_assign(HPX_MOVE(other), get_empty_vtable());
709
            return *this;
710
        }
711

712
        template <typename Exec, typename PE = std::decay_t<Exec>,
713
            typename Enable =
714
                std::enable_if_t<!std::is_same_v<PE, polymorphic_executor>>>
715
        polymorphic_executor(Exec&& exec)
2✔
716
          : base_type(get_empty_vtable())
2✔
717
        {
2✔
718
            assign(HPX_FORWARD(Exec, exec));
2✔
719
        }
2✔
720

721
        template <typename Exec, typename PE = std::decay_t<Exec>,
722
            typename Enable =
723
                std::enable_if_t<!std::is_same_v<PE, polymorphic_executor>>>
724
        polymorphic_executor& operator=(Exec&& exec)
725
        {
726
            assign(HPX_FORWARD(Exec, exec));
727
            return *this;
728
        }
729

730
    private:
731
        void assign(std::nullptr_t) noexcept
732
        {
733
            base_type::reset(get_empty_vtable());
734
        }
735

736
        template <typename Exec>
737
        void assign(Exec&& exec)
2✔
738
        {
739
            using T = std::decay_t<Exec>;
740
            static_assert(std::is_constructible_v<T, T const&>,
741
                "Exec shall be CopyConstructible");
742

743
            if (!detail::is_empty_polymorphic_executor(exec))
2✔
744
            {
745
                vtable const* exec_vptr = get_vtable<T>();
2✔
746
                void* buffer = nullptr;
2✔
747
                if (vptr == exec_vptr)
2✔
748
                {
749
                    // reuse object storage
750
                    HPX_ASSERT(object != nullptr);
×
751
                    buffer = object;
×
752
                    vtable::template get<T>(object).~T();
×
753
                }
×
754
                else
755
                {
756
                    destroy();
2✔
757
                    vptr = exec_vptr;
2✔
758
                    buffer = vtable::template allocate<T>(
2✔
759
                        storage, detail::polymorphic_executor_storage_size);
2✔
760
                }
761
                object = hpx::construct_at(
2✔
762
                    static_cast<T*>(buffer), HPX_FORWARD(Exec, exec));
2✔
763
            }
2✔
764
            else
765
            {
766
                base_type::reset(get_empty_vtable());
×
767
            }
768
        }
2✔
769

770
    public:
771
        void reset() noexcept
772
        {
773
            base_type::reset(get_empty_vtable());
774
        }
775

776
        using base_type::empty;
777
        using base_type::swap;
778
        using base_type::operator bool;
779

780
        ///////////////////////////////////////////////////////////////////////
781
        // actual executor interface
782

783
        // This executor always exposes future<R>
784
        template <typename>
785
        using future_type = hpx::future<R>;
786

787
    private:
788
        // NonBlockingOneWayExecutor interface
789
        template <typename F>
790
        HPX_FORCEINLINE friend void tag_invoke(hpx::parallel::execution::post_t,
791
            polymorphic_executor const& exec, F&& f, Ts... ts)
792
        {
793
            using function_type = typename vtable::post_function_type;
794

795
            vtable const* vptr_ =
796
                static_cast<vtable const*>(exec.base_type::vptr);
797
            vptr_->post(exec.object, function_type(HPX_FORWARD(F, f)),
798
                HPX_FORWARD(Ts, ts)...);
799
        }
800

801
        // OneWayExecutor interface
802
        template <typename F>
803
        HPX_FORCEINLINE friend R tag_invoke(
2✔
804
            hpx::parallel::execution::sync_execute_t,
805
            polymorphic_executor const& exec, F&& f, Ts... ts)
806
        {
807
            using function_type = typename vtable::sync_execute_function_type;
808

809
            vtable const* vptr_ =
2✔
810
                static_cast<vtable const*>(exec.base_type::vptr);
2✔
811
            return vptr_->sync_execute(exec.object,
4✔
812
                function_type(HPX_FORWARD(F, f)), HPX_FORWARD(Ts, ts)...);
2✔
813
        }
×
814

815
        // TwoWayExecutor interface
816
        template <typename F>
817
        HPX_FORCEINLINE friend hpx::future<R> tag_invoke(
2✔
818
            hpx::parallel::execution::async_execute_t,
819
            polymorphic_executor const& exec, F&& f, Ts... ts)
820
        {
821
            using function_type = typename vtable::async_execute_function_type;
822

823
            vtable const* vptr_ =
2✔
824
                static_cast<vtable const*>(exec.base_type::vptr);
2✔
825
            return vptr_->async_execute(exec.object,
4✔
826
                function_type(HPX_FORWARD(F, f)), HPX_FORWARD(Ts, ts)...);
2✔
827
        }
×
828

829
        template <typename F, typename Future>
830
        HPX_FORCEINLINE friend hpx::future<R> tag_invoke(
2✔
831
            hpx::parallel::execution::then_execute_t,
832
            polymorphic_executor const& exec, F&& f, Future&& predecessor,
833
            Ts&&... ts)
834
        {
835
            using function_type = typename vtable::then_execute_function_type;
836

837
            vtable const* vptr_ =
2✔
838
                static_cast<vtable const*>(exec.base_type::vptr);
2✔
839
            return vptr_->then_execute(exec.object,
4✔
840
                function_type(HPX_FORWARD(F, f)),
2✔
841
                hpx::make_shared_future(HPX_FORWARD(Future, predecessor)),
2✔
842
                HPX_FORWARD(Ts, ts)...);
2✔
843
        }
×
844

845
        // BulkOneWayExecutor interface
846
        // clang-format off
847
        template <typename F, typename Shape,
848
            HPX_CONCEPT_REQUIRES_(
849
                !std::is_integral_v<Shape>
850
            )>
851
        // clang-format on
852
        HPX_FORCEINLINE friend std::vector<R> tag_invoke(
4✔
853
            hpx::parallel::execution::bulk_sync_execute_t,
854
            polymorphic_executor const& exec, F&& f, Shape const& s, Ts&&... ts)
855
        {
856
            using function_type =
857
                typename vtable::bulk_sync_execute_function_type;
858

859
            detail::range_proxy shape(s);
4✔
860
            vtable const* vptr_ =
4✔
861
                static_cast<vtable const*>(exec.base_type::vptr);
4✔
862
            return vptr_->bulk_sync_execute(exec.object,
4✔
863
                function_type(HPX_FORWARD(F, f)), shape,
4✔
864
                HPX_FORWARD(Ts, ts)...);
4✔
865
        }
4✔
866

867
        // BulkTwoWayExecutor interface
868
        // clang-format off
869
        template <typename F, typename Shape,
870
            HPX_CONCEPT_REQUIRES_(
871
                !std::is_integral_v<Shape>
872
            )>
873
        // clang-format on
874
        HPX_FORCEINLINE friend std::vector<hpx::future<R>> tag_invoke(
4✔
875
            hpx::parallel::execution::bulk_async_execute_t,
876
            polymorphic_executor const& exec, F&& f, Shape const& s, Ts&&... ts)
877
        {
878
            using function_type =
879
                typename vtable::bulk_async_execute_function_type;
880

881
            detail::range_proxy shape(s);
4✔
882
            vtable const* vptr_ =
4✔
883
                static_cast<vtable const*>(exec.base_type::vptr);
4✔
884
            return vptr_->bulk_async_execute(exec.object,
4✔
885
                function_type(HPX_FORWARD(F, f)), shape,
4✔
886
                HPX_FORWARD(Ts, ts)...);
4✔
887
        }
4✔
888

889
        // clang-format off
890
        template <typename F, typename Shape,
891
            HPX_CONCEPT_REQUIRES_(
892
                !std::is_integral_v<Shape>
893
            )>
894
        // clang-format on
895
        HPX_FORCEINLINE friend hpx::future<std::vector<R>> tag_invoke(
4✔
896
            hpx::parallel::execution::bulk_then_execute_t,
897
            polymorphic_executor const& exec, F&& f, Shape const& s,
898
            hpx::shared_future<void> const& predecessor, Ts&&... ts)
899
        {
900
            using function_type =
901
                typename vtable::bulk_then_execute_function_type;
902

903
            detail::range_proxy shape(s);
4✔
904
            vtable const* vptr_ =
4✔
905
                static_cast<vtable const*>(exec.base_type::vptr);
4✔
906
            return vptr_->bulk_then_execute(exec.object,
4✔
907
                function_type(HPX_FORWARD(F, f)), shape, predecessor,
4✔
908
                HPX_FORWARD(Ts, ts)...);
4✔
909
        }
4✔
910

911
    private:
912
        static constexpr vtable const* get_empty_vtable() noexcept
2✔
913
        {
914
            return detail::get_empty_polymorphic_executor_vtable<R(Ts...)>();
2✔
915
        }
916

917
        template <typename T>
918
        static constexpr vtable const* get_vtable() noexcept
2✔
919
        {
920
            return detail::get_polymorphic_executor_vtable<vtable, T>();
2✔
921
        }
922

923
    protected:
924
        using base_type::object;
925
        using base_type::storage;
926
        using base_type::vptr;
927
    };
928

929
    /// \cond NOINTERNAL
930
    template <typename Sig>
931
    struct is_never_blocking_one_way_executor<
932
        parallel::execution::polymorphic_executor<Sig>> : std::true_type
933
    {
934
    };
935

936
    template <typename Sig>
937
    struct is_one_way_executor<parallel::execution::polymorphic_executor<Sig>>
938
      : std::true_type
939
    {
940
    };
941

942
    template <typename Sig>
943
    struct is_two_way_executor<parallel::execution::polymorphic_executor<Sig>>
944
      : std::true_type
945
    {
946
    };
947

948
    template <typename Sig>
949
    struct is_bulk_one_way_executor<
950
        parallel::execution::polymorphic_executor<Sig>> : std::true_type
951
    {
952
    };
953

954
    template <typename Sig>
955
    struct is_bulk_two_way_executor<
956
        parallel::execution::polymorphic_executor<Sig>> : std::true_type
957
    {
958
    };
959
    /// \endcond
960
}    // namespace hpx::parallel::execution
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