mirror of https://github.com/lukechilds/node.git
Browse Source
Due to the race window between the master's "disconnect" message and the worker's "handle received" message, connections sometimes got stuck in the pending handles queue when calling `worker.disconnect()` in the master process. The observable effect from the client's perspective was a TCP or HTTP connection that simply stalled. This commit fixes that by closing open handles in the master when the "disconnect" message is sent. Fixes: https://github.com/nodejs/node/issues/3551 PR-URL: https://github.com/nodejs/node/pull/3677 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Fedor Indutny <fedor@indutny.com> Reviewed-By: James M Snell <jasnell@gmail.com>v4.x
Ben Noordhuis
9 years ago
committed by
James M Snell
4 changed files with 96 additions and 23 deletions
@ -0,0 +1,4 @@ |
|||
'use strict'; |
|||
|
|||
// Used in tests.
|
|||
exports.handles = {}; |
@ -0,0 +1,65 @@ |
|||
/* eslint-disable no-debugger */ |
|||
// Flags: --expose_internals
|
|||
'use strict'; |
|||
|
|||
const common = require('../common'); |
|||
const assert = require('assert'); |
|||
const cluster = require('cluster'); |
|||
const net = require('net'); |
|||
|
|||
const Protocol = require('_debugger').Protocol; |
|||
|
|||
if (common.isWindows) { |
|||
console.log('1..0 # Skipped: SCHED_RR not reliable on Windows'); |
|||
return; |
|||
} |
|||
|
|||
cluster.schedulingPolicy = cluster.SCHED_RR; |
|||
|
|||
// Worker sends back a "I'm here" message, then immediately suspends
|
|||
// inside the debugger. The master connects to the debug agent first,
|
|||
// connects to the TCP server second, then disconnects the worker and
|
|||
// unsuspends it again. The ultimate goal of this tortured exercise
|
|||
// is to make sure the connection is still sitting in the master's
|
|||
// pending handle queue.
|
|||
if (cluster.isMaster) { |
|||
const handles = require('internal/cluster').handles; |
|||
// FIXME(bnoordhuis) lib/cluster.js scans the execArgv arguments for
|
|||
// debugger flags and renumbers any port numbers it sees starting
|
|||
// from the default port 5858. Add a '.' that circumvents the
|
|||
// scanner but is ignored by atoi(3). Heinous hack.
|
|||
cluster.setupMaster({ execArgv: [`--debug=${common.PORT}.`] }); |
|||
const worker = cluster.fork(); |
|||
worker.on('message', common.mustCall(message => { |
|||
assert.strictEqual(Array.isArray(message), true); |
|||
assert.strictEqual(message[0], 'listening'); |
|||
const address = message[1]; |
|||
const host = address.address; |
|||
const debugClient = net.connect({ host, port: common.PORT }); |
|||
const protocol = new Protocol(); |
|||
debugClient.setEncoding('utf8'); |
|||
debugClient.on('data', data => protocol.execute(data)); |
|||
debugClient.once('connect', common.mustCall(() => { |
|||
protocol.onResponse = common.mustCall(res => { |
|||
protocol.onResponse = () => {}; |
|||
const conn = net.connect({ host, port: address.port }); |
|||
conn.once('connect', common.mustCall(() => { |
|||
conn.destroy(); |
|||
assert.notDeepStrictEqual(handles, {}); |
|||
worker.disconnect(); |
|||
assert.deepStrictEqual(handles, {}); |
|||
const req = protocol.serialize({ command: 'continue' }); |
|||
debugClient.write(req); |
|||
})); |
|||
}); |
|||
})); |
|||
})); |
|||
process.on('exit', () => assert.deepStrictEqual(handles, {})); |
|||
} else { |
|||
const server = net.createServer(socket => socket.pipe(socket)); |
|||
server.listen(() => { |
|||
process.send(['listening', server.address()]); |
|||
debugger; |
|||
}); |
|||
process.on('disconnect', process.exit); |
|||
} |
Loading…
Reference in new issue