1 // Distributed under the Boost Software License, Version 1.0. (See 2 // accompanying file LICENSE_1_0.txt or copy at 3 // http://www.boost.org/LICENSE_1_0.txt) 4 // (C) Copyright 2007 Anthony Williams 5 // (C) Copyright 2007 David Deakins 6 // (C) Copyright 2011-2018 Vicente J. Botet Escriba 7 8 //#define BOOST_THREAD_VERSION 3 9 10 #include <boost/winapi/config.hpp> 11 #include <boost/thread/thread_only.hpp> 12 #include <boost/thread/once.hpp> 13 #include <boost/thread/tss.hpp> 14 #include <boost/thread/condition_variable.hpp> 15 #include <boost/thread/detail/tss_hooks.hpp> 16 #include <boost/thread/future.hpp> 17 #include <boost/assert.hpp> 18 #include <boost/cstdint.hpp> 19 #if defined BOOST_THREAD_USES_DATETIME 20 #include <boost/date_time/posix_time/conversion.hpp> 21 #include <boost/thread/thread_time.hpp> 22 #endif 23 #include <boost/thread/csbl/memory/unique_ptr.hpp> 24 #include <memory> 25 #include <algorithm> 26 #ifndef UNDER_CE 27 #include <process.h> 28 #endif 29 #include <stdio.h> 30 #include <windows.h> 31 #include <boost/predef/platform.h> 32 33 #if BOOST_PLAT_WINDOWS_RUNTIME 34 #include <mutex> 35 #include <atomic> 36 #include <Activation.h> 37 #include <wrl\client.h> 38 #include <wrl\event.h> 39 #include <wrl\wrappers\corewrappers.h> 40 #include <wrl\ftm.h> 41 #include <windows.system.threading.h> 42 #pragma comment(lib, "runtimeobject.lib") 43 #endif 44 45 namespace boost 46 { 47 namespace detail 48 { ~thread_data_base()49 thread_data_base::~thread_data_base() 50 { 51 for (notify_list_t::iterator i = notify.begin(), e = notify.end(); 52 i != e; ++i) 53 { 54 i->second->unlock(); 55 i->first->notify_all(); 56 } 57 //#ifndef BOOST_NO_EXCEPTIONS 58 for (async_states_t::iterator i = async_states_.begin(), e = async_states_.end(); 59 i != e; ++i) 60 { 61 (*i)->notify_deferred(); 62 } 63 //#endif 64 } 65 } 66 67 namespace 68 { 69 #ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 70 boost::once_flag current_thread_tls_init_flag; 71 #else 72 boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; 73 #endif 74 #if defined(UNDER_CE) 75 // Windows CE does not define the TLS_OUT_OF_INDEXES constant. 76 #define TLS_OUT_OF_INDEXES 0xFFFFFFFF 77 #endif 78 #if !BOOST_PLAT_WINDOWS_RUNTIME 79 DWORD current_thread_tls_key=TLS_OUT_OF_INDEXES; 80 #else 81 __declspec(thread) boost::detail::thread_data_base* current_thread_data_base; 82 #endif 83 create_current_thread_tls_key()84 void create_current_thread_tls_key() 85 { 86 tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in 87 #if !BOOST_PLAT_WINDOWS_RUNTIME 88 current_thread_tls_key=TlsAlloc(); 89 BOOST_ASSERT(current_thread_tls_key!=TLS_OUT_OF_INDEXES); 90 #endif 91 } 92 cleanup_tls_key()93 void cleanup_tls_key() 94 { 95 #if !BOOST_PLAT_WINDOWS_RUNTIME 96 if(current_thread_tls_key!=TLS_OUT_OF_INDEXES) 97 { 98 TlsFree(current_thread_tls_key); 99 current_thread_tls_key=TLS_OUT_OF_INDEXES; 100 } 101 #endif 102 } 103 set_current_thread_data(detail::thread_data_base * new_data)104 void set_current_thread_data(detail::thread_data_base* new_data) 105 { 106 boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); 107 #if BOOST_PLAT_WINDOWS_RUNTIME 108 current_thread_data_base = new_data; 109 #else 110 if (current_thread_tls_key != TLS_OUT_OF_INDEXES) 111 { 112 BOOST_VERIFY(TlsSetValue(current_thread_tls_key, new_data)); 113 } 114 else 115 { 116 BOOST_VERIFY(false); 117 //boost::throw_exception(thread_resource_error()); 118 } 119 #endif 120 } 121 } 122 123 namespace detail 124 { get_current_thread_data()125 thread_data_base* get_current_thread_data() 126 { 127 #if BOOST_PLAT_WINDOWS_RUNTIME 128 return current_thread_data_base; 129 #else 130 if (current_thread_tls_key == TLS_OUT_OF_INDEXES) 131 { 132 return 0; 133 } 134 return (detail::thread_data_base*)TlsGetValue(current_thread_tls_key); 135 #endif 136 } 137 } 138 139 namespace 140 { 141 #ifndef BOOST_HAS_THREADEX 142 // Windows CE doesn't define _beginthreadex 143 144 struct ThreadProxyData 145 { 146 typedef unsigned (__stdcall* func)(void*); 147 func start_address_; 148 void* arglist_; ThreadProxyDataboost::__anon5c4229bc0211::ThreadProxyData149 ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} 150 }; 151 ThreadProxy(LPVOID args)152 DWORD WINAPI ThreadProxy(LPVOID args) 153 { 154 boost::csbl::unique_ptr<ThreadProxyData> data(reinterpret_cast<ThreadProxyData*>(args)); 155 DWORD ret=data->start_address_(data->arglist_); 156 return ret; 157 } 158 _beginthreadex(void * security,unsigned stack_size,unsigned (__stdcall * start_address)(void *),void * arglist,unsigned initflag,unsigned * thrdaddr)159 inline uintptr_t _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), 160 void* arglist, unsigned initflag, unsigned* thrdaddr) 161 { 162 DWORD threadID; 163 ThreadProxyData* data = new ThreadProxyData(start_address,arglist); 164 HANDLE hthread=CreateThread(static_cast<LPSECURITY_ATTRIBUTES>(security),stack_size,ThreadProxy, 165 data,initflag,&threadID); 166 if (hthread==0) { 167 delete data; 168 return 0; 169 } 170 *thrdaddr=threadID; 171 return reinterpret_cast<uintptr_t const>(hthread); 172 } 173 174 #endif 175 176 } 177 178 namespace detail 179 { 180 struct thread_exit_callback_node 181 { 182 boost::detail::thread_exit_function_base* func; 183 thread_exit_callback_node* next; 184 thread_exit_callback_nodeboost::detail::thread_exit_callback_node185 thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, 186 thread_exit_callback_node* next_): 187 func(func_),next(next_) 188 {} 189 }; 190 191 } 192 193 #if BOOST_PLAT_WINDOWS_RUNTIME 194 namespace detail 195 { 196 std::atomic_uint threadCount; 197 start(thread_func address,void * parameter,unsigned int * thrdId)198 bool win32::scoped_winrt_thread::start(thread_func address, void *parameter, unsigned int *thrdId) 199 { 200 Microsoft::WRL::ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> threadPoolFactory; 201 HRESULT hr = ::Windows::Foundation::GetActivationFactory( 202 Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), 203 &threadPoolFactory); 204 if (hr != S_OK) 205 { 206 return false; 207 } 208 209 // Create event for tracking work item completion. 210 *thrdId = ++threadCount; 211 handle completionHandle = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS); 212 if (!completionHandle) 213 { 214 return false; 215 } 216 m_completionHandle = completionHandle; 217 218 // Create new work item. 219 Microsoft::WRL::ComPtr<ABI::Windows::System::Threading::IWorkItemHandler> workItem = 220 Microsoft::WRL::Callback<Microsoft::WRL::Implements<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, ABI::Windows::System::Threading::IWorkItemHandler, Microsoft::WRL::FtmBase>> 221 ([address, parameter, completionHandle](ABI::Windows::Foundation::IAsyncAction *) 222 { 223 // Add a reference since we need to access the completionHandle after the thread_start_function. 224 // This is to handle cases where detach() was called and run_thread_exit_callbacks() would end 225 // up closing the handle. 226 ::boost::detail::thread_data_base* const thread_info(reinterpret_cast<::boost::detail::thread_data_base*>(parameter)); 227 intrusive_ptr_add_ref(thread_info); 228 229 __try 230 { 231 address(parameter); 232 } 233 __finally 234 { 235 SetEvent(completionHandle); 236 intrusive_ptr_release(thread_info); 237 } 238 return S_OK; 239 }); 240 241 // Schedule work item on the threadpool. 242 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> asyncAction; 243 hr = threadPoolFactory->RunWithPriorityAndOptionsAsync( 244 workItem.Get(), 245 ABI::Windows::System::Threading::WorkItemPriority_Normal, 246 ABI::Windows::System::Threading::WorkItemOptions_TimeSliced, 247 &asyncAction); 248 return hr == S_OK; 249 } 250 } 251 #endif 252 253 namespace 254 { run_thread_exit_callbacks()255 void run_thread_exit_callbacks() 256 { 257 detail::thread_data_ptr current_thread_data(detail::get_current_thread_data(),false); 258 if(current_thread_data) 259 { 260 while(! current_thread_data->tss_data.empty() || current_thread_data->thread_exit_callbacks) 261 { 262 while(current_thread_data->thread_exit_callbacks) 263 { 264 detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; 265 current_thread_data->thread_exit_callbacks=current_node->next; 266 if(current_node->func) 267 { 268 (*current_node->func)(); 269 boost::detail::heap_delete(current_node->func); 270 } 271 boost::detail::heap_delete(current_node); 272 } 273 while (!current_thread_data->tss_data.empty()) 274 { 275 std::map<void const*,detail::tss_data_node>::iterator current 276 = current_thread_data->tss_data.begin(); 277 if(current->second.func && (current->second.value!=0)) 278 { 279 (*current->second.caller)(current->second.func,current->second.value); 280 } 281 current_thread_data->tss_data.erase(current); 282 } 283 } 284 set_current_thread_data(0); 285 } 286 } 287 thread_start_function(void * param)288 unsigned __stdcall thread_start_function(void* param) 289 { 290 detail::thread_data_base* const thread_info(reinterpret_cast<detail::thread_data_base*>(param)); 291 set_current_thread_data(thread_info); 292 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 293 BOOST_TRY 294 { 295 #endif 296 thread_info->run(); 297 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 298 } 299 BOOST_CATCH(thread_interrupted const&) 300 { 301 } 302 // Unhandled exceptions still cause the application to terminate 303 BOOST_CATCH_END 304 #endif 305 run_thread_exit_callbacks(); 306 return 0; 307 } 308 } 309 thread()310 thread::thread() BOOST_NOEXCEPT 311 {} 312 start_thread_noexcept()313 bool thread::start_thread_noexcept() 314 { 315 #if BOOST_PLAT_WINDOWS_RUNTIME 316 intrusive_ptr_add_ref(thread_info.get()); 317 if (!thread_info->thread_handle.start(&thread_start_function, thread_info.get(), &thread_info->id)) 318 { 319 intrusive_ptr_release(thread_info.get()); 320 return false; 321 } 322 return true; 323 #else 324 uintptr_t const new_thread=_beginthreadex(0,0,&thread_start_function,thread_info.get(),CREATE_SUSPENDED,&thread_info->id); 325 if(!new_thread) 326 { 327 return false; 328 } 329 intrusive_ptr_add_ref(thread_info.get()); 330 thread_info->thread_handle=(detail::win32::handle)(new_thread); 331 ResumeThread(thread_info->thread_handle); 332 return true; 333 #endif 334 } 335 start_thread_noexcept(const attributes & attr)336 bool thread::start_thread_noexcept(const attributes& attr) 337 { 338 #if BOOST_PLAT_WINDOWS_RUNTIME 339 // Stack size isn't supported with Windows Runtime. 340 attr; 341 return start_thread_noexcept(); 342 #else 343 uintptr_t const new_thread=_beginthreadex(0,static_cast<unsigned int>(attr.get_stack_size()),&thread_start_function,thread_info.get(), 344 CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_info->id); 345 if(!new_thread) 346 { 347 return false; 348 } 349 intrusive_ptr_add_ref(thread_info.get()); 350 thread_info->thread_handle=(detail::win32::handle)(new_thread); 351 ResumeThread(thread_info->thread_handle); 352 return true; 353 #endif 354 } 355 thread(detail::thread_data_ptr data)356 thread::thread(detail::thread_data_ptr data): 357 thread_info(data) 358 {} 359 360 namespace 361 { 362 struct externally_launched_thread: 363 detail::thread_data_base 364 { externally_launched_threadboost::__anon5c4229bc0511::externally_launched_thread365 externally_launched_thread() 366 { 367 ++count; 368 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 369 interruption_enabled=false; 370 #endif 371 } ~externally_launched_threadboost::__anon5c4229bc0511::externally_launched_thread372 ~externally_launched_thread() { 373 BOOST_ASSERT(notify.empty()); 374 notify.clear(); 375 //#ifndef BOOST_NO_EXCEPTIONS 376 BOOST_ASSERT(async_states_.empty()); 377 async_states_.clear(); 378 //#endif 379 } 380 runboost::__anon5c4229bc0511::externally_launched_thread381 void run() 382 {} notify_all_at_thread_exitboost::__anon5c4229bc0511::externally_launched_thread383 void notify_all_at_thread_exit(condition_variable*, mutex*) 384 {} 385 386 private: 387 externally_launched_thread(externally_launched_thread&); 388 void operator=(externally_launched_thread&); 389 }; 390 make_external_thread_data()391 void make_external_thread_data() 392 { 393 externally_launched_thread* me=detail::heap_new<externally_launched_thread>(); 394 BOOST_TRY 395 { 396 set_current_thread_data(me); 397 } 398 BOOST_CATCH(...) 399 { 400 detail::heap_delete(me); 401 BOOST_RETHROW 402 } 403 BOOST_CATCH_END 404 } 405 get_or_make_current_thread_data()406 detail::thread_data_base* get_or_make_current_thread_data() 407 { 408 detail::thread_data_base* current_thread_data(detail::get_current_thread_data()); 409 if(!current_thread_data) 410 { 411 make_external_thread_data(); 412 current_thread_data=detail::get_current_thread_data(); 413 } 414 return current_thread_data; 415 } 416 } 417 get_id() const418 thread::id thread::get_id() const BOOST_NOEXCEPT 419 { 420 #if defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID 421 detail::thread_data_ptr local_thread_info=(get_thread_info)(); 422 if(!local_thread_info) 423 { 424 return 0; 425 } 426 return local_thread_info->id; 427 #else 428 return thread::id((get_thread_info)()); 429 #endif 430 } 431 joinable() const432 bool thread::joinable() const BOOST_NOEXCEPT 433 { 434 detail::thread_data_ptr local_thread_info = (get_thread_info)(); 435 if(!local_thread_info) 436 { 437 return false; 438 } 439 return true; 440 } join_noexcept()441 bool thread::join_noexcept() 442 { 443 detail::thread_data_ptr local_thread_info=(get_thread_info)(); 444 if(local_thread_info) 445 { 446 this_thread::interruptible_wait(this->native_handle(), detail::internal_platform_timepoint::getMax()); 447 release_handle(); 448 return true; 449 } 450 else 451 { 452 return false; 453 } 454 } 455 do_try_join_until_noexcept(detail::internal_platform_timepoint const & timeout,bool & res)456 bool thread::do_try_join_until_noexcept(detail::internal_platform_timepoint const &timeout, bool& res) 457 { 458 detail::thread_data_ptr local_thread_info=(get_thread_info)(); 459 if(local_thread_info) 460 { 461 if(!this_thread::interruptible_wait(this->native_handle(), timeout)) 462 { 463 res=false; 464 return true; 465 } 466 release_handle(); 467 res=true; 468 return true; 469 } 470 else 471 { 472 return false; 473 } 474 } 475 detach()476 void thread::detach() 477 { 478 release_handle(); 479 } 480 release_handle()481 void thread::release_handle() 482 { 483 thread_info=0; 484 } 485 486 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS interrupt()487 void thread::interrupt() 488 { 489 detail::thread_data_ptr local_thread_info=(get_thread_info)(); 490 if(local_thread_info) 491 { 492 local_thread_info->interrupt(); 493 } 494 } 495 interruption_requested() const496 bool thread::interruption_requested() const BOOST_NOEXCEPT 497 { 498 detail::thread_data_ptr local_thread_info=(get_thread_info)(); 499 return local_thread_info.get() && (winapi::WaitForSingleObjectEx(local_thread_info->interruption_handle,0,0)==0); 500 } 501 502 #endif 503 hardware_concurrency()504 unsigned thread::hardware_concurrency() BOOST_NOEXCEPT 505 { 506 detail::win32::system_info info; 507 detail::win32::get_system_info(&info); 508 return info.dwNumberOfProcessors; 509 } 510 physical_concurrency()511 unsigned thread::physical_concurrency() BOOST_NOEXCEPT 512 { 513 // a bit too strict: Windows XP with SP3 would be sufficient 514 #if BOOST_PLAT_WINDOWS_RUNTIME \ 515 || ( BOOST_USE_WINAPI_VERSION <= BOOST_WINAPI_VERSION_WINXP ) \ 516 || ( ( defined(__MINGW32__) && !defined(__MINGW64__) ) && _WIN32_WINNT < 0x0600) 517 return 0; 518 #else 519 unsigned cores = 0; 520 DWORD size = 0; 521 522 GetLogicalProcessorInformation(NULL, &size); 523 if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) 524 return 0; 525 const size_t Elements = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); 526 527 std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(Elements); 528 if (GetLogicalProcessorInformation(&buffer.front(), &size) == FALSE) 529 return 0; 530 531 532 for (size_t i = 0; i < Elements; ++i) { 533 if (buffer[i].Relationship == RelationProcessorCore) 534 ++cores; 535 } 536 return cores; 537 #endif 538 } 539 native_handle()540 thread::native_handle_type thread::native_handle() 541 { 542 detail::thread_data_ptr local_thread_info=(get_thread_info)(); 543 if(!local_thread_info) 544 { 545 return detail::win32::invalid_handle_value; 546 } 547 #if BOOST_PLAT_WINDOWS_RUNTIME 548 // There is no 'real' Win32 handle so we return a handle that at least can be waited on. 549 return local_thread_info->thread_handle.waitable_handle(); 550 #else 551 return (detail::win32::handle)local_thread_info->thread_handle; 552 #endif 553 } 554 BOOST_PREVENT_MACRO_SUBSTITUTION() const555 detail::thread_data_ptr thread::get_thread_info BOOST_PREVENT_MACRO_SUBSTITUTION () const 556 { 557 return thread_info; 558 } 559 560 namespace this_thread 561 { 562 #ifndef UNDER_CE 563 #if !BOOST_PLAT_WINDOWS_RUNTIME 564 namespace detail_ 565 { 566 typedef struct _REASON_CONTEXT { 567 ULONG Version; 568 DWORD Flags; 569 union { 570 LPWSTR SimpleReasonString; 571 struct { 572 HMODULE LocalizedReasonModule; 573 ULONG LocalizedReasonId; 574 ULONG ReasonStringCount; 575 LPWSTR *ReasonStrings; 576 } Detailed; 577 } Reason; 578 } REASON_CONTEXT, *PREASON_CONTEXT; 579 typedef BOOL (WINAPI *setwaitabletimerex_t)(HANDLE, const LARGE_INTEGER *, LONG, PTIMERAPCROUTINE, LPVOID, PREASON_CONTEXT, ULONG); SetWaitableTimerEx_emulation(HANDLE hTimer,const LARGE_INTEGER * lpDueTime,LONG lPeriod,PTIMERAPCROUTINE pfnCompletionRoutine,LPVOID lpArgToCompletionRoutine,PREASON_CONTEXT WakeContext,ULONG TolerableDelay)580 static inline BOOL WINAPI SetWaitableTimerEx_emulation(HANDLE hTimer, const LARGE_INTEGER *lpDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, PREASON_CONTEXT WakeContext, ULONG TolerableDelay) 581 { 582 return SetWaitableTimer(hTimer, lpDueTime, lPeriod, pfnCompletionRoutine, lpArgToCompletionRoutine, FALSE); 583 } 584 #ifdef _MSC_VER 585 #pragma warning(push) 586 #pragma warning(disable: 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail 587 #endif SetWaitableTimerEx()588 static inline setwaitabletimerex_t SetWaitableTimerEx() 589 { 590 static setwaitabletimerex_t setwaitabletimerex_impl; 591 if(setwaitabletimerex_impl) 592 return setwaitabletimerex_impl; 593 void (*addr)()=(void (*)()) GetProcAddress( 594 #if !defined(BOOST_NO_ANSI_APIS) 595 GetModuleHandleA("KERNEL32.DLL"), 596 #else 597 GetModuleHandleW(L"KERNEL32.DLL"), 598 #endif 599 "SetWaitableTimerEx"); 600 if(addr) 601 setwaitabletimerex_impl=(setwaitabletimerex_t) addr; 602 else 603 setwaitabletimerex_impl=&SetWaitableTimerEx_emulation; 604 return setwaitabletimerex_impl; 605 } 606 #ifdef _MSC_VER 607 #pragma warning(pop) 608 #endif 609 } 610 #endif 611 #endif interruptible_wait(detail::win32::handle handle_to_wait_for,detail::internal_platform_timepoint const & timeout)612 bool interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout) 613 { 614 detail::win32::handle handles[4]={0}; 615 unsigned handle_count=0; 616 unsigned wait_handle_index=~0U; 617 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 618 unsigned interruption_index=~0U; 619 #endif 620 unsigned timeout_index=~0U; 621 if(handle_to_wait_for!=detail::win32::invalid_handle_value) 622 { 623 wait_handle_index=handle_count; 624 handles[handle_count++]=handle_to_wait_for; 625 } 626 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 627 if(detail::get_current_thread_data() && detail::get_current_thread_data()->interruption_enabled) 628 { 629 interruption_index=handle_count; 630 handles[handle_count++]=detail::get_current_thread_data()->interruption_handle; 631 } 632 #endif 633 detail::win32::handle_manager timer_handle; 634 635 #ifndef UNDER_CE 636 #if !BOOST_PLAT_WINDOWS_RUNTIME 637 // Preferentially use coalescing timers for better power consumption and timer accuracy 638 if(timeout != detail::internal_platform_timepoint::getMax()) 639 { 640 boost::intmax_t const time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); 641 timer_handle=CreateWaitableTimer(NULL,false,NULL); 642 if(timer_handle!=0) 643 { 644 ULONG tolerable=32; // Empirical testing shows Windows ignores this when <= 26 645 if(time_left_msec/20>tolerable) // 5% 646 tolerable=static_cast<ULONG>(time_left_msec/20); 647 LARGE_INTEGER due_time={{0,0}}; 648 if(time_left_msec>0) 649 { 650 due_time.QuadPart=-(time_left_msec*10000); // negative indicates relative time 651 } 652 bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,NULL,tolerable)!=0; 653 if(set_time_succeeded) 654 { 655 timeout_index=handle_count; 656 handles[handle_count++]=timer_handle; 657 } 658 } 659 } 660 #endif 661 #endif 662 663 bool const using_timer=timeout_index!=~0u; 664 boost::intmax_t time_left_msec(INFINITE); 665 if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) 666 { 667 time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); 668 if(time_left_msec < 0) 669 { 670 time_left_msec = 0; 671 } 672 } 673 674 do 675 { 676 if(handle_count) 677 { 678 unsigned long const notified_index=winapi::WaitForMultipleObjectsEx(handle_count,handles,false,static_cast<DWORD>(time_left_msec), 0); 679 if(notified_index<handle_count) 680 { 681 if(notified_index==wait_handle_index) 682 { 683 return true; 684 } 685 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 686 else if(notified_index==interruption_index) 687 { 688 winapi::ResetEvent(detail::get_current_thread_data()->interruption_handle); 689 throw thread_interrupted(); 690 } 691 #endif 692 else if(notified_index==timeout_index) 693 { 694 return false; 695 } 696 } 697 } 698 else 699 { 700 detail::win32::sleep(static_cast<unsigned long>(time_left_msec)); 701 } 702 703 if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) 704 { 705 time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); 706 } 707 } 708 while(time_left_msec == INFINITE || time_left_msec > 0); 709 return false; 710 } 711 712 namespace no_interruption_point 713 { non_interruptible_wait(detail::win32::handle handle_to_wait_for,detail::internal_platform_timepoint const & timeout)714 bool non_interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout) 715 { 716 detail::win32::handle handles[3]={0}; 717 unsigned handle_count=0; 718 unsigned wait_handle_index=~0U; 719 unsigned timeout_index=~0U; 720 if(handle_to_wait_for!=detail::win32::invalid_handle_value) 721 { 722 wait_handle_index=handle_count; 723 handles[handle_count++]=handle_to_wait_for; 724 } 725 detail::win32::handle_manager timer_handle; 726 727 #ifndef UNDER_CE 728 #if !BOOST_PLAT_WINDOWS_RUNTIME 729 // Preferentially use coalescing timers for better power consumption and timer accuracy 730 if(timeout != detail::internal_platform_timepoint::getMax()) 731 { 732 boost::intmax_t const time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); 733 timer_handle=CreateWaitableTimer(NULL,false,NULL); 734 if(timer_handle!=0) 735 { 736 ULONG tolerable=32; // Empirical testing shows Windows ignores this when <= 26 737 if(time_left_msec/20>tolerable) // 5% 738 tolerable=static_cast<ULONG>(time_left_msec/20); 739 LARGE_INTEGER due_time={{0,0}}; 740 if(time_left_msec>0) 741 { 742 due_time.QuadPart=-(time_left_msec*10000); // negative indicates relative time 743 } 744 bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,NULL,tolerable)!=0; 745 if(set_time_succeeded) 746 { 747 timeout_index=handle_count; 748 handles[handle_count++]=timer_handle; 749 } 750 } 751 } 752 #endif 753 #endif 754 755 bool const using_timer=timeout_index!=~0u; 756 boost::intmax_t time_left_msec(INFINITE); 757 if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) 758 { 759 time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); 760 if(time_left_msec < 0) 761 { 762 time_left_msec = 0; 763 } 764 } 765 766 do 767 { 768 if(handle_count) 769 { 770 unsigned long const notified_index=winapi::WaitForMultipleObjectsEx(handle_count,handles,false,static_cast<DWORD>(time_left_msec), 0); 771 if(notified_index<handle_count) 772 { 773 if(notified_index==wait_handle_index) 774 { 775 return true; 776 } 777 else if(notified_index==timeout_index) 778 { 779 return false; 780 } 781 } 782 } 783 else 784 { 785 detail::win32::sleep(static_cast<unsigned long>(time_left_msec)); 786 } 787 788 if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) 789 { 790 time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); 791 } 792 } 793 while(time_left_msec == INFINITE || time_left_msec > 0); 794 return false; 795 } 796 } 797 get_id()798 thread::id get_id() BOOST_NOEXCEPT 799 { 800 #if defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID 801 #if BOOST_PLAT_WINDOWS_RUNTIME 802 detail::thread_data_base* current_thread_data(detail::get_current_thread_data()); 803 if (current_thread_data) 804 { 805 return current_thread_data->id; 806 } 807 #endif 808 return winapi::GetCurrentThreadId(); 809 #else 810 return thread::id(get_or_make_current_thread_data()); 811 #endif 812 } 813 814 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS interruption_point()815 void interruption_point() 816 { 817 if(interruption_enabled() && interruption_requested()) 818 { 819 winapi::ResetEvent(detail::get_current_thread_data()->interruption_handle); 820 throw thread_interrupted(); 821 } 822 } 823 interruption_enabled()824 bool interruption_enabled() BOOST_NOEXCEPT 825 { 826 return detail::get_current_thread_data() && detail::get_current_thread_data()->interruption_enabled; 827 } 828 interruption_requested()829 bool interruption_requested() BOOST_NOEXCEPT 830 { 831 return detail::get_current_thread_data() && (winapi::WaitForSingleObjectEx(detail::get_current_thread_data()->interruption_handle,0,0)==0); 832 } 833 #endif 834 yield()835 void yield() BOOST_NOEXCEPT 836 { 837 detail::win32::sleep(0); 838 } 839 840 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS disable_interruption()841 disable_interruption::disable_interruption() BOOST_NOEXCEPT: 842 interruption_was_enabled(interruption_enabled()) 843 { 844 if(interruption_was_enabled) 845 { 846 detail::get_current_thread_data()->interruption_enabled=false; 847 } 848 } 849 ~disable_interruption()850 disable_interruption::~disable_interruption() BOOST_NOEXCEPT 851 { 852 if(detail::get_current_thread_data()) 853 { 854 detail::get_current_thread_data()->interruption_enabled=interruption_was_enabled; 855 } 856 } 857 restore_interruption(disable_interruption & d)858 restore_interruption::restore_interruption(disable_interruption& d) BOOST_NOEXCEPT 859 { 860 if(d.interruption_was_enabled) 861 { 862 detail::get_current_thread_data()->interruption_enabled=true; 863 } 864 } 865 ~restore_interruption()866 restore_interruption::~restore_interruption() BOOST_NOEXCEPT 867 { 868 if(detail::get_current_thread_data()) 869 { 870 detail::get_current_thread_data()->interruption_enabled=false; 871 } 872 } 873 #endif 874 } 875 876 namespace detail 877 { add_thread_exit_function(thread_exit_function_base * func)878 void add_thread_exit_function(thread_exit_function_base* func) 879 { 880 detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); 881 thread_exit_callback_node* const new_node= 882 heap_new<thread_exit_callback_node>( 883 func,current_thread_data->thread_exit_callbacks); 884 current_thread_data->thread_exit_callbacks=new_node; 885 } 886 find_tss_data(void const * key)887 tss_data_node* find_tss_data(void const* key) 888 { 889 detail::thread_data_base* const current_thread_data(get_current_thread_data()); 890 if(current_thread_data) 891 { 892 std::map<void const*,tss_data_node>::iterator current_node= 893 current_thread_data->tss_data.find(key); 894 if(current_node!=current_thread_data->tss_data.end()) 895 { 896 return ¤t_node->second; 897 } 898 } 899 return NULL; 900 } 901 get_tss_data(void const * key)902 void* get_tss_data(void const* key) 903 { 904 if(tss_data_node* const current_node=find_tss_data(key)) 905 { 906 return current_node->value; 907 } 908 return NULL; 909 } 910 add_new_tss_node(void const * key,detail::tss_data_node::cleanup_caller_t caller,detail::tss_data_node::cleanup_func_t func,void * tss_data)911 void add_new_tss_node(void const* key, 912 detail::tss_data_node::cleanup_caller_t caller, 913 detail::tss_data_node::cleanup_func_t func, 914 void* tss_data) 915 { 916 detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); 917 current_thread_data->tss_data.insert(std::make_pair(key,tss_data_node(caller,func,tss_data))); 918 } 919 erase_tss_node(void const * key)920 void erase_tss_node(void const* key) 921 { 922 detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); 923 current_thread_data->tss_data.erase(key); 924 } 925 set_tss_data(void const * key,detail::tss_data_node::cleanup_caller_t caller,detail::tss_data_node::cleanup_func_t func,void * tss_data,bool cleanup_existing)926 void set_tss_data(void const* key, 927 detail::tss_data_node::cleanup_caller_t caller, 928 detail::tss_data_node::cleanup_func_t func, 929 void* tss_data,bool cleanup_existing) 930 { 931 if(tss_data_node* const current_node=find_tss_data(key)) 932 { 933 if(cleanup_existing && current_node->func && (current_node->value!=0)) 934 { 935 (*current_node->caller)(current_node->func,current_node->value); 936 } 937 if(func || (tss_data!=0)) 938 { 939 current_node->caller=caller; 940 current_node->func=func; 941 current_node->value=tss_data; 942 } 943 else 944 { 945 erase_tss_node(key); 946 } 947 } 948 else if(func || (tss_data!=0)) 949 { 950 add_new_tss_node(key,caller,func,tss_data); 951 } 952 } 953 } 954 on_process_enter()955 BOOST_THREAD_DECL void __cdecl on_process_enter() 956 {} 957 on_thread_enter()958 BOOST_THREAD_DECL void __cdecl on_thread_enter() 959 {} 960 on_process_exit()961 BOOST_THREAD_DECL void __cdecl on_process_exit() 962 { 963 boost::cleanup_tls_key(); 964 } 965 on_thread_exit()966 BOOST_THREAD_DECL void __cdecl on_thread_exit() 967 { 968 boost::run_thread_exit_callbacks(); 969 } 970 notify_all_at_thread_exit(condition_variable & cond,unique_lock<mutex> lk)971 BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk) 972 { 973 detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); 974 if(current_thread_data) 975 { 976 current_thread_data->notify_all_at_thread_exit(&cond, lk.release()); 977 } 978 } 979 } 980 981