... | ... |
@@ -102,7 +102,7 @@ ARCH_OBJS=./boost/libs/filesystem/src/path_traits.o \ |
102 | 102 |
./boost/libs/thread/src/win32/tss_pe.o \ |
103 | 103 |
./boost/libs/thread/src/win32/tss_dll.o \ |
104 | 104 |
./boost/libs/thread/src/win32/thread.o \ |
105 |
-./boost_aux/boost/nowide/iostream.o |
|
105 |
+./boost_aux/libs/nowide/src/iostream.o |
|
106 | 106 |
ARCH_CPPFLAGS=-fpermissive -DWINDOWS_NATIVE -DWIN32 -DBOOST_HAS_WINTHREADS -DBOOST_THREAD_BUILD_LIB |
107 | 107 |
ARCH_LIBS=-lws2_32 -lz |
108 | 108 |
RHDF5_LIBS=$(shell echo 'Rhdf5lib::pkgconfig("PKG_CXX_LIBS")'| "${R_HOME}/bin/R" --vanilla --slave) |
109 | 109 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,980 @@ |
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 |
+ { |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ { |
|
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 |
+ |
|
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 |
+ |
|
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()); |
|
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 |
+ |
|
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 |
+ |
|
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(); |
|
375 |
+//#ifndef BOOST_NO_EXCEPTIONS |
|
376 |
+ BOOST_ASSERT(async_states_.empty()); |
|
377 |
+ async_states_.clear(); |
|
378 |
+//#endif |
|
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 |
+ { |
|
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 |
+ |
|
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 |
+ |
|
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)(); |
|
499 |
+ return local_thread_info.get() && (winapi::WaitForSingleObjectEx(local_thread_info->interruption_handle,0,0)==0); |
|
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; |
|
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 |
+ |
|
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 |
+ { |
|
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); |
|
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 |
|
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 |
+ { |
|
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 |
+ |
|
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 |
|
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 |
+ |
|
824 |
+ bool interruption_enabled() BOOST_NOEXCEPT |
|
825 |
+ { |
|
826 |
+ return detail::get_current_thread_data() && detail::get_current_thread_data()->interruption_enabled; |
|
827 |
+ } |
|
828 |
+ |
|
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 |
+ |
|
835 |
+ void yield() BOOST_NOEXCEPT |
|
836 |
+ { |
|
837 |
+ detail::win32::sleep(0); |
|
838 |
+ } |
|
839 |
+ |
|
840 |
+#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ { |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ |
|
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 |
+ |
|
955 |
+ BOOST_THREAD_DECL void __cdecl on_process_enter() |
|
956 |
+ {} |
|
957 |
+ |
|
958 |
+ BOOST_THREAD_DECL void __cdecl on_thread_enter() |
|
959 |
+ {} |
|
960 |
+ |
|
961 |
+ BOOST_THREAD_DECL void __cdecl on_process_exit() |
|
962 |
+ { |
|
963 |
+ boost::cleanup_tls_key(); |
|
964 |
+ } |
|
965 |
+ |
|
966 |
+ BOOST_THREAD_DECL void __cdecl on_thread_exit() |
|
967 |
+ { |
|
968 |
+ boost::run_thread_exit_callbacks(); |
|
969 |
+ } |
|
970 |
+ |
|
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 |
+ |
0 | 981 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,156 @@ |
1 |
+// thread_primitives.cpp |
|
2 |
+// |
|
3 |
+// (C) Copyright 2018 Andrey Semashev |
|
4 |
+// |
|
5 |
+// Distributed under the Boost Software License, Version 1.0. (See |
|
6 |
+// accompanying file LICENSE_1_0.txt or copy at |
|
7 |
+// http://www.boost.org/LICENSE_1_0.txt) |
|
8 |
+ |
|
9 |
+#include <boost/winapi/config.hpp> |
|
10 |
+#include <boost/winapi/dll.hpp> |
|
11 |
+#include <boost/winapi/time.hpp> |
|
12 |
+#include <boost/winapi/event.hpp> |
|
13 |
+#include <boost/winapi/handles.hpp> |
|
14 |
+#include <boost/winapi/thread_pool.hpp> |
|
15 |
+#include <cstdlib> |
|
16 |
+#include <boost/config.hpp> |
|
17 |
+#include <boost/cstdint.hpp> |
|
18 |
+#include <boost/memory_order.hpp> |
|
19 |
+#include <boost/atomic/atomic.hpp> |
|
20 |
+#include <boost/thread/win32/interlocked_read.hpp> |
|
21 |
+#include <boost/thread/win32/thread_primitives.hpp> |
|
22 |
+ |
|
23 |
+namespace boost { |
|
24 |
+namespace detail { |
|
25 |
+namespace win32 { |
|
26 |
+ |
|
27 |
+#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
|
28 |
+ |
|
29 |
+// Directly use API from Vista and later |
|
30 |
+BOOST_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64 = &::boost::winapi::GetTickCount64; |
|
31 |
+ |
|
32 |
+#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
|
33 |
+ |
|
34 |
+namespace { |
|
35 |
+ |
|
36 |
+enum init_state |
|
37 |
+{ |
|
38 |
+ uninitialized = 0, |
|
39 |
+ in_progress, |
|
40 |
+ initialized |
|
41 |
+}; |
|
42 |
+ |
|
43 |
+struct get_tick_count64_state |
|
44 |
+{ |
|
45 |
+ boost::atomic< uint64_t > ticks; |
|
46 |
+ boost::atomic< init_state > init; |
|
47 |
+ boost::winapi::HANDLE_ wait_event; |
|
48 |
+ boost::winapi::HANDLE_ wait_handle; |
|
49 |
+}; |
|
50 |
+ |
|
51 |
+// Zero-initialized initially |
|
52 |
+BOOST_ALIGNMENT(64) static get_tick_count64_state g_state; |
|
53 |
+ |
|
54 |
+//! Artifical implementation of GetTickCount64 |
|
55 |
+ticks_type WINAPI get_tick_count64() |
|
56 |
+{ |
|
57 |
+ uint64_t old_state = g_state.ticks.load(boost::memory_order_acquire); |
|
58 |
+ |
|
59 |
+ uint32_t new_ticks = boost::winapi::GetTickCount(); |
|
60 |
+ |
|
61 |
+ uint32_t old_ticks = static_cast< uint32_t >(old_state & UINT64_C(0x00000000ffffffff)); |
|
62 |
+ uint64_t new_state = ((old_state & UINT64_C(0xffffffff00000000)) + (static_cast< uint64_t >(new_ticks < old_ticks) << 32)) | static_cast< uint64_t >(new_ticks); |
|
63 |
+ |
|
64 |
+ g_state.ticks.store(new_state, boost::memory_order_release); |
|
65 |
+ |
|
66 |
+ return new_state; |
|
67 |
+} |
|
68 |
+ |
|
69 |
+//! The function is called periodically in the system thread pool to make sure g_state.ticks is timely updated |
|
70 |
+void NTAPI refresh_get_tick_count64(boost::winapi::PVOID_, boost::winapi::BOOLEAN_) |
|
71 |
+{ |
|
72 |
+ get_tick_count64(); |
|
73 |
+} |
|
74 |
+ |
|
75 |
+//! Cleanup function to stop get_tick_count64 refreshes |
|
76 |
+void cleanup_get_tick_count64() |
|
77 |
+{ |
|
78 |
+ if (g_state.wait_handle) |
|
79 |
+ { |
|
80 |
+ boost::winapi::UnregisterWait(g_state.wait_handle); |
|
81 |
+ g_state.wait_handle = NULL; |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ if (g_state.wait_event) |
|
85 |
+ { |
|
86 |
+ boost::winapi::CloseHandle(g_state.wait_event); |
|
87 |
+ g_state.wait_event = NULL; |
|
88 |
+ } |
|
89 |
+} |
|
90 |
+ |
|
91 |
+ticks_type WINAPI get_tick_count_init() |
|
92 |
+{ |
|
93 |
+ boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); |
|
94 |
+ if (hKernel32) |
|
95 |
+ { |
|
96 |
+ // GetProcAddress returns a pointer to some function. It can return |
|
97 |
+ // pointers to different functions, so it has to return something that is |
|
98 |
+ // suitable to store any pointer to function. Microsoft chose FARPROC, |
|
99 |
+ // which is int (WINAPI *)() on 32-bit Windows. The user is supposed to |
|
100 |
+ // know the signature of the function he requests and perform a cast |
|
101 |
+ // (which is a nop on this platform). The result is a pointer to function |
|
102 |
+ // with the required signature, which is bitwise equal to what |
|
103 |
+ // GetProcAddress returned. |
|
104 |
+ // However, gcc >= 8 warns about that. |
|
105 |
+#if defined(BOOST_GCC) && BOOST_GCC >= 80000 |
|
106 |
+#pragma GCC diagnostic push |
|
107 |
+#pragma GCC diagnostic ignored "-Wcast-function-type" |
|
108 |
+#endif |
|
109 |
+ boost::detail::win32::detail::gettickcount64_t p = |
|
110 |
+ (boost::detail::win32::detail::gettickcount64_t)boost::winapi::get_proc_address(hKernel32, "GetTickCount64"); |
|
111 |
+#if defined(BOOST_GCC) && BOOST_GCC >= 80000 |
|
112 |
+#pragma GCC diagnostic pop |
|
113 |
+#endif |
|
114 |
+ if (p) |
|
115 |
+ { |
|
116 |
+ // Use native API |
|
117 |
+ boost::detail::interlocked_write_release((void**)&gettickcount64, (void*)p); |
|
118 |
+ return p(); |
|
119 |
+ } |
|
120 |
+ } |
|
121 |
+ |
|
122 |
+ // No native API available. Use emulation with periodic refreshes to make sure the GetTickCount wrap arounds are properly counted. |
|
123 |
+ init_state old_init = uninitialized; |
|
124 |
+ if (g_state.init.compare_exchange_strong(old_init, in_progress, boost::memory_order_acq_rel, boost::memory_order_relaxed)) |
|
125 |
+ { |
|
126 |
+ if (!g_state.wait_event) |
|
127 |
+ g_state.wait_event = boost::winapi::create_anonymous_event(NULL, false, false); |
|
128 |
+ if (g_state.wait_event) |
|
129 |
+ { |
|
130 |
+ boost::winapi::BOOL_ res = boost::winapi::RegisterWaitForSingleObject(&g_state.wait_handle, g_state.wait_event, &refresh_get_tick_count64, NULL, 0x7fffffff, boost::winapi::WT_EXECUTEINWAITTHREAD_); |
|
131 |
+ if (res) |
|
132 |
+ { |
|
133 |
+ std::atexit(&cleanup_get_tick_count64); |
|
134 |
+ |
|
135 |
+ boost::detail::interlocked_write_release((void**)&gettickcount64, (void*)&get_tick_count64); |
|
136 |
+ g_state.init.store(initialized, boost::memory_order_release); |
|
137 |
+ goto finish; |
|
138 |
+ } |
|
139 |
+ } |
|
140 |
+ |
|
141 |
+ g_state.init.store(uninitialized, boost::memory_order_release); |
|
142 |
+ } |
|
143 |
+ |
|
144 |
+finish: |
|
145 |
+ return get_tick_count64(); |
|
146 |
+} |
|
147 |
+ |
|
148 |
+} // namespace |
|
149 |
+ |
|
150 |
+BOOST_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64 = &get_tick_count_init; |
|
151 |
+ |
|
152 |
+#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
|
153 |
+ |
|
154 |
+} // namespace win32 |
|
155 |
+} // namespace detail |
|
156 |
+} // namespace boost |
0 | 157 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,87 @@ |
1 |
+// (C) Copyright Michael Glassford 2004. |
|
2 |
+// Use, modification and distribution are subject to the |
|
3 |
+// Boost Software License, Version 1.0. (See accompanying file |
|
4 |
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|
5 |
+ |
|
6 |
+#include <boost/winapi/config.hpp> |
|
7 |
+#include <boost/thread/detail/config.hpp> |
|
8 |
+ |
|
9 |
+ |
|
10 |
+#if defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_DLL) |
|
11 |
+ |
|
12 |
+ #include <boost/thread/detail/tss_hooks.hpp> |
|
13 |
+ |
|
14 |
+ #include <windows.h> |
|
15 |
+ |
|
16 |
+ #if defined(BOOST_BORLANDC) |
|
17 |
+ extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE /*hInstance*/, DWORD dwReason, LPVOID /*lpReserved*/) |
|
18 |
+ #elif defined(BOOST_EMBTC) |
|
19 |
+ extern "C" int _libmain(DWORD dwReason) |
|
20 |
+ #elif defined(_WIN32_WCE) |
|
21 |
+ extern "C" BOOL WINAPI DllMain(HANDLE /*hInstance*/, DWORD dwReason, LPVOID /*lpReserved*/) |
|
22 |
+ #else |
|
23 |
+ extern "C" BOOL WINAPI DllMain(HINSTANCE /*hInstance*/, DWORD dwReason, LPVOID /*lpReserved*/) |
|
24 |
+ #endif |
|
25 |
+ { |
|
26 |
+ switch(dwReason) |
|
27 |
+ { |
|
28 |
+ case DLL_PROCESS_ATTACH: |
|
29 |
+ { |
|
30 |
+ boost::on_process_enter(); |
|
31 |
+ boost::on_thread_enter(); |
|
32 |
+ break; |
|
33 |
+ } |
|
34 |
+ |
|
35 |
+ case DLL_THREAD_ATTACH: |
|
36 |
+ { |
|
37 |
+ boost::on_thread_enter(); |
|
38 |
+ break; |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ case DLL_THREAD_DETACH: |
|
42 |
+ { |
|
43 |
+ boost::on_thread_exit(); |
|
44 |
+ break; |
|
45 |
+ } |
|
46 |
+ |
|
47 |
+ case DLL_PROCESS_DETACH: |
|
48 |
+ { |
|
49 |
+ boost::on_thread_exit(); |
|
50 |
+ boost::on_process_exit(); |
|
51 |
+ break; |
|
52 |
+ } |
|
53 |
+ } |
|
54 |
+ |
|
55 |
+ return TRUE; |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+namespace boost |
|
59 |
+{ |
|
60 |
+ void tss_cleanup_implemented() |
|
61 |
+ { |
|
62 |
+ /* |
|
63 |
+ This function's sole purpose is to cause a link error in cases where |
|
64 |
+ automatic tss cleanup is not implemented by Boost.Threads as a |
|
65 |
+ reminder that user code is responsible for calling the necessary |
|
66 |
+ functions at the appropriate times (and for implementing an a |
|
67 |
+ tss_cleanup_implemented() function to eliminate the linker's |
|
68 |
+ missing symbol error). |
|
69 |
+ |
|
70 |
+ If Boost.Threads later implements automatic tss cleanup in cases |
|
71 |
+ where it currently doesn't (which is the plan), the duplicate |
|
72 |
+ symbol error will warn the user that their custom solution is no |
|
73 |
+ longer needed and can be removed. |
|
74 |
+ */ |
|
75 |
+ } |
|
76 |
+} |
|
77 |
+ |
|
78 |
+#else //defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_DLL) |
|
79 |
+ |
|
80 |
+#ifdef _MSC_VER |
|
81 |
+// Prevent LNK4221 warning with link=static |
|
82 |
+namespace boost { namespace link_static_warning_inhibit { |
|
83 |
+ extern __declspec(dllexport) void foo() { } |
|
84 |
+} } |
|
85 |
+#endif |
|
86 |
+ |
|
87 |
+#endif //defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_DLL) |
0 | 88 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,346 @@ |
1 |
+// $Id$ |
|
2 |
+// (C) Copyright Aaron W. LaFramboise, Roland Schwarz, Michael Glassford 2004. |
|
3 |
+// (C) Copyright 2007 Roland Schwarz |
|
4 |
+// (C) Copyright 2007 Anthony Williams |
|
5 |
+// (C) Copyright 2007 David Deakins |
|
6 |
+// Use, modification and distribution are subject to the |
|
7 |
+// Boost Software License, Version 1.0. (See accompanying file |
|
8 |
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|
9 |
+ |
|
10 |
+#include <boost/winapi/config.hpp> |
|
11 |
+#include <boost/thread/detail/config.hpp> |
|
12 |
+ |
|
13 |
+#if defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_LIB) |
|
14 |
+ |
|
15 |
+#if (defined(__MINGW32__) && !defined(_WIN64)) || defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) |
|
16 |
+ |
|
17 |
+#include <boost/thread/detail/tss_hooks.hpp> |
|
18 |
+ |
|
19 |
+#include <windows.h> |
|
20 |
+ |
|
21 |
+#include <cstdlib> |
|
22 |
+ |
|
23 |
+namespace boost |
|
24 |
+{ |
|
25 |
+ void tss_cleanup_implemented() {} |
|
26 |
+} |
|
27 |
+ |
|
28 |
+namespace { |
|
29 |
+ void NTAPI on_tls_callback(void* , DWORD dwReason, PVOID ) |
|
30 |
+ { |
|
31 |
+ switch (dwReason) |
|
32 |
+ { |
|
33 |
+ case DLL_THREAD_DETACH: |
|
34 |
+ { |
|
35 |
+ boost::on_thread_exit(); |
|
36 |
+ break; |
|
37 |
+ } |
|
38 |
+ } |
|
39 |
+ } |
|
40 |
+} |
|
41 |
+ |
|
42 |
+#if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || (__MINGW32__) || (__MINGW32_MAJOR_VERSION >3) || \ |
|
43 |
+ ((__MINGW32_MAJOR_VERSION==3) && (__MINGW32_MINOR_VERSION>=18)) |
|
44 |
+extern "C" |
|
45 |
+{ |
|
46 |
+ PIMAGE_TLS_CALLBACK __crt_xl_tls_callback__ __attribute__ ((section(".CRT$XLB"))) = on_tls_callback; |
|
47 |
+} |
|
48 |
+#else |
|
49 |
+extern "C" { |
|
50 |
+ |
|
51 |
+ void (* after_ctors )() __attribute__((section(".ctors"))) = boost::on_process_enter; |
|
52 |
+ void (* before_dtors)() __attribute__((section(".dtors"))) = boost::on_thread_exit; |
|
53 |
+ void (* after_dtors )() __attribute__((section(".dtors.zzz"))) = boost::on_process_exit; |
|
54 |
+ |
|
55 |
+ ULONG __tls_index__ = 0; |
|
56 |
+ char __tls_end__ __attribute__((section(".tls$zzz"))) = 0; |
|
57 |
+ char __tls_start__ __attribute__((section(".tls"))) = 0; |
|
58 |
+ |
|
59 |
+ |
|
60 |
+ PIMAGE_TLS_CALLBACK __crt_xl_start__ __attribute__ ((section(".CRT$XLA"))) = 0; |
|
61 |
+ PIMAGE_TLS_CALLBACK __crt_xl_end__ __attribute__ ((section(".CRT$XLZ"))) = 0; |
|
62 |
+} |
|
63 |
+extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata$T"))) = |
|
64 |
+{ |
|
65 |
+ (DWORD) &__tls_start__, |
|
66 |
+ (DWORD) &__tls_end__, |
|
67 |
+ (DWORD) &__tls_index__, |
|
68 |
+ (DWORD) (&__crt_xl_start__+1), |
|
69 |
+ (DWORD) 0, |
|
70 |
+ (DWORD) 0 |
|
71 |
+}; |
|
72 |
+#endif |
|
73 |
+ |
|
74 |
+ |
|
75 |
+#elif defined(_MSC_VER) && !defined(UNDER_CE) |
|
76 |
+ |
|
77 |
+ #include <boost/thread/detail/tss_hooks.hpp> |
|
78 |
+ |
|
79 |
+ #include <stdlib.h> |
|
80 |
+ |
|
81 |
+ #include <windows.h> |
|
82 |
+ |
|
83 |
+ |
|
84 |
+// _pRawDllMainOrig can be defined by including boost/thread/win32/mfc_thread_init.hpp |
|
85 |
+// into your dll; it ensures that MFC-Dll-initialization will be done properly |
|
86 |
+// The following code is adapted from the MFC-Dll-init code |
|
87 |
+/* |
|
88 |
+ * _pRawDllMainOrig MUST be an extern const variable, which will be aliased to |
|
89 |
+ * _pDefaultRawDllMainOrig if no real user definition is present, thanks to the |
|
90 |
+ * alternatename directive. |
|
91 |
+ */ |
|
92 |
+ |
|
93 |
+// work at least with _MSC_VER 1500 (MSVC++ 9.0, VS 2008) |
|
94 |
+#if (_MSC_VER >= 1500) |
|
95 |
+ |
|
96 |
+extern "C" { |
|
97 |
+extern BOOL (WINAPI * const _pRawDllMainOrig)(HINSTANCE, DWORD, LPVOID); |
|
98 |
+extern BOOL (WINAPI * const _pDefaultRawDllMainOrig)(HINSTANCE, DWORD, LPVOID) = NULL; |
|
99 |
+#if defined (_M_IX86) |
|
100 |
+#pragma comment(linker, "/alternatename:__pRawDllMainOrig=__pDefaultRawDllMainOrig") |
|
101 |
+#elif defined (_M_X64) || defined (_M_ARM) || defined (_M_ARM64) |
|
102 |
+#pragma comment(linker, "/alternatename:_pRawDllMainOrig=_pDefaultRawDllMainOrig") |
|
103 |
+#else /* unknown Windows target (not x86, x64, ARM, ARM64) */ |
|
104 |
+#error Unsupported platform |
|
105 |
+#endif /* defined (_M_X64) || defined (_M_ARM) || defined (_M_ARM64) */ |
|
106 |
+} |
|
107 |
+ |
|
108 |
+#endif |
|
109 |
+ |
|
110 |
+ |
|
111 |
+ |
|
112 |
+ |
|
113 |
+ //Definitions required by implementation |
|
114 |
+ #if (_MSC_VER < 1300) || ((_MSC_VER > 1900) && (_MSC_VER < 1910)) // 1300 == VC++ 7.0, 1900 == VC++ 14.0, 1910 == VC++ 2017 |
|
115 |
+ typedef void ( __cdecl *_PVFV_ )(); |
|
116 |
+ typedef void ( __cdecl *_PIFV_ )(); |
|
117 |
+ #define INIRETSUCCESS_V |
|
118 |
+ #define INIRETSUCCESS_I |
|
119 |
+ #define PVAPI_V void __cdecl |
|
120 |
+ #define PVAPI_I void __cdecl |
|
121 |
+ #elif (_MSC_VER >= 1910) |
|
122 |
+ typedef void ( __cdecl *_PVFV_ )(); |
|
123 |
+ typedef int ( __cdecl *_PIFV_ )(); |
|
124 |
+ #define INIRETSUCCESS_V |
|
125 |
+ #define INIRETSUCCESS_I 0 |
|
126 |
+ #define PVAPI_V void __cdecl |
|
127 |
+ #define PVAPI_I int __cdecl |
|
128 |
+ #else |
|
129 |
+ typedef int ( __cdecl *_PVFV_ )(); |
|
130 |
+ typedef int ( __cdecl *_PIFV_ )(); |
|
131 |
+ #define INIRETSUCCESS_V 0 |
|
132 |
+ #define INIRETSUCCESS_I 0 |
|
133 |
+ #define PVAPI_V int __cdecl |
|
134 |
+ #define PVAPI_I int __cdecl |
|
135 |
+ #endif |
|
136 |
+ |
|
137 |
+ typedef void (NTAPI* _TLSCB)(HINSTANCE, DWORD, PVOID); |
|
138 |
+ |
|
139 |
+ //Symbols for connection to the runtime environment |
|
140 |
+ |
|
141 |
+ extern "C" |
|
142 |
+ { |
|
143 |
+ extern DWORD _tls_used; //the tls directory (located in .rdata segment) |
|
144 |
+ extern _TLSCB __xl_a[], __xl_z[]; //tls initializers */ |
|
145 |
+ } |
|
146 |
+ |
|
147 |
+ namespace |
|
148 |
+ { |
|
149 |
+ //Forward declarations |
|
150 |
+ |
|
151 |
+ static PVAPI_I on_tls_prepare(); |
|
152 |
+ static PVAPI_V on_process_init(); |
|
153 |
+ static PVAPI_V on_process_term(); |
|
154 |
+ static void NTAPI on_tls_callback(HINSTANCE, DWORD, PVOID); |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ namespace boost |
|
158 |
+ { |
|
159 |
+ //The .CRT$Xxx information is taken from Codeguru: |
|
160 |
+ //http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c6945__2/ |
|
161 |
+ |
|