• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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