js/src/prmjtime.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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 }

mercurial