LCOV - code coverage report
Current view: directory - js/src - prmjtime.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 129 103 79.8 %
Date: 2012-06-02 Functions: 7 7 100.0 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla Communicator client code, released
      17                 :  * March 31, 1998.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Netscape Communications Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : /*
      41                 :  * PR time code.
      42                 :  */
      43                 : #ifdef SOLARIS
      44                 : #define _REENTRANT 1
      45                 : #endif
      46                 : #include <string.h>
      47                 : #include <time.h>
      48                 : 
      49                 : #include "jstypes.h"
      50                 : #include "jsutil.h"
      51                 : 
      52                 : #include "jsprf.h"
      53                 : #include "jslock.h"
      54                 : #include "prmjtime.h"
      55                 : 
      56                 : #define PRMJ_DO_MILLISECONDS 1
      57                 : 
      58                 : #ifdef XP_OS2
      59                 : #include <sys/timeb.h>
      60                 : #endif
      61                 : #ifdef XP_WIN
      62                 : #include <windef.h>
      63                 : #include <winbase.h>
      64                 : #include <math.h>     /* for fabs */
      65                 : #include <mmsystem.h> /* for timeBegin/EndPeriod */
      66                 : /* VC++ 8.0 or later */
      67                 : #if _MSC_VER >= 1400
      68                 : #define NS_HAVE_INVALID_PARAMETER_HANDLER 1
      69                 : #endif
      70                 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
      71                 : #include <stdlib.h>   /* for _set_invalid_parameter_handler */
      72                 : #include <crtdbg.h>   /* for _CrtSetReportMode */
      73                 : #endif
      74                 : 
      75                 : #ifdef JS_THREADSAFE
      76                 : #include <prinit.h>
      77                 : #endif
      78                 : 
      79                 : #endif
      80                 : 
      81                 : #ifdef XP_UNIX
      82                 : 
      83                 : #ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
      84                 : extern int gettimeofday(struct timeval *tv);
      85                 : #endif
      86                 : 
      87                 : #include <sys/time.h>
      88                 : 
      89                 : #endif /* XP_UNIX */
      90                 : 
      91                 : #define PRMJ_YEAR_DAYS 365L
      92                 : #define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
      93                 : #define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
      94                 : #define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
      95                 : #define PRMJ_HOUR_SECONDS  3600L
      96                 : #define PRMJ_DAY_SECONDS  (24L * PRMJ_HOUR_SECONDS)
      97                 : #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
      98                 : #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
      99                 : 
     100                 : /* Get the local time. localtime_r is preferred as it is reentrant. */
     101                 : static inline bool
     102           22342 : ComputeLocalTime(time_t local, struct tm *ptm)
     103                 : {
     104                 : #ifdef HAVE_LOCALTIME_R
     105           22342 :     return localtime_r(&local, ptm);
     106                 : #else
     107                 :     struct tm *otm = localtime(&local);
     108                 :     if (!otm)
     109                 :         return false;
     110                 :     *ptm = *otm;
     111                 :     return true;
     112                 : #endif
     113                 : }
     114                 : 
     115                 : /*
     116                 :  * get the difference in seconds between this time zone and UTC (GMT)
     117                 :  */
     118                 : int32_t
     119           12360 : PRMJ_LocalGMTDifference()
     120                 : {
     121                 : #if defined(XP_WIN)
     122                 :     /* Windows does not follow POSIX. Updates to the
     123                 :      * TZ environment variable are not reflected
     124                 :      * immediately on that platform as they are
     125                 :      * on UNIX systems without this call.
     126                 :      */
     127                 :     _tzset();
     128                 : #endif
     129                 : 
     130                 :     /*
     131                 :      * Get the difference between this time zone and GMT, by checking the local
     132                 :      * time for days 0 and 180 of 1970, using a date for which daylight savings
     133                 :      * time was not in effect.
     134                 :      */
     135           12360 :     int day = 0;
     136                 :     struct tm tm;
     137                 : 
     138           12360 :     if (!ComputeLocalTime(0, &tm))
     139               0 :         return 0;
     140           12360 :     if (tm.tm_isdst > 0) {
     141               0 :         day = 180;
     142               0 :         if (!ComputeLocalTime(PRMJ_DAY_SECONDS * day, &tm))
     143               0 :             return 0;
     144                 :     }
     145                 : 
     146           12360 :     int time = (tm.tm_hour * 3600) + (tm.tm_min * 60) + tm.tm_sec;
     147           12360 :     time = PRMJ_DAY_SECONDS - time;
     148                 : 
     149           12360 :     if (tm.tm_yday == day)
     150               0 :         time -= PRMJ_DAY_SECONDS;
     151                 : 
     152           12360 :     return time;
     153                 : }
     154                 : 
     155                 : /* Constants for GMT offset from 1970 */
     156                 : #define G1970GMTMICROHI        0x00dcdcad /* micro secs to 1970 hi */
     157                 : #define G1970GMTMICROLOW       0x8b3fa000 /* micro secs to 1970 low */
     158                 : 
     159                 : #define G2037GMTMICROHI        0x00e45fab /* micro secs to 2037 high */
     160                 : #define G2037GMTMICROLOW       0x7a238000 /* micro secs to 2037 low */
     161                 : 
     162                 : #ifdef HAVE_SYSTEMTIMETOFILETIME
     163                 : 
     164                 : static const int64_t win2un = 0x19DB1DED53E8000;
     165                 : 
     166                 : #define FILETIME2INT64(ft) (((int64_t)ft.dwHighDateTime) << 32LL | (int64_t)ft.dwLowDateTime)
     167                 : 
     168                 : #endif
     169                 : 
     170                 : #if defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME)
     171                 : 
     172                 : #if defined(HAVE_GETSYSTEMTIMEASFILETIME)
     173                 : inline void
     174                 : LowResTime(LPFILETIME lpft)
     175                 : {
     176                 :     GetSystemTimeAsFileTime(lpft);
     177                 : }
     178                 : #elif defined(HAVE_SYSTEMTIMETOFILETIME)
     179                 : inline void
     180                 : LowResTime(LPFILETIME lpft)
     181                 : {
     182                 :     GetCurrentFT(lpft);
     183                 : }
     184                 : #else
     185                 : #error "No implementation of PRMJ_Now was selected."
     186                 : #endif
     187                 : 
     188                 : typedef struct CalibrationData {
     189                 :     long double freq;         /* The performance counter frequency */
     190                 :     long double offset;       /* The low res 'epoch' */
     191                 :     long double timer_offset; /* The high res 'epoch' */
     192                 : 
     193                 :     /* The last high res time that we returned since recalibrating */
     194                 :     int64_t last;
     195                 : 
     196                 :     JSBool calibrated;
     197                 : 
     198                 : #ifdef JS_THREADSAFE
     199                 :     CRITICAL_SECTION data_lock;
     200                 :     CRITICAL_SECTION calibration_lock;
     201                 : #endif
     202                 : } CalibrationData;
     203                 : 
     204                 : static CalibrationData calibration = { 0 };
     205                 : 
     206                 : static void
     207                 : NowCalibrate()
     208                 : {
     209                 :     FILETIME ft, ftStart;
     210                 :     LARGE_INTEGER liFreq, now;
     211                 : 
     212                 :     if (calibration.freq == 0.0) {
     213                 :         if(!QueryPerformanceFrequency(&liFreq)) {
     214                 :             /* High-performance timer is unavailable */
     215                 :             calibration.freq = -1.0;
     216                 :         } else {
     217                 :             calibration.freq = (long double) liFreq.QuadPart;
     218                 :         }
     219                 :     }
     220                 :     if (calibration.freq > 0.0) {
     221                 :         int64_t calibrationDelta = 0;
     222                 : 
     223                 :         /* By wrapping a timeBegin/EndPeriod pair of calls around this loop,
     224                 :            the loop seems to take much less time (1 ms vs 15ms) on Vista. */
     225                 :         timeBeginPeriod(1);
     226                 :         LowResTime(&ftStart);
     227                 :         do {
     228                 :             LowResTime(&ft);
     229                 :         } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
     230                 :         timeEndPeriod(1);
     231                 : 
     232                 :         /*
     233                 :         calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
     234                 :         fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
     235                 :         */
     236                 : 
     237                 :         QueryPerformanceCounter(&now);
     238                 : 
     239                 :         calibration.offset = (long double) FILETIME2INT64(ft);
     240                 :         calibration.timer_offset = (long double) now.QuadPart;
     241                 : 
     242                 :         /* The windows epoch is around 1600. The unix epoch is around
     243                 :            1970. win2un is the difference (in windows time units which
     244                 :            are 10 times more highres than the JS time unit) */
     245                 :         calibration.offset -= win2un;
     246                 :         calibration.offset *= 0.1;
     247                 :         calibration.last = 0;
     248                 : 
     249                 :         calibration.calibrated = JS_TRUE;
     250                 :     }
     251                 : }
     252                 : 
     253                 : #define CALIBRATIONLOCK_SPINCOUNT 0
     254                 : #define DATALOCK_SPINCOUNT 4096
     255                 : #define LASTLOCK_SPINCOUNT 4096
     256                 : 
     257                 : #ifdef JS_THREADSAFE
     258                 : static PRStatus
     259                 : NowInit(void)
     260                 : {
     261                 :     memset(&calibration, 0, sizeof(calibration));
     262                 :     NowCalibrate();
     263                 :     InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
     264                 :     InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
     265                 :     return PR_SUCCESS;
     266                 : }
     267                 : 
     268                 : void
     269                 : PRMJ_NowShutdown()
     270                 : {
     271                 :     DeleteCriticalSection(&calibration.calibration_lock);
     272                 :     DeleteCriticalSection(&calibration.data_lock);
     273                 : }
     274                 : 
     275                 : #define MUTEX_LOCK(m) EnterCriticalSection(m)
     276                 : #define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
     277                 : #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
     278                 : #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
     279                 : 
     280                 : static PRCallOnceType calibrationOnce = { 0 };
     281                 : 
     282                 : #else
     283                 : 
     284                 : #define MUTEX_LOCK(m)
     285                 : #define MUTEX_TRYLOCK(m) 1
     286                 : #define MUTEX_UNLOCK(m)
     287                 : #define MUTEX_SETSPINCOUNT(m, c)
     288                 : 
     289                 : #endif
     290                 : 
     291                 : #endif /* HAVE_GETSYSTEMTIMEASFILETIME */
     292                 : 
     293                 : 
     294                 : #if defined(XP_OS2)
     295                 : int64_t
     296                 : PRMJ_Now(void)
     297                 : {
     298                 :     struct timeb b;
     299                 :     ftime(&b);
     300                 :     return (int64_t(b.time) * PRMJ_USEC_PER_SEC) + (int64_t(b.millitm) * PRMJ_USEC_PER_MSEC);
     301                 : }
     302                 : 
     303                 : #elif defined(XP_UNIX)
     304                 : int64_t
     305        10835474 : PRMJ_Now(void)
     306                 : {
     307                 :     struct timeval tv;
     308                 : 
     309                 : #ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
     310                 :     gettimeofday(&tv);
     311                 : #else
     312        10835474 :     gettimeofday(&tv, 0);
     313                 : #endif /* _SVID_GETTOD */
     314                 : 
     315        10835474 :     return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
     316                 : }
     317                 : 
     318                 : #else
     319                 : /*
     320                 : 
     321                 : Win32 python-esque pseudo code
     322                 : Please see bug 363258 for why the win32 timing code is so complex.
     323                 : 
     324                 : calibration mutex : Win32CriticalSection(spincount=0)
     325                 : data mutex : Win32CriticalSection(spincount=4096)
     326                 : 
     327                 : def NowInit():
     328                 :   init mutexes
     329                 :   PRMJ_NowCalibration()
     330                 : 
     331                 : def NowCalibration():
     332                 :   expensive up-to-15ms call
     333                 : 
     334                 : def PRMJ_Now():
     335                 :   returnedTime = 0
     336                 :   needCalibration = False
     337                 :   cachedOffset = 0.0
     338                 :   calibrated = False
     339                 :   PR_CallOnce(PRMJ_NowInit)
     340                 :   do
     341                 :     if not global.calibrated or needCalibration:
     342                 :       acquire calibration mutex
     343                 :         acquire data mutex
     344                 : 
     345                 :           // Only recalibrate if someone didn't already
     346                 :           if cachedOffset == calibration.offset:
     347                 :             // Have all waiting threads immediately wait
     348                 :             set data mutex spin count = 0
     349                 :             PRMJ_NowCalibrate()
     350                 :             calibrated = 1
     351                 : 
     352                 :             set data mutex spin count = default
     353                 :         release data mutex
     354                 :       release calibration mutex
     355                 : 
     356                 :     calculate lowres time
     357                 : 
     358                 :     if highres timer available:
     359                 :       acquire data mutex
     360                 :         calculate highres time
     361                 :         cachedOffset = calibration.offset
     362                 :         highres time = calibration.last = max(highres time, calibration.last)
     363                 :       release data mutex
     364                 : 
     365                 :       get kernel tick interval
     366                 : 
     367                 :       if abs(highres - lowres) < kernel tick:
     368                 :         returnedTime = highres time
     369                 :         needCalibration = False
     370                 :       else:
     371                 :         if calibrated:
     372                 :           returnedTime = lowres
     373                 :           needCalibration = False
     374                 :         else:
     375                 :           needCalibration = True
     376                 :     else:
     377                 :       returnedTime = lowres
     378                 :   while needCalibration
     379                 : 
     380                 : */
     381                 : 
     382                 : // We parameterize the delay count just so that shell builds can
     383                 : // set it to 0 in order to get high-resolution benchmarking.
     384                 : // 10 seems to be the number of calls to load with a blank homepage.
     385                 : int CALIBRATION_DELAY_COUNT = 10;
     386                 : 
     387                 : int64_t
     388                 : PRMJ_Now(void)
     389                 : {
     390                 :     static int nCalls = 0;
     391                 :     long double lowresTime, highresTimerValue;
     392                 :     FILETIME ft;
     393                 :     LARGE_INTEGER now;
     394                 :     JSBool calibrated = JS_FALSE;
     395                 :     JSBool needsCalibration = JS_FALSE;
     396                 :     int64_t returnedTime;
     397                 :     long double cachedOffset = 0.0;
     398                 : 
     399                 :     /* To avoid regressing startup time (where high resolution is likely
     400                 :        not needed), give the old behavior for the first few calls.
     401                 :        This does not appear to be needed on Vista as the timeBegin/timeEndPeriod
     402                 :        calls seem to immediately take effect. */
     403                 :     int thiscall = JS_ATOMIC_INCREMENT(&nCalls);
     404                 :     if (thiscall <= CALIBRATION_DELAY_COUNT) {
     405                 :         LowResTime(&ft);
     406                 :         return (FILETIME2INT64(ft)-win2un)/10L;
     407                 :     }
     408                 : 
     409                 :     /* For non threadsafe platforms, NowInit is not necessary */
     410                 : #ifdef JS_THREADSAFE
     411                 :     PR_CallOnce(&calibrationOnce, NowInit);
     412                 : #endif
     413                 :     do {
     414                 :         if (!calibration.calibrated || needsCalibration) {
     415                 :             MUTEX_LOCK(&calibration.calibration_lock);
     416                 :             MUTEX_LOCK(&calibration.data_lock);
     417                 : 
     418                 :             /* Recalibrate only if no one else did before us */
     419                 :             if(calibration.offset == cachedOffset) {
     420                 :                 /* Since calibration can take a while, make any other
     421                 :                    threads immediately wait */
     422                 :                 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
     423                 : 
     424                 :                 NowCalibrate();
     425                 : 
     426                 :                 calibrated = JS_TRUE;
     427                 : 
     428                 :                 /* Restore spin count */
     429                 :                 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
     430                 :             }
     431                 :             MUTEX_UNLOCK(&calibration.data_lock);
     432                 :             MUTEX_UNLOCK(&calibration.calibration_lock);
     433                 :         }
     434                 : 
     435                 : 
     436                 :         /* Calculate a low resolution time */
     437                 :         LowResTime(&ft);
     438                 :         lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
     439                 : 
     440                 :         if (calibration.freq > 0.0) {
     441                 :             long double highresTime, diff;
     442                 : 
     443                 :             DWORD timeAdjustment, timeIncrement;
     444                 :             BOOL timeAdjustmentDisabled;
     445                 : 
     446                 :             /* Default to 15.625 ms if the syscall fails */
     447                 :             long double skewThreshold = 15625.25;
     448                 :             /* Grab high resolution time */
     449                 :             QueryPerformanceCounter(&now);
     450                 :             highresTimerValue = (long double)now.QuadPart;
     451                 : 
     452                 :             MUTEX_LOCK(&calibration.data_lock);
     453                 :             highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
     454                 :                  (highresTimerValue-calibration.timer_offset)/calibration.freq;
     455                 :             cachedOffset = calibration.offset;
     456                 : 
     457                 :             /* On some dual processor/core systems, we might get an earlier time
     458                 :                so we cache the last time that we returned */
     459                 :             calibration.last = JS_MAX(calibration.last, int64_t(highresTime));
     460                 :             returnedTime = calibration.last;
     461                 :             MUTEX_UNLOCK(&calibration.data_lock);
     462                 : 
     463                 :             /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
     464                 :             if (GetSystemTimeAdjustment(&timeAdjustment,
     465                 :                                         &timeIncrement,
     466                 :                                         &timeAdjustmentDisabled)) {
     467                 :                 if (timeAdjustmentDisabled) {
     468                 :                     /* timeAdjustment is in units of 100ns */
     469                 :                     skewThreshold = timeAdjustment/10.0;
     470                 :                 } else {
     471                 :                     /* timeIncrement is in units of 100ns */
     472                 :                     skewThreshold = timeIncrement/10.0;
     473                 :                 }
     474                 :             }
     475                 : 
     476                 :             /* Check for clock skew */
     477                 :             diff = lowresTime - highresTime;
     478                 : 
     479                 :             /* For some reason that I have not determined, the skew can be
     480                 :                up to twice a kernel tick. This does not seem to happen by
     481                 :                itself, but I have only seen it triggered by another program
     482                 :                doing some kind of file I/O. The symptoms are a negative diff
     483                 :                followed by an equally large positive diff. */
     484                 :             if (fabs(diff) > 2*skewThreshold) {
     485                 :                 /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/
     486                 : 
     487                 :                 if (calibrated) {
     488                 :                     /* If we already calibrated once this instance, and the
     489                 :                        clock is still skewed, then either the processor(s) are
     490                 :                        wildly changing clockspeed or the system is so busy that
     491                 :                        we get switched out for long periods of time. In either
     492                 :                        case, it would be infeasible to make use of high
     493                 :                        resolution results for anything, so let's resort to old
     494                 :                        behavior for this call. It's possible that in the
     495                 :                        future, the user will want the high resolution timer, so
     496                 :                        we don't disable it entirely. */
     497                 :                     returnedTime = int64_t(lowresTime);
     498                 :                     needsCalibration = JS_FALSE;
     499                 :                 } else {
     500                 :                     /* It is possible that when we recalibrate, we will return a
     501                 :                        value less than what we have returned before; this is
     502                 :                        unavoidable. We cannot tell the different between a
     503                 :                        faulty QueryPerformanceCounter implementation and user
     504                 :                        changes to the operating system time. Since we must
     505                 :                        respect user changes to the operating system time, we
     506                 :                        cannot maintain the invariant that Date.now() never
     507                 :                        decreases; the old implementation has this behavior as
     508                 :                        well. */
     509                 :                     needsCalibration = JS_TRUE;
     510                 :                 }
     511                 :             } else {
     512                 :                 /* No detectable clock skew */
     513                 :                 returnedTime = int64_t(highresTime);
     514                 :                 needsCalibration = JS_FALSE;
     515                 :             }
     516                 :         } else {
     517                 :             /* No high resolution timer is available, so fall back */
     518                 :             returnedTime = int64_t(lowresTime);
     519                 :         }
     520                 :     } while (needsCalibration);
     521                 : 
     522                 :     return returnedTime;
     523                 : }
     524                 : #endif
     525                 : 
     526                 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
     527                 : static void
     528                 : PRMJ_InvalidParameterHandler(const wchar_t *expression,
     529                 :                              const wchar_t *function,
     530                 :                              const wchar_t *file,
     531                 :                              unsigned int   line,
     532                 :                              uintptr_t      pReserved)
     533                 : {
     534                 :     /* empty */
     535                 : }
     536                 : #endif
     537                 : 
     538                 : /* Format a time value into a buffer. Same semantics as strftime() */
     539                 : size_t
     540             733 : PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
     541                 : {
     542             733 :     size_t result = 0;
     543                 : #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2)
     544                 :     struct tm a;
     545             733 :     int fake_tm_year = 0;
     546                 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
     547                 :     _invalid_parameter_handler oldHandler;
     548                 :     int oldReportMode;
     549                 : #endif
     550                 : 
     551             733 :     memset(&a, 0, sizeof(struct tm));
     552                 : 
     553             733 :     a.tm_sec = prtm->tm_sec;
     554             733 :     a.tm_min = prtm->tm_min;
     555             733 :     a.tm_hour = prtm->tm_hour;
     556             733 :     a.tm_mday = prtm->tm_mday;
     557             733 :     a.tm_mon = prtm->tm_mon;
     558             733 :     a.tm_wday = prtm->tm_wday;
     559                 : 
     560                 :     /*
     561                 :      * On systems where |struct tm| has members tm_gmtoff and tm_zone, we
     562                 :      * must fill in those values, or else strftime will return wrong results
     563                 :      * (e.g., bug 511726, bug 554338).
     564                 :      */
     565                 : #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
     566                 :     {
     567                 :         /*
     568                 :          * Fill out |td| to the time represented by |prtm|, leaving the
     569                 :          * timezone fields zeroed out. localtime_r will then fill in the
     570                 :          * timezone fields for that local time according to the system's
     571                 :          * timezone parameters.
     572                 :          */
     573                 :         struct tm td;
     574             733 :         memset(&td, 0, sizeof(td));
     575             733 :         td.tm_sec = prtm->tm_sec;
     576             733 :         td.tm_min = prtm->tm_min;
     577             733 :         td.tm_hour = prtm->tm_hour;
     578             733 :         td.tm_mday = prtm->tm_mday;
     579             733 :         td.tm_mon = prtm->tm_mon;
     580             733 :         td.tm_wday = prtm->tm_wday;
     581             733 :         td.tm_year = prtm->tm_year - 1900;
     582             733 :         td.tm_yday = prtm->tm_yday;
     583             733 :         td.tm_isdst = prtm->tm_isdst;
     584             733 :         time_t t = mktime(&td);
     585             733 :         localtime_r(&t, &td);
     586                 : 
     587             733 :         a.tm_gmtoff = td.tm_gmtoff;
     588             733 :         a.tm_zone = td.tm_zone;
     589                 :     }
     590                 : #endif
     591                 : 
     592                 :     /*
     593                 :      * Years before 1900 and after 9999 cause strftime() to abort on Windows.
     594                 :      * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
     595                 :      * replace matching substrings in the strftime() result with the real year.
     596                 :      * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
     597                 :      * year formats (%y) work correctly (since we won't find the fake year
     598                 :      * in that case).
     599                 :      * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
     600                 :      * See bug 327869.
     601                 :      */
     602                 : #define FAKE_YEAR_BASE 9900
     603             733 :     if (prtm->tm_year < 1900 || prtm->tm_year > 9999) {
     604               0 :         fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100;
     605               0 :         a.tm_year = fake_tm_year - 1900;
     606                 :     }
     607                 :     else {
     608             733 :         a.tm_year = prtm->tm_year - 1900;
     609                 :     }
     610             733 :     a.tm_yday = prtm->tm_yday;
     611             733 :     a.tm_isdst = prtm->tm_isdst;
     612                 : 
     613                 :     /*
     614                 :      * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
     615                 :      * are null.  This doesn't quite work, though - the timezone is off by
     616                 :      * tzoff + dst.  (And mktime seems to return -1 for the exact dst
     617                 :      * changeover time.)
     618                 :      */
     619                 : 
     620                 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
     621                 :     oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
     622                 :     oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
     623                 : #endif
     624                 : 
     625             733 :     result = strftime(buf, buflen, fmt, &a);
     626                 : 
     627                 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
     628                 :     _set_invalid_parameter_handler(oldHandler);
     629                 :     _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
     630                 : #endif
     631                 : 
     632             733 :     if (fake_tm_year && result) {
     633                 :         char real_year[16];
     634                 :         char fake_year[16];
     635                 :         size_t real_year_len;
     636                 :         size_t fake_year_len;
     637                 :         char* p;
     638                 : 
     639               0 :         sprintf(real_year, "%d", prtm->tm_year);
     640               0 :         real_year_len = strlen(real_year);
     641               0 :         sprintf(fake_year, "%d", fake_tm_year);
     642               0 :         fake_year_len = strlen(fake_year);
     643                 : 
     644                 :         /* Replace the fake year in the result with the real year. */
     645               0 :         for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) {
     646               0 :             size_t new_result = result + real_year_len - fake_year_len;
     647               0 :             if ((int)new_result >= buflen) {
     648               0 :                 return 0;
     649                 :             }
     650               0 :             memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len));
     651               0 :             memcpy(p, real_year, real_year_len);
     652               0 :             result = new_result;
     653               0 :             *(buf + result) = '\0';
     654                 :         }
     655                 :     }
     656                 : #endif
     657             733 :     return result;
     658                 : }
     659                 : 
     660                 : int64_t
     661            9982 : DSTOffsetCache::computeDSTOffsetMilliseconds(int64_t localTimeSeconds)
     662                 : {
     663            9982 :     JS_ASSERT(localTimeSeconds >= 0);
     664            9982 :     JS_ASSERT(localTimeSeconds <= MAX_UNIX_TIMET);
     665                 : 
     666                 : #if defined(XP_WIN)
     667                 :     /* Windows does not follow POSIX. Updates to the
     668                 :      * TZ environment variable are not reflected
     669                 :      * immediately on that platform as they are
     670                 :      * on UNIX systems without this call.
     671                 :      */
     672                 :     _tzset();
     673                 : #endif
     674                 : 
     675                 :     struct tm tm;
     676            9982 :     if (!ComputeLocalTime(static_cast<time_t>(localTimeSeconds), &tm))
     677               0 :         return 0;
     678                 : 
     679            9982 :     int32_t base = PRMJ_LocalGMTDifference();
     680                 : 
     681            9982 :     int32_t dayoff = int32_t((localTimeSeconds - base) % (SECONDS_PER_HOUR * 24));
     682                 :     int32_t tmoff = tm.tm_sec + (tm.tm_min * SECONDS_PER_MINUTE) +
     683            9982 :         (tm.tm_hour * SECONDS_PER_HOUR);
     684                 : 
     685            9982 :     int32_t diff = tmoff - dayoff;
     686                 : 
     687            9982 :     if (diff < 0)
     688             134 :         diff += SECONDS_PER_DAY;
     689                 : 
     690            9982 :     return diff * MILLISECONDS_PER_SECOND;
     691                 : }
     692                 : 
     693                 : int64_t
     694           55701 : DSTOffsetCache::getDSTOffsetMilliseconds(int64_t localTimeMilliseconds, JSContext *cx)
     695                 : {
     696           55701 :     sanityCheck();
     697                 : 
     698           55701 :     int64_t localTimeSeconds = localTimeMilliseconds / MILLISECONDS_PER_SECOND;
     699                 : 
     700           55701 :     if (localTimeSeconds > MAX_UNIX_TIMET) {
     701               0 :         localTimeSeconds = MAX_UNIX_TIMET;
     702           55701 :     } else if (localTimeSeconds < 0) {
     703                 :         /* Go ahead a day to make localtime work (does not work with 0). */
     704               0 :         localTimeSeconds = SECONDS_PER_DAY;
     705                 :     }
     706                 : 
     707                 :     /*
     708                 :      * NB: Be aware of the initial range values when making changes to this
     709                 :      *     code: the first call to this method, with those initial range
     710                 :      *     values, must result in a cache miss.
     711                 :      */
     712                 : 
     713           55701 :     if (rangeStartSeconds <= localTimeSeconds &&
     714                 :         localTimeSeconds <= rangeEndSeconds) {
     715           44980 :         return offsetMilliseconds;
     716                 :     }
     717                 : 
     718           10721 :     if (oldRangeStartSeconds <= localTimeSeconds &&
     719                 :         localTimeSeconds <= oldRangeEndSeconds) {
     720            4517 :         return oldOffsetMilliseconds;
     721                 :     }
     722                 : 
     723            6204 :     oldOffsetMilliseconds = offsetMilliseconds;
     724            6204 :     oldRangeStartSeconds = rangeStartSeconds;
     725            6204 :     oldRangeEndSeconds = rangeEndSeconds;
     726                 : 
     727            6204 :     if (rangeStartSeconds <= localTimeSeconds) {
     728            5690 :         int64_t newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
     729            5690 :         if (newEndSeconds >= localTimeSeconds) {
     730            4964 :             int64_t endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);
     731            4964 :             if (endOffsetMilliseconds == offsetMilliseconds) {
     732            1198 :                 rangeEndSeconds = newEndSeconds;
     733            1198 :                 return offsetMilliseconds;
     734                 :             }
     735                 : 
     736            3766 :             offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
     737            3766 :             if (offsetMilliseconds == endOffsetMilliseconds) {
     738             198 :                 rangeStartSeconds = localTimeSeconds;
     739             198 :                 rangeEndSeconds = newEndSeconds;
     740                 :             } else {
     741            3568 :                 rangeEndSeconds = localTimeSeconds;
     742                 :             }
     743            3766 :             return offsetMilliseconds;
     744                 :         }
     745                 : 
     746             726 :         offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
     747             726 :         rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
     748             726 :         return offsetMilliseconds;
     749                 :     }
     750                 : 
     751             514 :     int64_t newStartSeconds = JS_MAX(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, 0);
     752             514 :     if (newStartSeconds <= localTimeSeconds) {
     753             149 :         int64_t startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds);
     754             149 :         if (startOffsetMilliseconds == offsetMilliseconds) {
     755             137 :             rangeStartSeconds = newStartSeconds;
     756             137 :             return offsetMilliseconds;
     757                 :         }
     758                 : 
     759              12 :         offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
     760              12 :         if (offsetMilliseconds == startOffsetMilliseconds) {
     761               0 :             rangeStartSeconds = newStartSeconds;
     762               0 :             rangeEndSeconds = localTimeSeconds;
     763                 :         } else {
     764              12 :             rangeStartSeconds = localTimeSeconds;
     765                 :         }
     766              12 :         return offsetMilliseconds;
     767                 :     }
     768                 : 
     769             365 :     rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
     770             365 :     offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
     771             365 :     return offsetMilliseconds;
     772                 : }
     773                 : 
     774                 : void
     775           81932 : DSTOffsetCache::sanityCheck()
     776                 : {
     777           81932 :     JS_ASSERT(rangeStartSeconds <= rangeEndSeconds);
     778           81932 :     JS_ASSERT_IF(rangeStartSeconds == INT64_MIN, rangeEndSeconds == INT64_MIN);
     779           81932 :     JS_ASSERT_IF(rangeEndSeconds == INT64_MIN, rangeStartSeconds == INT64_MIN);
     780               0 :     JS_ASSERT_IF(rangeStartSeconds != INT64_MIN,
     781           81932 :                  rangeStartSeconds >= 0 && rangeEndSeconds >= 0);
     782               0 :     JS_ASSERT_IF(rangeStartSeconds != INT64_MIN,
     783           81932 :                  rangeStartSeconds <= MAX_UNIX_TIMET && rangeEndSeconds <= MAX_UNIX_TIMET);
     784           81932 : }

Generated by: LCOV version 1.7