| 1 | /* |
| 2 | * Copyright Andrey Semashev 2007 - 2018. |
| 3 | * Distributed under the Boost Software License, Version 1.0. |
| 4 | * (See accompanying file LICENSE_1_0.txt or copy at |
| 5 | * http://www.boost.org/LICENSE_1_0.txt) |
| 6 | */ |
| 7 | /*! |
| 8 | * \file timestamp.cpp |
| 9 | * \author Andrey Semashev |
| 10 | * \date 31.07.2011 |
| 11 | * |
| 12 | * \brief This header is the Boost.Log library implementation, see the library documentation |
| 13 | * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. |
| 14 | */ |
| 15 | |
| 16 | #include <boost/log/detail/config.hpp> |
| 17 | #include <boost/log/detail/timestamp.hpp> |
| 18 | |
| 19 | #if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| 20 | #include <cstddef> |
| 21 | #include <cstdlib> |
| 22 | #include <boost/memory_order.hpp> |
| 23 | #include <boost/atomic/atomic.hpp> |
| 24 | #include <boost/winapi/dll.hpp> |
| 25 | #include <boost/winapi/time.hpp> |
| 26 | #include <boost/winapi/event.hpp> |
| 27 | #include <boost/winapi/handles.hpp> |
| 28 | #include <boost/winapi/thread_pool.hpp> |
| 29 | #else |
| 30 | #include <unistd.h> // for config macros |
| 31 | #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) |
| 32 | #include <mach/mach_time.h> |
| 33 | #include <mach/kern_return.h> |
| 34 | #include <boost/log/utility/once_block.hpp> |
| 35 | #include <boost/system/error_code.hpp> |
| 36 | #endif |
| 37 | #include <time.h> |
| 38 | #include <errno.h> |
| 39 | #include <boost/throw_exception.hpp> |
| 40 | #include <boost/log/exceptions.hpp> |
| 41 | #endif |
| 42 | #include <boost/log/detail/header.hpp> |
| 43 | |
| 44 | namespace boost { |
| 45 | |
| 46 | BOOST_LOG_OPEN_NAMESPACE |
| 47 | |
| 48 | namespace aux { |
| 49 | |
| 50 | #if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| 51 | |
| 52 | #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
| 53 | |
| 54 | // Directly use API from Vista and later |
| 55 | BOOST_LOG_API get_tick_count_t get_tick_count = &boost::winapi::GetTickCount64; |
| 56 | |
| 57 | #else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
| 58 | |
| 59 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
| 60 | |
| 61 | enum init_state |
| 62 | { |
| 63 | uninitialized = 0, |
| 64 | in_progress, |
| 65 | initialized |
| 66 | }; |
| 67 | |
| 68 | struct get_tick_count64_state |
| 69 | { |
| 70 | boost::atomic< uint64_t > ticks; |
| 71 | boost::atomic< init_state > init; |
| 72 | boost::winapi::HANDLE_ wait_event; |
| 73 | boost::winapi::HANDLE_ wait_handle; |
| 74 | }; |
| 75 | |
| 76 | // Zero-initialized initially |
| 77 | BOOST_ALIGNMENT(BOOST_LOG_CPU_CACHE_LINE_SIZE) static get_tick_count64_state g_state; |
| 78 | |
| 79 | //! Artifical implementation of GetTickCount64 |
| 80 | uint64_t BOOST_WINAPI_WINAPI_CC get_tick_count64() |
| 81 | { |
| 82 | // Note: Even in single-threaded builds we have to implement get_tick_count64 in a thread-safe way because |
| 83 | // it can be called in the system thread pool during refreshes concurrently with user's calls. |
| 84 | uint64_t old_state = g_state.ticks.load(boost::memory_order_acquire); |
| 85 | |
| 86 | uint32_t new_ticks = boost::winapi::GetTickCount(); |
| 87 | |
| 88 | uint32_t old_ticks = static_cast< uint32_t >(old_state & UINT64_C(0x00000000ffffffff)); |
| 89 | uint64_t new_state = ((old_state & UINT64_C(0xffffffff00000000)) + (static_cast< uint64_t >(new_ticks < old_ticks) << 32)) | static_cast< uint64_t >(new_ticks); |
| 90 | |
| 91 | g_state.ticks.store(new_state, boost::memory_order_release); |
| 92 | |
| 93 | return new_state; |
| 94 | } |
| 95 | |
| 96 | //! The function is called periodically in the system thread pool to make sure g_state.ticks is timely updated |
| 97 | void BOOST_WINAPI_NTAPI_CC refresh_get_tick_count64(boost::winapi::PVOID_, boost::winapi::BOOLEAN_) |
| 98 | { |
| 99 | get_tick_count64(); |
| 100 | } |
| 101 | |
| 102 | //! Cleanup function to stop get_tick_count64 refreshes |
| 103 | void cleanup_get_tick_count64() |
| 104 | { |
| 105 | if (g_state.wait_handle) |
| 106 | { |
| 107 | boost::winapi::UnregisterWait(g_state.wait_handle); |
| 108 | g_state.wait_handle = NULL; |
| 109 | } |
| 110 | |
| 111 | if (g_state.wait_event) |
| 112 | { |
| 113 | boost::winapi::CloseHandle(g_state.wait_event); |
| 114 | g_state.wait_event = NULL; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | uint64_t BOOST_WINAPI_WINAPI_CC get_tick_count_init() |
| 119 | { |
| 120 | boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll" ); |
| 121 | if (hKernel32) |
| 122 | { |
| 123 | get_tick_count_t p = (get_tick_count_t)boost::winapi::get_proc_address(hKernel32, "GetTickCount64" ); |
| 124 | if (p) |
| 125 | { |
| 126 | // Use native API |
| 127 | const_cast< get_tick_count_t volatile& >(get_tick_count) = p; |
| 128 | return p(); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | // No native API available. Use emulation with periodic refreshes to make sure the GetTickCount wrap arounds are properly counted. |
| 133 | init_state old_init = uninitialized; |
| 134 | if (g_state.init.compare_exchange_strong(old_init, in_progress, boost::memory_order_acq_rel, boost::memory_order_relaxed)) |
| 135 | { |
| 136 | if (!g_state.wait_event) |
| 137 | g_state.wait_event = boost::winapi::create_anonymous_event(NULL, false, false); |
| 138 | if (g_state.wait_event) |
| 139 | { |
| 140 | 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_); |
| 141 | if (res) |
| 142 | { |
| 143 | std::atexit(&cleanup_get_tick_count64); |
| 144 | |
| 145 | const_cast< get_tick_count_t volatile& >(get_tick_count) = &get_tick_count64; |
| 146 | g_state.init.store(initialized, boost::memory_order_release); |
| 147 | goto finish; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | g_state.init.store(uninitialized, boost::memory_order_release); |
| 152 | } |
| 153 | |
| 154 | finish: |
| 155 | return get_tick_count64(); |
| 156 | } |
| 157 | |
| 158 | } // namespace |
| 159 | |
| 160 | BOOST_LOG_API get_tick_count_t get_tick_count = &get_tick_count_init; |
| 161 | |
| 162 | #endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
| 163 | |
| 164 | #elif (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0) > 0) /* POSIX timers supported */ \ |
| 165 | || defined(__GNU__) || defined(__OpenBSD__) || defined(__CloudABI__) /* GNU Hurd, OpenBSD and Nuxi CloudABI don't support POSIX timers fully but do provide clock_gettime() */ |
| 166 | |
| 167 | BOOST_LOG_API int64_t duration::milliseconds() const |
| 168 | { |
| 169 | // Timestamps are always in nanoseconds |
| 170 | return m_ticks / INT64_C(1000000); |
| 171 | } |
| 172 | |
| 173 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
| 174 | |
| 175 | /*! |
| 176 | * \c get_timestamp implementation based on POSIX realtime clock. |
| 177 | * Note that this implementation is only used as a last resort since |
| 178 | * this timer can be manually set and may jump due to DST change. |
| 179 | */ |
| 180 | timestamp get_timestamp_realtime_clock() |
| 181 | { |
| 182 | timespec ts; |
| 183 | if (BOOST_UNLIKELY(clock_gettime(CLOCK_REALTIME, &ts) != 0)) |
| 184 | { |
| 185 | const int err = errno; |
| 186 | BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to acquire current time" , (err)); |
| 187 | } |
| 188 | |
| 189 | return timestamp(static_cast< uint64_t >(ts.tv_sec) * UINT64_C(1000000000) + ts.tv_nsec); |
| 190 | } |
| 191 | |
| 192 | # if defined(_POSIX_MONOTONIC_CLOCK) |
| 193 | |
| 194 | //! \c get_timestamp implementation based on POSIX monotonic clock |
| 195 | timestamp get_timestamp_monotonic_clock() |
| 196 | { |
| 197 | timespec ts; |
| 198 | if (BOOST_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) |
| 199 | { |
| 200 | const int err = errno; |
| 201 | if (err == EINVAL) |
| 202 | { |
| 203 | // The current platform does not support monotonic timer. |
| 204 | // Fall back to realtime clock, which is not exactly what we need |
| 205 | // but is better than nothing. |
| 206 | get_timestamp = &get_timestamp_realtime_clock; |
| 207 | return get_timestamp_realtime_clock(); |
| 208 | } |
| 209 | BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to acquire current time" , (err)); |
| 210 | } |
| 211 | |
| 212 | return timestamp(static_cast< uint64_t >(ts.tv_sec) * UINT64_C(1000000000) + ts.tv_nsec); |
| 213 | } |
| 214 | |
| 215 | # define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_monotonic_clock |
| 216 | |
| 217 | # else // if defined(_POSIX_MONOTONIC_CLOCK) |
| 218 | # define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_realtime_clock |
| 219 | # endif // if defined(_POSIX_MONOTONIC_CLOCK) |
| 220 | |
| 221 | } // namespace |
| 222 | |
| 223 | // Use POSIX API |
| 224 | BOOST_LOG_API get_timestamp_t get_timestamp = &BOOST_LOG_DEFAULT_GET_TIMESTAMP; |
| 225 | |
| 226 | # undef BOOST_LOG_DEFAULT_GET_TIMESTAMP |
| 227 | |
| 228 | #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) |
| 229 | |
| 230 | BOOST_LOG_API int64_t duration::milliseconds() const |
| 231 | { |
| 232 | static mach_timebase_info_data_t timebase_info = {}; |
| 233 | BOOST_LOG_ONCE_BLOCK() |
| 234 | { |
| 235 | kern_return_t err = mach_timebase_info(&timebase_info); |
| 236 | if (err != KERN_SUCCESS) |
| 237 | { |
| 238 | BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to initialize timebase info" , (boost::system::errc::not_supported)); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | // Often the timebase rational equals 1, we can optimize for this case |
| 243 | if (timebase_info.numer == timebase_info.denom) |
| 244 | { |
| 245 | // Timestamps are in nanoseconds |
| 246 | return m_ticks / INT64_C(1000000); |
| 247 | } |
| 248 | else |
| 249 | { |
| 250 | return (m_ticks * timebase_info.numer) / (INT64_C(1000000) * timebase_info.denom); |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
| 255 | |
| 256 | //! \c get_timestamp implementation based on MacOS X absolute time |
| 257 | timestamp get_timestamp_mach() |
| 258 | { |
| 259 | return timestamp(mach_absolute_time()); |
| 260 | } |
| 261 | |
| 262 | } // namespace |
| 263 | |
| 264 | // Use MacOS X API |
| 265 | BOOST_LOG_API get_timestamp_t get_timestamp = &get_timestamp_mach; |
| 266 | |
| 267 | #else |
| 268 | |
| 269 | # error Boost.Log: Timestamp generation is not supported for your platform |
| 270 | |
| 271 | #endif |
| 272 | |
| 273 | } // namespace aux |
| 274 | |
| 275 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
| 276 | |
| 277 | } // namespace boost |
| 278 | |
| 279 | #include <boost/log/detail/footer.hpp> |
| 280 | |