mirror of https://github.com/lukechilds/node.git
Browse Source
Original commit message: timers: use uv_now instead of Date.now This saves a few calls to gettimeofday which can be expensive, and potentially subject to clock drift. Instead use the loop time which uses hrtime internally. In addition to the backport, this commit: - keeps _idleStart timers' property which is still set to Date.now() to avoid breaking existing code that uses it, even if its use is discouraged. - adds automated tests. These tests use a specific branch of libfaketime that hasn't been submitted upstream yet. libfaketime is git cloned if needed when running automated tests. Signed-off-by: Timothy J Fontaine <tjfontaine@gmail.com>v0.10.30-release
Julien Gilli
11 years ago
committed by
Timothy J Fontaine
9 changed files with 228 additions and 13 deletions
@ -0,0 +1,53 @@ |
|||||
|
// FaketimeFlags: --exclude-monotonic -f '2014-07-21 09:00:00'
|
||||
|
|
||||
|
var common = require('../common'); |
||||
|
|
||||
|
var Timer = process.binding('timer_wrap').Timer; |
||||
|
var assert = require('assert'); |
||||
|
|
||||
|
var timerFired = false; |
||||
|
var intervalFired = false; |
||||
|
|
||||
|
/* |
||||
|
* This test case aims at making sure that timing utilities such |
||||
|
* as setTimeout and setInterval are not vulnerable to time |
||||
|
* drifting or inconsistent time changes (such as ntp time sync |
||||
|
* in the past, etc.). |
||||
|
* |
||||
|
* It is run using faketime so that we change how |
||||
|
* non-monotonic clocks perceive time movement. We freeze |
||||
|
* non-monotonic time, and check if setTimeout and setInterval |
||||
|
* work properly in that situation. |
||||
|
* |
||||
|
* We check this by setting a timer based on a monotonic clock |
||||
|
* to fire after setTimeout's callback is supposed to be called. |
||||
|
* This monotonic timer, by definition, is not subject to time drifting |
||||
|
* and inconsistent time changes, so it can be considered as a solid |
||||
|
* reference. |
||||
|
* |
||||
|
* When the monotonic timer fires, if the setTimeout's callback |
||||
|
* hasn't been called yet, it means that setTimeout's underlying timer |
||||
|
* is vulnerable to time drift or inconsistent time changes. |
||||
|
*/ |
||||
|
|
||||
|
var monoTimer = new Timer(); |
||||
|
monoTimer.ontimeout = function () { |
||||
|
/* |
||||
|
* Make sure that setTimeout's and setInterval's callbacks have |
||||
|
* already fired, otherwise it means that they are vulnerable to |
||||
|
* time drifting or inconsistent time changes. |
||||
|
*/ |
||||
|
assert(timerFired); |
||||
|
assert(intervalFired); |
||||
|
}; |
||||
|
|
||||
|
monoTimer.start(300, 0); |
||||
|
|
||||
|
var timer = setTimeout(function () { |
||||
|
timerFired = true; |
||||
|
}, 200); |
||||
|
|
||||
|
var interval = setInterval(function () { |
||||
|
intervalFired = true; |
||||
|
clearInterval(interval); |
||||
|
}, 200); |
@ -0,0 +1,102 @@ |
|||||
|
# Copyright 2008 the V8 project authors. All rights reserved. |
||||
|
# Redistribution and use in source and binary forms, with or without |
||||
|
# modification, are permitted provided that the following conditions are |
||||
|
# met: |
||||
|
# |
||||
|
# * Redistributions of source code must retain the above copyright |
||||
|
# notice, this list of conditions and the following disclaimer. |
||||
|
# * Redistributions in binary form must reproduce the above |
||||
|
# copyright notice, this list of conditions and the following |
||||
|
# disclaimer in the documentation and/or other materials provided |
||||
|
# with the distribution. |
||||
|
# * Neither the name of Google Inc. nor the names of its |
||||
|
# contributors may be used to endorse or promote products derived |
||||
|
# from this software without specific prior written permission. |
||||
|
# |
||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
|
||||
|
import test |
||||
|
import os |
||||
|
import shutil |
||||
|
from shutil import rmtree |
||||
|
from os import mkdir |
||||
|
from glob import glob |
||||
|
from os.path import join, dirname, exists |
||||
|
import re |
||||
|
import shlex |
||||
|
|
||||
|
FAKETIME_FLAGS_PATTERN = re.compile(r"//\s+FaketimeFlags:(.*)") |
||||
|
FAKETIME_BIN_PATH = os.path.join("tools", "faketime", "src", "faketime") |
||||
|
|
||||
|
class TimersTestCase(test.TestCase): |
||||
|
|
||||
|
def __init__(self, path, file, mode, context, config): |
||||
|
super(TimersTestCase, self).__init__(context, path, mode) |
||||
|
self.file = file |
||||
|
self.config = config |
||||
|
self.mode = mode |
||||
|
|
||||
|
def GetLabel(self): |
||||
|
return "%s %s" % (self.mode, self.GetName()) |
||||
|
|
||||
|
def GetName(self): |
||||
|
return self.path[-1] |
||||
|
|
||||
|
def GetCommand(self): |
||||
|
result = [FAKETIME_BIN_PATH]; |
||||
|
|
||||
|
source = open(self.file).read() |
||||
|
faketime_flags_match = FAKETIME_FLAGS_PATTERN.search(source) |
||||
|
if faketime_flags_match: |
||||
|
result += shlex.split(faketime_flags_match.group(1).strip()) |
||||
|
|
||||
|
result += [self.config.context.GetVm(self.mode)] |
||||
|
result += [self.file] |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
def GetSource(self): |
||||
|
return open(self.file).read() |
||||
|
|
||||
|
|
||||
|
class TimersTestConfiguration(test.TestConfiguration): |
||||
|
|
||||
|
def __init__(self, context, root): |
||||
|
super(TimersTestConfiguration, self).__init__(context, root) |
||||
|
|
||||
|
def Ls(self, path): |
||||
|
def SelectTest(name): |
||||
|
return name.startswith('test-') and name.endswith('.js') |
||||
|
return [f[:-3] for f in os.listdir(path) if SelectTest(f)] |
||||
|
|
||||
|
def ListTests(self, current_path, path, mode): |
||||
|
all_tests = [current_path + [t] for t in self.Ls(join(self.root))] |
||||
|
result = [] |
||||
|
for test in all_tests: |
||||
|
if self.Contains(path, test): |
||||
|
file_path = join(self.root, reduce(join, test[1:], "") + ".js") |
||||
|
result.append(TimersTestCase(test, file_path, mode, self.context, self)) |
||||
|
return result |
||||
|
|
||||
|
def GetBuildRequirements(self): |
||||
|
return ['sample', 'sample=shell'] |
||||
|
|
||||
|
def GetTestStatus(self, sections, defs): |
||||
|
status_file = join(self.root, 'simple.status') |
||||
|
if exists(status_file): |
||||
|
test.ReadConfigurationInto(status_file, sections, defs) |
||||
|
|
||||
|
|
||||
|
|
||||
|
def GetConfiguration(context, root): |
||||
|
return TimersTestConfiguration(context, root) |
@ -0,0 +1,20 @@ |
|||||
|
FAKETIME_REPO := git://github.com/wolfcw/libfaketime.git |
||||
|
FAKETIME_LOCAL_REPO := $(CURDIR)/faketime |
||||
|
FAKETIME_BRANCH := master |
||||
|
FAKETIME_BINARY := $(FAKETIME_PREFIX)/bin/faketime |
||||
|
|
||||
|
.PHONY: faketime |
||||
|
|
||||
|
faketime: $(FAKETIME_BINARY) |
||||
|
|
||||
|
clean: |
||||
|
$(RM) -r $(FAKETIME_LOCAL_REPO) |
||||
|
|
||||
|
$(FAKETIME_BINARY): $(FAKETIME_LOCAL_REPO) |
||||
|
cd $(FAKETIME_LOCAL_REPO) && \
|
||||
|
git checkout $(FAKETIME_BRANCH) && \
|
||||
|
PREFIX=$(FAKETIME_LOCAL_REPO)/src make |
||||
|
|
||||
|
$(FAKETIME_LOCAL_REPO): |
||||
|
git clone $(FAKETIME_REPO) $(FAKETIME_LOCAL_REPO) |
||||
|
|
Loading…
Reference in new issue