]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | |
11fdf7f2 | 6 | // (C) Copyright 2011-2018 Vicente J. Botet Escriba |
7c673cae FG |
7 | |
8 | //#define BOOST_THREAD_VERSION 3 | |
9 | ||
11fdf7f2 | 10 | #include <boost/winapi/config.hpp> |
7c673cae FG |
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 | { | |
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 | } | |
92f5a8d4 | 57 | //#ifndef BOOST_NO_EXCEPTIONS |
7c673cae FG |
58 | for (async_states_t::iterator i = async_states_.begin(), e = async_states_.end(); |
59 | i != e; ++i) | |
60 | { | |
b32b8144 | 61 | (*i)->notify_deferred(); |
7c673cae | 62 | } |
92f5a8d4 | 63 | //#endif |
7c673cae FG |
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 | ||
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 | ||
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 | ||
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 | { | |
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_; | |
149 | ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} | |
150 | }; | |
151 | ||
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 | ||
7c673cae FG |
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 | ||
185 | 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 | ||
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 | { | |
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 | { | |
92f5a8d4 | 279 | (*current->second.caller)(current->second.func,current->second.value); |
7c673cae FG |
280 | } |
281 | current_thread_data->tss_data.erase(current); | |
282 | } | |
283 | } | |
284 | set_current_thread_data(0); | |
285 | } | |
286 | } | |
287 | ||
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 | } | |
b32b8144 | 302 | // Unhandled exceptions still cause the application to terminate |
7c673cae FG |
303 | BOOST_CATCH_END |
304 | #endif | |
305 | run_thread_exit_callbacks(); | |
306 | return 0; | |
307 | } | |
308 | } | |
309 | ||
310 | thread::thread() BOOST_NOEXCEPT | |
311 | {} | |
312 | ||
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()); | |
7c673cae FG |
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; | |
7c673cae FG |
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 | ||
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 | |
b32b8144 FG |
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); | |
7c673cae FG |
345 | if(!new_thread) |
346 | { | |
347 | return false; | |
7c673cae FG |
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 | ||
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 | { | |
365 | externally_launched_thread() | |
366 | { | |
367 | ++count; | |
368 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS | |
369 | interruption_enabled=false; | |
370 | #endif | |
371 | } | |
372 | ~externally_launched_thread() { | |
373 | BOOST_ASSERT(notify.empty()); | |
374 | notify.clear(); | |
92f5a8d4 | 375 | //#ifndef BOOST_NO_EXCEPTIONS |
7c673cae FG |
376 | BOOST_ASSERT(async_states_.empty()); |
377 | async_states_.clear(); | |
92f5a8d4 | 378 | //#endif |
7c673cae FG |
379 | } |
380 | ||
381 | void run() | |
382 | {} | |
383 | 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 | ||
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 | ||
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 | ||
418 | 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 | ||
432 | 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 | } | |
441 | bool thread::join_noexcept() | |
442 | { | |
443 | detail::thread_data_ptr local_thread_info=(get_thread_info)(); | |
444 | if(local_thread_info) | |
445 | { | |
11fdf7f2 | 446 | this_thread::interruptible_wait(this->native_handle(), detail::internal_platform_timepoint::getMax()); |
7c673cae FG |
447 | release_handle(); |
448 | return true; | |
449 | } | |
450 | else | |
451 | { | |
452 | return false; | |
453 | } | |
454 | } | |
455 | ||
11fdf7f2 | 456 | bool thread::do_try_join_until_noexcept(detail::internal_platform_timepoint const &timeout, bool& res) |
7c673cae FG |
457 | { |
458 | detail::thread_data_ptr local_thread_info=(get_thread_info)(); | |
459 | if(local_thread_info) | |
460 | { | |
11fdf7f2 | 461 | if(!this_thread::interruptible_wait(this->native_handle(), timeout)) |
7c673cae FG |
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 | ||
476 | void thread::detach() | |
477 | { | |
478 | release_handle(); | |
479 | } | |
480 | ||
481 | void thread::release_handle() | |
482 | { | |
483 | thread_info=0; | |
484 | } | |
485 | ||
486 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS | |
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 | ||
496 | bool thread::interruption_requested() const BOOST_NOEXCEPT | |
497 | { | |
498 | detail::thread_data_ptr local_thread_info=(get_thread_info)(); | |
11fdf7f2 | 499 | return local_thread_info.get() && (winapi::WaitForSingleObjectEx(local_thread_info->interruption_handle,0,0)==0); |
7c673cae FG |
500 | } |
501 | ||
502 | #endif | |
503 | ||
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 | ||
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; | |
92f5a8d4 | 525 | const size_t Elements = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
7c673cae | 526 | |
92f5a8d4 | 527 | std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(Elements); |
7c673cae FG |
528 | if (GetLogicalProcessorInformation(&buffer.front(), &size) == FALSE) |
529 | return 0; | |
530 | ||
7c673cae FG |
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 | ||
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 | ||
555 | 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 | { | |
7c673cae FG |
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; | |
7c673cae FG |
579 | typedef BOOL (WINAPI *setwaitabletimerex_t)(HANDLE, const LARGE_INTEGER *, LONG, PTIMERAPCROUTINE, LPVOID, PREASON_CONTEXT, ULONG); |
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 | |
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 | |
11fdf7f2 | 612 | bool interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout) |
7c673cae FG |
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 | |
11fdf7f2 | 638 | if(timeout != detail::internal_platform_timepoint::getMax()) |
7c673cae | 639 | { |
11fdf7f2 | 640 | boost::intmax_t const time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); |
7c673cae FG |
641 | timer_handle=CreateWaitableTimer(NULL,false,NULL); |
642 | if(timer_handle!=0) | |
643 | { | |
1e59de90 TL |
644 | ULONG const min_tolerable=32; // Empirical testing shows Windows ignores this when <= 26 |
645 | ULONG const max_tolerable=1000; | |
646 | ULONG tolerable=min_tolerable; | |
11fdf7f2 | 647 | if(time_left_msec/20>tolerable) // 5% |
1e59de90 | 648 | { |
11fdf7f2 | 649 | tolerable=static_cast<ULONG>(time_left_msec/20); |
1e59de90 TL |
650 | if(tolerable>max_tolerable) |
651 | tolerable=max_tolerable; | |
652 | } | |
11fdf7f2 TL |
653 | LARGE_INTEGER due_time={{0,0}}; |
654 | if(time_left_msec>0) | |
655 | { | |
656 | due_time.QuadPart=-(time_left_msec*10000); // negative indicates relative time | |
657 | } | |
7c673cae FG |
658 | bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,NULL,tolerable)!=0; |
659 | if(set_time_succeeded) | |
660 | { | |
661 | timeout_index=handle_count; | |
662 | handles[handle_count++]=timer_handle; | |
663 | } | |
664 | } | |
665 | } | |
666 | #endif | |
667 | #endif | |
668 | ||
669 | bool const using_timer=timeout_index!=~0u; | |
11fdf7f2 TL |
670 | boost::intmax_t time_left_msec(INFINITE); |
671 | if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) | |
7c673cae | 672 | { |
11fdf7f2 TL |
673 | time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); |
674 | if(time_left_msec < 0) | |
7c673cae | 675 | { |
11fdf7f2 | 676 | time_left_msec = 0; |
7c673cae | 677 | } |
11fdf7f2 | 678 | } |
7c673cae | 679 | |
11fdf7f2 TL |
680 | do |
681 | { | |
7c673cae FG |
682 | if(handle_count) |
683 | { | |
11fdf7f2 | 684 | unsigned long const notified_index=winapi::WaitForMultipleObjectsEx(handle_count,handles,false,static_cast<DWORD>(time_left_msec), 0); |
7c673cae FG |
685 | if(notified_index<handle_count) |
686 | { | |
687 | if(notified_index==wait_handle_index) | |
688 | { | |
689 | return true; | |
690 | } | |
691 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS | |
692 | else if(notified_index==interruption_index) | |
693 | { | |
11fdf7f2 | 694 | winapi::ResetEvent(detail::get_current_thread_data()->interruption_handle); |
7c673cae FG |
695 | throw thread_interrupted(); |
696 | } | |
697 | #endif | |
698 | else if(notified_index==timeout_index) | |
699 | { | |
700 | return false; | |
701 | } | |
702 | } | |
703 | } | |
704 | else | |
705 | { | |
11fdf7f2 | 706 | detail::win32::sleep(static_cast<unsigned long>(time_left_msec)); |
7c673cae | 707 | } |
11fdf7f2 TL |
708 | |
709 | if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) | |
7c673cae | 710 | { |
11fdf7f2 | 711 | time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); |
7c673cae FG |
712 | } |
713 | } | |
11fdf7f2 | 714 | while(time_left_msec == INFINITE || time_left_msec > 0); |
7c673cae FG |
715 | return false; |
716 | } | |
717 | ||
718 | namespace no_interruption_point | |
719 | { | |
11fdf7f2 | 720 | bool non_interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout) |
7c673cae FG |
721 | { |
722 | detail::win32::handle handles[3]={0}; | |
723 | unsigned handle_count=0; | |
724 | unsigned wait_handle_index=~0U; | |
725 | unsigned timeout_index=~0U; | |
726 | if(handle_to_wait_for!=detail::win32::invalid_handle_value) | |
727 | { | |
728 | wait_handle_index=handle_count; | |
729 | handles[handle_count++]=handle_to_wait_for; | |
730 | } | |
731 | detail::win32::handle_manager timer_handle; | |
732 | ||
733 | #ifndef UNDER_CE | |
734 | #if !BOOST_PLAT_WINDOWS_RUNTIME | |
735 | // Preferentially use coalescing timers for better power consumption and timer accuracy | |
11fdf7f2 | 736 | if(timeout != detail::internal_platform_timepoint::getMax()) |
7c673cae | 737 | { |
11fdf7f2 | 738 | boost::intmax_t const time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); |
7c673cae FG |
739 | timer_handle=CreateWaitableTimer(NULL,false,NULL); |
740 | if(timer_handle!=0) | |
741 | { | |
1e59de90 TL |
742 | ULONG const min_tolerable=32; // Empirical testing shows Windows ignores this when <= 26 |
743 | ULONG const max_tolerable=1000; | |
744 | ULONG tolerable=min_tolerable; | |
11fdf7f2 | 745 | if(time_left_msec/20>tolerable) // 5% |
1e59de90 | 746 | { |
11fdf7f2 | 747 | tolerable=static_cast<ULONG>(time_left_msec/20); |
1e59de90 TL |
748 | if(tolerable>max_tolerable) |
749 | tolerable=max_tolerable; | |
750 | } | |
11fdf7f2 TL |
751 | LARGE_INTEGER due_time={{0,0}}; |
752 | if(time_left_msec>0) | |
753 | { | |
754 | due_time.QuadPart=-(time_left_msec*10000); // negative indicates relative time | |
755 | } | |
7c673cae FG |
756 | bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,NULL,tolerable)!=0; |
757 | if(set_time_succeeded) | |
758 | { | |
759 | timeout_index=handle_count; | |
760 | handles[handle_count++]=timer_handle; | |
761 | } | |
762 | } | |
763 | } | |
764 | #endif | |
765 | #endif | |
766 | ||
767 | bool const using_timer=timeout_index!=~0u; | |
11fdf7f2 TL |
768 | boost::intmax_t time_left_msec(INFINITE); |
769 | if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) | |
7c673cae | 770 | { |
11fdf7f2 TL |
771 | time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); |
772 | if(time_left_msec < 0) | |
7c673cae | 773 | { |
11fdf7f2 | 774 | time_left_msec = 0; |
7c673cae | 775 | } |
11fdf7f2 | 776 | } |
7c673cae | 777 | |
11fdf7f2 TL |
778 | do |
779 | { | |
7c673cae FG |
780 | if(handle_count) |
781 | { | |
11fdf7f2 | 782 | unsigned long const notified_index=winapi::WaitForMultipleObjectsEx(handle_count,handles,false,static_cast<DWORD>(time_left_msec), 0); |
7c673cae FG |
783 | if(notified_index<handle_count) |
784 | { | |
785 | if(notified_index==wait_handle_index) | |
786 | { | |
787 | return true; | |
788 | } | |
789 | else if(notified_index==timeout_index) | |
790 | { | |
791 | return false; | |
792 | } | |
793 | } | |
794 | } | |
795 | else | |
796 | { | |
11fdf7f2 | 797 | detail::win32::sleep(static_cast<unsigned long>(time_left_msec)); |
7c673cae | 798 | } |
11fdf7f2 TL |
799 | |
800 | if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) | |
7c673cae | 801 | { |
11fdf7f2 | 802 | time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); |
7c673cae FG |
803 | } |
804 | } | |
11fdf7f2 | 805 | while(time_left_msec == INFINITE || time_left_msec > 0); |
7c673cae FG |
806 | return false; |
807 | } | |
808 | } | |
809 | ||
810 | thread::id get_id() BOOST_NOEXCEPT | |
811 | { | |
812 | #if defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID | |
813 | #if BOOST_PLAT_WINDOWS_RUNTIME | |
814 | detail::thread_data_base* current_thread_data(detail::get_current_thread_data()); | |
815 | if (current_thread_data) | |
816 | { | |
817 | return current_thread_data->id; | |
818 | } | |
819 | #endif | |
11fdf7f2 | 820 | return winapi::GetCurrentThreadId(); |
7c673cae FG |
821 | #else |
822 | return thread::id(get_or_make_current_thread_data()); | |
823 | #endif | |
824 | } | |
825 | ||
826 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS | |
827 | void interruption_point() | |
828 | { | |
829 | if(interruption_enabled() && interruption_requested()) | |
830 | { | |
11fdf7f2 | 831 | winapi::ResetEvent(detail::get_current_thread_data()->interruption_handle); |
7c673cae FG |
832 | throw thread_interrupted(); |
833 | } | |
834 | } | |
835 | ||
836 | bool interruption_enabled() BOOST_NOEXCEPT | |
837 | { | |
838 | return detail::get_current_thread_data() && detail::get_current_thread_data()->interruption_enabled; | |
839 | } | |
840 | ||
841 | bool interruption_requested() BOOST_NOEXCEPT | |
842 | { | |
11fdf7f2 | 843 | return detail::get_current_thread_data() && (winapi::WaitForSingleObjectEx(detail::get_current_thread_data()->interruption_handle,0,0)==0); |
7c673cae FG |
844 | } |
845 | #endif | |
846 | ||
847 | void yield() BOOST_NOEXCEPT | |
848 | { | |
849 | detail::win32::sleep(0); | |
850 | } | |
851 | ||
852 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS | |
853 | disable_interruption::disable_interruption() BOOST_NOEXCEPT: | |
854 | interruption_was_enabled(interruption_enabled()) | |
855 | { | |
856 | if(interruption_was_enabled) | |
857 | { | |
858 | detail::get_current_thread_data()->interruption_enabled=false; | |
859 | } | |
860 | } | |
861 | ||
862 | disable_interruption::~disable_interruption() BOOST_NOEXCEPT | |
863 | { | |
864 | if(detail::get_current_thread_data()) | |
865 | { | |
866 | detail::get_current_thread_data()->interruption_enabled=interruption_was_enabled; | |
867 | } | |
868 | } | |
869 | ||
870 | restore_interruption::restore_interruption(disable_interruption& d) BOOST_NOEXCEPT | |
871 | { | |
872 | if(d.interruption_was_enabled) | |
873 | { | |
874 | detail::get_current_thread_data()->interruption_enabled=true; | |
875 | } | |
876 | } | |
877 | ||
878 | restore_interruption::~restore_interruption() BOOST_NOEXCEPT | |
879 | { | |
880 | if(detail::get_current_thread_data()) | |
881 | { | |
882 | detail::get_current_thread_data()->interruption_enabled=false; | |
883 | } | |
884 | } | |
885 | #endif | |
886 | } | |
887 | ||
888 | namespace detail | |
889 | { | |
890 | void add_thread_exit_function(thread_exit_function_base* func) | |
891 | { | |
892 | detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); | |
893 | thread_exit_callback_node* const new_node= | |
894 | heap_new<thread_exit_callback_node>( | |
895 | func,current_thread_data->thread_exit_callbacks); | |
896 | current_thread_data->thread_exit_callbacks=new_node; | |
897 | } | |
898 | ||
899 | tss_data_node* find_tss_data(void const* key) | |
900 | { | |
901 | detail::thread_data_base* const current_thread_data(get_current_thread_data()); | |
902 | if(current_thread_data) | |
903 | { | |
904 | std::map<void const*,tss_data_node>::iterator current_node= | |
905 | current_thread_data->tss_data.find(key); | |
906 | if(current_node!=current_thread_data->tss_data.end()) | |
907 | { | |
908 | return ¤t_node->second; | |
909 | } | |
910 | } | |
911 | return NULL; | |
912 | } | |
913 | ||
914 | void* get_tss_data(void const* key) | |
915 | { | |
916 | if(tss_data_node* const current_node=find_tss_data(key)) | |
917 | { | |
918 | return current_node->value; | |
919 | } | |
920 | return NULL; | |
921 | } | |
922 | ||
923 | void add_new_tss_node(void const* key, | |
92f5a8d4 TL |
924 | detail::tss_data_node::cleanup_caller_t caller, |
925 | detail::tss_data_node::cleanup_func_t func, | |
7c673cae FG |
926 | void* tss_data) |
927 | { | |
928 | detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); | |
92f5a8d4 | 929 | current_thread_data->tss_data.insert(std::make_pair(key,tss_data_node(caller,func,tss_data))); |
7c673cae FG |
930 | } |
931 | ||
932 | void erase_tss_node(void const* key) | |
933 | { | |
934 | detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); | |
935 | current_thread_data->tss_data.erase(key); | |
936 | } | |
937 | ||
938 | void set_tss_data(void const* key, | |
92f5a8d4 TL |
939 | detail::tss_data_node::cleanup_caller_t caller, |
940 | detail::tss_data_node::cleanup_func_t func, | |
7c673cae FG |
941 | void* tss_data,bool cleanup_existing) |
942 | { | |
943 | if(tss_data_node* const current_node=find_tss_data(key)) | |
944 | { | |
945 | if(cleanup_existing && current_node->func && (current_node->value!=0)) | |
946 | { | |
92f5a8d4 | 947 | (*current_node->caller)(current_node->func,current_node->value); |
7c673cae FG |
948 | } |
949 | if(func || (tss_data!=0)) | |
950 | { | |
92f5a8d4 | 951 | current_node->caller=caller; |
7c673cae FG |
952 | current_node->func=func; |
953 | current_node->value=tss_data; | |
954 | } | |
955 | else | |
956 | { | |
957 | erase_tss_node(key); | |
958 | } | |
959 | } | |
960 | else if(func || (tss_data!=0)) | |
961 | { | |
92f5a8d4 | 962 | add_new_tss_node(key,caller,func,tss_data); |
7c673cae FG |
963 | } |
964 | } | |
965 | } | |
966 | ||
967 | BOOST_THREAD_DECL void __cdecl on_process_enter() | |
968 | {} | |
969 | ||
970 | BOOST_THREAD_DECL void __cdecl on_thread_enter() | |
971 | {} | |
972 | ||
973 | BOOST_THREAD_DECL void __cdecl on_process_exit() | |
974 | { | |
975 | boost::cleanup_tls_key(); | |
976 | } | |
977 | ||
978 | BOOST_THREAD_DECL void __cdecl on_thread_exit() | |
979 | { | |
980 | boost::run_thread_exit_callbacks(); | |
981 | } | |
982 | ||
983 | BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk) | |
984 | { | |
985 | detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); | |
986 | if(current_thread_data) | |
987 | { | |
988 | current_thread_data->notify_all_at_thread_exit(&cond, lk.release()); | |
989 | } | |
990 | } | |
7c673cae FG |
991 | } |
992 |