// Copyright (C) 2001-2003 // William E. Kempf // Copyright (C) 2007-8 Anthony Williams // (C) Copyright 2011-2012 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #if defined BOOST_THREAD_USES_DATETIME #include #endif #include #include #include #include #include #ifdef __GLIBC__ #include #elif defined(__APPLE__) || defined(__FreeBSD__) #include #include #elif defined BOOST_HAS_UNISTD_H #include #endif #include #include #include #include #include #include #include #include "./timeconv.inl" #pragma GCC diagnostic ignored "-Wreturn-type" namespace boost { namespace detail { thread_data_base::~thread_data_base() { for (notify_list_t::iterator i = notify.begin(), e = notify.end(); i != e; ++i) { i->second->unlock(); i->first->notify_all(); } for (async_states_t::iterator i = async_states_.begin(), e = async_states_.end(); i != e; ++i) { (*i)->make_ready(); } } struct thread_exit_callback_node { boost::detail::thread_exit_function_base* func; thread_exit_callback_node* next; thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, thread_exit_callback_node* next_): func(func_),next(next_) {} }; namespace { #ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 boost::once_flag current_thread_tls_init_flag; #else boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; #endif pthread_key_t current_thread_tls_key; extern "C" { static void tls_destructor(void* data) { boost::detail::thread_data_base* thread_info=static_cast(data); if(thread_info) { while(!thread_info->tss_data.empty() || thread_info->thread_exit_callbacks) { while(thread_info->thread_exit_callbacks) { detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; thread_info->thread_exit_callbacks=current_node->next; if(current_node->func) { (*current_node->func)(); delete current_node->func; } delete current_node; } for(std::map::iterator next=thread_info->tss_data.begin(), current, end=thread_info->tss_data.end(); next!=end;) { current=next; ++next; if(current->second.func && (current->second.value!=0)) { (*current->second.func)(current->second.value); } thread_info->tss_data.erase(current); } } if (thread_info) // fixme: should we test this? { thread_info->self.reset(); } } } } #if defined BOOST_THREAD_PATCH struct delete_current_thread_tls_key_on_dlclose_t { delete_current_thread_tls_key_on_dlclose_t() { } ~delete_current_thread_tls_key_on_dlclose_t() { if (current_thread_tls_init_flag.epoch!=BOOST_ONCE_INITIAL_FLAG_VALUE) { pthread_key_delete(current_thread_tls_key); } } }; delete_current_thread_tls_key_on_dlclose_t delete_current_thread_tls_key_on_dlclose; #endif void create_current_thread_tls_key() { BOOST_VERIFY(!pthread_key_create(¤t_thread_tls_key,&tls_destructor)); } } boost::detail::thread_data_base* get_current_thread_data() { boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); return (boost::detail::thread_data_base*)pthread_getspecific(current_thread_tls_key); } void set_current_thread_data(detail::thread_data_base* new_data) { boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); BOOST_VERIFY(!pthread_setspecific(current_thread_tls_key,new_data)); } } namespace { extern "C" { static void* thread_proxy(void* param) { boost::detail::thread_data_ptr thread_info = static_cast(param)->self; thread_info->self.reset(); detail::set_current_thread_data(thread_info.get()); #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS BOOST_TRY { #endif thread_info->run(); #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS } BOOST_CATCH (thread_interrupted const&) { } // Removed as it stops the debugger identifying the cause of the exception // Unhandled exceptions still cause the application to terminate // BOOST_CATCH(...) // { // throw; // // std::terminate(); // } BOOST_CATCH_END #endif detail::tls_destructor(thread_info.get()); detail::set_current_thread_data(0); boost::lock_guard lock(thread_info->data_mutex); thread_info->done=true; thread_info->done_condition.notify_all(); return 0; } } } namespace detail { struct externally_launched_thread: detail::thread_data_base { externally_launched_thread() { #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS interrupt_enabled=false; #endif } ~externally_launched_thread() { BOOST_ASSERT(notify.empty()); notify.clear(); BOOST_ASSERT(async_states_.empty()); async_states_.clear(); } void run() {} void notify_all_at_thread_exit(condition_variable*, mutex*) {} private: externally_launched_thread(externally_launched_thread&); void operator=(externally_launched_thread&); }; thread_data_base* make_external_thread_data() { thread_data_base* const me(detail::heap_new()); me->self.reset(me); set_current_thread_data(me); return me; } thread_data_base* get_or_make_current_thread_data() { thread_data_base* current_thread_data(get_current_thread_data()); if(!current_thread_data) { current_thread_data=make_external_thread_data(); } return current_thread_data; } } thread::thread() BOOST_NOEXCEPT {} bool thread::start_thread_noexcept() { thread_info->self=thread_info; int const res = pthread_create(&thread_info->thread_handle, 0, &thread_proxy, thread_info.get()); if (res != 0) { thread_info->self.reset(); return false; // boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_create")); } return true; } bool thread::start_thread_noexcept(const attributes& attr) { thread_info->self=thread_info; const attributes::native_handle_type* h = attr.native_handle(); int res = pthread_create(&thread_info->thread_handle, h, &thread_proxy, thread_info.get()); if (res != 0) { thread_info->self.reset(); return false; // boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_create")); } int detached_state; res = pthread_attr_getdetachstate(h, &detached_state); if (res != 0) { thread_info->self.reset(); return false; // boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_attr_getdetachstate")); } if (PTHREAD_CREATE_DETACHED==detached_state) { detail::thread_data_ptr local_thread_info; thread_info.swap(local_thread_info); if(local_thread_info) { //lock_guard lock(local_thread_info->data_mutex); if(!local_thread_info->join_started) { //BOOST_VERIFY(!pthread_detach(local_thread_info->thread_handle)); local_thread_info->join_started=true; local_thread_info->joined=true; } } } return true; } detail::thread_data_ptr thread::get_thread_info BOOST_PREVENT_MACRO_SUBSTITUTION () const { return thread_info; } bool thread::join_noexcept() { detail::thread_data_ptr const local_thread_info=(get_thread_info)(); if(local_thread_info) { bool do_join=false; { unique_lock lock(local_thread_info->data_mutex); while(!local_thread_info->done) { local_thread_info->done_condition.wait(lock); } do_join=!local_thread_info->join_started; if(do_join) { local_thread_info->join_started=true; } else { while(!local_thread_info->joined) { local_thread_info->done_condition.wait(lock); } } } if(do_join) { void* result=0; BOOST_VERIFY(!pthread_join(local_thread_info->thread_handle,&result)); lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); } if(thread_info==local_thread_info) { thread_info.reset(); } return true; } else { return false; } } bool thread::do_try_join_until_noexcept(struct timespec const &timeout, bool& res) { detail::thread_data_ptr const local_thread_info=(get_thread_info)(); if(local_thread_info) { bool do_join=false; { unique_lock lock(local_thread_info->data_mutex); while(!local_thread_info->done) { if(!local_thread_info->done_condition.do_wait_until(lock,timeout)) { res=false; return true; } } do_join=!local_thread_info->join_started; if(do_join) { local_thread_info->join_started=true; } else { while(!local_thread_info->joined) { local_thread_info->done_condition.wait(lock); } } } if(do_join) { void* result=0; BOOST_VERIFY(!pthread_join(local_thread_info->thread_handle,&result)); lock_guard lock(local_thread_info->data_mutex); local_thread_info->joined=true; local_thread_info->done_condition.notify_all(); } if(thread_info==local_thread_info) { thread_info.reset(); } res=true; return true; } else { return false; } } bool thread::joinable() const BOOST_NOEXCEPT { return (get_thread_info)()?true:false; } void thread::detach() { detail::thread_data_ptr local_thread_info; thread_info.swap(local_thread_info); if(local_thread_info) { lock_guard lock(local_thread_info->data_mutex); if(!local_thread_info->join_started) { BOOST_VERIFY(!pthread_detach(local_thread_info->thread_handle)); local_thread_info->join_started=true; local_thread_info->joined=true; } } } namespace this_thread { namespace no_interruption_point { namespace hiden { void BOOST_THREAD_DECL sleep_for(const timespec& ts) { if (boost::detail::timespec_ge(ts, boost::detail::timespec_zero())) { # if defined(BOOST_HAS_PTHREAD_DELAY_NP) # if defined(__IBMCPP__) || defined(_AIX) BOOST_VERIFY(!pthread_delay_np(const_cast(&ts))); # else BOOST_VERIFY(!pthread_delay_np(&ts)); # endif # elif defined(BOOST_HAS_NANOSLEEP) // nanosleep takes a timespec that is an offset, not // an absolute time. nanosleep(&ts, 0); # else mutex mx; unique_lock lock(mx); condition_variable cond; cond.do_wait_for(lock, ts); # endif } } void BOOST_THREAD_DECL sleep_until(const timespec& ts) { timespec now = boost::detail::timespec_now(); if (boost::detail::timespec_gt(ts, now)) { for (int foo=0; foo < 5; ++foo) { # if defined(BOOST_HAS_PTHREAD_DELAY_NP) timespec d = boost::detail::timespec_minus(ts, now); BOOST_VERIFY(!pthread_delay_np(&d)); # elif defined(BOOST_HAS_NANOSLEEP) // nanosleep takes a timespec that is an offset, not // an absolute time. timespec d = boost::detail::timespec_minus(ts, now); nanosleep(&d, 0); # else mutex mx; unique_lock lock(mx); condition_variable cond; cond.do_wait_until(lock, ts); # endif timespec now2 = boost::detail::timespec_now(); if (boost::detail::timespec_ge(now2, ts)) { return; } } } } } } namespace hiden { void BOOST_THREAD_DECL sleep_for(const timespec& ts) { boost::detail::thread_data_base* const thread_info=boost::detail::get_current_thread_data(); if(thread_info) { unique_lock lk(thread_info->sleep_mutex); while( thread_info->sleep_condition.do_wait_for(lk,ts)) {} } else { boost::this_thread::no_interruption_point::hiden::sleep_for(ts); } } void BOOST_THREAD_DECL sleep_until(const timespec& ts) { boost::detail::thread_data_base* const thread_info=boost::detail::get_current_thread_data(); if(thread_info) { unique_lock lk(thread_info->sleep_mutex); while(thread_info->sleep_condition.do_wait_until(lk,ts)) {} } else { boost::this_thread::no_interruption_point::hiden::sleep_until(ts); } } } // hiden } // this_thread namespace this_thread { void yield() BOOST_NOEXCEPT { # if defined(BOOST_HAS_SCHED_YIELD) BOOST_VERIFY(!sched_yield()); # elif defined(BOOST_HAS_PTHREAD_YIELD) BOOST_VERIFY(!pthread_yield()); //# elif defined BOOST_THREAD_USES_DATETIME // xtime xt; // xtime_get(&xt, TIME_UTC_); // sleep(xt); // sleep_for(chrono::milliseconds(0)); # else #error timespec ts; ts.tv_sec= 0; ts.tv_nsec= 0; hiden::sleep_for(ts); # endif } } unsigned thread::hardware_concurrency() BOOST_NOEXCEPT { #if defined(PTW32_VERSION) || defined(__hpux) return pthread_num_processors_np(); #elif defined(__APPLE__) || defined(__FreeBSD__) int count; size_t size=sizeof(count); return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; #elif defined(BOOST_HAS_UNISTD_H) && defined(_SC_NPROCESSORS_ONLN) int const count=sysconf(_SC_NPROCESSORS_ONLN); return (count>0)?count:0; #elif defined(__GLIBC__) return get_nprocs(); #else return 0; #endif } unsigned thread::physical_concurrency() BOOST_NOEXCEPT { #ifdef __linux__ try { using namespace std; ifstream proc_cpuinfo ("/proc/cpuinfo"); const string physical_id("physical id"), core_id("core id"); typedef std::pair core_entry; // [physical ID, core id] std::set cores; core_entry current_core_entry; string line; while ( getline(proc_cpuinfo, line) ) { if (line.empty()) continue; vector key_val(2); boost::split(key_val, line, boost::is_any_of(":")); if (key_val.size() != 2) return hardware_concurrency(); string key = key_val[0]; string value = key_val[1]; boost::trim(key); boost::trim(value); if (key == physical_id) { current_core_entry.first = boost::lexical_cast(value); continue; } if (key == core_id) { current_core_entry.second = boost::lexical_cast(value); cores.insert(current_core_entry); continue; } } // Fall back to hardware_concurrency() in case // /proc/cpuinfo is formatted differently than we expect. return cores.size() != 0 ? cores.size() : hardware_concurrency(); } catch(...) { return hardware_concurrency(); } #elif defined(__APPLE__) int count; size_t size=sizeof(count); return sysctlbyname("hw.physicalcpu",&count,&size,NULL,0)?0:count; #else return hardware_concurrency(); #endif } #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS void thread::interrupt() { detail::thread_data_ptr const local_thread_info=(get_thread_info)(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); local_thread_info->interrupt_requested=true; if(local_thread_info->current_cond) { boost::pthread::pthread_mutex_scoped_lock internal_lock(local_thread_info->cond_mutex); BOOST_VERIFY(!pthread_cond_broadcast(local_thread_info->current_cond)); } } } bool thread::interruption_requested() const BOOST_NOEXCEPT { detail::thread_data_ptr const local_thread_info=(get_thread_info)(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); return local_thread_info->interrupt_requested; } else { return false; } } #endif thread::native_handle_type thread::native_handle() { detail::thread_data_ptr const local_thread_info=(get_thread_info)(); if(local_thread_info) { lock_guard lk(local_thread_info->data_mutex); return local_thread_info->thread_handle; } else { return pthread_t(); } } #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS namespace this_thread { void interruption_point() { #ifndef BOOST_NO_EXCEPTIONS boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); if(thread_info && thread_info->interrupt_enabled) { lock_guard lg(thread_info->data_mutex); if(thread_info->interrupt_requested) { thread_info->interrupt_requested=false; throw thread_interrupted(); } } #endif } bool interruption_enabled() BOOST_NOEXCEPT { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); return thread_info && thread_info->interrupt_enabled; } bool interruption_requested() BOOST_NOEXCEPT { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); if(!thread_info) { return false; } else { lock_guard lg(thread_info->data_mutex); return thread_info->interrupt_requested; } } disable_interruption::disable_interruption() BOOST_NOEXCEPT: interruption_was_enabled(interruption_enabled()) { if(interruption_was_enabled) { detail::get_current_thread_data()->interrupt_enabled=false; } } disable_interruption::~disable_interruption() BOOST_NOEXCEPT { if(detail::get_current_thread_data()) { detail::get_current_thread_data()->interrupt_enabled=interruption_was_enabled; } } restore_interruption::restore_interruption(disable_interruption& d) BOOST_NOEXCEPT { if(d.interruption_was_enabled) { detail::get_current_thread_data()->interrupt_enabled=true; } } restore_interruption::~restore_interruption() BOOST_NOEXCEPT { if(detail::get_current_thread_data()) { detail::get_current_thread_data()->interrupt_enabled=false; } } } #endif namespace detail { void add_thread_exit_function(thread_exit_function_base* func) { detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); thread_exit_callback_node* const new_node= heap_new(func,current_thread_data->thread_exit_callbacks); current_thread_data->thread_exit_callbacks=new_node; } tss_data_node* find_tss_data(void const* key) { detail::thread_data_base* const current_thread_data(get_current_thread_data()); if(current_thread_data) { std::map::iterator current_node= current_thread_data->tss_data.find(key); if(current_node!=current_thread_data->tss_data.end()) { return ¤t_node->second; } } return 0; } void* get_tss_data(void const* key) { if(tss_data_node* const current_node=find_tss_data(key)) { return current_node->value; } return 0; } void add_new_tss_node(void const* key, boost::shared_ptr func, void* tss_data) { detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); current_thread_data->tss_data.insert(std::make_pair(key,tss_data_node(func,tss_data))); } void erase_tss_node(void const* key) { detail::thread_data_base* const current_thread_data(get_current_thread_data()); if(current_thread_data) { current_thread_data->tss_data.erase(key); } } void set_tss_data(void const* key, boost::shared_ptr func, void* tss_data,bool cleanup_existing) { if(tss_data_node* const current_node=find_tss_data(key)) { if(cleanup_existing && current_node->func && (current_node->value!=0)) { (*current_node->func)(current_node->value); } if(func || (tss_data!=0)) { current_node->func=func; current_node->value=tss_data; } else { erase_tss_node(key); } } else if(func || (tss_data!=0)) { add_new_tss_node(key,func,tss_data); } } } BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock lk) { detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); if(current_thread_data) { current_thread_data->notify_all_at_thread_exit(&cond, lk.release()); } } namespace detail { void BOOST_THREAD_DECL make_ready_at_thread_exit(shared_ptr as) { detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); if(current_thread_data) { current_thread_data->make_ready_at_thread_exit(as); } } } }