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