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

STEllAR-GROUP / hpx / #882

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

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

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

53.13
/libs/core/threading_base/include/hpx/threading_base/thread_data.hpp
1
//  Copyright (c) 2007-2025 Hartmut Kaiser
2
//  Copyright (c)      2011 Bryce Lelbach
3
//  Copyright (c) 2008-2009 Chirag Dekate, Anshul Tandon
4
//
5
//  SPDX-License-Identifier: BSL-1.0
6
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
7
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8

9
#pragma once
10

11
#include <hpx/config.hpp>
12
#include <hpx/assert.hpp>
13
#include <hpx/modules/concurrency.hpp>
14
#include <hpx/modules/coroutines.hpp>
15
#include <hpx/modules/debugging.hpp>
16
#include <hpx/modules/errors.hpp>
17
#include <hpx/modules/execution_base.hpp>
18
#include <hpx/modules/functional.hpp>
19
#include <hpx/modules/logging.hpp>
20
#include <hpx/threading_base/thread_description.hpp>
21
#include <hpx/threading_base/thread_init_data.hpp>
22
#include <hpx/threading_base/threading_base_fwd.hpp>
23
#if defined(HPX_HAVE_APEX)
24
#include <hpx/threading_base/external_timer.hpp>
25
#endif
26

27
#include <atomic>
28
#include <cstddef>
29
#include <cstdint>
30
#include <forward_list>
31
#include <limits>
32
#include <memory>
33
#include <mutex>
34
#include <utility>
35

36
#if defined(HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION)
37
#include <string>
38
#endif
39

40
#include <hpx/config/warnings_prefix.hpp>
41

42
////////////////////////////////////////////////////////////////////////////////
43
namespace hpx::threads {
44

45
    namespace detail {
46

47
        HPX_CXX_EXPORT using get_locality_id_type =
48
            std::uint32_t(hpx::error_code&);
49
        HPX_CXX_EXPORT HPX_CORE_EXPORT void set_get_locality_id(
50
            get_locality_id_type* f);
51
        HPX_CXX_EXPORT HPX_CORE_EXPORT std::uint32_t get_locality_id(
52
            hpx::error_code&);
53
    }    // namespace detail
54

55
    ////////////////////////////////////////////////////////////////////////////
56
    /// A \a thread is the representation of a HPX thread. It's a first class
57
    /// object in HPX. In our implementation this is a user level thread running
58
    /// on top of one of the OS threads spawned by the \a thread-manager.
59
    ///
60
    /// A \a thread encapsulates:
61
    ///  - A thread status word (see the functions \a thread#get_state and
62
    ///    \a thread#set_state)
63
    ///  - A function to execute (the thread function)
64
    ///  - A frame (in this implementation this is a block of memory used as the
65
    ///    threads stack)
66
    ///  - A block of registers (not implemented yet)
67
    ///
68
    /// Generally, \a threads are not created or executed directly. All
69
    /// functionality related to the management of \a threads is implemented by
70
    /// the thread-manager.
71
    HPX_CXX_EXPORT class thread_data
72
      : public detail::thread_data_reference_counting
73
    {
74
    public:
75
        thread_data(thread_data const&) = delete;
76
        thread_data(thread_data&&) = delete;
77
        thread_data& operator=(thread_data const&) = delete;
78
        thread_data& operator=(thread_data&&) = delete;
79

80
    public:
81
        using spinlock_pool = util::spinlock_pool<thread_data>;
82

83
        /// The get_state function queries the state of this thread instance.
84
        ///
85
        /// \returns        This function returns the current state of this
86
        ///                 thread. It will return one of the values as defined
87
        ///                 by the \a thread_state enumeration.
88
        ///
89
        /// \note           This function will seldom be used directly. Most of
90
        ///                 the time the state of a thread will be retrieved
91
        ///                 by using the function \a threadmanager#get_state.
92
        thread_state get_state(std::memory_order const order =
93
                                   std::memory_order_acquire) const noexcept
94
        {
95
            return current_state_.load(order);
96
        }
97

98
        /// The set_state function changes the state of this thread instance.
99
        ///
100
        /// \param state    [in] The new state to be set for the thread.
101
        /// \param state_ex       [in]
102
        /// \param load_order     [in]
103
        /// \param exchange_order [in]
104
        ///
105
        /// \note           This function will seldom be used directly. Most of
106
        ///                 the time the state of a thread will have to be
107
        ///                 changed using the thread-manager. Moreover,
108
        ///                 changing the thread state using this function does
109
        ///                 not change its scheduling status. It only sets the
110
        ///                 thread's status word. To change the thread's
111
        ///                 scheduling status \a threadmanager#set_state should
112
        ///                 be used.
130✔
113
        thread_state set_state(thread_schedule_state const state,
114
            thread_restart_state state_ex = thread_restart_state::unknown,
115
            std::memory_order const load_order = std::memory_order_acquire,
116
            std::memory_order const exchange_order =
117
                std::memory_order_acq_rel) const noexcept
118
        {
119
            thread_state prev_state = current_state_.load(load_order);
120

121
            for (;;)
122
            {
130✔
123
                thread_state tmp = prev_state;
124

125
                // ABA prevention for state only (not for state_ex)
126
                std::int64_t tag = tmp.tag();
130✔
127
                if (state != tmp.state())
130✔
128
                    ++tag;
129

130✔
130
                if (state_ex == thread_restart_state::unknown)
131
                    state_ex = tmp.state_ex();
132

130✔
133
                if (HPX_LIKELY(current_state_.compare_exchange_strong(tmp,
134
                        thread_state(state, state_ex, tag), exchange_order)))
135
                {
130✔
136
                    return prev_state;
137
                }
138

×
139
                prev_state = tmp;
×
140
            }
141
        }
142

1,831,158✔
143
        bool set_state_tagged(thread_schedule_state const newstate,
144
            thread_state const& prev_state, thread_state& new_tagged_state,
145
            std::memory_order exchange_order =
146
                std::memory_order_acq_rel) const noexcept
147
        {
1,831,158✔
148
            new_tagged_state = thread_state(
149
                newstate, prev_state.state_ex(), prev_state.tag() + 1);
150

1,831,158✔
151
            thread_state tmp = prev_state;
1,831,158✔
152
            return current_state_.compare_exchange_strong(
1,831,158✔
153
                tmp, new_tagged_state, exchange_order);
154
        }
155

156
        /// The restore_state function changes the state of this thread
157
        /// instance depending on its current state. It will change the state
158
        /// atomically only if the current state is still the same as passed
159
        /// as the second parameter. Otherwise, it won't touch the thread state
160
        /// of this instance.
161
        ///
162
        /// \param new_state [in] The new state to be set for the thread.
163
        /// \param old_state [in] The old state of the thread which still has to
164
        ///                 be the current state.
165
        /// \param load_order    [in]
166
        /// \param load_exchange [in]
167
        ///
168
        /// \note           This function will seldom be used directly. Most of
169
        ///                 the time the state of a thread will have to be
170
        ///                 changed using the threadmanager. Moreover,
171
        ///                 changing the thread state using this function does
172
        ///                 not change its scheduling status. It only sets the
173
        ///                 thread's status word. To change the thread's
174
        ///                 scheduling status \a threadmanager#set_state should
175
        ///                 be used.
176
        ///
177
        /// \returns This function returns \a true if the state has been
178
        ///          changed successfully
1,831,158✔
179
        bool restore_state(thread_state const new_state,
180
            thread_state const old_state,
181
            std::memory_order const load_order = std::memory_order_relaxed,
182
            std::memory_order const load_exchange =
183
                std::memory_order_acq_rel) const noexcept
184
        {
185
            // ignore the state_ex while compare-exchanging
186
            thread_state const current_state = current_state_.load(load_order);
187
            thread_restart_state const state_ex = current_state.state_ex();
188

189
            // ABA prevention for state only (not for state_ex)
1,831,158✔
190
            std::int64_t tag = current_state.tag();
1,831,158✔
191
            if (new_state.state() != old_state.state())
192
                ++tag;
193

194
            thread_state old_tmp(old_state.state(), state_ex, old_state.tag());
195
            thread_state const new_tmp(new_state.state(), state_ex, tag);
1,831,158✔
196

1,831,158✔
197
            return current_state_.compare_exchange_strong(
198
                old_tmp, new_tmp, load_exchange);
199
        }
28,247✔
200

201
        bool restore_state(thread_schedule_state new_state,
202
            thread_restart_state const state_ex, thread_state old_state,
203
            std::memory_order const load_exchange =
204
                std::memory_order_acq_rel) const noexcept
205
        {
206
            // ABA prevention for state only (not for state_ex)
28,247✔
207
            std::int64_t tag = old_state.tag();
28,247✔
208
            if (new_state != old_state.state())
209
                ++tag;
28,247✔
210

28,247✔
211
            return current_state_.compare_exchange_strong(old_state,
212
                thread_state(new_state, state_ex, tag), load_exchange);
213
        }
214

215
    protected:
216
        /// The set_state function changes the extended state of this
217
        /// thread instance.
218
        ///
219
        /// \param new_state [in] The new extended state to be set for the
220
        ///                 thread.
221
        /// \param load_order [in]
222
        /// \param load_exchange [in]
223
        ///
1,831,158✔
224
        /// \note           This function will seldom be used directly. Most of
225
        ///                 the time the state of a thread will have to be
226
        ///                 changed using the threadmanager.
227
        thread_restart_state set_state_ex(thread_restart_state const new_state,
228
            std::memory_order const load_order = std::memory_order_acquire,
229
            std::memory_order const load_exchange =
230
                std::memory_order_acq_rel) const noexcept
231
        {
1,831,158✔
232
            thread_state prev_state = current_state_.load(load_order);
233

1,831,158✔
234
            for (;;)
235
            {
236
                thread_state tmp = prev_state;
1,831,158✔
237

238
                if (HPX_LIKELY(current_state_.compare_exchange_strong(tmp,
239
                        thread_state(tmp.state(), new_state, tmp.tag()),
×
240
                        load_exchange)))
×
241
                {
242
                    return prev_state.state_ex();
243
                }
244

245
                prev_state = tmp;
246
            }
247
        }
248

249
    public:
250
        /// Return the id of the component this thread is running in
251
        static constexpr std::uint64_t    // same as naming::address_type
252
        get_component_id() noexcept
253
        {
254
            return 0;
255
        }
256

257
#if !defined(HPX_HAVE_THREAD_DESCRIPTION)
258
        static constexpr threads::thread_description get_description() noexcept
259
        {
260
            return {"<unknown>"};
261
        }
262
        static constexpr threads::thread_description set_description(
263
            threads::thread_description /*value*/) noexcept
264
        {
265
            return {"<unknown>"};
266
        }
267

268
        static constexpr threads::thread_description
269
        get_lco_description() noexcept    //-V524
270
        {
271
            return {"<unknown>"};
272
        }
273
        static constexpr threads::thread_description set_lco_description(
274
            threads::thread_description /*value*/) noexcept    //-V524
275
        {
276
            return {"<unknown>"};
277
        }
278
#else
279
        threads::thread_description get_description() const
280
        {
281
            std::lock_guard<hpx::util::detail::spinlock> l(
282
                spinlock_pool::spinlock_for(this));
283
            return description_;
284
        }
285
        threads::thread_description set_description(
286
            threads::thread_description value)
287
        {
288
            std::lock_guard<hpx::util::detail::spinlock> l(
289
                spinlock_pool::spinlock_for(this));
290
            std::swap(description_, value);
291
            return value;
292
        }
293

294
        threads::thread_description get_lco_description() const
295
        {
296
            std::lock_guard<hpx::util::detail::spinlock> l(
297
                spinlock_pool::spinlock_for(this));
298
            return lco_description_;
299
        }
300
        threads::thread_description set_lco_description(
301
            threads::thread_description value)
302
        {
303
            std::lock_guard<hpx::util::detail::spinlock> l(
304
                spinlock_pool::spinlock_for(this));
305
            std::swap(lco_description_, value);
306
            return value;
307
        }
308
#endif
309

310
#if !defined(HPX_HAVE_THREAD_PARENT_REFERENCE)
311
        /// Return the locality of the parent thread
312
        static constexpr std::uint32_t get_parent_locality_id() noexcept
313
        {
314
            // this is the same as naming::invalid_locality_id
315
            return ~static_cast<std::uint32_t>(0);
316
        }
317

318
        /// Return the thread id of the parent thread
319
        static constexpr thread_id_type get_parent_thread_id() noexcept
320
        {
321
            return threads::invalid_thread_id;
322
        }
323

324
        /// Return the phase of the parent thread
325
        static constexpr std::size_t get_parent_thread_phase() noexcept
326
        {
327
            return 0;
328
        }
329
#else
330
        /// Return the locality of the parent thread
331
        std::uint32_t get_parent_locality_id() const noexcept
332
        {
333
            return parent_locality_id_;
334
        }
335

336
        /// Return the thread id of the parent thread
337
        thread_id_type get_parent_thread_id() const noexcept
338
        {
339
            return parent_thread_id_;
340
        }
341

342
        /// Return the phase of the parent thread
343
        std::size_t get_parent_thread_phase() const noexcept
344
        {
345
            return parent_thread_phase_;
346
        }
347
#endif
348

349
#ifdef HPX_HAVE_THREAD_MINIMAL_DEADLOCK_DETECTION
350
        void set_marked_state(thread_schedule_state mark) const noexcept
351
        {
352
            marked_state_ = mark;
353
        }
354
        thread_schedule_state get_marked_state() const noexcept
355
        {
356
            return marked_state_;
357
        }
358
#endif
359

360
#if !defined(HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION)
361

362
#ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
363
        static constexpr char const* get_backtrace() noexcept
364
        {
365
            return nullptr;
366
        }
367
        static constexpr char const* set_backtrace(char const*) noexcept
368
        {
369
            return nullptr;
370
        }
371
#else
372
        static constexpr util::backtrace const* get_backtrace() noexcept
373
        {
374
            return nullptr;
375
        }
376
        static constexpr util::backtrace const* set_backtrace(
377
            util::backtrace const*) noexcept
378
        {
379
            return nullptr;
380
        }
381
#endif
382

383
#else    // defined(HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
384

385
#ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
386
        char const* get_backtrace() const noexcept
387
        {
388
            std::lock_guard<hpx::util::detail::spinlock> l(
389
                spinlock_pool::spinlock_for(this));
390
            return backtrace_;
391
        }
392
        char const* set_backtrace(char const* value) noexcept
393
        {
394
            std::lock_guard<hpx::util::detail::spinlock> l(
395
                spinlock_pool::spinlock_for(this));
396

397
            char const* bt = backtrace_;
398
            backtrace_ = value;
399
            return bt;
400
        }
401
#else
402
        util::backtrace const* get_backtrace() const noexcept
403
        {
404
            std::lock_guard<hpx::util::detail::spinlock> l(
405
                spinlock_pool::spinlock_for(this));
406
            return backtrace_;
407
        }
408
        util::backtrace const* set_backtrace(
409
            util::backtrace const* value) noexcept
410
        {
411
            std::lock_guard<hpx::util::detail::spinlock> l(
412
                spinlock_pool::spinlock_for(this));
413

414
            util::backtrace const* bt = backtrace_;
415
            backtrace_ = value;
416
            return bt;
417
        }
418
#endif
419

420
        // Generate full backtrace for captured stack
421
        std::string backtrace()
422
        {
423
            std::lock_guard<hpx::util::detail::spinlock> l(
424
                spinlock_pool::spinlock_for(this));
425

426
            std::string bt;
427
            if (0 != backtrace_)
428
            {
429
#ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
430
                bt = *backtrace_;
431
#else
432
                bt = backtrace_->trace();
433
#endif
434
            }
71,485✔
435
            return bt;
436
        }
437
#endif
438

439
        constexpr thread_priority get_priority() const noexcept
440
        {
441
            return priority_;
442
        }
×
443
        void set_priority(thread_priority priority) noexcept
444
        {
445
            priority_ = priority;
×
446
        }
×
447

448
        // handle thread interruption
449
        bool interruption_requested() const noexcept
×
450
        {
451
            std::lock_guard<hpx::util::detail::spinlock> l(
452
                spinlock_pool::spinlock_for(this));
×
453
            return requested_interrupt_;
×
454
        }
455

456
        bool interruption_enabled() const noexcept
×
457
        {
458
            std::lock_guard<hpx::util::detail::spinlock> l(
459
                spinlock_pool::spinlock_for(this));
×
460
            return enabled_interrupt_;
461
        }
×
462

463
        bool set_interruption_enabled(bool enable) noexcept
464
        {
×
465
            std::lock_guard<hpx::util::detail::spinlock> l(
466
                spinlock_pool::spinlock_for(this));
467
            std::swap(enabled_interrupt_, enable);
468
            return enable;
×
469
        }
470

×
471
        void interrupt(bool flag = true)
×
472
        {
473
            std::unique_lock<hpx::util::detail::spinlock> l(
474
                spinlock_pool::spinlock_for(this));
475
            if (flag && !enabled_interrupt_)
×
476
            {
×
477
                l.unlock();
478
                HPX_THROW_EXCEPTION(hpx::error::thread_not_interruptable,
479
                    "thread_data::interrupt",
480
                    "interrupts are disabled for this thread");
481
            }
482
            requested_interrupt_ = flag;
483
        }
484

485
        bool interruption_point(bool throw_on_interrupt = true);
486

487
        bool add_thread_exit_callback(function<void()> const& f);
488
        void run_thread_exit_callbacks();
489
        void free_thread_exit_callbacks();
490

491
        // no need to protect the variables related to scoped children as those
492
        // are supposed to be accessed by ourselves only
493
        bool runs_as_child(
494
            std::memory_order mo = std::memory_order_acquire) const noexcept
1,831,158✔
495
        {
496
            return runs_as_child_.load(mo);
497
        }
498

499
        HPX_FORCEINLINE constexpr bool is_stackless() const noexcept
×
500
        {
501
            return is_stackless_;
116,985✔
502
        }
503

504
        void destroy_thread() override;
505

506
        constexpr policies::scheduler_base* get_scheduler_base() const noexcept
×
507
        {
508
            return scheduler_base_;
509
        }
510

511
        constexpr std::uint16_t get_last_worker_thread_num() const noexcept
512
        {
714,230✔
513
            return last_worker_thread_num_;
514
        }
515

516
        void set_last_worker_thread_num(
517
            std::uint16_t last_worker_thread_num) noexcept
85,663✔
518
        {
519
            last_worker_thread_num_ = last_worker_thread_num;
520
        }
521

522
        constexpr std::ptrdiff_t get_stack_size() const noexcept
2✔
523
        {
524
            return stacksize_enum_ == thread_stacksize::nostack ?
525
                (std::numeric_limits<std::ptrdiff_t>::max)() :
526
                stacksize_;
527
        }
528

43,912✔
529
        thread_stacksize get_stack_size_enum() const noexcept
530
        {
531
            return stacksize_enum_;
532
        }
533

534
        template <typename ThreadQueue>
535
        constexpr ThreadQueue& get_queue() noexcept
536
        {
537
            return *static_cast<ThreadQueue*>(queue_);
538
        }
539

540
        /// \brief Execute the thread function
541
        ///
542
        /// \returns        This function returns the thread state the thread
543
        ///                 should be scheduled from this point on. The thread
544
        ///                 manager will use the returned value to set the
545
        ///                 thread's scheduling status.
546
        inline coroutine_type::result_type operator()(
547
            hpx::execution_base::this_thread::detail::agent_storage*
548
                agent_storage);
549

×
550
        /// \brief Directly execute the thread function (inline)
551
        ///
×
552
        /// \returns        This function returns the thread state the thread
553
        ///                 should be scheduled from this point on. The thread
554
        ///                 manager will use the returned value to set the
555
        ///                 thread's scheduling status.
×
556
        inline coroutine_type::result_type invoke_directly();
557

×
558
        virtual thread_id_type get_thread_id() const
559
        {
560
            return thread_id_type{const_cast<thread_data*>(this)};
561
        }
562

563
#if !defined(HPX_HAVE_THREAD_PHASE_INFORMATION)
564
        virtual std::size_t get_thread_phase() const noexcept
565
        {
566
            return 0;
567
        }
568
#else
569
        virtual std::size_t get_thread_phase() const noexcept = 0;
570
#endif
571
        virtual std::size_t get_thread_data() const = 0;
572
        virtual std::size_t set_thread_data(std::size_t data) = 0;
573

574
#if defined(HPX_HAVE_LIBCDS)
575
        virtual std::size_t get_libcds_data() const = 0;
576
        virtual std::size_t set_libcds_data(std::size_t data) = 0;
577
        virtual std::size_t get_libcds_hazard_pointer_data() const = 0;
578
        virtual std::size_t set_libcds_hazard_pointer_data(
579
            std::size_t data) = 0;
580
        virtual std::size_t get_libcds_dynamic_hazard_pointer_data() const = 0;
581
        virtual std::size_t set_libcds_dynamic_hazard_pointer_data(
582
            std::size_t data) = 0;
583
#endif
584

585
        virtual void init() = 0;
586
        virtual void rebind(thread_init_data& init_data) = 0;
587

588
#if defined(HPX_HAVE_APEX)
589
        std::shared_ptr<util::external_timer::task_wrapper> get_timer_data()
590
            const noexcept
591
        {
592
            return timer_data_;
593
        }
594
        void set_timer_data(
595
            std::shared_ptr<util::external_timer::task_wrapper> data) noexcept
596
        {
597
            timer_data_ = data;
598
        }
599
#endif
600

601
        // Construct a new \a thread
602
        thread_data(thread_init_data& init_data, void* queue,
603
            std::ptrdiff_t stacksize, bool is_stackless = false,
604
            thread_id_addref addref = thread_id_addref::yes);
605

606
        ~thread_data() override;
607
        virtual void destroy() noexcept = 0;
608

609
    protected:
610
        void rebind_base(thread_init_data& init_data);
611

612
    private:
613
        thread_priority priority_;
614

615
        bool requested_interrupt_;
616
        bool enabled_interrupt_;
617
        bool ran_exit_funcs_;
618
        bool const is_stackless_;
619

620
        // support scoped child execution
621
        std::atomic<bool> runs_as_child_;
622

623
        std::uint16_t last_worker_thread_num_;
624

625
        thread_stacksize stacksize_enum_;
626
        std::int32_t stacksize_;
627

628
        mutable std::atomic<thread_state> current_state_;
629

630
        // Singly linked list (heap-allocated)
631
        std::forward_list<hpx::function<void()>> exit_funcs_;
632

633
        // reference to scheduler which created/manages this thread
634
        policies::scheduler_base* scheduler_base_;
635

636
        void* queue_;
637

638
        ///////////////////////////////////////////////////////////////////////
639
        // Debugging/logging information
640
#ifdef HPX_HAVE_THREAD_DESCRIPTION
641
        threads::thread_description description_;
642
        threads::thread_description lco_description_;
643
#endif
644

645
#ifdef HPX_HAVE_THREAD_MINIMAL_DEADLOCK_DETECTION
646
        mutable thread_schedule_state marked_state_;
647
#endif
648

649
#ifdef HPX_HAVE_THREAD_PARENT_REFERENCE
650
        std::uint32_t parent_locality_id_;
651
        thread_id_type parent_thread_id_;
652
        std::size_t parent_thread_phase_;
653
#endif
654

655
#ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
656
#ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
657
        char const* backtrace_ = nullptr;
658
#else
659
        util::backtrace const* backtrace_ = nullptr;
660
#endif
661
#endif
662

×
663
    public:
664
#if defined(HPX_HAVE_APEX)
665
        std::shared_ptr<util::external_timer::task_wrapper> timer_data_;
666
#endif
667
    };
668

×
669
    HPX_CXX_EXPORT HPX_FORCEINLINE constexpr thread_data* get_thread_id_data(
670
        thread_id_ref_type const& tid) noexcept
671
    {
672
        return static_cast<thread_data*>(tid.get().get());
673
    }
674

675
    HPX_CXX_EXPORT HPX_FORCEINLINE constexpr thread_data* get_thread_id_data(
676
        thread_id_type const& tid) noexcept
677
    {
678
        return static_cast<thread_data*>(tid.get());
679
    }
680
}    // namespace hpx::threads
681

682
#include <hpx/config/warnings_suffix.hpp>
683

684
#include <hpx/threading_base/thread_data_stackful.hpp>
685
#include <hpx/threading_base/thread_data_stackless.hpp>
1,831,158✔
686

687
namespace hpx::threads {
×
688

689
    HPX_FORCEINLINE coroutine_type::result_type thread_data::operator()(
690
        hpx::execution_base::this_thread::detail::agent_storage* agent_storage)
691
    {
692
        // once a thread has started it can't be run directly anymore
693
        runs_as_child_.store(false, std::memory_order_release);
694

695
        if (is_stackless())
696
        {
×
697
            return static_cast<thread_data_stackless*>(this)->call();
698
        }
×
699
        return static_cast<thread_data_stackful*>(this)->call(agent_storage);
700
    }
701

702
    HPX_FORCEINLINE coroutine_type::result_type thread_data::invoke_directly()
703
    {
704
        HPX_ASSERT(runs_as_child(std::memory_order_relaxed));
705

706
        if (is_stackless())
707
        {
708
            return static_cast<thread_data_stackless*>(this)->call();
709
        }
710
        return static_cast<thread_data_stackful*>(this)->invoke_directly();
711
    }
712
}    // namespace hpx::threads
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