1 | /* |
---|
2 | * tclWinTime.c -- |
---|
3 | * |
---|
4 | * Contains Windows specific versions of Tcl functions that obtain time |
---|
5 | * values from the operating system. |
---|
6 | * |
---|
7 | * Copyright 1995-1998 by Sun Microsystems, Inc. |
---|
8 | * |
---|
9 | * See the file "license.terms" for information on usage and redistribution of |
---|
10 | * this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
11 | * |
---|
12 | * RCS: @(#) $Id: tclWinTime.c,v 1.33 2005/11/04 00:06:51 dkf Exp $ |
---|
13 | */ |
---|
14 | |
---|
15 | #include "tclInt.h" |
---|
16 | |
---|
17 | #define SECSPERDAY (60L * 60L * 24L) |
---|
18 | #define SECSPERYEAR (SECSPERDAY * 365L) |
---|
19 | #define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY) |
---|
20 | |
---|
21 | /* |
---|
22 | * Number of samples over which to estimate the performance counter. |
---|
23 | */ |
---|
24 | |
---|
25 | #define SAMPLES 64 |
---|
26 | |
---|
27 | /* |
---|
28 | * The following arrays contain the day of year for the last day of each |
---|
29 | * month, where index 1 is January. |
---|
30 | */ |
---|
31 | |
---|
32 | static int normalDays[] = { |
---|
33 | -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 |
---|
34 | }; |
---|
35 | |
---|
36 | static int leapDays[] = { |
---|
37 | -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 |
---|
38 | }; |
---|
39 | |
---|
40 | typedef struct ThreadSpecificData { |
---|
41 | char tzName[64]; /* Time zone name */ |
---|
42 | struct tm tm; /* time information */ |
---|
43 | } ThreadSpecificData; |
---|
44 | static Tcl_ThreadDataKey dataKey; |
---|
45 | |
---|
46 | /* |
---|
47 | * Data for managing high-resolution timers. |
---|
48 | */ |
---|
49 | |
---|
50 | typedef struct TimeInfo { |
---|
51 | CRITICAL_SECTION cs; /* Mutex guarding this structure. */ |
---|
52 | int initialized; /* Flag == 1 if this structure is |
---|
53 | * initialized. */ |
---|
54 | int perfCounterAvailable; /* Flag == 1 if the hardware has a performance |
---|
55 | * counter. */ |
---|
56 | HANDLE calibrationThread; /* Handle to the thread that keeps the virtual |
---|
57 | * clock calibrated. */ |
---|
58 | HANDLE readyEvent; /* System event used to trigger the requesting |
---|
59 | * thread when the clock calibration procedure |
---|
60 | * is initialized for the first time. */ |
---|
61 | HANDLE exitEvent; /* Event to signal out of an exit handler to |
---|
62 | * tell the calibration loop to terminate. */ |
---|
63 | LARGE_INTEGER nominalFreq; /* Nominal frequency of the system performance |
---|
64 | * counter, that is, the value returned from |
---|
65 | * QueryPerformanceFrequency. */ |
---|
66 | |
---|
67 | /* |
---|
68 | * The following values are used for calculating virtual time. Virtual |
---|
69 | * time is always equal to: |
---|
70 | * lastFileTime + (current perf counter - lastCounter) |
---|
71 | * * 10000000 / curCounterFreq |
---|
72 | * and lastFileTime and lastCounter are updated any time that virtual time |
---|
73 | * is returned to a caller. |
---|
74 | */ |
---|
75 | |
---|
76 | ULARGE_INTEGER fileTimeLastCall; |
---|
77 | LARGE_INTEGER perfCounterLastCall; |
---|
78 | LARGE_INTEGER curCounterFreq; |
---|
79 | |
---|
80 | /* |
---|
81 | * Data used in developing the estimate of performance counter frequency |
---|
82 | */ |
---|
83 | |
---|
84 | Tcl_WideUInt fileTimeSample[SAMPLES]; |
---|
85 | /* Last 64 samples of system time. */ |
---|
86 | Tcl_WideInt perfCounterSample[SAMPLES]; |
---|
87 | /* Last 64 samples of performance counter. */ |
---|
88 | int sampleNo; /* Current sample number. */ |
---|
89 | } TimeInfo; |
---|
90 | |
---|
91 | static TimeInfo timeInfo = { |
---|
92 | { NULL }, |
---|
93 | 0, |
---|
94 | 0, |
---|
95 | (HANDLE) NULL, |
---|
96 | (HANDLE) NULL, |
---|
97 | (HANDLE) NULL, |
---|
98 | #ifdef HAVE_CAST_TO_UNION |
---|
99 | (LARGE_INTEGER) (Tcl_WideInt) 0, |
---|
100 | (ULARGE_INTEGER) (DWORDLONG) 0, |
---|
101 | (LARGE_INTEGER) (Tcl_WideInt) 0, |
---|
102 | (LARGE_INTEGER) (Tcl_WideInt) 0, |
---|
103 | #else |
---|
104 | 0, |
---|
105 | 0, |
---|
106 | 0, |
---|
107 | 0, |
---|
108 | #endif |
---|
109 | { 0 }, |
---|
110 | { 0 }, |
---|
111 | 0 |
---|
112 | }; |
---|
113 | |
---|
114 | /* |
---|
115 | * Declarations for functions defined later in this file. |
---|
116 | */ |
---|
117 | |
---|
118 | static struct tm * ComputeGMT(const time_t *tp); |
---|
119 | static void StopCalibration(ClientData clientData); |
---|
120 | static DWORD WINAPI CalibrationThread(LPVOID arg); |
---|
121 | static void UpdateTimeEachSecond(void); |
---|
122 | static void ResetCounterSamples(Tcl_WideUInt fileTime, |
---|
123 | Tcl_WideInt perfCounter, Tcl_WideInt perfFreq); |
---|
124 | static Tcl_WideInt AccumulateSample(Tcl_WideInt perfCounter, |
---|
125 | Tcl_WideUInt fileTime); |
---|
126 | static void NativeScaleTime(Tcl_Time* timebuf, |
---|
127 | ClientData clientData); |
---|
128 | static void NativeGetTime(Tcl_Time* timebuf, |
---|
129 | ClientData clientData); |
---|
130 | |
---|
131 | /* |
---|
132 | * TIP #233 (Virtualized Time): Data for the time hooks, if any. |
---|
133 | */ |
---|
134 | |
---|
135 | Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime; |
---|
136 | Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime; |
---|
137 | ClientData tclTimeClientData = NULL; |
---|
138 | |
---|
139 | /* |
---|
140 | *---------------------------------------------------------------------- |
---|
141 | * |
---|
142 | * TclpGetSeconds -- |
---|
143 | * |
---|
144 | * This procedure returns the number of seconds from the epoch. On most |
---|
145 | * Unix systems the epoch is Midnight Jan 1, 1970 GMT. |
---|
146 | * |
---|
147 | * Results: |
---|
148 | * Number of seconds from the epoch. |
---|
149 | * |
---|
150 | * Side effects: |
---|
151 | * None. |
---|
152 | * |
---|
153 | *---------------------------------------------------------------------- |
---|
154 | */ |
---|
155 | |
---|
156 | unsigned long |
---|
157 | TclpGetSeconds(void) |
---|
158 | { |
---|
159 | Tcl_Time t; |
---|
160 | |
---|
161 | (*tclGetTimeProcPtr) (&t, tclTimeClientData); /* Tcl_GetTime inlined. */ |
---|
162 | return t.sec; |
---|
163 | } |
---|
164 | |
---|
165 | /* |
---|
166 | *---------------------------------------------------------------------- |
---|
167 | * |
---|
168 | * TclpGetClicks -- |
---|
169 | * |
---|
170 | * This procedure returns a value that represents the highest resolution |
---|
171 | * clock available on the system. There are no guarantees on what the |
---|
172 | * resolution will be. In Tcl we will call this value a "click". The |
---|
173 | * start time is also system dependant. |
---|
174 | * |
---|
175 | * Results: |
---|
176 | * Number of clicks from some start time. |
---|
177 | * |
---|
178 | * Side effects: |
---|
179 | * None. |
---|
180 | * |
---|
181 | *---------------------------------------------------------------------- |
---|
182 | */ |
---|
183 | |
---|
184 | unsigned long |
---|
185 | TclpGetClicks(void) |
---|
186 | { |
---|
187 | /* |
---|
188 | * Use the Tcl_GetTime abstraction to get the time in microseconds, as |
---|
189 | * nearly as we can, and return it. |
---|
190 | */ |
---|
191 | |
---|
192 | Tcl_Time now; /* Current Tcl time */ |
---|
193 | unsigned long retval; /* Value to return */ |
---|
194 | |
---|
195 | (*tclGetTimeProcPtr) (&now, tclTimeClientData); /* Tcl_GetTime inlined */ |
---|
196 | |
---|
197 | retval = (now.sec * 1000000) + now.usec; |
---|
198 | return retval; |
---|
199 | |
---|
200 | } |
---|
201 | |
---|
202 | /* |
---|
203 | *---------------------------------------------------------------------- |
---|
204 | * |
---|
205 | * TclpGetTimeZone -- |
---|
206 | * |
---|
207 | * Determines the current timezone. The method varies wildly between |
---|
208 | * different Platform implementations, so its hidden in this function. |
---|
209 | * |
---|
210 | * Results: |
---|
211 | * Minutes west of GMT. |
---|
212 | * |
---|
213 | * Side effects: |
---|
214 | * None. |
---|
215 | * |
---|
216 | *---------------------------------------------------------------------- |
---|
217 | */ |
---|
218 | |
---|
219 | int |
---|
220 | TclpGetTimeZone( |
---|
221 | unsigned long currentTime) |
---|
222 | { |
---|
223 | int timeZone; |
---|
224 | |
---|
225 | tzset(); |
---|
226 | timeZone = timezone / 60; |
---|
227 | |
---|
228 | return timeZone; |
---|
229 | } |
---|
230 | |
---|
231 | /* |
---|
232 | *---------------------------------------------------------------------- |
---|
233 | * |
---|
234 | * Tcl_GetTime -- |
---|
235 | * |
---|
236 | * Gets the current system time in seconds and microseconds since the |
---|
237 | * beginning of the epoch: 00:00 UCT, January 1, 1970. |
---|
238 | * |
---|
239 | * Results: |
---|
240 | * Returns the current time in timePtr. |
---|
241 | * |
---|
242 | * Side effects: |
---|
243 | * On the first call, initializes a set of static variables to keep track |
---|
244 | * of the base value of the performance counter, the corresponding wall |
---|
245 | * clock (obtained through ftime) and the frequency of the performance |
---|
246 | * counter. Also spins a thread whose function is to wake up periodically |
---|
247 | * and monitor these values, adjusting them as necessary to correct for |
---|
248 | * drift in the performance counter's oscillator. |
---|
249 | * |
---|
250 | *---------------------------------------------------------------------- |
---|
251 | */ |
---|
252 | |
---|
253 | void |
---|
254 | Tcl_GetTime( |
---|
255 | Tcl_Time *timePtr) /* Location to store time information. */ |
---|
256 | { |
---|
257 | (*tclGetTimeProcPtr) (timePtr, tclTimeClientData); |
---|
258 | } |
---|
259 | |
---|
260 | /* |
---|
261 | *---------------------------------------------------------------------- |
---|
262 | * |
---|
263 | * NativeScaleTime -- |
---|
264 | * |
---|
265 | * TIP #233: Scale from virtual time to the real-time. For native scaling |
---|
266 | * the relationship is 1:1 and nothing has to be done. |
---|
267 | * |
---|
268 | * Results: |
---|
269 | * Scales the time in timePtr. |
---|
270 | * |
---|
271 | * Side effects: |
---|
272 | * See above. |
---|
273 | * |
---|
274 | *---------------------------------------------------------------------- |
---|
275 | */ |
---|
276 | |
---|
277 | static void |
---|
278 | NativeScaleTime( |
---|
279 | Tcl_Time *timePtr, |
---|
280 | ClientData clientData) |
---|
281 | { |
---|
282 | /* |
---|
283 | * Native scale is 1:1. Nothing is done. |
---|
284 | */ |
---|
285 | } |
---|
286 | |
---|
287 | /* |
---|
288 | *---------------------------------------------------------------------- |
---|
289 | * |
---|
290 | * NativeGetTime -- |
---|
291 | * |
---|
292 | * TIP #233: Gets the current system time in seconds and microseconds |
---|
293 | * since the beginning of the epoch: 00:00 UCT, January 1, 1970. |
---|
294 | * |
---|
295 | * Results: |
---|
296 | * Returns the current time in timePtr. |
---|
297 | * |
---|
298 | * Side effects: |
---|
299 | * On the first call, initializes a set of static variables to keep track |
---|
300 | * of the base value of the performance counter, the corresponding wall |
---|
301 | * clock (obtained through ftime) and the frequency of the performance |
---|
302 | * counter. Also spins a thread whose function is to wake up periodically |
---|
303 | * and monitor these values, adjusting them as necessary to correct for |
---|
304 | * drift in the performance counter's oscillator. |
---|
305 | * |
---|
306 | *---------------------------------------------------------------------- |
---|
307 | */ |
---|
308 | |
---|
309 | static void |
---|
310 | NativeGetTime( |
---|
311 | Tcl_Time *timePtr, |
---|
312 | ClientData clientData) |
---|
313 | { |
---|
314 | struct timeb t; |
---|
315 | int useFtime = 1; /* Flag == TRUE if we need to fall back on |
---|
316 | * ftime rather than using the perf counter. */ |
---|
317 | |
---|
318 | /* |
---|
319 | * Initialize static storage on the first trip through. |
---|
320 | * |
---|
321 | * Note: Outer check for 'initialized' is a performance win since it |
---|
322 | * avoids an extra mutex lock in the common case. |
---|
323 | */ |
---|
324 | |
---|
325 | if (!timeInfo.initialized) { |
---|
326 | TclpInitLock(); |
---|
327 | if (!timeInfo.initialized) { |
---|
328 | timeInfo.perfCounterAvailable = |
---|
329 | QueryPerformanceFrequency(&timeInfo.nominalFreq); |
---|
330 | |
---|
331 | /* |
---|
332 | * Some hardware abstraction layers use the CPU clock in place of |
---|
333 | * the real-time clock as a performance counter reference. This |
---|
334 | * results in: |
---|
335 | * - inconsistent results among the processors on |
---|
336 | * multi-processor systems. |
---|
337 | * - unpredictable changes in performance counter frequency on |
---|
338 | * "gearshift" processors such as Transmeta and SpeedStep. |
---|
339 | * |
---|
340 | * There seems to be no way to test whether the performance |
---|
341 | * counter is reliable, but a useful heuristic is that if its |
---|
342 | * frequency is 1.193182 MHz or 3.579545 MHz, it's derived from a |
---|
343 | * colorburst crystal and is therefore the RTC rather than the |
---|
344 | * TSC. |
---|
345 | * |
---|
346 | * A sloppier but serviceable heuristic is that the RTC crystal is |
---|
347 | * normally less than 15 MHz while the TSC crystal is virtually |
---|
348 | * assured to be greater than 100 MHz. Since Win98SE appears to |
---|
349 | * fiddle with the definition of the perf counter frequency |
---|
350 | * (perhaps in an attempt to calibrate the clock?), we use the |
---|
351 | * latter rule rather than an exact match. |
---|
352 | * |
---|
353 | * We also assume (perhaps questionably) that the vendors have |
---|
354 | * gotten their act together on Win64, so bypass all this rubbish |
---|
355 | * on that platform. |
---|
356 | */ |
---|
357 | |
---|
358 | #if !defined(_WIN64) |
---|
359 | if (timeInfo.perfCounterAvailable |
---|
360 | /* |
---|
361 | * The following lines would do an exact match on crystal |
---|
362 | * frequency: |
---|
363 | * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt)1193182 |
---|
364 | * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt)3579545 |
---|
365 | */ |
---|
366 | && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000){ |
---|
367 | /* |
---|
368 | * As an exception, if every logical processor on the system |
---|
369 | * is on the same chip, we use the performance counter anyway, |
---|
370 | * presuming that everyone's TSC is locked to the same |
---|
371 | * oscillator. |
---|
372 | */ |
---|
373 | |
---|
374 | SYSTEM_INFO systemInfo; |
---|
375 | unsigned int regs[4]; |
---|
376 | |
---|
377 | GetSystemInfo(&systemInfo); |
---|
378 | if (TclWinCPUID(0, regs) == TCL_OK |
---|
379 | && regs[1] == 0x756e6547 /* "Genu" */ |
---|
380 | && regs[3] == 0x49656e69 /* "ineI" */ |
---|
381 | && regs[2] == 0x6c65746e /* "ntel" */ |
---|
382 | && TclWinCPUID(1, regs) == TCL_OK |
---|
383 | && ((regs[0]&0x00000F00) == 0x00000F00 /* Pentium 4 */ |
---|
384 | || ((regs[0] & 0x00F00000) /* Extended family */ |
---|
385 | && (regs[3] & 0x10000000))) /* Hyperthread */ |
---|
386 | && (((regs[1]&0x00FF0000) >> 16)/* CPU count */ |
---|
387 | == systemInfo.dwNumberOfProcessors)) { |
---|
388 | timeInfo.perfCounterAvailable = TRUE; |
---|
389 | } else { |
---|
390 | timeInfo.perfCounterAvailable = FALSE; |
---|
391 | } |
---|
392 | } |
---|
393 | #endif /* above code is Win32 only */ |
---|
394 | |
---|
395 | /* |
---|
396 | * If the performance counter is available, start a thread to |
---|
397 | * calibrate it. |
---|
398 | */ |
---|
399 | |
---|
400 | if (timeInfo.perfCounterAvailable) { |
---|
401 | DWORD id; |
---|
402 | |
---|
403 | InitializeCriticalSection(&timeInfo.cs); |
---|
404 | timeInfo.readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
---|
405 | timeInfo.exitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
---|
406 | timeInfo.calibrationThread = CreateThread(NULL, 256, |
---|
407 | CalibrationThread, (LPVOID) NULL, 0, &id); |
---|
408 | SetThreadPriority(timeInfo.calibrationThread, |
---|
409 | THREAD_PRIORITY_HIGHEST); |
---|
410 | |
---|
411 | /* |
---|
412 | * Wait for the thread just launched to start running, and |
---|
413 | * create an exit handler that kills it so that it doesn't |
---|
414 | * outlive unloading tclXX.dll |
---|
415 | */ |
---|
416 | |
---|
417 | WaitForSingleObject(timeInfo.readyEvent, INFINITE); |
---|
418 | CloseHandle(timeInfo.readyEvent); |
---|
419 | Tcl_CreateExitHandler(StopCalibration, (ClientData) NULL); |
---|
420 | } |
---|
421 | timeInfo.initialized = TRUE; |
---|
422 | } |
---|
423 | TclpInitUnlock(); |
---|
424 | } |
---|
425 | |
---|
426 | if (timeInfo.perfCounterAvailable && timeInfo.curCounterFreq.QuadPart!=0) { |
---|
427 | /* |
---|
428 | * Query the performance counter and use it to calculate the current |
---|
429 | * time. |
---|
430 | */ |
---|
431 | |
---|
432 | LARGE_INTEGER curCounter; |
---|
433 | /* Current performance counter. */ |
---|
434 | Tcl_WideInt curFileTime;/* Current estimated time, expressed as 100-ns |
---|
435 | * ticks since the Windows epoch. */ |
---|
436 | static LARGE_INTEGER posixEpoch; |
---|
437 | /* Posix epoch expressed as 100-ns ticks since |
---|
438 | * the windows epoch. */ |
---|
439 | Tcl_WideInt usecSincePosixEpoch; |
---|
440 | /* Current microseconds since Posix epoch. */ |
---|
441 | |
---|
442 | posixEpoch.LowPart = 0xD53E8000; |
---|
443 | posixEpoch.HighPart = 0x019DB1DE; |
---|
444 | |
---|
445 | EnterCriticalSection(&timeInfo.cs); |
---|
446 | |
---|
447 | QueryPerformanceCounter(&curCounter); |
---|
448 | |
---|
449 | /* |
---|
450 | * If it appears to be more than 1.1 seconds since the last trip |
---|
451 | * through the calibration loop, the performance counter may have |
---|
452 | * jumped forward. (See MSDN Knowledge Base article Q274323 for a |
---|
453 | * description of the hardware problem that makes this test |
---|
454 | * necessary.) If the counter jumps, we don't want to use it directly. |
---|
455 | * Instead, we must return system time. Eventually, the calibration |
---|
456 | * loop should recover. |
---|
457 | */ |
---|
458 | |
---|
459 | if (curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart < |
---|
460 | 11 * timeInfo.curCounterFreq.QuadPart / 10) { |
---|
461 | curFileTime = timeInfo.fileTimeLastCall.QuadPart + |
---|
462 | ((curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart) |
---|
463 | * 10000000 / timeInfo.curCounterFreq.QuadPart); |
---|
464 | timeInfo.fileTimeLastCall.QuadPart = curFileTime; |
---|
465 | timeInfo.perfCounterLastCall.QuadPart = curCounter.QuadPart; |
---|
466 | usecSincePosixEpoch = (curFileTime - posixEpoch.QuadPart) / 10; |
---|
467 | timePtr->sec = (long) (usecSincePosixEpoch / 1000000); |
---|
468 | timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000); |
---|
469 | useFtime = 0; |
---|
470 | } |
---|
471 | |
---|
472 | LeaveCriticalSection(&timeInfo.cs); |
---|
473 | } |
---|
474 | |
---|
475 | if (useFtime) { |
---|
476 | /* |
---|
477 | * High resolution timer is not available. Just use ftime. |
---|
478 | */ |
---|
479 | |
---|
480 | ftime(&t); |
---|
481 | timePtr->sec = (long)t.time; |
---|
482 | timePtr->usec = t.millitm * 1000; |
---|
483 | } |
---|
484 | } |
---|
485 | |
---|
486 | /* |
---|
487 | *---------------------------------------------------------------------- |
---|
488 | * |
---|
489 | * StopCalibration -- |
---|
490 | * |
---|
491 | * Turns off the calibration thread in preparation for exiting the |
---|
492 | * process. |
---|
493 | * |
---|
494 | * Results: |
---|
495 | * None. |
---|
496 | * |
---|
497 | * Side effects: |
---|
498 | * Sets the 'exitEvent' event in the 'timeInfo' structure to ask the |
---|
499 | * thread in question to exit, and waits for it to do so. |
---|
500 | * |
---|
501 | *---------------------------------------------------------------------- |
---|
502 | */ |
---|
503 | |
---|
504 | static void |
---|
505 | StopCalibration( |
---|
506 | ClientData unused) /* Client data is unused */ |
---|
507 | { |
---|
508 | SetEvent(timeInfo.exitEvent); |
---|
509 | |
---|
510 | /* |
---|
511 | * If Tcl_Finalize was called from DllMain, the calibration thread is in a |
---|
512 | * paused state so we need to timeout and continue. |
---|
513 | */ |
---|
514 | |
---|
515 | WaitForSingleObject(timeInfo.calibrationThread, 100); |
---|
516 | CloseHandle(timeInfo.exitEvent); |
---|
517 | CloseHandle(timeInfo.calibrationThread); |
---|
518 | } |
---|
519 | |
---|
520 | /* |
---|
521 | *---------------------------------------------------------------------- |
---|
522 | * |
---|
523 | * TclpGetTZName -- |
---|
524 | * |
---|
525 | * Gets the current timezone string. |
---|
526 | * |
---|
527 | * Results: |
---|
528 | * Returns a pointer to a static string, or NULL on failure. |
---|
529 | * |
---|
530 | * Side effects: |
---|
531 | * None. |
---|
532 | * |
---|
533 | *---------------------------------------------------------------------- |
---|
534 | */ |
---|
535 | |
---|
536 | char * |
---|
537 | TclpGetTZName( |
---|
538 | int dst) |
---|
539 | { |
---|
540 | int len; |
---|
541 | char *zone, *p; |
---|
542 | TIME_ZONE_INFORMATION tz; |
---|
543 | Tcl_Encoding encoding; |
---|
544 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
---|
545 | char *name = tsdPtr->tzName; |
---|
546 | |
---|
547 | /* |
---|
548 | * tzset() under Borland doesn't seem to set up tzname[] at all. |
---|
549 | * tzset() under MSVC has the following weird observed behavior: |
---|
550 | * First time we call "clock format [clock seconds] -format %Z -gmt 1" |
---|
551 | * we get "GMT", but on all subsequent calls we get the current time |
---|
552 | * ezone string, even though env(TZ) is GMT and the variable _timezone |
---|
553 | * is 0. |
---|
554 | */ |
---|
555 | |
---|
556 | name[0] = '\0'; |
---|
557 | |
---|
558 | zone = getenv("TZ"); |
---|
559 | if (zone != NULL) { |
---|
560 | /* |
---|
561 | * TZ is of form "NST-4:30NDT", where "NST" would be the name of the |
---|
562 | * standard time zone for this area, "-4:30" is the offset from GMT in |
---|
563 | * hours, and "NDT is the name of the daylight savings time zone in |
---|
564 | * this area. The offset and DST strings are optional. |
---|
565 | */ |
---|
566 | |
---|
567 | len = strlen(zone); |
---|
568 | if (len > 3) { |
---|
569 | len = 3; |
---|
570 | } |
---|
571 | if (dst != 0) { |
---|
572 | /* |
---|
573 | * Skip the offset string and get the DST string. |
---|
574 | */ |
---|
575 | |
---|
576 | p = zone + len; |
---|
577 | p += strspn(p, "+-:0123456789"); |
---|
578 | if (*p != '\0') { |
---|
579 | zone = p; |
---|
580 | len = strlen(zone); |
---|
581 | if (len > 3) { |
---|
582 | len = 3; |
---|
583 | } |
---|
584 | } |
---|
585 | } |
---|
586 | Tcl_ExternalToUtf(NULL, NULL, zone, len, 0, NULL, name, |
---|
587 | sizeof(tsdPtr->tzName), NULL, NULL, NULL); |
---|
588 | } |
---|
589 | if (name[0] == '\0') { |
---|
590 | if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) { |
---|
591 | /* |
---|
592 | * MSDN: On NT this is returned if DST is not used in the current |
---|
593 | * TZ |
---|
594 | */ |
---|
595 | |
---|
596 | dst = 0; |
---|
597 | } |
---|
598 | encoding = Tcl_GetEncoding(NULL, "unicode"); |
---|
599 | Tcl_ExternalToUtf(NULL, encoding, |
---|
600 | (char *) ((dst) ? tz.DaylightName : tz.StandardName), -1, |
---|
601 | 0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL); |
---|
602 | Tcl_FreeEncoding(encoding); |
---|
603 | } |
---|
604 | return name; |
---|
605 | } |
---|
606 | |
---|
607 | /* |
---|
608 | *---------------------------------------------------------------------- |
---|
609 | * |
---|
610 | * TclpGetDate -- |
---|
611 | * |
---|
612 | * This function converts between seconds and struct tm. If useGMT is |
---|
613 | * true, then the returned date will be in Greenwich Mean Time (GMT). |
---|
614 | * Otherwise, it will be in the local time zone. |
---|
615 | * |
---|
616 | * Results: |
---|
617 | * Returns a static tm structure. |
---|
618 | * |
---|
619 | * Side effects: |
---|
620 | * None. |
---|
621 | * |
---|
622 | *---------------------------------------------------------------------- |
---|
623 | */ |
---|
624 | |
---|
625 | struct tm * |
---|
626 | TclpGetDate( |
---|
627 | CONST time_t *t, |
---|
628 | int useGMT) |
---|
629 | { |
---|
630 | struct tm *tmPtr; |
---|
631 | time_t time; |
---|
632 | |
---|
633 | if (!useGMT) { |
---|
634 | tzset(); |
---|
635 | |
---|
636 | /* |
---|
637 | * If we are in the valid range, let the C run-time library handle it. |
---|
638 | * Otherwise we need to fake it. Note that this algorithm ignores |
---|
639 | * daylight savings time before the epoch. |
---|
640 | */ |
---|
641 | |
---|
642 | /* |
---|
643 | * Hm, Borland's localtime manages to return NULL under certain |
---|
644 | * circumstances (e.g. wintime.test, test 1.2). Nobody tests for this, |
---|
645 | * since 'localtime' isn't supposed to do this, possibly leading to |
---|
646 | * crashes. |
---|
647 | * |
---|
648 | * Patch: We only call this function if we are at least one day into |
---|
649 | * the epoch, else we handle it ourselves (like we do for times < 0). |
---|
650 | * H. Giese, June 2003 |
---|
651 | */ |
---|
652 | |
---|
653 | #ifdef __BORLANDC__ |
---|
654 | #define LOCALTIME_VALIDITY_BOUNDARY SECSPERDAY |
---|
655 | #else |
---|
656 | #define LOCALTIME_VALIDITY_BOUNDARY 0 |
---|
657 | #endif |
---|
658 | |
---|
659 | if (*t >= LOCALTIME_VALIDITY_BOUNDARY) { |
---|
660 | return TclpLocaltime(t); |
---|
661 | } |
---|
662 | |
---|
663 | time = *t - timezone; |
---|
664 | |
---|
665 | /* |
---|
666 | * If we aren't near to overflowing the long, just add the bias and |
---|
667 | * use the normal calculation. Otherwise we will need to adjust the |
---|
668 | * result at the end. |
---|
669 | */ |
---|
670 | |
---|
671 | if (*t < (LONG_MAX - 2*SECSPERDAY) && *t > (LONG_MIN + 2*SECSPERDAY)) { |
---|
672 | tmPtr = ComputeGMT(&time); |
---|
673 | } else { |
---|
674 | tmPtr = ComputeGMT(t); |
---|
675 | |
---|
676 | tzset(); |
---|
677 | |
---|
678 | /* |
---|
679 | * Add the bias directly to the tm structure to avoid overflow. |
---|
680 | * Propagate seconds overflow into minutes, hours and days. |
---|
681 | */ |
---|
682 | |
---|
683 | time = tmPtr->tm_sec - timezone; |
---|
684 | tmPtr->tm_sec = (int)(time % 60); |
---|
685 | if (tmPtr->tm_sec < 0) { |
---|
686 | tmPtr->tm_sec += 60; |
---|
687 | time -= 60; |
---|
688 | } |
---|
689 | |
---|
690 | time = tmPtr->tm_min + time/60; |
---|
691 | tmPtr->tm_min = (int)(time % 60); |
---|
692 | if (tmPtr->tm_min < 0) { |
---|
693 | tmPtr->tm_min += 60; |
---|
694 | time -= 60; |
---|
695 | } |
---|
696 | |
---|
697 | time = tmPtr->tm_hour + time/60; |
---|
698 | tmPtr->tm_hour = (int)(time % 24); |
---|
699 | if (tmPtr->tm_hour < 0) { |
---|
700 | tmPtr->tm_hour += 24; |
---|
701 | time -= 24; |
---|
702 | } |
---|
703 | |
---|
704 | time /= 24; |
---|
705 | tmPtr->tm_mday += (int)time; |
---|
706 | tmPtr->tm_yday += (int)time; |
---|
707 | tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7; |
---|
708 | } |
---|
709 | } else { |
---|
710 | tmPtr = ComputeGMT(t); |
---|
711 | } |
---|
712 | return tmPtr; |
---|
713 | } |
---|
714 | |
---|
715 | /* |
---|
716 | *---------------------------------------------------------------------- |
---|
717 | * |
---|
718 | * ComputeGMT -- |
---|
719 | * |
---|
720 | * This function computes GMT given the number of seconds since the epoch |
---|
721 | * (midnight Jan 1 1970). |
---|
722 | * |
---|
723 | * Results: |
---|
724 | * Returns a (per thread) statically allocated struct tm. |
---|
725 | * |
---|
726 | * Side effects: |
---|
727 | * Updates the values of the static struct tm. |
---|
728 | * |
---|
729 | *---------------------------------------------------------------------- |
---|
730 | */ |
---|
731 | |
---|
732 | static struct tm * |
---|
733 | ComputeGMT( |
---|
734 | const time_t *tp) |
---|
735 | { |
---|
736 | struct tm *tmPtr; |
---|
737 | long tmp, rem; |
---|
738 | int isLeap; |
---|
739 | int *days; |
---|
740 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
---|
741 | |
---|
742 | tmPtr = &tsdPtr->tm; |
---|
743 | |
---|
744 | /* |
---|
745 | * Compute the 4 year span containing the specified time. |
---|
746 | */ |
---|
747 | |
---|
748 | tmp = (long)(*tp / SECSPER4YEAR); |
---|
749 | rem = (long)(*tp % SECSPER4YEAR); |
---|
750 | |
---|
751 | /* |
---|
752 | * Correct for weird mod semantics so the remainder is always positive. |
---|
753 | */ |
---|
754 | |
---|
755 | if (rem < 0) { |
---|
756 | tmp--; |
---|
757 | rem += SECSPER4YEAR; |
---|
758 | } |
---|
759 | |
---|
760 | /* |
---|
761 | * Compute the year after 1900 by taking the 4 year span and adjusting for |
---|
762 | * the remainder. This works because 2000 is a leap year, and 1900/2100 |
---|
763 | * are out of the range. |
---|
764 | */ |
---|
765 | |
---|
766 | tmp = (tmp * 4) + 70; |
---|
767 | isLeap = 0; |
---|
768 | if (rem >= SECSPERYEAR) { /* 1971, etc. */ |
---|
769 | tmp++; |
---|
770 | rem -= SECSPERYEAR; |
---|
771 | if (rem >= SECSPERYEAR) { /* 1972, etc. */ |
---|
772 | tmp++; |
---|
773 | rem -= SECSPERYEAR; |
---|
774 | if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */ |
---|
775 | tmp++; |
---|
776 | rem -= SECSPERYEAR + SECSPERDAY; |
---|
777 | } else { |
---|
778 | isLeap = 1; |
---|
779 | } |
---|
780 | } |
---|
781 | } |
---|
782 | tmPtr->tm_year = tmp; |
---|
783 | |
---|
784 | /* |
---|
785 | * Compute the day of year and leave the seconds in the current day in the |
---|
786 | * remainder. |
---|
787 | */ |
---|
788 | |
---|
789 | tmPtr->tm_yday = rem / SECSPERDAY; |
---|
790 | rem %= SECSPERDAY; |
---|
791 | |
---|
792 | /* |
---|
793 | * Compute the time of day. |
---|
794 | */ |
---|
795 | |
---|
796 | tmPtr->tm_hour = rem / 3600; |
---|
797 | rem %= 3600; |
---|
798 | tmPtr->tm_min = rem / 60; |
---|
799 | tmPtr->tm_sec = rem % 60; |
---|
800 | |
---|
801 | /* |
---|
802 | * Compute the month and day of month. |
---|
803 | */ |
---|
804 | |
---|
805 | days = (isLeap) ? leapDays : normalDays; |
---|
806 | for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) { |
---|
807 | /* empty body */ |
---|
808 | } |
---|
809 | tmPtr->tm_mon = --tmp; |
---|
810 | tmPtr->tm_mday = tmPtr->tm_yday - days[tmp]; |
---|
811 | |
---|
812 | /* |
---|
813 | * Compute day of week. Epoch started on a Thursday. |
---|
814 | */ |
---|
815 | |
---|
816 | tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4; |
---|
817 | if ((*tp % SECSPERDAY) < 0) { |
---|
818 | tmPtr->tm_wday--; |
---|
819 | } |
---|
820 | tmPtr->tm_wday %= 7; |
---|
821 | if (tmPtr->tm_wday < 0) { |
---|
822 | tmPtr->tm_wday += 7; |
---|
823 | } |
---|
824 | |
---|
825 | return tmPtr; |
---|
826 | } |
---|
827 | |
---|
828 | /* |
---|
829 | *---------------------------------------------------------------------- |
---|
830 | * |
---|
831 | * CalibrationThread -- |
---|
832 | * |
---|
833 | * Thread that manages calibration of the hi-resolution time derived from |
---|
834 | * the performance counter, to keep it synchronized with the system |
---|
835 | * clock. |
---|
836 | * |
---|
837 | * Parameters: |
---|
838 | * arg - Client data from the CreateThread call. This parameter points to |
---|
839 | * the static TimeInfo structure. |
---|
840 | * |
---|
841 | * Return value: |
---|
842 | * None. This thread embeds an infinite loop. |
---|
843 | * |
---|
844 | * Side effects: |
---|
845 | * At an interval of 1s, this thread performs virtual time discipline. |
---|
846 | * |
---|
847 | * Note: When this thread is entered, TclpInitLock has been called to |
---|
848 | * safeguard the static storage. There is therefore no synchronization in the |
---|
849 | * body of this procedure. |
---|
850 | * |
---|
851 | *---------------------------------------------------------------------- |
---|
852 | */ |
---|
853 | |
---|
854 | static DWORD WINAPI |
---|
855 | CalibrationThread( |
---|
856 | LPVOID arg) |
---|
857 | { |
---|
858 | FILETIME curFileTime; |
---|
859 | DWORD waitResult; |
---|
860 | |
---|
861 | /* |
---|
862 | * Get initial system time and performance counter. |
---|
863 | */ |
---|
864 | |
---|
865 | GetSystemTimeAsFileTime(&curFileTime); |
---|
866 | QueryPerformanceCounter(&timeInfo.perfCounterLastCall); |
---|
867 | QueryPerformanceFrequency(&timeInfo.curCounterFreq); |
---|
868 | timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime; |
---|
869 | timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime; |
---|
870 | |
---|
871 | ResetCounterSamples(timeInfo.fileTimeLastCall.QuadPart, |
---|
872 | timeInfo.perfCounterLastCall.QuadPart, |
---|
873 | timeInfo.curCounterFreq.QuadPart); |
---|
874 | |
---|
875 | /* |
---|
876 | * Wake up the calling thread. When it wakes up, it will release the |
---|
877 | * initialization lock. |
---|
878 | */ |
---|
879 | |
---|
880 | SetEvent(timeInfo.readyEvent); |
---|
881 | |
---|
882 | /* |
---|
883 | * Run the calibration once a second. |
---|
884 | */ |
---|
885 | |
---|
886 | while (timeInfo.perfCounterAvailable) { |
---|
887 | /* |
---|
888 | * If the exitEvent is set, break out of the loop. |
---|
889 | */ |
---|
890 | |
---|
891 | waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE); |
---|
892 | if (waitResult == WAIT_OBJECT_0) { |
---|
893 | break; |
---|
894 | } |
---|
895 | UpdateTimeEachSecond(); |
---|
896 | } |
---|
897 | |
---|
898 | /* lint */ |
---|
899 | return (DWORD) 0; |
---|
900 | } |
---|
901 | |
---|
902 | /* |
---|
903 | *---------------------------------------------------------------------- |
---|
904 | * |
---|
905 | * UpdateTimeEachSecond -- |
---|
906 | * |
---|
907 | * Callback from the waitable timer in the clock calibration thread that |
---|
908 | * updates system time. |
---|
909 | * |
---|
910 | * Parameters: |
---|
911 | * info - Pointer to the static TimeInfo structure |
---|
912 | * |
---|
913 | * Results: |
---|
914 | * None. |
---|
915 | * |
---|
916 | * Side effects: |
---|
917 | * Performs virtual time calibration discipline. |
---|
918 | * |
---|
919 | *---------------------------------------------------------------------- |
---|
920 | */ |
---|
921 | |
---|
922 | static void |
---|
923 | UpdateTimeEachSecond(void) |
---|
924 | { |
---|
925 | LARGE_INTEGER curPerfCounter; |
---|
926 | /* Current value returned from |
---|
927 | * QueryPerformanceCounter. */ |
---|
928 | FILETIME curSysTime; /* Current system time. */ |
---|
929 | LARGE_INTEGER curFileTime; /* File time at the time this callback was |
---|
930 | * scheduled. */ |
---|
931 | Tcl_WideInt estFreq; /* Estimated perf counter frequency. */ |
---|
932 | Tcl_WideInt vt0; /* Tcl time right now. */ |
---|
933 | Tcl_WideInt vt1; /* Tcl time one second from now. */ |
---|
934 | Tcl_WideInt tdiff; /* Difference between system clock and Tcl |
---|
935 | * time. */ |
---|
936 | Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time into |
---|
937 | * step over 1 second. */ |
---|
938 | |
---|
939 | /* |
---|
940 | * Sample performance counter and system time. |
---|
941 | */ |
---|
942 | |
---|
943 | QueryPerformanceCounter(&curPerfCounter); |
---|
944 | GetSystemTimeAsFileTime(&curSysTime); |
---|
945 | curFileTime.LowPart = curSysTime.dwLowDateTime; |
---|
946 | curFileTime.HighPart = curSysTime.dwHighDateTime; |
---|
947 | |
---|
948 | EnterCriticalSection(&timeInfo.cs); |
---|
949 | |
---|
950 | /* |
---|
951 | * We devide by timeInfo.curCounterFreq.QuadPart in several places. That |
---|
952 | * value should always be positive on a correctly functioning system. But |
---|
953 | * it is good to be defensive about such matters. So if something goes |
---|
954 | * wrong and the value does goes to zero, we clear the |
---|
955 | * timeInfo.perfCounterAvailable in order to cause the calibration thread |
---|
956 | * to shut itself down, then return without additional processing. |
---|
957 | */ |
---|
958 | |
---|
959 | if (timeInfo.curCounterFreq.QuadPart == 0){ |
---|
960 | LeaveCriticalSection(&timeInfo.cs); |
---|
961 | timeInfo.perfCounterAvailable = 0; |
---|
962 | return; |
---|
963 | } |
---|
964 | |
---|
965 | /* |
---|
966 | * Several things may have gone wrong here that have to be checked for. |
---|
967 | * (1) The performance counter may have jumped. |
---|
968 | * (2) The system clock may have been reset. |
---|
969 | * |
---|
970 | * In either case, we'll need to reinitialize the circular buffer with |
---|
971 | * samples relative to the current system time and the NOMINAL performance |
---|
972 | * frequency (not the actual, because the actual has probably run slow in |
---|
973 | * the first case). Our estimated frequency will be the nominal frequency. |
---|
974 | * |
---|
975 | * Store the current sample into the circular buffer of samples, and |
---|
976 | * estimate the performance counter frequency. |
---|
977 | */ |
---|
978 | |
---|
979 | estFreq = AccumulateSample(curPerfCounter.QuadPart, |
---|
980 | (Tcl_WideUInt) curFileTime.QuadPart); |
---|
981 | |
---|
982 | /* |
---|
983 | * We want to adjust things so that time appears to be continuous. |
---|
984 | * Virtual file time, right now, is |
---|
985 | * |
---|
986 | * vt0 = 10000000 * (curPerfCounter - perfCounterLastCall) |
---|
987 | * / curCounterFreq |
---|
988 | * + fileTimeLastCall |
---|
989 | * |
---|
990 | * Ideally, we would like to drift the clock into place over a period of 2 |
---|
991 | * sec, so that virtual time 2 sec from now will be |
---|
992 | * |
---|
993 | * vt1 = 20000000 + curFileTime |
---|
994 | * |
---|
995 | * The frequency that we need to use to drift the counter back into place |
---|
996 | * is estFreq * 20000000 / (vt1 - vt0) |
---|
997 | */ |
---|
998 | |
---|
999 | vt0 = 10000000 * (curPerfCounter.QuadPart |
---|
1000 | - timeInfo.perfCounterLastCall.QuadPart) |
---|
1001 | / timeInfo.curCounterFreq.QuadPart |
---|
1002 | + timeInfo.fileTimeLastCall.QuadPart; |
---|
1003 | vt1 = 20000000 + curFileTime.QuadPart; |
---|
1004 | |
---|
1005 | /* |
---|
1006 | * If we've gotten more than a second away from system time, then drifting |
---|
1007 | * the clock is going to be pretty hopeless. Just let it jump. Otherwise, |
---|
1008 | * compute the drift frequency and fill in everything. |
---|
1009 | */ |
---|
1010 | |
---|
1011 | tdiff = vt0 - curFileTime.QuadPart; |
---|
1012 | if (tdiff > 10000000 || tdiff < -10000000) { |
---|
1013 | timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart; |
---|
1014 | timeInfo.curCounterFreq.QuadPart = estFreq; |
---|
1015 | } else { |
---|
1016 | driftFreq = estFreq * 20000000 / (vt1 - vt0); |
---|
1017 | |
---|
1018 | if (driftFreq > 1003*estFreq/1000) { |
---|
1019 | driftFreq = 1003*estFreq/1000; |
---|
1020 | } else if (driftFreq < 997*estFreq/1000) { |
---|
1021 | driftFreq = 997*estFreq/1000; |
---|
1022 | } |
---|
1023 | |
---|
1024 | timeInfo.fileTimeLastCall.QuadPart = vt0; |
---|
1025 | timeInfo.curCounterFreq.QuadPart = driftFreq; |
---|
1026 | } |
---|
1027 | |
---|
1028 | timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart; |
---|
1029 | |
---|
1030 | LeaveCriticalSection(&timeInfo.cs); |
---|
1031 | } |
---|
1032 | |
---|
1033 | /* |
---|
1034 | *---------------------------------------------------------------------- |
---|
1035 | * |
---|
1036 | * ResetCounterSamples -- |
---|
1037 | * |
---|
1038 | * Fills the sample arrays in 'timeInfo' with dummy values that will |
---|
1039 | * yield the current performance counter and frequency. |
---|
1040 | * |
---|
1041 | * Results: |
---|
1042 | * None. |
---|
1043 | * |
---|
1044 | * Side effects: |
---|
1045 | * The array of samples is filled in so that it appears that there are |
---|
1046 | * SAMPLES samples at one-second intervals, separated by precisely the |
---|
1047 | * given frequency. |
---|
1048 | * |
---|
1049 | *---------------------------------------------------------------------- |
---|
1050 | */ |
---|
1051 | |
---|
1052 | static void |
---|
1053 | ResetCounterSamples( |
---|
1054 | Tcl_WideUInt fileTime, /* Current file time */ |
---|
1055 | Tcl_WideInt perfCounter, /* Current performance counter */ |
---|
1056 | Tcl_WideInt perfFreq) /* Target performance frequency */ |
---|
1057 | { |
---|
1058 | int i; |
---|
1059 | for (i=SAMPLES-1 ; i>=0 ; --i) { |
---|
1060 | timeInfo.perfCounterSample[i] = perfCounter; |
---|
1061 | timeInfo.fileTimeSample[i] = fileTime; |
---|
1062 | perfCounter -= perfFreq; |
---|
1063 | fileTime -= 10000000; |
---|
1064 | } |
---|
1065 | timeInfo.sampleNo = 0; |
---|
1066 | } |
---|
1067 | |
---|
1068 | /* |
---|
1069 | *---------------------------------------------------------------------- |
---|
1070 | * |
---|
1071 | * AccumulateSample -- |
---|
1072 | * |
---|
1073 | * Updates the circular buffer of performance counter and system time |
---|
1074 | * samples with a new data point. |
---|
1075 | * |
---|
1076 | * Results: |
---|
1077 | * None. |
---|
1078 | * |
---|
1079 | * Side effects: |
---|
1080 | * The new data point replaces the oldest point in the circular buffer, |
---|
1081 | * and the descriptive statistics are updated to accumulate the new |
---|
1082 | * point. |
---|
1083 | * |
---|
1084 | * Several things may have gone wrong here that have to be checked for. |
---|
1085 | * (1) The performance counter may have jumped. |
---|
1086 | * (2) The system clock may have been reset. |
---|
1087 | * |
---|
1088 | * In either case, we'll need to reinitialize the circular buffer with samples |
---|
1089 | * relative to the current system time and the NOMINAL performance frequency |
---|
1090 | * (not the actual, because the actual has probably run slow in the first |
---|
1091 | * case). |
---|
1092 | */ |
---|
1093 | |
---|
1094 | static Tcl_WideInt |
---|
1095 | AccumulateSample( |
---|
1096 | Tcl_WideInt perfCounter, |
---|
1097 | Tcl_WideUInt fileTime) |
---|
1098 | { |
---|
1099 | Tcl_WideUInt workFTSample; /* File time sample being removed from or |
---|
1100 | * added to the circular buffer. */ |
---|
1101 | Tcl_WideInt workPCSample; /* Performance counter sample being removed |
---|
1102 | * from or added to the circular buffer. */ |
---|
1103 | Tcl_WideUInt lastFTSample; /* Last file time sample recorded */ |
---|
1104 | Tcl_WideInt lastPCSample; /* Last performance counter sample recorded */ |
---|
1105 | Tcl_WideInt FTdiff; /* Difference between last FT and current */ |
---|
1106 | Tcl_WideInt PCdiff; /* Difference between last PC and current */ |
---|
1107 | Tcl_WideInt estFreq; /* Estimated performance counter frequency */ |
---|
1108 | |
---|
1109 | /* |
---|
1110 | * Test for jumps and reset the samples if we have one. |
---|
1111 | */ |
---|
1112 | |
---|
1113 | if (timeInfo.sampleNo == 0) { |
---|
1114 | lastPCSample = |
---|
1115 | timeInfo.perfCounterSample[timeInfo.sampleNo + SAMPLES - 1]; |
---|
1116 | lastFTSample = |
---|
1117 | timeInfo.fileTimeSample[timeInfo.sampleNo + SAMPLES - 1]; |
---|
1118 | } else { |
---|
1119 | lastPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo - 1]; |
---|
1120 | lastFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo - 1]; |
---|
1121 | } |
---|
1122 | |
---|
1123 | PCdiff = perfCounter - lastPCSample; |
---|
1124 | FTdiff = fileTime - lastFTSample; |
---|
1125 | if (PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10 |
---|
1126 | || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10 |
---|
1127 | || FTdiff < 9000000 || FTdiff > 11000000) { |
---|
1128 | ResetCounterSamples(fileTime, perfCounter, |
---|
1129 | timeInfo.nominalFreq.QuadPart); |
---|
1130 | return timeInfo.nominalFreq.QuadPart; |
---|
1131 | } else { |
---|
1132 | /* |
---|
1133 | * Estimate the frequency. |
---|
1134 | */ |
---|
1135 | |
---|
1136 | workPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo]; |
---|
1137 | workFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo]; |
---|
1138 | estFreq = 10000000 * (perfCounter - workPCSample) |
---|
1139 | / (fileTime - workFTSample); |
---|
1140 | timeInfo.perfCounterSample[timeInfo.sampleNo] = perfCounter; |
---|
1141 | timeInfo.fileTimeSample[timeInfo.sampleNo] = (Tcl_WideInt) fileTime; |
---|
1142 | |
---|
1143 | /* |
---|
1144 | * Advance the sample number. |
---|
1145 | */ |
---|
1146 | |
---|
1147 | if (++timeInfo.sampleNo >= SAMPLES) { |
---|
1148 | timeInfo.sampleNo = 0; |
---|
1149 | } |
---|
1150 | |
---|
1151 | return estFreq; |
---|
1152 | } |
---|
1153 | } |
---|
1154 | |
---|
1155 | /* |
---|
1156 | *---------------------------------------------------------------------- |
---|
1157 | * |
---|
1158 | * TclpGmtime -- |
---|
1159 | * |
---|
1160 | * Wrapper around the 'gmtime' library function to make it thread safe. |
---|
1161 | * |
---|
1162 | * Results: |
---|
1163 | * Returns a pointer to a 'struct tm' in thread-specific data. |
---|
1164 | * |
---|
1165 | * Side effects: |
---|
1166 | * Invokes gmtime or gmtime_r as appropriate. |
---|
1167 | * |
---|
1168 | *---------------------------------------------------------------------- |
---|
1169 | */ |
---|
1170 | |
---|
1171 | struct tm * |
---|
1172 | TclpGmtime( |
---|
1173 | CONST time_t *timePtr) /* Pointer to the number of seconds since the |
---|
1174 | * local system's epoch */ |
---|
1175 | { |
---|
1176 | /* |
---|
1177 | * The MS implementation of gmtime is thread safe because it returns the |
---|
1178 | * time in a block of thread-local storage, and Windows does not provide a |
---|
1179 | * Posix gmtime_r function. |
---|
1180 | */ |
---|
1181 | |
---|
1182 | return gmtime(timePtr); |
---|
1183 | } |
---|
1184 | |
---|
1185 | /* |
---|
1186 | *---------------------------------------------------------------------- |
---|
1187 | * |
---|
1188 | * TclpLocaltime -- |
---|
1189 | * |
---|
1190 | * Wrapper around the 'localtime' library function to make it thread |
---|
1191 | * safe. |
---|
1192 | * |
---|
1193 | * Results: |
---|
1194 | * Returns a pointer to a 'struct tm' in thread-specific data. |
---|
1195 | * |
---|
1196 | * Side effects: |
---|
1197 | * Invokes localtime or localtime_r as appropriate. |
---|
1198 | * |
---|
1199 | *---------------------------------------------------------------------- |
---|
1200 | */ |
---|
1201 | |
---|
1202 | struct tm * |
---|
1203 | TclpLocaltime( |
---|
1204 | CONST time_t *timePtr) /* Pointer to the number of seconds since the |
---|
1205 | * local system's epoch */ |
---|
1206 | |
---|
1207 | { |
---|
1208 | /* |
---|
1209 | * The MS implementation of localtime is thread safe because it returns |
---|
1210 | * the time in a block of thread-local storage, and Windows does not |
---|
1211 | * provide a Posix localtime_r function. |
---|
1212 | */ |
---|
1213 | |
---|
1214 | return localtime(timePtr); |
---|
1215 | } |
---|
1216 | |
---|
1217 | /* |
---|
1218 | *---------------------------------------------------------------------- |
---|
1219 | * |
---|
1220 | * Tcl_SetTimeProc -- |
---|
1221 | * |
---|
1222 | * TIP #233 (Virtualized Time): Registers two handlers for the |
---|
1223 | * virtualization of Tcl's access to time information. |
---|
1224 | * |
---|
1225 | * Results: |
---|
1226 | * None. |
---|
1227 | * |
---|
1228 | * Side effects: |
---|
1229 | * Remembers the handlers, alters core behaviour. |
---|
1230 | * |
---|
1231 | *---------------------------------------------------------------------- |
---|
1232 | */ |
---|
1233 | |
---|
1234 | void |
---|
1235 | Tcl_SetTimeProc( |
---|
1236 | Tcl_GetTimeProc *getProc, |
---|
1237 | Tcl_ScaleTimeProc *scaleProc, |
---|
1238 | ClientData clientData) |
---|
1239 | { |
---|
1240 | tclGetTimeProcPtr = getProc; |
---|
1241 | tclScaleTimeProcPtr = scaleProc; |
---|
1242 | tclTimeClientData = clientData; |
---|
1243 | } |
---|
1244 | |
---|
1245 | /* |
---|
1246 | *---------------------------------------------------------------------- |
---|
1247 | * |
---|
1248 | * Tcl_QueryTimeProc -- |
---|
1249 | * |
---|
1250 | * TIP #233 (Virtualized Time): Query which time handlers are registered. |
---|
1251 | * |
---|
1252 | * Results: |
---|
1253 | * None. |
---|
1254 | * |
---|
1255 | * Side effects: |
---|
1256 | * None. |
---|
1257 | * |
---|
1258 | *---------------------------------------------------------------------- |
---|
1259 | */ |
---|
1260 | |
---|
1261 | void |
---|
1262 | Tcl_QueryTimeProc( |
---|
1263 | Tcl_GetTimeProc **getProc, |
---|
1264 | Tcl_ScaleTimeProc **scaleProc, |
---|
1265 | ClientData *clientData) |
---|
1266 | { |
---|
1267 | if (getProc) { |
---|
1268 | *getProc = tclGetTimeProcPtr; |
---|
1269 | } |
---|
1270 | if (scaleProc) { |
---|
1271 | *scaleProc = tclScaleTimeProcPtr; |
---|
1272 | } |
---|
1273 | if (clientData) { |
---|
1274 | *clientData = tclTimeClientData; |
---|
1275 | } |
---|
1276 | } |
---|
1277 | |
---|
1278 | /* |
---|
1279 | * Local Variables: |
---|
1280 | * mode: c |
---|
1281 | * c-basic-offset: 4 |
---|
1282 | * fill-column: 78 |
---|
1283 | * End: |
---|
1284 | */ |
---|