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