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
44namespace boost {
45
46BOOST_LOG_OPEN_NAMESPACE
47
48namespace 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
55BOOST_LOG_API get_tick_count_t get_tick_count = &boost::winapi::GetTickCount64;
56
57#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
58
59BOOST_LOG_ANONYMOUS_NAMESPACE {
60
61enum init_state
62{
63 uninitialized = 0,
64 in_progress,
65 initialized
66};
67
68struct 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
77BOOST_ALIGNMENT(BOOST_LOG_CPU_CACHE_LINE_SIZE) static get_tick_count64_state g_state;
78
79//! Artifical implementation of GetTickCount64
80uint64_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
97void 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
103void 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
118uint64_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
154finish:
155 return get_tick_count64();
156}
157
158} // namespace
159
160BOOST_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
167BOOST_LOG_API int64_t duration::milliseconds() const
168{
169 // Timestamps are always in nanoseconds
170 return m_ticks / INT64_C(1000000);
171}
172
173BOOST_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 */
180timestamp 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
195timestamp 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
224BOOST_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
230BOOST_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
254BOOST_LOG_ANONYMOUS_NAMESPACE {
255
256//! \c get_timestamp implementation based on MacOS X absolute time
257timestamp get_timestamp_mach()
258{
259 return timestamp(mach_absolute_time());
260}
261
262} // namespace
263
264// Use MacOS X API
265BOOST_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
275BOOST_LOG_CLOSE_NAMESPACE // namespace log
276
277} // namespace boost
278
279#include <boost/log/detail/footer.hpp>
280

source code of boost/libs/log/src/timestamp.cpp