Skip to content

Commit 934837c

Browse files
committed
src,lib: the handle keeps loop alive in cluster rr mode
1 parent 28fe494 commit 934837c

5 files changed

+86
-8
lines changed

lib/internal/cluster/child.js

+22-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const { owner_symbol } = require('internal/async_hooks').symbols;
1616
const Worker = require('internal/cluster/worker');
1717
const { internal, sendHelper } = require('internal/cluster/utils');
1818
const { exitCodes: { kNoFailure } } = internalBinding('errors');
19+
const { TIMEOUT_MAX } = require('internal/timers');
20+
const { setInterval, clearInterval } = require('timers');
1921

2022
const cluster = new EventEmitter();
2123
const handles = new SafeMap();
@@ -162,6 +164,21 @@ function rr(message, { indexesKey, index }, cb) {
162164

163165
let key = message.key;
164166

167+
let fakeHandle = null;
168+
169+
function ref() {
170+
if (!fakeHandle) {
171+
fakeHandle = setInterval(noop, TIMEOUT_MAX);
172+
}
173+
}
174+
175+
function unref() {
176+
if (fakeHandle) {
177+
clearInterval(fakeHandle);
178+
fakeHandle = null;
179+
}
180+
}
181+
165182
function listen(backlog) {
166183
// TODO(bnoordhuis) Send a message to the primary that tells it to
167184
// update the backlog size. The actual backlog should probably be
@@ -177,8 +194,7 @@ function rr(message, { indexesKey, index }, cb) {
177194
// the primary.
178195
if (key === undefined)
179196
return;
180-
181-
send({ act: 'close', key });
197+
send({ act: 'close', key, needAck: true }, () => unref());
182198
handles.delete(key);
183199
removeIndexesKey(indexesKey, index);
184200
key = undefined;
@@ -191,12 +207,10 @@ function rr(message, { indexesKey, index }, cb) {
191207
return 0;
192208
}
193209

194-
// Faux handle. Mimics a TCPWrap with just enough fidelity to get away
195-
// with it. Fools net.Server into thinking that it's backed by a real
196-
// handle. Use a noop function for ref() and unref() because the control
197-
// channel is going to keep the worker alive anyway.
198-
const handle = { close, listen, ref: noop, unref: noop };
199-
210+
// Faux handle. net.Server is not associated with handle,
211+
// so we control its state(ref or unref) by setInterval.
212+
const handle = { close, listen, ref, unref };
213+
handle.ref();
200214
if (message.sockname) {
201215
handle.getsockname = getsockname; // TCP handles only.
202216
}

lib/internal/cluster/primary.js

+3
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ function close(worker, message) {
342342

343343
if (handle && handle.remove(worker))
344344
handles.delete(key);
345+
if (message.needAck) {
346+
send(worker, { ack: message.seq });
347+
}
345348
}
346349

347350
function send(worker, message, handle, cb) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const cluster = require('cluster');
5+
const net = require('net');
6+
7+
cluster.schedulingPolicy = cluster.SCHED_RR;
8+
9+
if (cluster.isPrimary) {
10+
const worker = cluster.fork();
11+
worker.on('exit', common.mustCall());
12+
} else {
13+
const server = net.createServer(common.mustNotCall());
14+
server.listen(0, common.mustCall(() => {
15+
process.channel.unref();
16+
server.close();
17+
}));
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const cluster = require('cluster');
5+
const net = require('net');
6+
const assert = require('assert');
7+
8+
cluster.schedulingPolicy = cluster.SCHED_RR;
9+
10+
if (cluster.isPrimary) {
11+
let exited = false;
12+
const worker = cluster.fork();
13+
worker.on('exit', () => {
14+
exited = true;
15+
});
16+
setTimeout(() => {
17+
assert.ok(!exited);
18+
worker.kill();
19+
}, 3000);
20+
} else {
21+
const server = net.createServer(common.mustNotCall());
22+
server.listen(0, common.mustCall(() => process.channel.unref()));
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const cluster = require('cluster');
5+
const net = require('net');
6+
7+
cluster.schedulingPolicy = cluster.SCHED_RR;
8+
9+
if (cluster.isPrimary) {
10+
const worker = cluster.fork();
11+
worker.on('exit', common.mustCall());
12+
} else {
13+
const server = net.createServer(common.mustNotCall());
14+
server.listen(0, common.mustCall(() => {
15+
server.ref();
16+
server.unref();
17+
process.channel.unref();
18+
}));
19+
server.unref();
20+
}

0 commit comments

Comments
 (0)