LCOV - app.info - js/src/prmjtime.cpp

LCOV - code coverage report
| Current view: | directory - js/src - prmjtime.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 129 | 101 | 78.3 %
| Date: | 2012-04-07 | 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           19166 : ComputeLocalTime(time_t local, struct tm *ptm)
         103                 : {
         104                 : #ifdef HAVE_LOCALTIME_R
         105           19166 :     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            9940 : 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            9940 :     int day = 0;
         136                 :     struct tm tm;
         137                 :
         138            9940 :     if (!ComputeLocalTime(0, &tm))
         139               0 :         return 0;
         140            9940 :     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            9940 :     int time = (tm.tm_hour * 3600) + (tm.tm_min * 60) + tm.tm_sec;
         147            9940 :     time = PRMJ_DAY_SECONDS - time;
         148                 :
         149            9940 :     if (tm.tm_yday == day)
         150               0 :         time -= PRMJ_DAY_SECONDS;
         151                 :
         152            9940 :     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         4956826 : 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         4956826 :     gettimeofday(&tv, 0);
         313                 : #endif /* _SVID_GETTOD */
         314                 :
         315         4956826 :     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             189 : PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
         541                 : {
         542             189 :     size_t result = 0;
         543                 : #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2)
         544                 :     struct tm a;
         545             189 :     int fake_tm_year = 0;
         546                 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
         547                 :     _invalid_parameter_handler oldHandler;
         548                 :     int oldReportMode;
         549                 : #endif
         550                 :
         551             189 :     memset(&a, 0, sizeof(struct tm));
         552                 :
         553             189 :     a.tm_sec = prtm->tm_sec;
         554             189 :     a.tm_min = prtm->tm_min;
         555             189 :     a.tm_hour = prtm->tm_hour;
         556             189 :     a.tm_mday = prtm->tm_mday;
         557             189 :     a.tm_mon = prtm->tm_mon;
         558             189 :     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             189 :         memset(&td, 0, sizeof(td));
         575             189 :         td.tm_sec = prtm->tm_sec;
         576             189 :         td.tm_min = prtm->tm_min;
         577             189 :         td.tm_hour = prtm->tm_hour;
         578             189 :         td.tm_mday = prtm->tm_mday;
         579             189 :         td.tm_mon = prtm->tm_mon;
         580             189 :         td.tm_wday = prtm->tm_wday;
         581             189 :         td.tm_year = prtm->tm_year - 1900;
         582             189 :         td.tm_yday = prtm->tm_yday;
         583             189 :         td.tm_isdst = prtm->tm_isdst;
         584             189 :         time_t t = mktime(&td);
         585             189 :         localtime_r(&t, &td);
         586                 :
         587             189 :         a.tm_gmtoff = td.tm_gmtoff;
         588             189 :         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             189 :     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             189 :         a.tm_year = prtm->tm_year - 1900;
         609                 :     }
         610             189 :     a.tm_yday = prtm->tm_yday;
         611             189 :     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             189 :     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             189 :     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             189 :     return result;
         658                 : }
         659                 :
         660                 : int64_t
         661            9226 : DSTOffsetCache::computeDSTOffsetMilliseconds(int64_t localTimeSeconds)
         662                 : {
         663            9226 :     JS_ASSERT(localTimeSeconds >= 0);
         664            9226 :     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            9226 :     if (!ComputeLocalTime(static_cast<time_t>(localTimeSeconds), &tm))
         677               0 :         return 0;
         678                 :
         679            9226 :     int32_t base = PRMJ_LocalGMTDifference();
         680                 :
         681            9226 :     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            9226 :         (tm.tm_hour * SECONDS_PER_HOUR);
         684                 :
         685            9226 :     int32_t diff = tmoff - dayoff;
         686                 :
         687            9226 :     if (diff < 0)
         688             126 :         diff += SECONDS_PER_DAY;
         689                 :
         690            9226 :     return diff * MILLISECONDS_PER_SECOND;
         691                 : }
         692                 :
         693                 : int64_t
         694           50274 : DSTOffsetCache::getDSTOffsetMilliseconds(int64_t localTimeMilliseconds, JSContext *cx)
         695                 : {
         696           50274 :     sanityCheck();
         697                 :
         698           50274 :     int64_t localTimeSeconds = localTimeMilliseconds / MILLISECONDS_PER_SECOND;
         699                 :
         700           50274 :     if (localTimeSeconds > MAX_UNIX_TIMET) {
         701               0 :         localTimeSeconds = MAX_UNIX_TIMET;
         702           50274 :     } 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           50274 :     if (rangeStartSeconds <= localTimeSeconds &&
         714                 :         localTimeSeconds <= rangeEndSeconds) {
         715           40319 :         return offsetMilliseconds;
         716                 :     }
         717                 :
         718            9955 :     if (oldRangeStartSeconds <= localTimeSeconds &&
         719                 :         localTimeSeconds <= oldRangeEndSeconds) {
         720            4500 :         return oldOffsetMilliseconds;
         721                 :     }
         722                 :
         723            5455 :     oldOffsetMilliseconds = offsetMilliseconds;
         724            5455 :     oldRangeStartSeconds = rangeStartSeconds;
         725            5455 :     oldRangeEndSeconds = rangeEndSeconds;
         726                 :
         727            5455 :     if (rangeStartSeconds <= localTimeSeconds) {
         728            5122 :         int64_t newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
         729            5122 :         if (newEndSeconds >= localTimeSeconds) {
         730            4762 :             int64_t endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);
         731            4762 :             if (endOffsetMilliseconds == offsetMilliseconds) {
         732            1000 :                 rangeEndSeconds = newEndSeconds;
         733            1000 :                 return offsetMilliseconds;
         734                 :             }
         735                 :
         736            3762 :             offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
         737            3762 :             if (offsetMilliseconds == endOffsetMilliseconds) {
         738             198 :                 rangeStartSeconds = localTimeSeconds;
         739             198 :                 rangeEndSeconds = newEndSeconds;
         740                 :             } else {
         741            3564 :                 rangeEndSeconds = localTimeSeconds;
         742                 :             }
         743            3762 :             return offsetMilliseconds;
         744                 :         }
         745                 :
         746             360 :         offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
         747             360 :         rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
         748             360 :         return offsetMilliseconds;
         749                 :     }
         750                 :
         751             333 :     int64_t newStartSeconds = JS_MAX(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, 0);
         752             333 :     if (newStartSeconds <= localTimeSeconds) {
         753               9 :         int64_t startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds);
         754               9 :         if (startOffsetMilliseconds == offsetMilliseconds) {
         755               0 :             rangeStartSeconds = newStartSeconds;
         756               0 :             return offsetMilliseconds;
         757                 :         }
         758                 :
         759               9 :         offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
         760               9 :         if (offsetMilliseconds == startOffsetMilliseconds) {
         761               0 :             rangeStartSeconds = newStartSeconds;
         762               0 :             rangeEndSeconds = localTimeSeconds;
         763                 :         } else {
         764               9 :             rangeStartSeconds = localTimeSeconds;
         765                 :         }
         766               9 :         return offsetMilliseconds;
         767                 :     }
         768                 :
         769             324 :     rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
         770             324 :     offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
         771             324 :     return offsetMilliseconds;
         772                 : }
         773                 :
         774                 : void
         775           69045 : DSTOffsetCache::sanityCheck()
         776                 : {
         777           69045 :     JS_ASSERT(rangeStartSeconds <= rangeEndSeconds);
         778           69045 :     JS_ASSERT_IF(rangeStartSeconds == INT64_MIN, rangeEndSeconds == INT64_MIN);
         779           69045 :     JS_ASSERT_IF(rangeEndSeconds == INT64_MIN, rangeStartSeconds == INT64_MIN);
         780               0 :     JS_ASSERT_IF(rangeStartSeconds != INT64_MIN,
         781           69045 :                  rangeStartSeconds >= 0 && rangeEndSeconds >= 0);
         782               0 :     JS_ASSERT_IF(rangeStartSeconds != INT64_MIN,
         783           69045 :                  rangeStartSeconds <= MAX_UNIX_TIMET && rangeEndSeconds <= MAX_UNIX_TIMET);
         784           69045 : }

Generated by: LCOV version 1.7