Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* PR time code. */
9 #include "prmjtime.h"
11 #include "mozilla/MathAlgorithms.h"
13 #ifdef SOLARIS
14 #define _REENTRANT 1
15 #endif
16 #include <string.h>
17 #include <time.h>
19 #include "jstypes.h"
20 #include "jsutil.h"
22 #define PRMJ_DO_MILLISECONDS 1
24 #ifdef XP_WIN
25 #include <windef.h>
26 #include <winbase.h>
27 #include <mmsystem.h> /* for timeBegin/EndPeriod */
28 /* VC++ 8.0 or later */
29 #if _MSC_VER >= 1400
30 #define NS_HAVE_INVALID_PARAMETER_HANDLER 1
31 #endif
32 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
33 #include <crtdbg.h> /* for _CrtSetReportMode */
34 #include <stdlib.h> /* for _set_invalid_parameter_handler */
35 #endif
37 #ifdef JS_THREADSAFE
38 #include "prinit.h"
39 #endif
41 #endif
43 #ifdef XP_UNIX
45 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
46 extern int gettimeofday(struct timeval *tv);
47 #endif
49 #include <sys/time.h>
51 #endif /* XP_UNIX */
53 #define PRMJ_YEAR_DAYS 365L
54 #define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
55 #define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
56 #define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
57 #define PRMJ_HOUR_SECONDS 3600L
58 #define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
59 #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
60 #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
62 /* Constants for GMT offset from 1970 */
63 #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
64 #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
66 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
67 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
69 #if defined(XP_WIN)
71 static const int64_t win2un = 0x19DB1DED53E8000;
73 #define FILETIME2INT64(ft) (((int64_t)ft.dwHighDateTime) << 32LL | (int64_t)ft.dwLowDateTime)
75 typedef struct CalibrationData {
76 long double freq; /* The performance counter frequency */
77 long double offset; /* The low res 'epoch' */
78 long double timer_offset; /* The high res 'epoch' */
80 /* The last high res time that we returned since recalibrating */
81 int64_t last;
83 bool calibrated;
85 #ifdef JS_THREADSAFE
86 CRITICAL_SECTION data_lock;
87 CRITICAL_SECTION calibration_lock;
88 #endif
89 } CalibrationData;
91 static CalibrationData calibration = { 0 };
93 static void
94 NowCalibrate()
95 {
96 FILETIME ft, ftStart;
97 LARGE_INTEGER liFreq, now;
99 if (calibration.freq == 0.0) {
100 if(!QueryPerformanceFrequency(&liFreq)) {
101 /* High-performance timer is unavailable */
102 calibration.freq = -1.0;
103 } else {
104 calibration.freq = (long double) liFreq.QuadPart;
105 }
106 }
107 if (calibration.freq > 0.0) {
108 int64_t calibrationDelta = 0;
110 /* By wrapping a timeBegin/EndPeriod pair of calls around this loop,
111 the loop seems to take much less time (1 ms vs 15ms) on Vista. */
112 timeBeginPeriod(1);
113 GetSystemTimeAsFileTime(&ftStart);
114 do {
115 GetSystemTimeAsFileTime(&ft);
116 } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
117 timeEndPeriod(1);
119 /*
120 calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
121 fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
122 */
124 QueryPerformanceCounter(&now);
126 calibration.offset = (long double) FILETIME2INT64(ft);
127 calibration.timer_offset = (long double) now.QuadPart;
129 /* The windows epoch is around 1600. The unix epoch is around
130 1970. win2un is the difference (in windows time units which
131 are 10 times more highres than the JS time unit) */
132 calibration.offset -= win2un;
133 calibration.offset *= 0.1;
134 calibration.last = 0;
136 calibration.calibrated = true;
137 }
138 }
140 #define CALIBRATIONLOCK_SPINCOUNT 0
141 #define DATALOCK_SPINCOUNT 4096
142 #define LASTLOCK_SPINCOUNT 4096
144 #ifdef JS_THREADSAFE
145 static PRStatus
146 NowInit(void)
147 {
148 memset(&calibration, 0, sizeof(calibration));
149 NowCalibrate();
150 InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
151 InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
152 return PR_SUCCESS;
153 }
155 void
156 PRMJ_NowShutdown()
157 {
158 DeleteCriticalSection(&calibration.calibration_lock);
159 DeleteCriticalSection(&calibration.data_lock);
160 }
162 #define MUTEX_LOCK(m) EnterCriticalSection(m)
163 #define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
164 #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
165 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
167 static PRCallOnceType calibrationOnce = { 0 };
169 #else
171 #define MUTEX_LOCK(m)
172 #define MUTEX_TRYLOCK(m) 1
173 #define MUTEX_UNLOCK(m)
174 #define MUTEX_SETSPINCOUNT(m, c)
176 #endif
178 #endif /* XP_WIN */
181 #if defined(XP_UNIX)
182 int64_t
183 PRMJ_Now(void)
184 {
185 struct timeval tv;
187 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
188 gettimeofday(&tv);
189 #else
190 gettimeofday(&tv, 0);
191 #endif /* _SVID_GETTOD */
193 return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
194 }
196 #else
197 /*
199 Win32 python-esque pseudo code
200 Please see bug 363258 for why the win32 timing code is so complex.
202 calibration mutex : Win32CriticalSection(spincount=0)
203 data mutex : Win32CriticalSection(spincount=4096)
205 def NowInit():
206 init mutexes
207 PRMJ_NowCalibration()
209 def NowCalibration():
210 expensive up-to-15ms call
212 def PRMJ_Now():
213 returnedTime = 0
214 needCalibration = False
215 cachedOffset = 0.0
216 calibrated = False
217 PR_CallOnce(PRMJ_NowInit)
218 do
219 if not global.calibrated or needCalibration:
220 acquire calibration mutex
221 acquire data mutex
223 // Only recalibrate if someone didn't already
224 if cachedOffset == calibration.offset:
225 // Have all waiting threads immediately wait
226 set data mutex spin count = 0
227 PRMJ_NowCalibrate()
228 calibrated = 1
230 set data mutex spin count = default
231 release data mutex
232 release calibration mutex
234 calculate lowres time
236 if highres timer available:
237 acquire data mutex
238 calculate highres time
239 cachedOffset = calibration.offset
240 highres time = calibration.last = max(highres time, calibration.last)
241 release data mutex
243 get kernel tick interval
245 if abs(highres - lowres) < kernel tick:
246 returnedTime = highres time
247 needCalibration = False
248 else:
249 if calibrated:
250 returnedTime = lowres
251 needCalibration = False
252 else:
253 needCalibration = True
254 else:
255 returnedTime = lowres
256 while needCalibration
258 */
260 int64_t
261 PRMJ_Now(void)
262 {
263 static int nCalls = 0;
264 long double lowresTime, highresTimerValue;
265 FILETIME ft;
266 LARGE_INTEGER now;
267 bool calibrated = false;
268 bool needsCalibration = false;
269 int64_t returnedTime;
270 long double cachedOffset = 0.0;
272 /* For non threadsafe platforms, NowInit is not necessary */
273 #ifdef JS_THREADSAFE
274 PR_CallOnce(&calibrationOnce, NowInit);
275 #endif
276 do {
277 if (!calibration.calibrated || needsCalibration) {
278 MUTEX_LOCK(&calibration.calibration_lock);
279 MUTEX_LOCK(&calibration.data_lock);
281 /* Recalibrate only if no one else did before us */
282 if(calibration.offset == cachedOffset) {
283 /* Since calibration can take a while, make any other
284 threads immediately wait */
285 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
287 NowCalibrate();
289 calibrated = true;
291 /* Restore spin count */
292 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
293 }
294 MUTEX_UNLOCK(&calibration.data_lock);
295 MUTEX_UNLOCK(&calibration.calibration_lock);
296 }
299 /* Calculate a low resolution time */
300 GetSystemTimeAsFileTime(&ft);
301 lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
303 if (calibration.freq > 0.0) {
304 long double highresTime, diff;
306 DWORD timeAdjustment, timeIncrement;
307 BOOL timeAdjustmentDisabled;
309 /* Default to 15.625 ms if the syscall fails */
310 long double skewThreshold = 15625.25;
311 /* Grab high resolution time */
312 QueryPerformanceCounter(&now);
313 highresTimerValue = (long double)now.QuadPart;
315 MUTEX_LOCK(&calibration.data_lock);
316 highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
317 (highresTimerValue-calibration.timer_offset)/calibration.freq;
318 cachedOffset = calibration.offset;
320 /* On some dual processor/core systems, we might get an earlier time
321 so we cache the last time that we returned */
322 calibration.last = js::Max(calibration.last, int64_t(highresTime));
323 returnedTime = calibration.last;
324 MUTEX_UNLOCK(&calibration.data_lock);
326 /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
327 if (GetSystemTimeAdjustment(&timeAdjustment,
328 &timeIncrement,
329 &timeAdjustmentDisabled)) {
330 if (timeAdjustmentDisabled) {
331 /* timeAdjustment is in units of 100ns */
332 skewThreshold = timeAdjustment/10.0;
333 } else {
334 /* timeIncrement is in units of 100ns */
335 skewThreshold = timeIncrement/10.0;
336 }
337 }
339 /* Check for clock skew */
340 diff = lowresTime - highresTime;
342 /* For some reason that I have not determined, the skew can be
343 up to twice a kernel tick. This does not seem to happen by
344 itself, but I have only seen it triggered by another program
345 doing some kind of file I/O. The symptoms are a negative diff
346 followed by an equally large positive diff. */
347 if (mozilla::Abs(diff) > 2 * skewThreshold) {
348 /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/
350 if (calibrated) {
351 /* If we already calibrated once this instance, and the
352 clock is still skewed, then either the processor(s) are
353 wildly changing clockspeed or the system is so busy that
354 we get switched out for long periods of time. In either
355 case, it would be infeasible to make use of high
356 resolution results for anything, so let's resort to old
357 behavior for this call. It's possible that in the
358 future, the user will want the high resolution timer, so
359 we don't disable it entirely. */
360 returnedTime = int64_t(lowresTime);
361 needsCalibration = false;
362 } else {
363 /* It is possible that when we recalibrate, we will return a
364 value less than what we have returned before; this is
365 unavoidable. We cannot tell the different between a
366 faulty QueryPerformanceCounter implementation and user
367 changes to the operating system time. Since we must
368 respect user changes to the operating system time, we
369 cannot maintain the invariant that Date.now() never
370 decreases; the old implementation has this behavior as
371 well. */
372 needsCalibration = true;
373 }
374 } else {
375 /* No detectable clock skew */
376 returnedTime = int64_t(highresTime);
377 needsCalibration = false;
378 }
379 } else {
380 /* No high resolution timer is available, so fall back */
381 returnedTime = int64_t(lowresTime);
382 }
383 } while (needsCalibration);
385 return returnedTime;
386 }
387 #endif
389 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
390 static void
391 PRMJ_InvalidParameterHandler(const wchar_t *expression,
392 const wchar_t *function,
393 const wchar_t *file,
394 unsigned int line,
395 uintptr_t pReserved)
396 {
397 /* empty */
398 }
399 #endif
401 /* Format a time value into a buffer. Same semantics as strftime() */
402 size_t
403 PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
404 {
405 size_t result = 0;
406 #if defined(XP_UNIX) || defined(XP_WIN)
407 struct tm a;
408 int fake_tm_year = 0;
409 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
410 _invalid_parameter_handler oldHandler;
411 int oldReportMode;
412 #endif
414 memset(&a, 0, sizeof(struct tm));
416 a.tm_sec = prtm->tm_sec;
417 a.tm_min = prtm->tm_min;
418 a.tm_hour = prtm->tm_hour;
419 a.tm_mday = prtm->tm_mday;
420 a.tm_mon = prtm->tm_mon;
421 a.tm_wday = prtm->tm_wday;
423 /*
424 * On systems where |struct tm| has members tm_gmtoff and tm_zone, we
425 * must fill in those values, or else strftime will return wrong results
426 * (e.g., bug 511726, bug 554338).
427 */
428 #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
429 {
430 /*
431 * Fill out |td| to the time represented by |prtm|, leaving the
432 * timezone fields zeroed out. localtime_r will then fill in the
433 * timezone fields for that local time according to the system's
434 * timezone parameters.
435 */
436 struct tm td;
437 memset(&td, 0, sizeof(td));
438 td.tm_sec = prtm->tm_sec;
439 td.tm_min = prtm->tm_min;
440 td.tm_hour = prtm->tm_hour;
441 td.tm_mday = prtm->tm_mday;
442 td.tm_mon = prtm->tm_mon;
443 td.tm_wday = prtm->tm_wday;
444 td.tm_year = prtm->tm_year - 1900;
445 td.tm_yday = prtm->tm_yday;
446 td.tm_isdst = prtm->tm_isdst;
447 time_t t = mktime(&td);
448 localtime_r(&t, &td);
450 a.tm_gmtoff = td.tm_gmtoff;
451 a.tm_zone = td.tm_zone;
452 }
453 #endif
455 /*
456 * Years before 1900 and after 9999 cause strftime() to abort on Windows.
457 * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
458 * replace matching substrings in the strftime() result with the real year.
459 * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
460 * year formats (%y) work correctly (since we won't find the fake year
461 * in that case).
462 * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
463 * See bug 327869.
464 */
465 #define FAKE_YEAR_BASE 9900
466 if (prtm->tm_year < 1900 || prtm->tm_year > 9999) {
467 fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100;
468 a.tm_year = fake_tm_year - 1900;
469 }
470 else {
471 a.tm_year = prtm->tm_year - 1900;
472 }
473 a.tm_yday = prtm->tm_yday;
474 a.tm_isdst = prtm->tm_isdst;
476 /*
477 * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
478 * are null. This doesn't quite work, though - the timezone is off by
479 * tzoff + dst. (And mktime seems to return -1 for the exact dst
480 * changeover time.)
481 */
483 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
484 oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
485 oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
486 #endif
488 result = strftime(buf, buflen, fmt, &a);
490 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
491 _set_invalid_parameter_handler(oldHandler);
492 _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
493 #endif
495 if (fake_tm_year && result) {
496 char real_year[16];
497 char fake_year[16];
498 size_t real_year_len;
499 size_t fake_year_len;
500 char* p;
502 sprintf(real_year, "%d", prtm->tm_year);
503 real_year_len = strlen(real_year);
504 sprintf(fake_year, "%d", fake_tm_year);
505 fake_year_len = strlen(fake_year);
507 /* Replace the fake year in the result with the real year. */
508 for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) {
509 size_t new_result = result + real_year_len - fake_year_len;
510 if ((int)new_result >= buflen) {
511 return 0;
512 }
513 memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len));
514 memcpy(p, real_year, real_year_len);
515 result = new_result;
516 *(buf + result) = '\0';
517 }
518 }
519 #endif
520 return result;
521 }