-
Notifications
You must be signed in to change notification settings - Fork 30.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net: use actual Timeout instances on Sockets #11154
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
'use strict'; | ||
|
||
const async_wrap = process.binding('async_wrap'); | ||
// Two arrays that share state between C++ and JS. | ||
const { async_hook_fields, async_id_fields } = async_wrap; | ||
const { | ||
initTriggerId, | ||
// The needed emit*() functions. | ||
emitInit | ||
} = require('internal/async_hooks'); | ||
// Grab the constants necessary for working with internal arrays. | ||
const { kInit, kAsyncIdCounter } = async_wrap.constants; | ||
// Symbols for storing async id state. | ||
const async_id_symbol = Symbol('asyncId'); | ||
const trigger_async_id_symbol = Symbol('triggerId'); | ||
|
||
const errors = require('internal/errors'); | ||
|
||
// Timeout values > TIMEOUT_MAX are set to 1. | ||
const TIMEOUT_MAX = 2147483647; // 2^31-1 | ||
|
||
module.exports = { | ||
TIMEOUT_MAX, | ||
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals. | ||
async_id_symbol, | ||
trigger_async_id_symbol, | ||
Timeout, | ||
setUnrefTimeout, | ||
}; | ||
|
||
// Timer constructor function. | ||
// The entire prototype is defined in lib/timers.js | ||
function Timeout(after, callback, args) { | ||
this._called = false; | ||
this._idleTimeout = after; | ||
this._idlePrev = this; | ||
this._idleNext = this; | ||
this._idleStart = null; | ||
this._onTimeout = callback; | ||
this._timerArgs = args; | ||
this._repeat = null; | ||
this._destroyed = false; | ||
this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter]; | ||
this[trigger_async_id_symbol] = initTriggerId(); | ||
if (async_hook_fields[kInit] > 0) | ||
emitInit( | ||
this[async_id_symbol], 'Timeout', this[trigger_async_id_symbol], this | ||
); | ||
} | ||
|
||
var timers; | ||
function getTimers() { | ||
if (timers === undefined) { | ||
timers = require('timers'); | ||
} | ||
return timers; | ||
} | ||
|
||
function setUnrefTimeout(callback, after) { | ||
// Type checking identical to setTimeout() | ||
if (typeof callback !== 'function') { | ||
throw new errors.TypeError('ERR_INVALID_CALLBACK'); | ||
} | ||
|
||
after *= 1; // coalesce to number or NaN | ||
if (!(after >= 1 && after <= TIMEOUT_MAX)) { | ||
if (after > TIMEOUT_MAX) { | ||
process.emitWarning(`${after} does not fit into` + | ||
' a 32-bit signed integer.' + | ||
'\nTimeout duration was set to 1.', | ||
'TimeoutOverflowWarning'); | ||
} | ||
after = 1; // schedule on next tick, follows browser behavior | ||
} | ||
|
||
const timer = new Timeout(after, callback, null); | ||
if (process.domain) | ||
timer.domain = process.domain; | ||
|
||
getTimers()._unrefActive(timer); | ||
|
||
return timer; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,8 @@ var cluster = null; | |
const errnoException = util._errnoException; | ||
const exceptionWithHostPort = util._exceptionWithHostPort; | ||
|
||
const { kTimeout, TIMEOUT_MAX, setUnrefTimeout } = require('internal/timers'); | ||
|
||
function noop() {} | ||
|
||
function createHandle(fd) { | ||
|
@@ -188,6 +190,7 @@ function Socket(options) { | |
this._handle = null; | ||
this._parent = null; | ||
this._host = null; | ||
this[kTimeout] = null; | ||
|
||
if (typeof options === 'number') | ||
options = { fd: options }; // Legacy interface. | ||
|
@@ -259,9 +262,12 @@ function Socket(options) { | |
} | ||
util.inherits(Socket, stream.Duplex); | ||
|
||
// Refresh existing timeouts. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In what way does this “refresh” timeouts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If your run @addaleax Can you think of a wording that would be more helpful based on that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thanks – I think the comment is fine, maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would agree. |
||
Socket.prototype._unrefTimer = function _unrefTimer() { | ||
for (var s = this; s !== null; s = s._parent) | ||
timers._unrefActive(s); | ||
for (var s = this; s !== null; s = s._parent) { | ||
if (s[kTimeout]) | ||
timers._unrefActive(s[kTimeout]); | ||
} | ||
}; | ||
|
||
// the user has called .end(), and all the bytes have been | ||
|
@@ -380,14 +386,36 @@ Socket.prototype.listen = function() { | |
|
||
|
||
Socket.prototype.setTimeout = function(msecs, callback) { | ||
// Type checking identical to timers.enroll() | ||
if (typeof msecs !== 'number') { | ||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'msecs', | ||
'number', msecs); | ||
} | ||
|
||
if (msecs < 0 || !isFinite(msecs)) { | ||
throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE', 'msecs', | ||
'a non-negative finite number', msecs); | ||
} | ||
|
||
// Ensure that msecs fits into signed int32 | ||
if (msecs > TIMEOUT_MAX) { | ||
process.emitWarning(`${msecs} does not fit into a 32-bit signed integer.` + | ||
`\nTimer duration was truncated to ${TIMEOUT_MAX}.`, | ||
'TimeoutOverflowWarning'); | ||
msecs = TIMEOUT_MAX; | ||
} | ||
|
||
if (msecs === 0) { | ||
timers.unenroll(this); | ||
clearTimeout(this[kTimeout]); | ||
|
||
if (callback) { | ||
this.removeListener('timeout', callback); | ||
} | ||
} else { | ||
timers.enroll(this, msecs); | ||
timers._unrefActive(this); | ||
this[kTimeout] = setUnrefTimeout(() => { | ||
this._onTimeout(); | ||
}, msecs); | ||
|
||
if (callback) { | ||
this.once('timeout', callback); | ||
} | ||
|
@@ -542,8 +570,9 @@ Socket.prototype._destroy = function(exception, cb) { | |
|
||
this.readable = this.writable = false; | ||
|
||
for (var s = this; s !== null; s = s._parent) | ||
timers.unenroll(s); | ||
for (var s = this; s !== null; s = s._parent) { | ||
clearTimeout(s[kTimeout]); | ||
} | ||
|
||
debug('close'); | ||
if (this._handle) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be placed at the bottom of the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? It works fine here, and other files have it like that. I don't see a good reason to change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe most of the other files have it at the bottom for a couple of reasons: it's easier to find and it works for situations where you are exporting something whose value is only set later on in the file. Having them all in the same location in each file helps with consistency.