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 157