1
/* $Id: os_timestamp_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
21
#include <pj/assert.h>
26
#define THIS_FILE "os_timestamp_win32.c"
30
# define TRACE_(x) PJ_LOG(3,x)
36
/////////////////////////////////////////////////////////////////////////////
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 && \
44
* Use rdtsc to get the OS timestamp.
47
static pj_int64_t CpuHz;
49
static pj_status_t GetCpuHz(void)
55
#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
56
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
57
L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
60
rc = RegOpenKey( HKEY_LOCAL_MACHINE,
61
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
65
if (rc != ERROR_SUCCESS)
66
return PJ_RETURN_OS_ERROR(rc);
68
size = sizeof(CpuMhz);
69
rc = RegQueryValueEx(key, "~MHz", NULL, NULL, (BYTE*)&CpuMhz, &size);
72
if (rc != ERROR_SUCCESS) {
73
return PJ_RETURN_OS_ERROR(rc);
77
CpuHz = CpuHz * 1000000;
82
/* __int64 is nicely returned in EDX:EAX */
83
__declspec(naked) __int64 rdtsc()
92
PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
98
PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
104
if (status != PJ_SUCCESS)
112
/////////////////////////////////////////////////////////////////////////////
114
#elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \
115
PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0
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
122
* THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME
126
static pj_timestamp g_ts_freq;
127
static pj_timestamp g_ts_base;
128
static pj_int64_t g_time_base;
130
PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
132
enum { MAX_RETRY = 10 };
136
/* pj_get_timestamp_freq() must have been called before.
137
* This is done when application called pj_init().
139
pj_assert(g_ts_freq.u64 != 0);
141
/* Retry QueryPerformanceCounter() until we're sure that the
142
* value returned makes sense.
147
pj_int64_t counter64, time64, diff;
148
pj_time_val time_now;
150
/* Retrieve the counter */
151
if (!QueryPerformanceCounter(&val))
152
return PJ_RETURN_OS_ERROR(GetLastError());
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.
158
ts->u64 = val.QuadPart;
161
pj_gettimeofday(&time_now);
163
/* Get the counter elapsed time in miliseconds */
164
counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64;
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.
171
time64 = time_now.sec;
172
time64 = time64 * 1000 + time_now.msec;
173
//time64 = GetTickCount();
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()).
180
diff = (time64 - g_time_base) - counter64;
181
if (diff >= -20 && diff <= 20) {
188
} while (i < MAX_RETRY);
190
TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value"));
194
static pj_status_t init_performance_counter(void)
197
pj_time_val time_base;
200
/* Get the frequency */
201
if (!QueryPerformanceFrequency(&val))
202
return PJ_RETURN_OS_ERROR(GetLastError());
204
g_ts_freq.u64 = val.QuadPart;
206
/* Get the base timestamp */
207
if (!QueryPerformanceCounter(&val))
208
return PJ_RETURN_OS_ERROR(GetLastError());
210
g_ts_base.u64 = val.QuadPart;
213
/* Get the base time */
214
status = pj_gettimeofday(&time_base);
215
if (status != PJ_SUCCESS)
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();
226
PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
228
if (g_ts_freq.u64 == 0) {
229
enum { MAX_REPEAT = 10 };
233
/* Make unellegant compiler happy */
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!
240
for (i=0; i<MAX_REPEAT; ++i) {
245
status = init_performance_counter();
246
if (status != PJ_SUCCESS)
249
/* Try the base time */
250
status = pj_get_timestamp(&dummy);
251
if (status == PJ_SUCCESS)
255
if (status != PJ_SUCCESS)
259
freq->u64 = g_ts_freq.u64;
263
/////////////////////////////////////////////////////////////////////////////
268
* Use QueryPerformanceCounter and QueryPerformanceFrequency.
269
* This should be the default implementation to be used on Windows.
271
PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
275
if (!QueryPerformanceCounter(&val))
276
return PJ_RETURN_OS_ERROR(GetLastError());
278
ts->u64 = val.QuadPart;
282
PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
286
if (!QueryPerformanceFrequency(&val))
287
return PJ_RETURN_OS_ERROR(GetLastError());
289
freq->u64 = val.QuadPart;
294
#endif /* PJ_TIMESTAMP_USE_RDTSC */