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