blob: 7042ab1d40c877e83f8c0458ea4d6c6cd96a190f [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id: os_timestamp_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pj/os.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
23#include <pj/log.h>
24#include <windows.h>
25
26#define THIS_FILE "os_timestamp_win32.c"
27
28
29#if 1
30# define TRACE_(x) PJ_LOG(3,x)
31#else
32# define TRACE_(x) ;
33#endif
34
35
36/////////////////////////////////////////////////////////////////////////////
37
38#if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \
39 defined(PJ_M_I386) && PJ_M_I386 != 0 && \
40 defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \
41 defined(_MSC_VER)
42
43/*
44 * Use rdtsc to get the OS timestamp.
45 */
46static LONG CpuMhz;
47static pj_int64_t CpuHz;
48
49static pj_status_t GetCpuHz(void)
50{
51 HKEY key;
52 LONG rc;
53 DWORD size;
54
55#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
56 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
57 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
58 0, 0, &key);
59#else
60 rc = RegOpenKey( HKEY_LOCAL_MACHINE,
61 "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
62 &key);
63#endif
64
65 if (rc != ERROR_SUCCESS)
66 return PJ_RETURN_OS_ERROR(rc);
67
68 size = sizeof(CpuMhz);
69 rc = RegQueryValueEx(key, "~MHz", NULL, NULL, (BYTE*)&CpuMhz, &size);
70 RegCloseKey(key);
71
72 if (rc != ERROR_SUCCESS) {
73 return PJ_RETURN_OS_ERROR(rc);
74 }
75
76 CpuHz = CpuMhz;
77 CpuHz = CpuHz * 1000000;
78
79 return PJ_SUCCESS;
80}
81
82/* __int64 is nicely returned in EDX:EAX */
83__declspec(naked) __int64 rdtsc()
84{
85 __asm
86 {
87 RDTSC
88 RET
89 }
90}
91
92PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
93{
94 ts->u64 = rdtsc();
95 return PJ_SUCCESS;
96}
97
98PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
99{
100 pj_status_t status;
101
102 if (CpuHz == 0) {
103 status = GetCpuHz();
104 if (status != PJ_SUCCESS)
105 return status;
106 }
107
108 freq->u64 = CpuHz;
109 return PJ_SUCCESS;
110}
111
112/////////////////////////////////////////////////////////////////////////////
113
114#elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \
115 PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0
116
117/* Use safe QueryPerformanceCounter.
118 * This implementation has some protection against bug in KB Q274323:
119 * Performance counter value may unexpectedly leap forward
120 * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
121 *
122 * THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME
123 * CHANGE.
124 */
125
126static pj_timestamp g_ts_freq;
127static pj_timestamp g_ts_base;
128static pj_int64_t g_time_base;
129
130PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
131{
132 enum { MAX_RETRY = 10 };
133 unsigned i;
134
135
136 /* pj_get_timestamp_freq() must have been called before.
137 * This is done when application called pj_init().
138 */
139 pj_assert(g_ts_freq.u64 != 0);
140
141 /* Retry QueryPerformanceCounter() until we're sure that the
142 * value returned makes sense.
143 */
144 i = 0;
145 do {
146 LARGE_INTEGER val;
147 pj_int64_t counter64, time64, diff;
148 pj_time_val time_now;
149
150 /* Retrieve the counter */
151 if (!QueryPerformanceCounter(&val))
152 return PJ_RETURN_OS_ERROR(GetLastError());
153
154 /* Regardless of the goodness of the value, we should put
155 * the counter here, because normally application wouldn't
156 * check the error result of this function.
157 */
158 ts->u64 = val.QuadPart;
159
160 /* Retrieve time */
161 pj_gettimeofday(&time_now);
162
163 /* Get the counter elapsed time in miliseconds */
164 counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64;
165
166 /* Get the time elapsed in miliseconds.
167 * We don't want to use PJ_TIME_VAL_MSEC() since it's using
168 * 32bit calculation, which limits the maximum elapsed time
169 * to around 49 days only.
170 */
171 time64 = time_now.sec;
172 time64 = time64 * 1000 + time_now.msec;
173 //time64 = GetTickCount();
174
175 /* It's good if the difference between two clocks are within
176 * some compile time constant (default: 20ms, which to allow
177 * context switch happen between QueryPerformanceCounter and
178 * pj_gettimeofday()).
179 */
180 diff = (time64 - g_time_base) - counter64;
181 if (diff >= -20 && diff <= 20) {
182 /* It's good */
183 return PJ_SUCCESS;
184 }
185
186 ++i;
187
188 } while (i < MAX_RETRY);
189
190 TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value"));
191 return PJ_ETIMEDOUT;
192}
193
194static pj_status_t init_performance_counter(void)
195{
196 LARGE_INTEGER val;
197 pj_time_val time_base;
198 pj_status_t status;
199
200 /* Get the frequency */
201 if (!QueryPerformanceFrequency(&val))
202 return PJ_RETURN_OS_ERROR(GetLastError());
203
204 g_ts_freq.u64 = val.QuadPart;
205
206 /* Get the base timestamp */
207 if (!QueryPerformanceCounter(&val))
208 return PJ_RETURN_OS_ERROR(GetLastError());
209
210 g_ts_base.u64 = val.QuadPart;
211
212
213 /* Get the base time */
214 status = pj_gettimeofday(&time_base);
215 if (status != PJ_SUCCESS)
216 return status;
217
218 /* Convert time base to 64bit value in msec */
219 g_time_base = time_base.sec;
220 g_time_base = g_time_base * 1000 + time_base.msec;
221 //g_time_base = GetTickCount();
222
223 return PJ_SUCCESS;
224}
225
226PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
227{
228 if (g_ts_freq.u64 == 0) {
229 enum { MAX_REPEAT = 10 };
230 unsigned i;
231 pj_status_t status;
232
233 /* Make unellegant compiler happy */
234 status = 0;
235
236 /* Repeat initializing performance counter until we're sure
237 * the base timing is correct. It is possible that the system
238 * returns bad counter during this initialization!
239 */
240 for (i=0; i<MAX_REPEAT; ++i) {
241
242 pj_timestamp dummy;
243
244 /* Init base time */
245 status = init_performance_counter();
246 if (status != PJ_SUCCESS)
247 return status;
248
249 /* Try the base time */
250 status = pj_get_timestamp(&dummy);
251 if (status == PJ_SUCCESS)
252 break;
253 }
254
255 if (status != PJ_SUCCESS)
256 return status;
257 }
258
259 freq->u64 = g_ts_freq.u64;
260 return PJ_SUCCESS;
261}
262
263/////////////////////////////////////////////////////////////////////////////
264
265#else
266
267/*
268 * Use QueryPerformanceCounter and QueryPerformanceFrequency.
269 * This should be the default implementation to be used on Windows.
270 */
271PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
272{
273 LARGE_INTEGER val;
274
275 if (!QueryPerformanceCounter(&val))
276 return PJ_RETURN_OS_ERROR(GetLastError());
277
278 ts->u64 = val.QuadPart;
279 return PJ_SUCCESS;
280}
281
282PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
283{
284 LARGE_INTEGER val;
285
286 if (!QueryPerformanceFrequency(&val))
287 return PJ_RETURN_OS_ERROR(GetLastError());
288
289 freq->u64 = val.QuadPart;
290 return PJ_SUCCESS;
291}
292
293
294#endif /* PJ_TIMESTAMP_USE_RDTSC */
295