1 //  Copyright (c) 2007-2016 Hartmut Kaiser
2 //  Copyright (c)      2011 Bryce Lelbach
3 //  Copyright (c) 2008-2009 Chirag Dekate, Anshul Tandon
4 //
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 #ifndef HPX_RUNTIME_THREADS_THREAD_DATA_HPP
9 #define HPX_RUNTIME_THREADS_THREAD_DATA_HPP
10 
11 #include <hpx/config.hpp>
12 #include <hpx/runtime/get_locality_id.hpp>
13 #include <hpx/runtime/naming_fwd.hpp>
14 #include <hpx/runtime/threads/coroutines/coroutine.hpp>
15 #include <hpx/runtime/threads/detail/combined_tagged_state.hpp>
16 #include <hpx/runtime/threads/thread_data_fwd.hpp>
17 #include <hpx/runtime/threads/thread_init_data.hpp>
18 #include <hpx/throw_exception.hpp>
19 
20 #include <hpx/util/assert.hpp>
21 #include <hpx/util/atomic_count.hpp>
22 #include <hpx/util/backtrace.hpp>
23 #include <hpx/util/function.hpp>
24 #include <hpx/util/logging.hpp>
25 #include <hpx/util/spinlock_pool.hpp>
26 #include <hpx/util/thread_description.hpp>
27 #if defined(HPX_HAVE_APEX)
28 #include <hpx/util/apex.hpp>
29 #endif
30 
31 #include <boost/intrusive_ptr.hpp>
32 
33 #include <atomic>
34 #include <cstddef>
35 #include <cstdint>
36 #include <stack>
37 #include <string>
38 #include <utility>
39 
40 #include <hpx/config/warnings_prefix.hpp>
41 
42 ///////////////////////////////////////////////////////////////////////////////
43 namespace hpx { namespace threads
44 {
45     class thread_data;
46 
47     namespace detail
48     {
49         ///////////////////////////////////////////////////////////////////////
50         struct thread_exit_callback_node
51         {
52             util::function_nonser<void()> f_;
53             thread_exit_callback_node* next_;
54 
thread_exit_callback_nodehpx::threads::detail::thread_exit_callback_node55             thread_exit_callback_node(util::function_nonser<void()> const& f,
56                     thread_exit_callback_node* next)
57               : f_(f), next_(next)
58             {}
59 
operator ()hpx::threads::detail::thread_exit_callback_node60             void operator()()
61             {
62                 f_();
63             }
64         };
65     }
66 
67     ///////////////////////////////////////////////////////////////////////////
68     /// A \a thread is the representation of a ParalleX thread. It's a first
69     /// class object in ParalleX. In our implementation this is a user level
70     /// thread running on top of one of the OS threads spawned by the \a
71     /// thread-manager.
72     ///
73     /// A \a thread encapsulates:
74     ///  - A thread status word (see the functions \a thread#get_state and
75     ///    \a thread#set_state)
76     ///  - A function to execute (the thread function)
77     ///  - A frame (in this implementation this is a block of memory used as
78     ///    the threads stack)
79     ///  - A block of registers (not implemented yet)
80     ///
81     /// Generally, \a threads are not created or executed directly. All
82     /// functionality related to the management of \a threads is
83     /// implemented by the thread-manager.
84     class thread_data
85     {
86     public:
87         HPX_NON_COPYABLE(thread_data);
88 
89     private:
90         // Avoid warning about using 'this' in initializer list
this_()91         thread_data* this_() { return this; }
92 
93     public:
94         typedef thread_function_type function_type;
95 
96         struct tag {};
97         typedef util::spinlock_pool<tag> mutex_type;
98 
~thread_data()99         ~thread_data()
100         {
101             free_thread_exit_callbacks();
102             LTM_(debug) << "~thread(" << this << "), description(" //-V128
103                         << get_description() << "), phase("
104                         << get_thread_phase() << ")";
105         }
106 
107         /// The get_state function queries the state of this thread instance.
108         ///
109         /// \returns        This function returns the current state of this
110         ///                 thread. It will return one of the values as defined
111         ///                 by the \a thread_state enumeration.
112         ///
113         /// \note           This function will be seldom used directly. Most of
114         ///                 the time the state of a thread will be retrieved
115         ///                 by using the function \a threadmanager#get_state.
get_state(std::memory_order order=std::memory_order_acquire) const116         thread_state get_state(
117             std::memory_order order = std::memory_order_acquire) const
118         {
119             return current_state_.load(order);
120         }
121 
122         /// The set_state function changes the state of this thread instance.
123         ///
124         /// \param newstate [in] The new state to be set for the thread.
125         ///
126         /// \note           This function will be seldomly used directly. Most of
127         ///                 the time the state of a thread will have to be
128         ///                 changed using the threadmanager. Moreover,
129         ///                 changing the thread state using this function does
130         ///                 not change its scheduling status. It only sets the
131         ///                 thread's status word. To change the thread's
132         ///                 scheduling status \a threadmanager#set_state should
133         ///                 be used.
set_state(thread_state_enum state,thread_state_ex_enum state_ex=wait_unknown,std::memory_order load_order=std::memory_order_acquire,std::memory_order exchange_order=std::memory_order_seq_cst)134         thread_state set_state(thread_state_enum state,
135             thread_state_ex_enum state_ex = wait_unknown,
136             std::memory_order load_order = std::memory_order_acquire,
137             std::memory_order exchange_order = std::memory_order_seq_cst)
138         {
139             thread_state prev_state = current_state_.load(load_order);
140 
141             for (;;) {
142                 thread_state tmp = prev_state;
143 
144                 // ABA prevention for state only (not for state_ex)
145                 std::int64_t tag = tmp.tag();
146                 if (state != tmp.state())
147                     ++tag;
148 
149                 if (state_ex == wait_unknown)
150                     state_ex = tmp.state_ex();
151 
152                 if (HPX_LIKELY(current_state_.compare_exchange_strong(tmp,
153                         thread_state(state, state_ex, tag), exchange_order)))
154                 {
155                     return prev_state;
156                 }
157 
158                 prev_state = tmp;
159             }
160         }
161 
set_state_tagged(thread_state_enum newstate,thread_state & prev_state,thread_state & new_tagged_state,std::memory_order exchange_order=std::memory_order_seq_cst)162         bool set_state_tagged(thread_state_enum newstate,
163             thread_state& prev_state, thread_state& new_tagged_state,
164             std::memory_order exchange_order = std::memory_order_seq_cst)
165         {
166             thread_state tmp = prev_state;
167             thread_state_ex_enum state_ex = tmp.state_ex();
168 
169             new_tagged_state = thread_state(newstate, state_ex,
170                 prev_state.tag() + 1);
171 
172             if (!current_state_.compare_exchange_strong(
173                     tmp, new_tagged_state, exchange_order))
174             {
175                 return false;
176             }
177 
178             prev_state = tmp;
179             return true;
180         }
181 
182         /// The restore_state function changes the state of this thread
183         /// instance depending on its current state. It will change the state
184         /// atomically only if the current state is still the same as passed
185         /// as the second parameter. Otherwise it won't touch the thread state
186         /// of this instance.
187         ///
188         /// \param newstate [in] The new state to be set for the thread.
189         /// \param oldstate [in] The old state of the thread which still has to
190         ///                 be the current state.
191         ///
192         /// \note           This function will be seldomly used directly. Most of
193         ///                 the time the state of a thread will have to be
194         ///                 changed using the threadmanager. Moreover,
195         ///                 changing the thread state using this function does
196         ///                 not change its scheduling status. It only sets the
197         ///                 thread's status word. To change the thread's
198         ///                 scheduling status \a threadmanager#set_state should
199         ///                 be used.
200         ///
201         /// \returns This function returns \a true if the state has been
202         ///          changed successfully
restore_state(thread_state new_state,thread_state old_state,std::memory_order load_order=std::memory_order_relaxed,std::memory_order load_exchange=std::memory_order_seq_cst)203         bool restore_state(thread_state new_state, thread_state old_state,
204             std::memory_order load_order = std::memory_order_relaxed,
205             std::memory_order load_exchange = std::memory_order_seq_cst)
206         {
207             // ABA prevention for state only (not for state_ex)
208             std::int64_t tag = old_state.tag();
209             if (new_state.state() != old_state.state())
210                 ++tag;
211 
212             // ignore the state_ex while compare-exchanging
213             thread_state_ex_enum state_ex =
214                 current_state_.load(load_order).state_ex();
215 
216             thread_state old_tmp(old_state.state(), state_ex, old_state.tag());
217             thread_state new_tmp(new_state.state(), state_ex, tag);
218 
219             return current_state_.compare_exchange_strong(
220                 old_tmp, new_tmp, load_exchange);
221         }
222 
restore_state(thread_state_enum new_state,thread_state_ex_enum state_ex,thread_state old_state,std::memory_order load_exchange=std::memory_order_seq_cst)223         bool restore_state(thread_state_enum new_state,
224             thread_state_ex_enum state_ex, thread_state old_state,
225             std::memory_order load_exchange = std::memory_order_seq_cst)
226         {
227             // ABA prevention for state only (not for state_ex)
228             std::int64_t tag = old_state.tag();
229             if (new_state != old_state.state())
230                 ++tag;
231 
232             return current_state_.compare_exchange_strong(old_state,
233                 thread_state(new_state, state_ex, tag), load_exchange);
234         }
235 
236     private:
237         /// The set_state function changes the extended state of this
238         /// thread instance.
239         ///
240         /// \param newstate [in] The new extended state to be set for the
241         ///                 thread.
242         ///
243         /// \note           This function will be seldom used directly. Most of
244         ///                 the time the state of a thread will have to be
245         ///                 changed using the threadmanager.
set_state_ex(thread_state_ex_enum new_state)246         thread_state_ex_enum set_state_ex(thread_state_ex_enum new_state)
247         {
248             thread_state prev_state =
249                 current_state_.load(std::memory_order_acquire);
250 
251             for (;;) {
252                 thread_state tmp = prev_state;
253 
254                 if (HPX_LIKELY(current_state_.compare_exchange_strong(tmp,
255                         thread_state(tmp.state(), new_state, tmp.tag()))))
256                 {
257                     return prev_state.state_ex();
258                 }
259 
260                 prev_state = tmp;
261             }
262         }
263 
264     public:
265         /// Return the id of the component this thread is running in
get_component_id() const266         naming::address_type get_component_id() const
267         {
268 #ifndef HPX_HAVE_THREAD_TARGET_ADDRESS
269             return 0;
270 #else
271             return component_id_;
272 #endif
273         }
274 
275 #ifndef HPX_HAVE_THREAD_DESCRIPTION
get_description() const276         util::thread_description get_description() const
277         {
278             return util::thread_description("<unknown>");
279         }
set_description(util::thread_description)280         util::thread_description set_description(util::thread_description /*value*/)
281         {
282             return util::thread_description("<unknown>");
283         }
284 
get_lco_description() const285         util::thread_description get_lco_description() const
286         {
287             return util::thread_description("<unknown>");
288         }
set_lco_description(util::thread_description)289         util::thread_description set_lco_description(util::thread_description /*value*/)
290         {
291             return util::thread_description("<unknown>");
292         }
293 #else
get_description() const294         util::thread_description get_description() const
295         {
296             mutex_type::scoped_lock l(this);
297             return description_;
298         }
set_description(util::thread_description value)299         util::thread_description set_description(util::thread_description value)
300         {
301             mutex_type::scoped_lock l(this);
302             std::swap(description_, value);
303             return value;
304         }
305 
get_lco_description() const306         util::thread_description get_lco_description() const
307         {
308             mutex_type::scoped_lock l(this);
309             return lco_description_;
310         }
set_lco_description(util::thread_description value)311         util::thread_description set_lco_description(
312             util::thread_description value)
313         {
314             mutex_type::scoped_lock l(this);
315             std::swap(lco_description_, value);
316             return value;
317         }
318 #endif
319 
320 #ifndef HPX_HAVE_THREAD_PARENT_REFERENCE
321         /// Return the locality of the parent thread
get_parent_locality_id() const322         std::uint32_t get_parent_locality_id() const
323         {
324             return naming::invalid_locality_id;
325         }
326 
327         /// Return the thread id of the parent thread
get_parent_thread_id() const328         thread_id_type get_parent_thread_id() const
329         {
330             return threads::invalid_thread_id;
331         }
332 
333         /// Return the phase of the parent thread
get_parent_thread_phase() const334         std::size_t get_parent_thread_phase() const
335         {
336             return 0;
337         }
338 #else
339         /// Return the locality of the parent thread
get_parent_locality_id() const340         std::uint32_t get_parent_locality_id() const
341         {
342             return parent_locality_id_;
343         }
344 
345         /// Return the thread id of the parent thread
get_parent_thread_id() const346         thread_id_type get_parent_thread_id() const
347         {
348             return parent_thread_id_;
349         }
350 
351         /// Return the phase of the parent thread
get_parent_thread_phase() const352         std::size_t get_parent_thread_phase() const
353         {
354             return parent_thread_phase_;
355         }
356 #endif
357 
358 #ifdef HPX_HAVE_THREAD_MINIMAL_DEADLOCK_DETECTION
set_marked_state(thread_state_enum mark) const359         void set_marked_state(thread_state_enum mark) const
360         {
361             marked_state_ = mark;
362         }
get_marked_state() const363         thread_state_enum get_marked_state() const
364         {
365             return marked_state_;
366         }
367 #endif
368 
369 #ifndef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
370 
371 # ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
get_backtrace() const372         char const* get_backtrace() const
373         {
374             return nullptr;
375         }
set_backtrace(char const *)376         char const* set_backtrace(char const*)
377         {
378             return nullptr;
379         }
380 # else
get_backtrace() const381         util::backtrace const* get_backtrace() const
382         {
383             return nullptr;
384         }
set_backtrace(util::backtrace const *)385         util::backtrace const* set_backtrace(util::backtrace const*)
386         {
387             return nullptr;
388         }
389 # endif
390 
391 #else  // defined(HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
392 
393 # ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
get_backtrace() const394         char const* get_backtrace() const
395         {
396             mutex_type::scoped_lock l(this);
397             return backtrace_;
398         }
set_backtrace(char const * value)399         char const* set_backtrace(char const* value)
400         {
401             mutex_type::scoped_lock l(this);
402 
403             char const* bt = backtrace_;
404             backtrace_ = value;
405             return bt;
406         }
407 # else
get_backtrace() const408         util::backtrace const* get_backtrace() const
409         {
410             mutex_type::scoped_lock l(this);
411             return backtrace_;
412         }
set_backtrace(util::backtrace const * value)413         util::backtrace const* set_backtrace(util::backtrace const* value)
414         {
415             mutex_type::scoped_lock l(this);
416 
417             util::backtrace const* bt = backtrace_;
418             backtrace_ = value;
419             return bt;
420         }
421 # endif
422 
423         // Generate full backtrace for captured stack
backtrace()424         std::string backtrace()
425         {
426             mutex_type::scoped_lock l(this);
427             std::string bt;
428             if (0 != backtrace_)
429             {
430 # ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
431                 bt = *backtrace_;
432 #else
433                 bt = backtrace_->trace();
434 #endif
435             }
436             return bt;
437         }
438 #endif
439 
get_priority() const440         thread_priority get_priority() const
441         {
442             return priority_;
443         }
set_priority(thread_priority priority)444         void set_priority(thread_priority priority)
445         {
446             priority_ = priority;
447         }
448 
449         // handle thread interruption
interruption_requested() const450         bool interruption_requested() const
451         {
452             mutex_type::scoped_lock l(this);
453             return requested_interrupt_;
454         }
455 
interruption_enabled() const456         bool interruption_enabled() const
457         {
458             mutex_type::scoped_lock l(this);
459             return enabled_interrupt_;
460         }
461 
set_interruption_enabled(bool enable)462         bool set_interruption_enabled(bool enable)
463         {
464             mutex_type::scoped_lock l(this);
465             std::swap(enabled_interrupt_, enable);
466             return enable;
467         }
468 
interrupt(bool flag=true)469         void interrupt(bool flag = true)
470         {
471             mutex_type::scoped_lock l(this);
472             if (flag && !enabled_interrupt_) {
473                 l.unlock();
474                 HPX_THROW_EXCEPTION(thread_not_interruptable,
475                     "thread_data::interrupt",
476                     "interrupts are disabled for this thread");
477                 return;
478             }
479             requested_interrupt_ = flag;
480         }
481 
482         bool interruption_point(bool throw_on_interrupt = true);
483 
484         bool add_thread_exit_callback(util::function_nonser<void()> const& f);
485         void run_thread_exit_callbacks();
486         void free_thread_exit_callbacks();
487 
get_scheduler_base() const488         policies::scheduler_base* get_scheduler_base() const
489         {
490             return scheduler_base_;
491         }
492 
get_stack_size() const493         std::ptrdiff_t get_stack_size() const
494         {
495             return stacksize_;
496         }
497 
498         template <typename ThreadQueue>
get_queue()499         ThreadQueue& get_queue()
500         {
501             return *static_cast<ThreadQueue *>(queue_);
502         }
503 
504         /// \brief Execute the thread function
505         ///
506         /// \returns        This function returns the thread state the thread
507         ///                 should be scheduled from this point on. The thread
508         ///                 manager will use the returned value to set the
509         ///                 thread's scheduling status.
operator ()()510         coroutine_type::result_type operator()()
511         {
512             HPX_ASSERT(this == coroutine_.get_thread_id().get());
513             return coroutine_(set_state_ex(wait_signaled));
514         }
515 
get_thread_id() const516         thread_id_type get_thread_id() const
517         {
518             HPX_ASSERT(this == coroutine_.get_thread_id().get());
519             return coroutine_.get_thread_id();
520         }
521 
get_thread_phase() const522         std::size_t get_thread_phase() const
523         {
524 #ifndef HPX_HAVE_THREAD_PHASE_INFORMATION
525             return 0;
526 #else
527             return coroutine_.get_thread_phase();
528 #endif
529         }
530 
get_thread_data() const531         std::size_t get_thread_data() const
532         {
533             return coroutine_.get_thread_data();
534         }
535 
set_thread_data(std::size_t data)536         std::size_t set_thread_data(std::size_t data)
537         {
538             return coroutine_.set_thread_data(data);
539         }
540 
541 #if defined(HPX_HAVE_APEX)
get_apex_data() const542         apex_task_wrapper get_apex_data() const
543         {
544             return coroutine_.get_apex_data();
545         }
set_apex_data(apex_task_wrapper data)546         void set_apex_data(apex_task_wrapper data)
547         {
548             return coroutine_.set_apex_data(data);
549         }
550 #endif
551 
rebind(thread_init_data & init_data,thread_state_enum newstate)552         void rebind(thread_init_data& init_data,
553             thread_state_enum newstate)
554         {
555             LTM_(debug) << "~thread(" << this << "), description(" //-V128
556                         << get_description() << "), phase("
557                         << get_thread_phase() << "), rebind";
558 
559             rebind_base(init_data, newstate);
560 
561             coroutine_.rebind(std::move(init_data.func), thread_id_type(this));
562 
563             HPX_ASSERT(init_data.stacksize != 0);
564             HPX_ASSERT(coroutine_.is_ready());
565         }
566 
567         /// This function will be called when the thread is about to be deleted
568         //virtual void reset() {}
569 
570         /// Construct a new \a thread
thread_data(thread_init_data & init_data,void * queue,thread_state_enum newstate)571         thread_data(thread_init_data& init_data,
572             void* queue, thread_state_enum newstate)
573           : current_state_(thread_state(newstate, wait_signaled)),
574 #ifdef HPX_HAVE_THREAD_TARGET_ADDRESS
575             component_id_(init_data.lva),
576 #endif
577 #ifdef HPX_HAVE_THREAD_DESCRIPTION
578             description_(init_data.description),
579             lco_description_(),
580 #endif
581 #ifdef HPX_HAVE_THREAD_PARENT_REFERENCE
582             parent_locality_id_(init_data.parent_locality_id),
583             parent_thread_id_(init_data.parent_id),
584             parent_thread_phase_(init_data.parent_phase),
585 #endif
586 #ifdef HPX_HAVE_THREAD_MINIMAL_DEADLOCK_DETECTION
587             marked_state_(unknown),
588 #endif
589 #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
590             backtrace_(nullptr),
591 #endif
592             priority_(init_data.priority),
593             requested_interrupt_(false),
594             enabled_interrupt_(true),
595             ran_exit_funcs_(false),
596             scheduler_base_(init_data.scheduler_base),
597             stacksize_(init_data.stacksize),
598             coroutine_(std::move(init_data.func),
599                 thread_id_type(this_()), init_data.stacksize),
600             queue_(queue)
601         {
602             LTM_(debug) << "thread::thread(" << this << "), description("
603                         << get_description() << ")";
604 
605 #ifdef HPX_HAVE_THREAD_PARENT_REFERENCE
606             // store the thread id of the parent thread, mainly for debugging
607             // purposes
608             if (parent_thread_id_) {
609                 thread_self* self = get_self_ptr();
610                 if (self)
611                 {
612                     parent_thread_id_ = threads::get_self_id();
613                     parent_thread_phase_ = self->get_thread_phase();
614                 }
615             }
616             if (0 == parent_locality_id_)
617                 parent_locality_id_ = get_locality_id();
618 #endif
619 #if defined(HPX_HAVE_APEX)
620             set_apex_data(init_data.apex_data);
621 #endif
622             HPX_ASSERT(init_data.stacksize != 0);
623             HPX_ASSERT(coroutine_.is_ready());
624         }
625 
626     private:
rebind_base(thread_init_data & init_data,thread_state_enum newstate)627         void rebind_base(thread_init_data& init_data, thread_state_enum newstate)
628         {
629             free_thread_exit_callbacks();
630 
631             current_state_.store(thread_state(newstate, wait_signaled));
632 
633 #ifdef HPX_HAVE_THREAD_TARGET_ADDRESS
634             component_id_ = init_data.lva;
635 #endif
636 #ifdef HPX_HAVE_THREAD_DESCRIPTION
637             description_ = (init_data.description);
638             lco_description_ = util::thread_description();
639 #endif
640 #ifdef HPX_HAVE_THREAD_PARENT_REFERENCE
641             parent_locality_id_ = init_data.parent_locality_id;
642             parent_thread_id_ = init_data.parent_id;
643             parent_thread_phase_ = init_data.parent_phase;
644 #endif
645 #ifdef HPX_HAVE_THREAD_MINIMAL_DEADLOCK_DETECTION
646             set_marked_state(unknown);
647 #endif
648 #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
649             backtrace_ = nullptr;
650 #endif
651             priority_ = init_data.priority;
652             requested_interrupt_ = false;
653             enabled_interrupt_ = true;
654             ran_exit_funcs_ = false;
655             exit_funcs_.clear();
656             scheduler_base_ = init_data.scheduler_base;
657 
658             HPX_ASSERT(init_data.stacksize == get_stack_size());
659 
660             LTM_(debug) << "thread::thread(" << this << "), description("
661                         << get_description() << "), rebind";
662 
663 #ifdef HPX_HAVE_THREAD_PARENT_REFERENCE
664             // store the thread id of the parent thread, mainly for debugging
665             // purposes
666             if (nullptr == parent_thread_id_) {
667                 thread_self* self = get_self_ptr();
668                 if (self)
669                 {
670                     parent_thread_id_ = threads::get_self_id();
671                     parent_thread_phase_ = self->get_thread_phase();
672                 }
673             }
674             if (0 == parent_locality_id_)
675                 parent_locality_id_ = get_locality_id();
676 #endif
677 #if defined(HPX_HAVE_APEX)
678             set_apex_data(init_data.apex_data);
679 #endif
680         }
681 
682         mutable std::atomic<thread_state> current_state_;
683 
684         ///////////////////////////////////////////////////////////////////////
685         // Debugging/logging information
686 #ifdef HPX_HAVE_THREAD_TARGET_ADDRESS
687         naming::address_type component_id_;
688 #endif
689 
690 #ifdef HPX_HAVE_THREAD_DESCRIPTION
691         util::thread_description description_;
692         util::thread_description lco_description_;
693 #endif
694 
695 #ifdef HPX_HAVE_THREAD_PARENT_REFERENCE
696         std::uint32_t parent_locality_id_;
697         thread_id_type parent_thread_id_;
698         std::size_t parent_thread_phase_;
699 #endif
700 
701 #ifdef HPX_HAVE_THREAD_MINIMAL_DEADLOCK_DETECTION
702         mutable thread_state_enum marked_state_;
703 #endif
704 
705 #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION
706 # ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION
707         char const* backtrace_;
708 # else
709         util::backtrace const* backtrace_;
710 # endif
711 #endif
712 
713         ///////////////////////////////////////////////////////////////////////
714         thread_priority priority_;
715 
716         bool requested_interrupt_;
717         bool enabled_interrupt_;
718         bool ran_exit_funcs_;
719 
720         // Singly linked list (heap-allocated)
721         // FIXME: replace with forward_list eventually.
722         std::deque<util::function_nonser<void()> > exit_funcs_;
723 
724         // reference to scheduler which created/manages this thread
725         policies::scheduler_base* scheduler_base_;
726 
727         std::ptrdiff_t stacksize_;
728 
729         coroutine_type coroutine_;
730         void* queue_;
731     };
732 }}
733 
734 #include <hpx/config/warnings_suffix.hpp>
735 
736 #endif /*HPX_RUNTIME_THREADS_THREAD_DATA_HPP*/
737