blob: 5f6e6f03a60de691aca7b45a8b4b362228ae66f0 [file] [log] [blame]
// thread_primitives.cpp
//
// (C) Copyright 2018 Andrey Semashev
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/winapi/config.hpp>
#include <boost/winapi/dll.hpp>
#include <boost/winapi/time.hpp>
#include <boost/winapi/event.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/winapi/thread_pool.hpp>
#include <cstdlib>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/memory_order.hpp>
#include <boost/atomic/atomic.hpp>
#include <boost/thread/win32/interlocked_read.hpp>
#include <boost/thread/win32/thread_primitives.hpp>
namespace boost {
namespace detail {
namespace win32 {
#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
// Directly use API from Vista and later
BOOST_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64 = &::boost::winapi::GetTickCount64;
#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
namespace {
enum init_state
{
uninitialized = 0,
in_progress,
initialized
};
struct get_tick_count64_state
{
boost::atomic< uint64_t > ticks;
boost::atomic< init_state > init;
boost::winapi::HANDLE_ wait_event;
boost::winapi::HANDLE_ wait_handle;
};
// Zero-initialized initially
BOOST_ALIGNMENT(64) static get_tick_count64_state g_state;
//! Artifical implementation of GetTickCount64
ticks_type WINAPI get_tick_count64()
{
uint64_t old_state = g_state.ticks.load(boost::memory_order_acquire);
uint32_t new_ticks = boost::winapi::GetTickCount();
uint32_t old_ticks = static_cast< uint32_t >(old_state & UINT64_C(0x00000000ffffffff));
uint64_t new_state = ((old_state & UINT64_C(0xffffffff00000000)) + (static_cast< uint64_t >(new_ticks < old_ticks) << 32)) | static_cast< uint64_t >(new_ticks);
g_state.ticks.store(new_state, boost::memory_order_release);
return new_state;
}
//! The function is called periodically in the system thread pool to make sure g_state.ticks is timely updated
void NTAPI refresh_get_tick_count64(boost::winapi::PVOID_, boost::winapi::BOOLEAN_)
{
get_tick_count64();
}
//! Cleanup function to stop get_tick_count64 refreshes
void cleanup_get_tick_count64()
{
if (g_state.wait_handle)
{
boost::winapi::UnregisterWait(g_state.wait_handle);
g_state.wait_handle = NULL;
}
if (g_state.wait_event)
{
boost::winapi::CloseHandle(g_state.wait_event);
g_state.wait_event = NULL;
}
}
ticks_type WINAPI get_tick_count_init()
{
boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll");
if (hKernel32)
{
// GetProcAddress returns a pointer to some function. It can return
// pointers to different functions, so it has to return something that is
// suitable to store any pointer to function. Microsoft chose FARPROC,
// which is int (WINAPI *)() on 32-bit Windows. The user is supposed to
// know the signature of the function he requests and perform a cast
// (which is a nop on this platform). The result is a pointer to function
// with the required signature, which is bitwise equal to what
// GetProcAddress returned.
// However, gcc >= 8 warns about that.
#if defined(BOOST_GCC) && BOOST_GCC >= 80000
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
boost::detail::win32::detail::gettickcount64_t p =
(boost::detail::win32::detail::gettickcount64_t)boost::winapi::get_proc_address(hKernel32, "GetTickCount64");
#if defined(BOOST_GCC) && BOOST_GCC >= 80000
#pragma GCC diagnostic pop
#endif
if (p)
{
// Use native API
boost::detail::interlocked_write_release((void**)&gettickcount64, (void*)p);
return p();
}
}
// No native API available. Use emulation with periodic refreshes to make sure the GetTickCount wrap arounds are properly counted.
init_state old_init = uninitialized;
if (g_state.init.compare_exchange_strong(old_init, in_progress, boost::memory_order_acq_rel, boost::memory_order_relaxed))
{
if (!g_state.wait_event)
g_state.wait_event = boost::winapi::create_anonymous_event(NULL, false, false);
if (g_state.wait_event)
{
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_);
if (res)
{
std::atexit(&cleanup_get_tick_count64);
boost::detail::interlocked_write_release((void**)&gettickcount64, (void*)&get_tick_count64);
g_state.init.store(initialized, boost::memory_order_release);
goto finish;
}
}
g_state.init.store(uninitialized, boost::memory_order_release);
}
finish:
return get_tick_count64();
}
} // namespace
BOOST_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64 = &get_tick_count_init;
#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
} // namespace win32
} // namespace detail
} // namespace boost