Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 22b0971

Browse filesBrowse files
Trottrvagg
authored andcommitted
test: eliminate multicast test FreeBSD flakiness
test-dgram-multicast-multi-process was flaky on FreeBSD and Raspeberry Pi. This refactoring fixes the issue by eliminating a race condition. Fixes: #2474 PR-URL: #4042 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
1 parent c93e267 commit 22b0971
Copy full SHA for 22b0971

File tree

Expand file treeCollapse file tree

1 file changed

+150
-151
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

1 file changed

+150
-151
lines changed
Open diff view settings
Collapse file
+150-151Lines changed: 150 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,122 @@
11
'use strict';
2-
var common = require('../common'),
3-
assert = require('assert'),
4-
dgram = require('dgram'),
5-
util = require('util'),
6-
Buffer = require('buffer').Buffer,
7-
fork = require('child_process').fork,
8-
LOCAL_BROADCAST_HOST = '224.0.0.114',
9-
TIMEOUT = common.platformTimeout(5000),
10-
messages = [
11-
new Buffer('First message to send'),
12-
new Buffer('Second message to send'),
13-
new Buffer('Third message to send'),
14-
new Buffer('Fourth message to send')
15-
];
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const dgram = require('dgram');
5+
const fork = require('child_process').fork;
6+
const LOCAL_BROADCAST_HOST = '224.0.0.114';
7+
const TIMEOUT = common.platformTimeout(5000);
8+
const messages = [
9+
new Buffer('First message to send'),
10+
new Buffer('Second message to send'),
11+
new Buffer('Third message to send'),
12+
new Buffer('Fourth message to send')
13+
];
14+
const workers = {};
15+
const listeners = 3;
16+
17+
18+
// Skip test in FreeBSD jails.
19+
if (common.inFreeBSDJail) {
20+
console.log('1..0 # Skipped: In a FreeBSD jail');
21+
return;
22+
}
23+
24+
function launchChildProcess(index) {
25+
const worker = fork(__filename, ['child']);
26+
workers[worker.pid] = worker;
27+
28+
worker.messagesReceived = [];
29+
30+
// Handle the death of workers.
31+
worker.on('exit', function(code, signal) {
32+
// Don't consider this the true death if the worker has finished
33+
// successfully or if the exit code is 0.
34+
if (worker.isDone || code === 0) {
35+
return;
36+
}
37+
38+
dead += 1;
39+
console.error('[PARENT] Worker %d died. %d dead of %d',
40+
worker.pid,
41+
dead,
42+
listeners);
43+
44+
if (dead === listeners) {
45+
console.error('[PARENT] All workers have died.');
46+
console.error('[PARENT] Fail');
47+
process.exit(1);
48+
}
49+
});
50+
51+
worker.on('message', function(msg) {
52+
if (msg.listening) {
53+
listening += 1;
54+
55+
if (listening === listeners) {
56+
// All child process are listening, so start sending.
57+
sendSocket.sendNext();
58+
}
59+
return;
60+
}
61+
if (msg.message) {
62+
worker.messagesReceived.push(msg.message);
63+
64+
if (worker.messagesReceived.length === messages.length) {
65+
done += 1;
66+
worker.isDone = true;
67+
console.error('[PARENT] %d received %d messages total.',
68+
worker.pid,
69+
worker.messagesReceived.length);
70+
}
71+
72+
if (done === listeners) {
73+
console.error('[PARENT] All workers have received the ' +
74+
'required number of messages. Will now compare.');
75+
76+
Object.keys(workers).forEach(function(pid) {
77+
const worker = workers[pid];
78+
79+
var count = 0;
80+
81+
worker.messagesReceived.forEach(function(buf) {
82+
for (var i = 0; i < messages.length; ++i) {
83+
if (buf.toString() === messages[i].toString()) {
84+
count++;
85+
break;
86+
}
87+
}
88+
});
89+
90+
console.error('[PARENT] %d received %d matching messages.',
91+
worker.pid, count);
92+
93+
assert.strictEqual(count, messages.length,
94+
'A worker received an invalid multicast message');
95+
});
96+
97+
clearTimeout(timer);
98+
console.error('[PARENT] Success');
99+
killChildren(workers);
100+
}
101+
}
102+
});
103+
}
104+
105+
function killChildren(children) {
106+
Object.keys(children).forEach(function(key) {
107+
const child = children[key];
108+
child.kill();
109+
});
110+
}
16111

17112
if (process.argv[2] !== 'child') {
18-
var workers = {},
19-
listeners = 3,
20-
listening = 0,
21-
dead = 0,
22-
i = 0,
23-
done = 0,
24-
timer = null;
25-
26-
//exit the test if it doesn't succeed within TIMEOUT
27-
timer = setTimeout(function() {
113+
var listening = 0;
114+
var dead = 0;
115+
var i = 0;
116+
var done = 0;
117+
118+
// Exit the test if it doesn't succeed within TIMEOUT.
119+
var timer = setTimeout(function() {
28120
console.error('[PARENT] Responses were not received within %d ms.',
29121
TIMEOUT);
30122
console.error('[PARENT] Fail');
@@ -34,101 +126,18 @@ if (process.argv[2] !== 'child') {
34126
process.exit(1);
35127
}, TIMEOUT);
36128

37-
//launch child processes
129+
// Launch child processes.
38130
for (var x = 0; x < listeners; x++) {
39-
(function() {
40-
var worker = fork(process.argv[1], ['child']);
41-
workers[worker.pid] = worker;
42-
43-
worker.messagesReceived = [];
44-
45-
//handle the death of workers
46-
worker.on('exit', function(code, signal) {
47-
// don't consider this the true death if the
48-
// worker has finished successfully
49-
50-
// or if the exit code is 0
51-
if (worker.isDone || code === 0) {
52-
return;
53-
}
54-
55-
dead += 1;
56-
console.error('[PARENT] Worker %d died. %d dead of %d',
57-
worker.pid,
58-
dead,
59-
listeners);
60-
61-
if (dead === listeners) {
62-
console.error('[PARENT] All workers have died.');
63-
console.error('[PARENT] Fail');
64-
65-
killChildren(workers);
66-
67-
process.exit(1);
68-
}
69-
});
70-
71-
worker.on('message', function(msg) {
72-
if (msg.listening) {
73-
listening += 1;
74-
75-
if (listening === listeners) {
76-
//all child process are listening, so start sending
77-
sendSocket.sendNext();
78-
}
79-
}
80-
else if (msg.message) {
81-
worker.messagesReceived.push(msg.message);
82-
83-
if (worker.messagesReceived.length === messages.length) {
84-
done += 1;
85-
worker.isDone = true;
86-
console.error('[PARENT] %d received %d messages total.',
87-
worker.pid,
88-
worker.messagesReceived.length);
89-
}
90-
91-
if (done === listeners) {
92-
console.error('[PARENT] All workers have received the ' +
93-
'required number of messages. Will now compare.');
94-
95-
Object.keys(workers).forEach(function(pid) {
96-
var worker = workers[pid];
97-
98-
var count = 0;
99-
100-
worker.messagesReceived.forEach(function(buf) {
101-
for (var i = 0; i < messages.length; ++i) {
102-
if (buf.toString() === messages[i].toString()) {
103-
count++;
104-
break;
105-
}
106-
}
107-
});
108-
109-
console.error('[PARENT] %d received %d matching messages.',
110-
worker.pid, count);
111-
112-
assert.equal(count, messages.length,
113-
'A worker received an invalid multicast message');
114-
});
115-
116-
clearTimeout(timer);
117-
console.error('[PARENT] Success');
118-
killChildren(workers);
119-
}
120-
}
121-
});
122-
})(x);
131+
launchChildProcess(x);
123132
}
124133

125134
var sendSocket = dgram.createSocket('udp4');
126-
// FIXME a libuv limitation makes it necessary to bind()
127-
// before calling any of the set*() functions - the bind()
128-
// call is what creates the actual socket...
135+
// FIXME: a libuv limitation makes it necessary to bind()
136+
// before calling any of the set*() functions. The bind()
137+
// call is what creates the actual socket.
129138
sendSocket.bind();
130139

131-
// The socket is actually created async now
140+
// The socket is actually created async now.
132141
sendSocket.on('listening', function() {
133142
sendSocket.setTTL(1);
134143
sendSocket.setBroadcast(true);
@@ -141,7 +150,7 @@ if (process.argv[2] !== 'child') {
141150
});
142151

143152
sendSocket.sendNext = function() {
144-
var buf = messages[i++];
153+
const buf = messages[i++];
145154

146155
if (!buf) {
147156
try { sendSocket.close(); } catch (e) {}
@@ -151,61 +160,51 @@ if (process.argv[2] !== 'child') {
151160
sendSocket.send(buf, 0, buf.length,
152161
common.PORT, LOCAL_BROADCAST_HOST, function(err) {
153162
if (err) throw err;
154-
console.error('[PARENT] sent %s to %s:%s',
155-
util.inspect(buf.toString()),
163+
console.error('[PARENT] sent "%s" to %s:%s',
164+
buf.toString(),
156165
LOCAL_BROADCAST_HOST, common.PORT);
157166
process.nextTick(sendSocket.sendNext);
158167
});
159168
};
160-
161-
function killChildren(children) {
162-
Object.keys(children).forEach(function(key) {
163-
var child = children[key];
164-
child.kill();
165-
});
166-
}
167169
}
168170

169171
if (process.argv[2] === 'child') {
170-
var receivedMessages = [];
171-
var listenSocket = dgram.createSocket({
172+
const receivedMessages = [];
173+
const listenSocket = dgram.createSocket({
172174
type: 'udp4',
173175
reuseAddr: true
174176
});
175177

176-
listenSocket.on('message', function(buf, rinfo) {
177-
console.error('[CHILD] %s received %s from %j', process.pid,
178-
util.inspect(buf.toString()), rinfo);
178+
listenSocket.on('listening', function() {
179+
listenSocket.addMembership(LOCAL_BROADCAST_HOST);
179180

180-
receivedMessages.push(buf);
181+
listenSocket.on('message', function(buf, rinfo) {
182+
console.error('[CHILD] %s received "%s" from %j', process.pid,
183+
buf.toString(), rinfo);
181184

182-
process.send({ message: buf.toString() });
185+
receivedMessages.push(buf);
183186

184-
if (receivedMessages.length == messages.length) {
185-
// .dropMembership() not strictly needed but here as a sanity check
186-
listenSocket.dropMembership(LOCAL_BROADCAST_HOST);
187-
process.nextTick(function() {
188-
listenSocket.close();
189-
});
190-
}
191-
});
187+
process.send({ message: buf.toString() });
192188

193-
listenSocket.on('close', function() {
194-
//HACK: Wait to exit the process to ensure that the parent
195-
//process has had time to receive all messages via process.send()
196-
//This may be indicitave of some other issue.
197-
setTimeout(function() {
198-
process.exit();
199-
}, 1000);
200-
});
189+
if (receivedMessages.length == messages.length) {
190+
// .dropMembership() not strictly needed but here as a sanity check.
191+
listenSocket.dropMembership(LOCAL_BROADCAST_HOST);
192+
process.nextTick(function() {
193+
listenSocket.close();
194+
});
195+
}
196+
});
201197

202-
listenSocket.on('listening', function() {
198+
listenSocket.on('close', function() {
199+
// HACK: Wait to exit the process to ensure that the parent
200+
// process has had time to receive all messages via process.send()
201+
// This may be indicative of some other issue.
202+
setTimeout(function() {
203+
process.exit();
204+
}, common.platformTimeout(1000));
205+
});
203206
process.send({ listening: true });
204207
});
205208

206209
listenSocket.bind(common.PORT);
207-
208-
listenSocket.on('listening', function() {
209-
listenSocket.addMembership(LOCAL_BROADCAST_HOST);
210-
});
211210
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.