Skip to content

Commit e80ae13

Browse files
Eugene OstroukhovMyles Borins
authored andcommitted
inspector: address race conditions
Stress tests uncovered 2 race conditions, when IO events happened during V8 entering event loop on pause or during Node.js shutdown. Fixes: #8669 PR-URL: #8672 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
1 parent a62999a commit e80ae13

File tree

2 files changed

+33
-18
lines changed

2 files changed

+33
-18
lines changed

src/inspector_agent.cc

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,13 @@ class AgentImpl {
265265
const String16& message);
266266
void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2);
267267
void PostIncomingMessage(const String16& message);
268+
void WaitForFrontendMessage();
269+
void NotifyMessageReceived();
268270
State ToState(State state);
269271

270272
uv_sem_t start_sem_;
271-
ConditionVariable pause_cond_;
272-
Mutex pause_lock_;
273-
Mutex queue_lock_;
273+
ConditionVariable incoming_message_cond_;
274+
Mutex state_lock_;
274275
uv_thread_t thread_;
275276
uv_loop_t child_loop_;
276277

@@ -370,15 +371,11 @@ class V8NodeInspector : public v8_inspector::V8InspectorClient {
370371
return;
371372
terminated_ = false;
372373
running_nested_loop_ = true;
373-
agent_->DispatchMessages();
374-
do {
375-
{
376-
Mutex::ScopedLock scoped_lock(agent_->pause_lock_);
377-
agent_->pause_cond_.Wait(scoped_lock);
378-
}
374+
while (!terminated_) {
375+
agent_->WaitForFrontendMessage();
379376
while (v8::platform::PumpMessageLoop(platform_, env_->isolate()))
380377
{}
381-
} while (!terminated_);
378+
}
382379
terminated_ = false;
383380
running_nested_loop_ = false;
384381
}
@@ -661,7 +658,6 @@ bool AgentImpl::OnInspectorHandshakeIO(InspectorSocket* socket,
661658
void AgentImpl::OnRemoteDataIO(InspectorSocket* socket,
662659
ssize_t read,
663660
const uv_buf_t* buf) {
664-
Mutex::ScopedLock scoped_lock(pause_lock_);
665661
if (read > 0) {
666662
String16 str = String16::fromUTF8(buf->base, read);
667663
// TODO(pfeldman): Instead of blocking execution while debugger
@@ -686,7 +682,6 @@ void AgentImpl::OnRemoteDataIO(InspectorSocket* socket,
686682
if (buf) {
687683
delete[] buf->base;
688684
}
689-
pause_cond_.Broadcast(scoped_lock);
690685
}
691686

692687
// static
@@ -752,14 +747,14 @@ void AgentImpl::WorkerRunIO() {
752747

753748
bool AgentImpl::AppendMessage(MessageQueue* queue, int session_id,
754749
const String16& message) {
755-
Mutex::ScopedLock scoped_lock(queue_lock_);
750+
Mutex::ScopedLock scoped_lock(state_lock_);
756751
bool trigger_pumping = queue->empty();
757752
queue->push_back(std::make_pair(session_id, message));
758753
return trigger_pumping;
759754
}
760755

761756
void AgentImpl::SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2) {
762-
Mutex::ScopedLock scoped_lock(queue_lock_);
757+
Mutex::ScopedLock scoped_lock(state_lock_);
763758
vector1->swap(*vector2);
764759
}
765760

@@ -771,6 +766,18 @@ void AgentImpl::PostIncomingMessage(const String16& message) {
771766
isolate->RequestInterrupt(InterruptCallback, this);
772767
uv_async_send(data_written_);
773768
}
769+
NotifyMessageReceived();
770+
}
771+
772+
void AgentImpl::WaitForFrontendMessage() {
773+
Mutex::ScopedLock scoped_lock(state_lock_);
774+
if (incoming_message_queue_.empty())
775+
incoming_message_cond_.Wait(scoped_lock);
776+
}
777+
778+
void AgentImpl::NotifyMessageReceived() {
779+
Mutex::ScopedLock scoped_lock(state_lock_);
780+
incoming_message_cond_.Broadcast(scoped_lock);
774781
}
775782

776783
void AgentImpl::OnInspectorConnectionIO(InspectorSocket* socket) {

test/inspector/inspector-helper.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ const http = require('http');
66
const path = require('path');
77
const spawn = require('child_process').spawn;
88

9+
const DEBUG = false;
10+
911
const TIMEOUT = 15 * 1000;
1012

1113
const mainScript = path.join(common.fixturesDir, 'loop.js');
1214

1315
function send(socket, message, id, callback) {
1416
const msg = JSON.parse(JSON.stringify(message)); // Clone!
1517
msg['id'] = id;
18+
if (DEBUG)
19+
console.log('[sent]', JSON.stringify(msg));
1620
const messageBuf = Buffer.from(JSON.stringify(msg));
1721

1822
const wsHeaderBuf = Buffer.allocUnsafe(16);
@@ -61,6 +65,8 @@ function parseWSFrame(buffer, handler) {
6165
return 0;
6266
const message = JSON.parse(
6367
buffer.slice(bodyOffset, bodyOffset + dataLen).toString('utf8'));
68+
if (DEBUG)
69+
console.log('[received]', JSON.stringify(message));
6470
handler(message);
6571
return bodyOffset + dataLen;
6672
}
@@ -117,7 +123,7 @@ const TestSession = function(socket, harness) {
117123
this.expectedId_ = 1;
118124
this.lastMessageResponseCallback_ = null;
119125

120-
let buffer = Buffer.from('');
126+
let buffer = new Buffer(0);
121127
socket.on('data', (data) => {
122128
buffer = Buffer.concat([buffer, data]);
123129
let consumed;
@@ -195,9 +201,10 @@ TestSession.prototype.sendInspectorCommands = function(commands) {
195201
timeoutId = setTimeout(() => {
196202
let s = '';
197203
for (const id in this.messages_) {
198-
s += this.messages_[id] + '\n';
204+
s += id + ', ';
199205
}
200-
common.fail(s.substring(0, s.length - 1));
206+
common.fail('Messages without response: ' +
207+
s.substring(0, s.length - 2));
201208
}, TIMEOUT);
202209
});
203210
});
@@ -269,6 +276,7 @@ TestSession.prototype.disconnect = function(childDone) {
269276
this.harness_.childInstanceDone =
270277
this.harness_.childInstanceDone || childDone;
271278
this.socket_.end();
279+
console.log('[test]', 'Connection terminated');
272280
callback();
273281
});
274282
};
@@ -293,7 +301,7 @@ const Harness = function(port, childProcess) {
293301
if (!filter(message)) pending.push(filter);
294302
this.stderrFilters_ = pending;
295303
}));
296-
childProcess.on('close', (code, signal) => {
304+
childProcess.on('exit', (code, signal) => {
297305
assert(this.childInstanceDone, 'Child instance died prematurely');
298306
this.returnCode_ = code;
299307
this.running_ = false;

0 commit comments

Comments
 (0)