Commit b927af88 authored by ulan@chromium.org's avatar ulan@chromium.org

Fix a race in initialization of timezone cache in platform-win32.

This allocates a timezone cache per isolate.

BUG=
R=jochen@chromium.org

Review URL: https://codereview.chromium.org/197023002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19943 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 17d040e2
......@@ -62,7 +62,7 @@ void DateCache::ResetDateCache() {
after_ = &dst_[1];
local_offset_ms_ = kInvalidLocalOffsetInMs;
ymd_valid_ = false;
OS::TimeZoneChanged();
OS::ClearTimezoneCache(tz_cache_);
}
......
......@@ -62,11 +62,14 @@ class DateCache {
// It is an invariant of DateCache that cache stamp is non-negative.
static const int kInvalidStamp = -1;
DateCache() : stamp_(0) {
DateCache() : stamp_(0), tz_cache_(OS::CreateTimezoneCache()) {
ResetDateCache();
}
virtual ~DateCache() {}
virtual ~DateCache() {
OS::DisposeTimezoneCache(tz_cache_);
tz_cache_ = NULL;
}
// Clears cached timezone information and increments the cache stamp.
......@@ -113,7 +116,7 @@ class DateCache {
if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) {
time_ms = EquivalentTime(time_ms);
}
return OS::LocalTimezone(static_cast<double>(time_ms));
return OS::LocalTimezone(static_cast<double>(time_ms), tz_cache_);
}
// ECMA 262 - 15.9.5.26
......@@ -182,11 +185,11 @@ class DateCache {
// These functions are virtual so that we can override them when testing.
virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) {
double time_ms = static_cast<double>(time_sec * 1000);
return static_cast<int>(OS::DaylightSavingsOffset(time_ms));
return static_cast<int>(OS::DaylightSavingsOffset(time_ms, tz_cache_));
}
virtual int GetLocalOffsetFromOS() {
double offset = OS::LocalTimeOffset();
double offset = OS::LocalTimeOffset(tz_cache_);
ASSERT(offset < kInvalidLocalOffsetInMs);
return static_cast<int>(offset);
}
......@@ -253,6 +256,8 @@ class DateCache {
int ymd_year_;
int ymd_month_;
int ymd_day_;
TimezoneCache* tz_cache_;
};
} } // namespace v8::internal
......
......@@ -51,7 +51,7 @@ namespace v8 {
namespace internal {
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -60,7 +60,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
// On Cygwin, struct tm does not contain a tm_gmtoff field.
time_t utc = time(NULL);
ASSERT(utc != -1);
......
......@@ -61,7 +61,7 @@ namespace v8 {
namespace internal {
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -70,7 +70,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
time_t tv = time(NULL);
struct tm* t = localtime(&tv);
// tm_gmtoff includes any daylight savings offset, so subtract it.
......
......@@ -118,7 +118,7 @@ bool OS::ArmUsingHardFloat() {
#endif // def __arm__
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -127,7 +127,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
time_t tv = time(NULL);
struct tm* t = localtime(&tv);
// tm_gmtoff includes any daylight savings offset, so subtract it.
......
......@@ -182,7 +182,7 @@ void OS::SignalCodeMovingGC() {
}
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -191,7 +191,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
time_t tv = time(NULL);
struct tm* t = localtime(&tv);
// tm_gmtoff includes any daylight savings offset, so subtract it.
......
......@@ -59,7 +59,7 @@ namespace v8 {
namespace internal {
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -68,7 +68,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
time_t tv = time(NULL);
struct tm* t = localtime(&tv);
// tm_gmtoff includes any daylight savings offset, so subtract it.
......
......@@ -354,7 +354,25 @@ double OS::TimeCurrentMillis() {
}
double OS::DaylightSavingsOffset(double time) {
class TimezoneCache {};
TimezoneCache* OS::CreateTimezoneCache() {
return NULL;
}
void OS::DisposeTimezoneCache(TimezoneCache* cache) {
ASSERT(cache == NULL);
}
void OS::ClearTimezoneCache(TimezoneCache* cache) {
ASSERT(cache == NULL);
}
double OS::DaylightSavingsOffset(double time, TimezoneCache*) {
if (std::isnan(time)) return nan_value();
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -363,9 +381,6 @@ double OS::DaylightSavingsOffset(double time) {
}
void OS::TimeZoneChanged() {}
int OS::GetLastError() {
return errno;
}
......
......@@ -110,7 +110,7 @@ bool OS::ArmUsingHardFloat() {
#endif // __arm__
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -119,7 +119,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
time_t tv = time(NULL);
struct tm* t = localtime(&tv);
// tm_gmtoff includes any daylight savings offset, so subtract it.
......
......@@ -80,7 +80,7 @@ namespace v8 {
namespace internal {
const char* OS::LocalTimezone(double time) {
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
if (std::isnan(time)) return "";
time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
struct tm* t = localtime(&tv);
......@@ -89,7 +89,7 @@ const char* OS::LocalTimezone(double time) {
}
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
tzset();
return -static_cast<double>(timezone * msPerSecond);
}
......
......@@ -218,6 +218,97 @@ void MathSetup() {
}
class TimezoneCache {
public:
TimezoneCache() : initialized_(false) { }
void Clear() {
initialized_ = false;
}
// Initialize timezone information. The timezone information is obtained from
// windows. If we cannot get the timezone information we fall back to CET.
void InitializeIfNeeded() {
// Just return if timezone information has already been initialized.
if (initialized_) return;
// Initialize POSIX time zone data.
_tzset();
// Obtain timezone information from operating system.
memset(&tzinfo_, 0, sizeof(tzinfo_));
if (GetTimeZoneInformation(&tzinfo_) == TIME_ZONE_ID_INVALID) {
// If we cannot get timezone information we fall back to CET.
tzinfo_.Bias = -60;
tzinfo_.StandardDate.wMonth = 10;
tzinfo_.StandardDate.wDay = 5;
tzinfo_.StandardDate.wHour = 3;
tzinfo_.StandardBias = 0;
tzinfo_.DaylightDate.wMonth = 3;
tzinfo_.DaylightDate.wDay = 5;
tzinfo_.DaylightDate.wHour = 2;
tzinfo_.DaylightBias = -60;
}
// Make standard and DST timezone names.
WideCharToMultiByte(CP_UTF8, 0, tzinfo_.StandardName, -1,
std_tz_name_, kTzNameSize, NULL, NULL);
std_tz_name_[kTzNameSize - 1] = '\0';
WideCharToMultiByte(CP_UTF8, 0, tzinfo_.DaylightName, -1,
dst_tz_name_, kTzNameSize, NULL, NULL);
dst_tz_name_[kTzNameSize - 1] = '\0';
// If OS returned empty string or resource id (like "@tzres.dll,-211")
// simply guess the name from the UTC bias of the timezone.
// To properly resolve the resource identifier requires a library load,
// which is not possible in a sandbox.
if (std_tz_name_[0] == '\0' || std_tz_name_[0] == '@') {
OS::SNPrintF(Vector<char>(std_tz_name_, kTzNameSize - 1),
"%s Standard Time",
GuessTimezoneNameFromBias(tzinfo_.Bias));
}
if (dst_tz_name_[0] == '\0' || dst_tz_name_[0] == '@') {
OS::SNPrintF(Vector<char>(dst_tz_name_, kTzNameSize - 1),
"%s Daylight Time",
GuessTimezoneNameFromBias(tzinfo_.Bias));
}
// Timezone information initialized.
initialized_ = true;
}
// Guess the name of the timezone from the bias.
// The guess is very biased towards the northern hemisphere.
const char* GuessTimezoneNameFromBias(int bias) {
static const int kHour = 60;
switch (-bias) {
case -9*kHour: return "Alaska";
case -8*kHour: return "Pacific";
case -7*kHour: return "Mountain";
case -6*kHour: return "Central";
case -5*kHour: return "Eastern";
case -4*kHour: return "Atlantic";
case 0*kHour: return "GMT";
case +1*kHour: return "Central Europe";
case +2*kHour: return "Eastern Europe";
case +3*kHour: return "Russia";
case +5*kHour + 30: return "India";
case +8*kHour: return "China";
case +9*kHour: return "Japan";
case +12*kHour: return "New Zealand";
default: return "Local";
}
}
private:
static const int kTzNameSize = 128;
bool initialized_;
char std_tz_name_[kTzNameSize];
char dst_tz_name_[kTzNameSize];
TIME_ZONE_INFORMATION tzinfo_;
friend class Win32Time;
};
// ----------------------------------------------------------------------------
// The Time class represents time on win32. A timestamp is represented as
// a 64-bit integer in 100 nanoseconds since January 1, 1601 (UTC). JavaScript
......@@ -242,16 +333,14 @@ class Win32Time {
// LocalOffset(CET) = 3600000 and LocalOffset(PST) = -28800000. This
// routine also takes into account whether daylight saving is effect
// at the time.
int64_t LocalOffset();
int64_t LocalOffset(TimezoneCache* cache);
// Returns the daylight savings time offset for the time in milliseconds.
int64_t DaylightSavingsOffset();
int64_t DaylightSavingsOffset(TimezoneCache* cache);
// Returns a string identifying the current timezone for the
// timestamp taking into account daylight saving.
char* LocalTimezone();
static void TimeZoneChanged() { tz_initialized_ = false; }
char* LocalTimezone(TimezoneCache* cache);
private:
// Constants for time conversion.
......@@ -260,25 +349,10 @@ class Win32Time {
static const int64_t kMsPerMinute = 60000;
// Constants for timezone information.
static const int kTzNameSize = 128;
static const bool kShortTzNames = false;
// Timezone information. We need to have static buffers for the
// timezone names because we return pointers to these in
// LocalTimezone().
static bool tz_initialized_;
static TIME_ZONE_INFORMATION tzinfo_;
static char std_tz_name_[kTzNameSize];
static char dst_tz_name_[kTzNameSize];
// Initialize the timezone information (if not already done).
static void TzSet();
// Guess the name of the timezone from the bias.
static const char* GuessTimezoneNameFromBias(int bias);
// Return whether or not daylight savings time is in effect at this time.
bool InDST();
bool InDST(TimezoneCache* cache);
// Accessor for FILETIME representation.
FILETIME& ft() { return time_.ft_; }
......@@ -300,13 +374,6 @@ class Win32Time {
};
// Static variables.
bool Win32Time::tz_initialized_ = false;
TIME_ZONE_INFORMATION Win32Time::tzinfo_;
char Win32Time::std_tz_name_[kTzNameSize];
char Win32Time::dst_tz_name_[kTzNameSize];
// Initialize timestamp to start of epoc.
Win32Time::Win32Time() {
t() = 0;
......@@ -395,90 +462,13 @@ void Win32Time::SetToCurrentTime() {
}
// Guess the name of the timezone from the bias.
// The guess is very biased towards the northern hemisphere.
const char* Win32Time::GuessTimezoneNameFromBias(int bias) {
static const int kHour = 60;
switch (-bias) {
case -9*kHour: return "Alaska";
case -8*kHour: return "Pacific";
case -7*kHour: return "Mountain";
case -6*kHour: return "Central";
case -5*kHour: return "Eastern";
case -4*kHour: return "Atlantic";
case 0*kHour: return "GMT";
case +1*kHour: return "Central Europe";
case +2*kHour: return "Eastern Europe";
case +3*kHour: return "Russia";
case +5*kHour + 30: return "India";
case +8*kHour: return "China";
case +9*kHour: return "Japan";
case +12*kHour: return "New Zealand";
default: return "Local";
}
}
// Initialize timezone information. The timezone information is obtained from
// windows. If we cannot get the timezone information we fall back to CET.
// Please notice that this code is not thread-safe.
void Win32Time::TzSet() {
// Just return if timezone information has already been initialized.
if (tz_initialized_) return;
// Initialize POSIX time zone data.
_tzset();
// Obtain timezone information from operating system.
memset(&tzinfo_, 0, sizeof(tzinfo_));
if (GetTimeZoneInformation(&tzinfo_) == TIME_ZONE_ID_INVALID) {
// If we cannot get timezone information we fall back to CET.
tzinfo_.Bias = -60;
tzinfo_.StandardDate.wMonth = 10;
tzinfo_.StandardDate.wDay = 5;
tzinfo_.StandardDate.wHour = 3;
tzinfo_.StandardBias = 0;
tzinfo_.DaylightDate.wMonth = 3;
tzinfo_.DaylightDate.wDay = 5;
tzinfo_.DaylightDate.wHour = 2;
tzinfo_.DaylightBias = -60;
}
// Make standard and DST timezone names.
WideCharToMultiByte(CP_UTF8, 0, tzinfo_.StandardName, -1,
std_tz_name_, kTzNameSize, NULL, NULL);
std_tz_name_[kTzNameSize - 1] = '\0';
WideCharToMultiByte(CP_UTF8, 0, tzinfo_.DaylightName, -1,
dst_tz_name_, kTzNameSize, NULL, NULL);
dst_tz_name_[kTzNameSize - 1] = '\0';
// If OS returned empty string or resource id (like "@tzres.dll,-211")
// simply guess the name from the UTC bias of the timezone.
// To properly resolve the resource identifier requires a library load,
// which is not possible in a sandbox.
if (std_tz_name_[0] == '\0' || std_tz_name_[0] == '@') {
OS::SNPrintF(Vector<char>(std_tz_name_, kTzNameSize - 1),
"%s Standard Time",
GuessTimezoneNameFromBias(tzinfo_.Bias));
}
if (dst_tz_name_[0] == '\0' || dst_tz_name_[0] == '@') {
OS::SNPrintF(Vector<char>(dst_tz_name_, kTzNameSize - 1),
"%s Daylight Time",
GuessTimezoneNameFromBias(tzinfo_.Bias));
}
// Timezone information initialized.
tz_initialized_ = true;
}
// Return the local timezone offset in milliseconds east of UTC. This
// takes into account whether daylight saving is in effect at the time.
// Only times in the 32-bit Unix range may be passed to this function.
// Also, adding the time-zone offset to the input must not overflow.
// The function EquivalentTime() in date.js guarantees this.
int64_t Win32Time::LocalOffset() {
// Initialize timezone information, if needed.
TzSet();
int64_t Win32Time::LocalOffset(TimezoneCache* cache) {
cache->InitializeIfNeeded();
Win32Time rounded_to_second(*this);
rounded_to_second.t() = rounded_to_second.t() / 1000 / kTimeScaler *
......@@ -501,29 +491,30 @@ int64_t Win32Time::LocalOffset() {
if (localtime_s(&posix_local_time_struct, &posix_time)) return 0;
if (posix_local_time_struct.tm_isdst > 0) {
return (tzinfo_.Bias + tzinfo_.DaylightBias) * -kMsPerMinute;
return (cache->tzinfo_.Bias + cache->tzinfo_.DaylightBias) * -kMsPerMinute;
} else if (posix_local_time_struct.tm_isdst == 0) {
return (tzinfo_.Bias + tzinfo_.StandardBias) * -kMsPerMinute;
return (cache->tzinfo_.Bias + cache->tzinfo_.StandardBias) * -kMsPerMinute;
} else {
return tzinfo_.Bias * -kMsPerMinute;
return cache->tzinfo_.Bias * -kMsPerMinute;
}
}
// Return whether or not daylight savings time is in effect at this time.
bool Win32Time::InDST() {
// Initialize timezone information, if needed.
TzSet();
bool Win32Time::InDST(TimezoneCache* cache) {
cache->InitializeIfNeeded();
// Determine if DST is in effect at the specified time.
bool in_dst = false;
if (tzinfo_.StandardDate.wMonth != 0 || tzinfo_.DaylightDate.wMonth != 0) {
if (cache->tzinfo_.StandardDate.wMonth != 0 ||
cache->tzinfo_.DaylightDate.wMonth != 0) {
// Get the local timezone offset for the timestamp in milliseconds.
int64_t offset = LocalOffset();
int64_t offset = LocalOffset(cache);
// Compute the offset for DST. The bias parameters in the timezone info
// are specified in minutes. These must be converted to milliseconds.
int64_t dstofs = -(tzinfo_.Bias + tzinfo_.DaylightBias) * kMsPerMinute;
int64_t dstofs =
-(cache->tzinfo_.Bias + cache->tzinfo_.DaylightBias) * kMsPerMinute;
// If the local time offset equals the timezone bias plus the daylight
// bias then DST is in effect.
......@@ -535,17 +526,17 @@ bool Win32Time::InDST() {
// Return the daylight savings time offset for this time.
int64_t Win32Time::DaylightSavingsOffset() {
return InDST() ? 60 * kMsPerMinute : 0;
int64_t Win32Time::DaylightSavingsOffset(TimezoneCache* cache) {
return InDST(cache) ? 60 * kMsPerMinute : 0;
}
// Returns a string identifying the current timezone for the
// timestamp taking into account daylight saving.
char* Win32Time::LocalTimezone() {
char* Win32Time::LocalTimezone(TimezoneCache* cache) {
// Return the standard or DST time zone name based on whether daylight
// saving is in effect at the given time.
return InDST() ? dst_tz_name_ : std_tz_name_;
return InDST(cache) ? cache->dst_tz_name_ : cache->std_tz_name_;
}
......@@ -588,36 +579,47 @@ double OS::TimeCurrentMillis() {
}
TimezoneCache* OS::CreateTimezoneCache() {
return new TimezoneCache();
}
void OS::DisposeTimezoneCache(TimezoneCache* cache) {
delete cache;
}
void OS::ClearTimezoneCache(TimezoneCache* cache) {
cache->Clear();
}
// Returns a string identifying the current timezone taking into
// account daylight saving.
const char* OS::LocalTimezone(double time) {
return Win32Time(time).LocalTimezone();
const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
return Win32Time(time).LocalTimezone(cache);
}
// Returns the local time offset in milliseconds east of UTC without
// taking daylight savings time into account.
double OS::LocalTimeOffset() {
double OS::LocalTimeOffset(TimezoneCache* cache) {
// Use current time, rounded to the millisecond.
Win32Time t(TimeCurrentMillis());
// Time::LocalOffset inlcudes any daylight savings offset, so subtract it.
return static_cast<double>(t.LocalOffset() - t.DaylightSavingsOffset());
return static_cast<double>(t.LocalOffset(cache) -
t.DaylightSavingsOffset(cache));
}
// Returns the daylight savings offset in milliseconds for the given
// time.
double OS::DaylightSavingsOffset(double time) {
int64_t offset = Win32Time(time).DaylightSavingsOffset();
double OS::DaylightSavingsOffset(double time, TimezoneCache* cache) {
int64_t offset = Win32Time(time).DaylightSavingsOffset(cache);
return static_cast<double>(offset);
}
void OS::TimeZoneChanged() {
Win32Time::TimeZoneChanged();
}
int OS::GetLastError() {
return ::GetLastError();
}
......
......@@ -159,6 +159,9 @@ inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
#endif // V8_NO_FAST_TLS
class TimezoneCache;
// ----------------------------------------------------------------------------
// OS
//
......@@ -182,18 +185,20 @@ class OS {
// 00:00:00 UTC, January 1, 1970.
static double TimeCurrentMillis();
static TimezoneCache* CreateTimezoneCache();
static void DisposeTimezoneCache(TimezoneCache* cache);
static void ClearTimezoneCache(TimezoneCache* cache);
// Returns a string identifying the current time zone. The
// timestamp is used for determining if DST is in effect.
static const char* LocalTimezone(double time);
static const char* LocalTimezone(double time, TimezoneCache* cache);
// Returns the local time offset in milliseconds east of UTC without
// taking daylight savings time into account.
static double LocalTimeOffset();
static double LocalTimeOffset(TimezoneCache* cache);
// Returns the daylight savings offset for the given time.
static double DaylightSavingsOffset(double time);
static void TimeZoneChanged();
static double DaylightSavingsOffset(double time, TimezoneCache* cache);
// Returns last OS error.
static int GetLastError();
......
......@@ -9583,8 +9583,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimezone) {
ASSERT(args.length() == 1);
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
int64_t time = isolate->date_cache()->EquivalentTime(static_cast<int64_t>(x));
const char* zone = OS::LocalTimezone(static_cast<double>(time));
const char* zone =
isolate->date_cache()->LocalTimezone(static_cast<int64_t>(x));
return isolate->heap()->AllocateStringFromUtf8(CStrVector(zone));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment