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