src: refactor timers to remove TimerWrap

Refactor Timers to behave more similarly to Immediates by having
a single uv_timer_t handle which is stored on the Environment.

No longer expose timers in a public binding and instead make
it part of the internalBinding.

PR-URL: https://github.com/nodejs/node/pull/20894
Fixes: https://github.com/nodejs/node/issues/10154
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
This commit is contained in:
Anatoli Papirovski 2018-05-13 17:42:22 +02:00
parent 6f63f8d730
commit 2930bd1317
No known key found for this signature in database
GPG Key ID: 614E2E1ABEB4B2C0
36 changed files with 218 additions and 320 deletions

View File

@ -238,7 +238,7 @@ resource's constructor.
```text
FSEVENTWRAP, FSREQWRAP, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPPARSER,
JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP,
SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TIMERWRAP, TTYWRAP,
SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TTYWRAP,
UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Timeout, Immediate, TickObject
```

View File

@ -6,6 +6,7 @@ const {
initHooksExist,
emitInit
} = require('internal/async_hooks');
const { internalBinding } = require('internal/bootstrap/loaders');
// Symbols for storing async id state.
const async_id_symbol = Symbol('asyncId');
const trigger_async_id_symbol = Symbol('triggerId');
@ -30,7 +31,8 @@ module.exports = {
kRefed,
initAsyncResource,
setUnrefTimeout,
validateTimerDuration
validateTimerDuration,
getLibuvNow: internalBinding('timers').getLibuvNow,
};
var timers;

View File

@ -21,10 +21,15 @@
'use strict';
const { internalBinding } = require('internal/bootstrap/loaders');
const {
Timer: TimerWrap,
getLibuvNow,
setupTimers,
} = process.binding('timer_wrap');
scheduleTimer,
toggleTimerRef,
immediateInfo,
toggleImmediateRef
} = internalBinding('timers');
const L = require('internal/linkedlist');
const PriorityQueue = require('internal/priority_queue');
const {
@ -53,8 +58,9 @@ const kCount = 0;
const kRefCount = 1;
const kHasOutstanding = 2;
const [immediateInfo, toggleImmediateRef] =
setupTimers(processImmediate, processTimers);
// Call into C++ to assign callbacks that are responsible for processing
// Immediates and TimerLists.
setupTimers(processImmediate, processTimers);
// HOW and WHY the timers implementation works the way it does.
//
@ -156,7 +162,6 @@ function setPosition(node, pos) {
node.priorityQueuePosition = pos;
}
let handle = null;
let nextExpiry = Infinity;
let timerListId = Number.MIN_SAFE_INTEGER;
@ -164,39 +169,31 @@ let refCount = 0;
function incRefCount() {
if (refCount++ === 0)
handle.ref();
toggleTimerRef(true);
}
function decRefCount() {
if (--refCount === 0)
handle.unref();
}
function createHandle(refed) {
debug('initial run, creating TimerWrap handle');
handle = new TimerWrap();
if (!refed)
handle.unref();
toggleTimerRef(false);
}
// Schedule or re-schedule a timer.
// The item must have been enroll()'d first.
const active = exports.active = function(item) {
insert(item, true, TimerWrap.now());
insert(item, true, getLibuvNow());
};
// Internal APIs that need timeouts should use `_unrefActive()` instead of
// `active()` so that they do not unnecessarily keep the process open.
exports._unrefActive = function(item) {
insert(item, false, TimerWrap.now());
insert(item, false, getLibuvNow());
};
// The underlying logic for scheduling or re-scheduling a timer.
//
// Appends a timer onto the end of an existing timers list, or creates a new
// TimerWrap backed list if one does not already exist for the specified timeout
// duration.
// list if one does not already exist for the specified timeout duration.
function insert(item, refed, start) {
const msecs = item._idleTimeout;
if (msecs < 0 || msecs === undefined)
@ -213,9 +210,7 @@ function insert(item, refed, start) {
queue.insert(list);
if (nextExpiry > expiry) {
if (handle === null)
createHandle(refed);
handle.start(msecs);
scheduleTimer(msecs);
nextExpiry = expiry;
}
}
@ -252,32 +247,23 @@ function processTimers(now) {
let list, ran;
while (list = queue.peek()) {
if (list.expiry > now)
break;
if (list.expiry > now) {
nextExpiry = list.expiry;
return refCount > 0 ? nextExpiry : -nextExpiry;
}
if (ran)
runNextTicks();
else
ran = true;
listOnTimeout(list, now);
ran = true;
}
if (refCount > 0)
handle.ref();
else
handle.unref();
if (list !== undefined) {
nextExpiry = list.expiry;
handle.start(Math.max(nextExpiry - TimerWrap.now(), 1));
}
return true;
return 0;
}
function listOnTimeout(list, now) {
const msecs = list.msecs;
debug('timeout callback %d', msecs);
debug('now: %d', now);
var diff, timer;
while (timer = L.peek(list)) {
@ -336,7 +322,7 @@ function listOnTimeout(list, now) {
// 4.7) what is in this smaller function.
function tryOnTimeout(timer, start) {
if (start === undefined && timer._repeat)
start = TimerWrap.now();
start = getLibuvNow();
try {
ontimeout(timer);
} finally {
@ -474,7 +460,7 @@ function ontimeout(timer) {
}
function rearm(timer, start) {
// // Do not re-arm unenroll'd or closed timers.
// Do not re-arm unenroll'd or closed timers.
if (timer._idleTimeout === -1)
return;

View File

@ -371,7 +371,7 @@
'src/stream_pipe.cc',
'src/stream_wrap.cc',
'src/tcp_wrap.cc',
'src/timer_wrap.cc',
'src/timers.cc',
'src/tracing/agent.cc',
'src/tracing/node_trace_buffer.cc',
'src/tracing/node_trace_writer.cc',

View File

@ -63,7 +63,6 @@ namespace node {
V(TCPCONNECTWRAP) \
V(TCPSERVERWRAP) \
V(TCPWRAP) \
V(TIMERWRAP) \
V(TTYWRAP) \
V(UDPSENDWRAP) \
V(UDPWRAP) \

View File

@ -334,6 +334,14 @@ inline tracing::Agent* Environment::tracing_agent() const {
return tracing_agent_;
}
inline Environment* Environment::from_timer_handle(uv_timer_t* handle) {
return ContainerOf(&Environment::timer_handle_, handle);
}
inline uv_timer_t* Environment::timer_handle() {
return &timer_handle_;
}
inline Environment* Environment::from_immediate_check_handle(
uv_check_t* handle) {
return ContainerOf(&Environment::immediate_check_handle_, handle);

View File

@ -13,6 +13,7 @@
namespace node {
using v8::Context;
using v8::Function;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Integer;
@ -25,6 +26,7 @@ using v8::StackFrame;
using v8::StackTrace;
using v8::String;
using v8::Symbol;
using v8::TryCatch;
using v8::Value;
using worker::Worker;
@ -173,6 +175,9 @@ void Environment::Start(int argc,
HandleScope handle_scope(isolate());
Context::Scope context_scope(context());
CHECK_EQ(0, uv_timer_init(event_loop(), timer_handle()));
uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle()));
uv_check_init(event_loop(), immediate_check_handle());
uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
@ -227,6 +232,10 @@ void Environment::RegisterHandleCleanups() {
env->CloseHandle(handle, [](uv_handle_t* handle) {});
};
RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(timer_handle()),
close_and_finish,
nullptr);
RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(immediate_check_handle()),
close_and_finish,
@ -470,6 +479,78 @@ void Environment::RunAndClearNativeImmediates() {
}
void Environment::ScheduleTimer(int64_t duration_ms) {
uv_timer_start(timer_handle(), RunTimers, duration_ms, 0);
}
void Environment::ToggleTimerRef(bool ref) {
if (ref) {
uv_ref(reinterpret_cast<uv_handle_t*>(timer_handle()));
} else {
uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle()));
}
}
void Environment::RunTimers(uv_timer_t* handle) {
Environment* env = Environment::from_timer_handle(handle);
if (!env->can_call_into_js())
return;
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
Local<Object> process = env->process_object();
InternalCallbackScope scope(env, process, {0, 0});
Local<Function> cb = env->timers_callback_function();
MaybeLocal<Value> ret;
Local<Value> arg = env->GetNow();
// This code will loop until all currently due timers will process. It is
// impossible for us to end up in an infinite loop due to how the JS-side
// is structured.
do {
TryCatch try_catch(env->isolate());
try_catch.SetVerbose(true);
ret = cb->Call(env->context(), process, 1, &arg);
} while (ret.IsEmpty() && env->can_call_into_js());
// NOTE(apapirovski): If it ever becomes possibble that `call_into_js` above
// is reset back to `true` after being previously set to `false` then this
// code becomes invalid and needs to be rewritten. Otherwise catastrophic
// timers corruption will occurr and all timers behaviour will become
// entirely unpredictable.
if (ret.IsEmpty())
return;
// To allow for less JS-C++ boundary crossing, the value returned from JS
// serves a few purposes:
// 1. If it's 0, no more timers exist and the handle should be unrefed
// 2. If it's > 0, the value represents the next timer's expiry and there
// is at least one timer remaining that is refed.
// 3. If it's < 0, the absolute value represents the next timer's expiry
// and there are no timers that are refed.
int64_t expiry_ms =
ret.ToLocalChecked()->IntegerValue(env->context()).FromJust();
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(handle);
if (expiry_ms != 0) {
int64_t duration_ms =
llabs(expiry_ms) - (uv_now(env->event_loop()) - env->timer_base());
env->ScheduleTimer(duration_ms > 0 ? duration_ms : 1);
if (expiry_ms > 0)
uv_ref(h);
else
uv_unref(h);
} else {
uv_unref(h);
}
}
void Environment::CheckImmediate(uv_check_t* handle) {
Environment* env = Environment::from_immediate_check_handle(handle);

View File

@ -628,6 +628,9 @@ class Environment {
inline uv_loop_t* event_loop() const;
inline uint32_t watched_providers() const;
static inline Environment* from_timer_handle(uv_timer_t* handle);
inline uv_timer_t* timer_handle();
static inline Environment* from_immediate_check_handle(uv_check_t* handle);
inline uv_check_t* immediate_check_handle();
inline uv_idle_t* immediate_idle_handle();
@ -840,6 +843,8 @@ class Environment {
static inline Environment* ForAsyncHooks(AsyncHooks* hooks);
v8::Local<v8::Value> GetNow();
void ScheduleTimer(int64_t duration);
void ToggleTimerRef(bool ref);
inline void AddCleanupHook(void (*fn)(void*), void* arg);
inline void RemoveCleanupHook(void (*fn)(void*), void* arg);
@ -857,6 +862,7 @@ class Environment {
v8::Isolate* const isolate_;
IsolateData* const isolate_data_;
tracing::Agent* const tracing_agent_;
uv_timer_t timer_handle_;
uv_check_t immediate_check_handle_;
uv_idle_t immediate_idle_handle_;
uv_prepare_t idle_prepare_handle_;
@ -919,6 +925,8 @@ class Environment {
worker::Worker* worker_context_ = nullptr;
static void RunTimers(uv_timer_t* handle);
struct ExitCallback {
void (*cb_)(void* arg);
void* arg_;

View File

@ -129,7 +129,7 @@ struct sockaddr;
V(string_decoder) \
V(symbols) \
V(tcp_wrap) \
V(timer_wrap) \
V(timers) \
V(trace_events) \
V(tty_wrap) \
V(types) \

64
src/timers.cc Normal file
View File

@ -0,0 +1,64 @@
#include "node_internals.h"
#include <stdint.h>
namespace node {
namespace {
using v8::Array;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::Value;
void SetupTimers(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsFunction());
CHECK(args[1]->IsFunction());
auto env = Environment::GetCurrent(args);
env->set_immediate_callback_function(args[0].As<Function>());
env->set_timers_callback_function(args[1].As<Function>());
}
void GetLibuvNow(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
args.GetReturnValue().Set(env->GetNow());
}
void ScheduleTimer(const FunctionCallbackInfo<Value>& args) {
auto env = Environment::GetCurrent(args);
env->ScheduleTimer(args[0]->IntegerValue(env->context()).FromJust());
}
void ToggleTimerRef(const FunctionCallbackInfo<Value>& args) {
Environment::GetCurrent(args)->ToggleTimerRef(args[0]->IsTrue());
}
void ToggleImmediateRef(const FunctionCallbackInfo<Value>& args) {
Environment::GetCurrent(args)->ToggleImmediateRef(args[0]->IsTrue());
}
void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context) {
Environment* env = Environment::GetCurrent(context);
env->SetMethod(target, "getLibuvNow", GetLibuvNow);
env->SetMethod(target, "setupTimers", SetupTimers);
env->SetMethod(target, "scheduleTimer", ScheduleTimer);
env->SetMethod(target, "toggleTimerRef", ToggleTimerRef);
env->SetMethod(target, "toggleImmediateRef", ToggleImmediateRef);
target->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
env->immediate_info()->fields().GetJSArray()).FromJust();
}
} // anonymous namespace
} // namespace node
NODE_MODULE_CONTEXT_AWARE_INTERNAL(timers, node::Initialize)

View File

@ -78,9 +78,8 @@ assert.throws(function() {
}));
});
} else if (arg === 2) {
// setTimeout runs via the TimerWrap, which runs through
// AsyncWrap::MakeCallback(). Make sure there are no conflicts using
// node::MakeCallback() within it.
// Make sure there are no conflicts using node::MakeCallback()
// within timers.
setTimeout(common.mustCall(function() {
verifyExecutionOrder(3);
}), 10);

View File

@ -79,9 +79,8 @@ assert.throws(function() {
}));
});
} else if (arg === 2) {
// setTimeout runs via the TimerWrap, which runs through
// AsyncWrap::MakeCallback(). Make sure there are no conflicts using
// node::MakeCallback() within it.
// Make sure there are no conflicts using node::MakeCallback()
// within timers.
setTimeout(common.mustCall(function() {
verifyExecutionOrder(3);
}), 10);

View File

@ -23,7 +23,6 @@ Showing which kind of async resource is covered by which test:
| STATWATCHER | test-statwatcher.js |
| TCPCONNECTWRAP | test-tcpwrap.js |
| TCPWRAP | test-tcpwrap.js |
| TIMERWRAP | test-timerwrap.set{Timeout,Interval}.js|
| TLSWRAP | test-tlswrap.js |
| TTYWRAP | test-ttywrap.{read,write}stream.js |
| UDPSENDWRAP | test-udpsendwrap.js |

View File

@ -43,7 +43,6 @@ process.on('exit', function() {
triggerAsyncId: 'tcpserver:1' },
{ type: 'TCPWRAP', id: 'tcp:2', triggerAsyncId: 'tcpserver:1' },
{ type: 'Timeout', id: 'timeout:1', triggerAsyncId: 'tcp:2' },
{ type: 'TIMERWRAP', id: 'timer:1', triggerAsyncId: 'tcp:2' },
{ type: 'HTTPPARSER',
id: 'httpparser:3',
triggerAsyncId: 'tcp:2' },
@ -53,9 +52,6 @@ process.on('exit', function() {
{ type: 'Timeout',
id: 'timeout:2',
triggerAsyncId: 'httpparser:4' },
{ type: 'TIMERWRAP',
id: 'timer:2',
triggerAsyncId: 'httpparser:4' },
{ type: 'SHUTDOWNWRAP',
id: 'shutdown:1',
triggerAsyncId: 'tcp:2' } ]

View File

@ -30,8 +30,6 @@ function onexit() {
verifyGraph(
hooks,
[ { type: 'Timeout', id: 'timeout:1', triggerAsyncId: null },
{ type: 'TIMERWRAP', id: 'timer:1', triggerAsyncId: null },
{ type: 'Timeout', id: 'timeout:2', triggerAsyncId: 'timeout:1' },
{ type: 'TIMERWRAP', id: 'timer:2', triggerAsyncId: 'timeout:1' } ]
{ type: 'Timeout', id: 'timeout:2', triggerAsyncId: 'timeout:1' }]
);
}

View File

@ -26,10 +26,7 @@ function onexit() {
verifyGraph(
hooks,
[ { type: 'Timeout', id: 'timeout:1', triggerAsyncId: null },
{ type: 'TIMERWRAP', id: 'timer:1', triggerAsyncId: null },
{ type: 'Timeout', id: 'timeout:2', triggerAsyncId: 'timeout:1' },
{ type: 'TIMERWRAP', id: 'timer:2', triggerAsyncId: 'timeout:1' },
{ type: 'Timeout', id: 'timeout:3', triggerAsyncId: 'timeout:2' },
{ type: 'TIMERWRAP', id: 'timer:3', triggerAsyncId: 'timeout:2' } ]
{ type: 'Timeout', id: 'timeout:3', triggerAsyncId: 'timeout:2' }]
);
}

View File

@ -65,7 +65,6 @@ function onexit() {
{ type: 'WRITEWRAP', id: 'write:1', triggerAsyncId: 'tcpconnect:1' },
{ type: 'TCPWRAP', id: 'tcp:2', triggerAsyncId: 'tcpserver:1' },
{ type: 'TLSWRAP', id: 'tls:2', triggerAsyncId: 'tcpserver:1' },
{ type: 'TIMERWRAP', id: 'timer:1', triggerAsyncId: 'tcpserver:1' },
{ type: 'WRITEWRAP', id: 'write:2', triggerAsyncId: null },
{ type: 'WRITEWRAP', id: 'write:3', triggerAsyncId: null },
{ type: 'WRITEWRAP', id: 'write:4', triggerAsyncId: null },

View File

@ -1,56 +0,0 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const tick = require('./tick');
const initHooks = require('./init-hooks');
const { checkInvocations } = require('./hook-checks');
const TIMEOUT = 1;
const hooks = initHooks();
hooks.enable();
let count = 0;
const iv = setInterval(common.mustCall(oninterval, 3), TIMEOUT);
const as = hooks.activitiesOfTypes('TIMERWRAP');
assert.strictEqual(as.length, 1);
const t = as[0];
assert.strictEqual(t.type, 'TIMERWRAP');
assert.strictEqual(typeof t.uid, 'number');
assert.strictEqual(typeof t.triggerAsyncId, 'number');
checkInvocations(t, { init: 1 }, 't: when first timer installed');
function oninterval() {
count++;
assert.strictEqual(as.length, 1);
switch (count) {
case 1: {
checkInvocations(t, { init: 1, before: 1 },
't: when first timer triggered first time');
break;
}
case 2: {
checkInvocations(t, { init: 1, before: 2, after: 1 },
't: when first timer triggered second time');
break;
}
case 3: {
clearInterval(iv);
checkInvocations(t, { init: 1, before: 3, after: 2 },
't: when first timer triggered third time');
tick(2);
break;
}
}
}
process.on('exit', onexit);
function onexit() {
hooks.disable();
hooks.sanityCheck('TIMERWRAP');
checkInvocations(t, { init: 1, before: 3, after: 3 },
't: when process exits');
}

View File

@ -1,59 +0,0 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const tick = require('./tick');
const initHooks = require('./init-hooks');
const { checkInvocations } = require('./hook-checks');
const TIMEOUT = common.platformTimeout(100);
const hooks = initHooks();
hooks.enable();
// install first timeout
setTimeout(common.mustCall(ontimeout), TIMEOUT);
const as = hooks.activitiesOfTypes('TIMERWRAP');
assert.strictEqual(as.length, 1);
const t1 = as[0];
assert.strictEqual(t1.type, 'TIMERWRAP');
assert.strictEqual(typeof t1.uid, 'number');
assert.strictEqual(typeof t1.triggerAsyncId, 'number');
checkInvocations(t1, { init: 1 }, 't1: when first timer installed');
function ontimeout() {
checkInvocations(t1, { init: 1, before: 1 }, 't1: when first timer fired');
setTimeout(onsecondTimeout, TIMEOUT);
const as = hooks.activitiesOfTypes('TIMERWRAP');
assert.strictEqual(as.length, 1);
checkInvocations(t1, { init: 1, before: 1 },
't1: when second timer installed');
}
function onsecondTimeout() {
const as = hooks.activitiesOfTypes('TIMERWRAP');
assert.strictEqual(as.length, 1);
checkInvocations(t1, { init: 1, before: 2, after: 1 },
't1: when second timer fired');
// install third timeout with different TIMEOUT
setTimeout(onthirdTimeout, TIMEOUT + 1);
checkInvocations(t1, { init: 1, before: 2, after: 1 },
't1: when third timer installed');
}
function onthirdTimeout() {
checkInvocations(t1, { init: 1, before: 3, after: 2 },
't1: when third timer fired');
tick(2);
}
process.on('exit', onexit);
function onexit() {
hooks.disable();
hooks.sanityCheck('TIMERWRAP');
checkInvocations(t1, { init: 1, before: 3, after: 3 },
't1: when process exits');
}

View File

@ -42,7 +42,7 @@ class TestHandleWrap : public node::HandleWrap {
: node::HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(handle),
node::AsyncWrap::PROVIDER_TIMERWRAP) {}
node::AsyncWrap::PROVIDER_TCPWRAP) {}
};
@ -53,7 +53,7 @@ class TestReqWrap : public node::ReqWrap<uv_req_t> {
TestReqWrap(node::Environment* env, v8::Local<v8::Object> object)
: node::ReqWrap<uv_req_t>(env,
object,
node::AsyncWrap::PROVIDER_TIMERWRAP) {}
node::AsyncWrap::PROVIDER_FSREQWRAP) {}
};
TEST_F(DebugSymbolsTest, ContextEmbedderEnvironmentIndex) {

View File

@ -29,7 +29,6 @@ const os = require('os');
const { exec, execSync, spawn, spawnSync } = require('child_process');
const stream = require('stream');
const util = require('util');
const Timer = process.binding('timer_wrap').Timer;
const { hasTracing } = process.binding('config');
const { fixturesDir } = require('./fixtures');
const tmpdir = require('./tmpdir');
@ -600,9 +599,9 @@ exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) {
};
exports.busyLoop = function busyLoop(time) {
const startTime = Timer.now();
const startTime = Date.now();
const stopTime = startTime + time;
while (Timer.now() < stopTime) {}
while (Date.now() < stopTime) {}
};
exports.isAlive = function isAlive(pid) {

View File

@ -6,4 +6,4 @@ ReferenceError: undefined_reference_error_maker is not defined
at ontimeout (timers.js:*:*)
at tryOnTimeout (timers.js:*:*)
at listOnTimeout (timers.js:*:*)
at Timer.processTimers (timers.js:*:*)
at processTimers (timers.js:*:*)

View File

@ -110,25 +110,4 @@ const dgram = require('dgram');
}
// timers
{
const { Timer } = process.binding('timer_wrap');
strictEqual(process._getActiveHandles().filter(
(handle) => (handle instanceof Timer)).length, 0);
const timer = setTimeout(() => {}, 500);
const handles = process._getActiveHandles().filter(
(handle) => (handle instanceof Timer));
strictEqual(handles.length, 1);
const handle = handles[0];
strictEqual(Object.getPrototypeOf(handle).hasOwnProperty('hasRef'),
true, 'timer_wrap: hasRef() missing');
strictEqual(handle.hasRef(), true);
timer.unref();
strictEqual(handle.hasRef(),
false, 'timer_wrap: unref() ineffective');
timer.ref();
strictEqual(handle.hasRef(),
true, 'timer_wrap: ref() ineffective');
}
// see also test/pseudo-tty/test-handle-wrap-isrefed-tty.js

View File

@ -28,7 +28,7 @@ function post(message, data) {
function generateTrace() {
return new Promise((resolve) => setTimeout(() => {
for (let i = 0; i << 1000000; i++) {
for (let i = 0; i < 1000000; i++) {
'test' + i;
}
resolve();
@ -52,7 +52,7 @@ async function test() {
'node.perf.timerify', 'v8'],
categories);
const traceConfig = { includedCategories: ['node'] };
const traceConfig = { includedCategories: ['v8'] };
await post('NodeTracing.start', { traceConfig });
for (let i = 0; i < 5; i++)

View File

@ -1,11 +0,0 @@
'use strict';
const common = require('../common');
// Make sure handle._handle.close(callback) is idempotent by closing a timer
// twice. The first function should be called, the second one should not.
const Timer = process.binding('timer_wrap').Timer;
const t = new Timer();
t.close(common.mustCall());
t.close(common.mustNotCall());

View File

@ -1,8 +1,9 @@
'use strict';
// Flags: --expose-internals
require('../common');
const assert = require('assert');
const { getLibuvNow } = require('internal/timers');
// Return value of Timer.now() should easily fit in a SMI right after start-up.
const Timer = process.binding('timer_wrap').Timer;
assert(Timer.now() < 0x3ffffff);
// Return value of getLibuvNow() should easily fit in a SMI after start-up.
assert(getLibuvNow() < 0x3ffffff);

View File

@ -19,11 +19,13 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const { getLibuvNow } = require('internal/timers');
const Timer = process.binding('timer_wrap').Timer;
const N = 30;
let last_i = 0;
@ -36,8 +38,7 @@ function f(i) {
last_i = i;
// check that this iteration is fired at least 1ms later than the previous
const now = Timer.now();
console.log(i, now);
const now = getLibuvNow();
assert(now >= last_ts + 1,
`current ts ${now} < prev ts ${last_ts} + 1`);
last_ts = now;
@ -46,4 +47,4 @@ function f(i) {
setTimeout(f, 1, i + 1);
}
}
f(1);
setTimeout(f, 1, 1);

View File

@ -1,13 +0,0 @@
'use strict';
require('../common');
const Timer = process.binding('timer_wrap').Timer;
Timer.now = function() { return ++Timer.now.ticks; };
Timer.now.ticks = 0;
const t = setInterval(() => {}, 1);
const o = { _idleStart: 0, _idleTimeout: 1 };
t.unref.call(o);
setTimeout(clearInterval.bind(null, t), 2);

View File

@ -40,8 +40,6 @@ proc.once('exit', common.mustCall(() => {
return false;
if (trace.cat !== 'node,node.async_hooks')
return false;
if (trace.name !== 'TIMERWRAP')
return false;
return true;
}));

View File

@ -42,8 +42,6 @@ proc.once('exit', common.mustCall(() => {
return false;
if (trace.cat !== 'node,node.async_hooks')
return false;
if (trace.name !== 'TIMERWRAP')
return false;
return true;
}));

View File

@ -42,8 +42,6 @@ proc.once('exit', common.mustCall(() => {
return false;
if (trace.cat !== 'node.async_hooks')
return false;
if (trace.name !== 'TIMERWRAP')
return false;
return true;
}));

View File

@ -1,35 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const Timer = process.binding('timer_wrap').Timer;
const kOnTimeout = Timer.kOnTimeout;
const t = new Timer();
t.start(1000);
t[kOnTimeout] = common.mustCall(function() {
console.log('timeout');
t.close();
});

View File

@ -1,29 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
// Test that allocating a timer does not increase the loop's reference
// count.
const Timer = process.binding('timer_wrap').Timer;
new Timer();

View File

@ -256,12 +256,6 @@ if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
}
{
const TimerWrap = process.binding('timer_wrap').Timer;
testInitialized(new TimerWrap(), 'Timer');
}
if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
const { TCP, constants: TCPConstants } = process.binding('tcp_wrap');
const tcp = new TCP(TCPConstants.SOCKET);

View File

@ -25,7 +25,6 @@
const common = require('../common');
const assert = require('assert');
const Timer = process.binding('timer_wrap').Timer;
const TIMEOUT = 100;
@ -49,7 +48,7 @@ function blockingCallback(retry, callback) {
++nbBlockingCallbackCalls;
if (nbBlockingCallbackCalls > 1) {
latestDelay = Timer.now() - timeCallbackScheduled;
latestDelay = Date.now() - timeCallbackScheduled;
// Even if timers can fire later than when they've been scheduled
// to fire, they shouldn't generally be more than 100% late in this case.
// But they are guaranteed to be at least 100ms late given the bug in
@ -68,7 +67,7 @@ function blockingCallback(retry, callback) {
// block by busy-looping to trigger the issue
common.busyLoop(TIMEOUT);
timeCallbackScheduled = Timer.now();
timeCallbackScheduled = Date.now();
setTimeout(blockingCallback.bind(null, retry, callback), TIMEOUT);
}
}

View File

@ -1,6 +1,5 @@
'use strict';
const common = require('../common');
const Timer = process.binding('timer_wrap').Timer;
const assert = require('assert');
let cntr = 0;
@ -11,9 +10,9 @@ const t = setInterval(() => {
common.busyLoop(100);
// ensure that the event loop passes before the second interval
setImmediate(() => assert.strictEqual(cntr, 1));
first = Timer.now();
first = Date.now();
} else if (cntr === 2) {
assert(Timer.now() - first < 100);
assert(Date.now() - first < 100);
clearInterval(t);
}
}, 100);