DevTools: use a barrier to sync runIfWaitingForDebugger from multiple sessions
This introduces a barrier that ensures that `V8InspectorClient::runIfWaitingForDebugger()` is only invoked once all sessions that requested a paused have invoked runIfWaitingForDebugger. Downstream change: https://chromium-review.googlesource.com/c/chromium/src/+/3977348 Bug: chromium:1352175 Change-Id: I9049c2de6da8e690ad4312cd6cb799619125bb62 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3976353 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Commit-Queue: Andrey Kosyakov <caseq@chromium.org> Cr-Commit-Position: refs/heads/main@{#84191}
This commit is contained in:
parent
c80394dd3c
commit
aa684004d0
@ -3249,6 +3249,8 @@ filegroup(
|
||||
"src/inspector/v8-debugger.h",
|
||||
"src/inspector/v8-debugger-agent-impl.cc",
|
||||
"src/inspector/v8-debugger-agent-impl.h",
|
||||
"src/inspector/v8-debugger-barrier.cc",
|
||||
"src/inspector/v8-debugger-barrier.h",
|
||||
"src/inspector/v8-debugger-id.cc",
|
||||
"src/inspector/v8-debugger-id.h",
|
||||
"src/inspector/v8-debugger-script.cc",
|
||||
|
@ -365,9 +365,12 @@ class V8_EXPORT V8Inspector {
|
||||
virtual void flushProtocolNotifications() = 0;
|
||||
};
|
||||
enum ClientTrustLevel { kUntrusted, kFullyTrusted };
|
||||
enum SessionPauseState { kWaitingForDebugger, kNotWaitingForDebugger };
|
||||
// TODO(chromium:1352175): remove default value once downstream change lands.
|
||||
virtual std::unique_ptr<V8InspectorSession> connect(
|
||||
int contextGroupId, Channel*, StringView state,
|
||||
ClientTrustLevel client_trust_level) {
|
||||
ClientTrustLevel client_trust_level,
|
||||
SessionPauseState = kNotWaitingForDebugger) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -4175,7 +4175,8 @@ class InspectorClient : public v8_inspector::V8InspectorClient {
|
||||
inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
|
||||
session_ =
|
||||
inspector_->connect(1, channel_.get(), v8_inspector::StringView(),
|
||||
v8_inspector::V8Inspector::kFullyTrusted);
|
||||
v8_inspector::V8Inspector::kFullyTrusted,
|
||||
v8_inspector::V8Inspector::kNotWaitingForDebugger);
|
||||
context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
|
||||
inspector_->contextCreated(v8_inspector::V8ContextInfo(
|
||||
context, kContextGroupId, v8_inspector::StringView()));
|
||||
|
@ -125,6 +125,8 @@ v8_source_set("inspector") {
|
||||
"v8-console.h",
|
||||
"v8-debugger-agent-impl.cc",
|
||||
"v8-debugger-agent-impl.h",
|
||||
"v8-debugger-barrier.cc",
|
||||
"v8-debugger-barrier.h",
|
||||
"v8-debugger-id.cc",
|
||||
"v8-debugger-id.h",
|
||||
"v8-debugger-script.cc",
|
||||
|
19
src/inspector/v8-debugger-barrier.cc
Normal file
19
src/inspector/v8-debugger-barrier.cc
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/inspector/v8-debugger-barrier.h"
|
||||
|
||||
#include "include/v8-inspector.h"
|
||||
|
||||
namespace v8_inspector {
|
||||
|
||||
V8DebuggerBarrier::V8DebuggerBarrier(V8InspectorClient* client,
|
||||
int contextGroupId)
|
||||
: m_client(client), m_contextGroupId(contextGroupId) {}
|
||||
|
||||
V8DebuggerBarrier::~V8DebuggerBarrier() {
|
||||
m_client->runIfWaitingForDebugger(m_contextGroupId);
|
||||
}
|
||||
|
||||
} // namespace v8_inspector
|
28
src/inspector/v8-debugger-barrier.h
Normal file
28
src/inspector/v8-debugger-barrier.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_INSPECTOR_V8_DEBUGGER_BARRIER_H_
|
||||
#define V8_INSPECTOR_V8_DEBUGGER_BARRIER_H_
|
||||
|
||||
namespace v8_inspector {
|
||||
|
||||
class V8InspectorClient;
|
||||
|
||||
// This class is used to synchronize multiple sessions issuing
|
||||
// `Runtime.runIfWaitingForDebbuger` so that the global client
|
||||
// `runIfWaitingForDebugger` method is only invoked when all
|
||||
// sessions have invoked `Runtime.runIfWaitingForDebugger`.
|
||||
class V8DebuggerBarrier {
|
||||
public:
|
||||
V8DebuggerBarrier(V8InspectorClient* client, int contextGroupId);
|
||||
~V8DebuggerBarrier();
|
||||
|
||||
private:
|
||||
V8InspectorClient* const m_client;
|
||||
int m_contextGroupId;
|
||||
};
|
||||
|
||||
} // namespace v8_inspector
|
||||
|
||||
#endif // V8_INSPECTOR_V8_DEBUGGER_BARRIER_H_
|
@ -44,6 +44,7 @@
|
||||
#include "src/inspector/v8-console-message.h"
|
||||
#include "src/inspector/v8-console.h"
|
||||
#include "src/inspector/v8-debugger-agent-impl.h"
|
||||
#include "src/inspector/v8-debugger-barrier.h"
|
||||
#include "src/inspector/v8-debugger-id.h"
|
||||
#include "src/inspector/v8-debugger.h"
|
||||
#include "src/inspector/v8-inspector-session-impl.h"
|
||||
@ -147,11 +148,26 @@ std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
|
||||
|
||||
std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
|
||||
int contextGroupId, V8Inspector::Channel* channel, StringView state,
|
||||
ClientTrustLevel client_trust_level) {
|
||||
ClientTrustLevel client_trust_level, SessionPauseState pause_state) {
|
||||
int sessionId = ++m_lastSessionId;
|
||||
std::shared_ptr<V8DebuggerBarrier> debuggerBarrier;
|
||||
if (pause_state == kWaitingForDebugger) {
|
||||
auto it = m_debuggerBarriers.find(contextGroupId);
|
||||
if (it != m_debuggerBarriers.end()) {
|
||||
// Note this will be empty in case a pre-existent barrier is already
|
||||
// released. This is by design, as a released throttle is no longer
|
||||
// efficient.
|
||||
debuggerBarrier = it->second.lock();
|
||||
} else {
|
||||
debuggerBarrier =
|
||||
std::make_shared<V8DebuggerBarrier>(m_client, contextGroupId);
|
||||
m_debuggerBarriers.insert(it, {contextGroupId, debuggerBarrier});
|
||||
}
|
||||
}
|
||||
std::unique_ptr<V8InspectorSessionImpl> session =
|
||||
V8InspectorSessionImpl::create(this, contextGroupId, sessionId, channel,
|
||||
state, client_trust_level);
|
||||
state, client_trust_level,
|
||||
std::move(debuggerBarrier));
|
||||
m_sessions[contextGroupId][sessionId] = session.get();
|
||||
return std::move(session);
|
||||
}
|
||||
@ -159,7 +175,10 @@ std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
|
||||
void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
|
||||
auto& map = m_sessions[session->contextGroupId()];
|
||||
map.erase(session->sessionId());
|
||||
if (map.empty()) m_sessions.erase(session->contextGroupId());
|
||||
if (map.empty()) {
|
||||
m_sessions.erase(session->contextGroupId());
|
||||
m_debuggerBarriers.erase(session->contextGroupId());
|
||||
}
|
||||
}
|
||||
|
||||
InspectedContext* V8InspectorImpl::getContext(int groupId,
|
||||
|
@ -49,6 +49,7 @@ class V8Console;
|
||||
class V8ConsoleMessageStorage;
|
||||
class V8Debugger;
|
||||
class V8DebuggerAgentImpl;
|
||||
class V8DebuggerBarrier;
|
||||
class V8InspectorSessionImpl;
|
||||
class V8ProfilerAgentImpl;
|
||||
class V8RuntimeAgentImpl;
|
||||
@ -81,7 +82,8 @@ class V8InspectorImpl : public V8Inspector {
|
||||
std::unique_ptr<V8InspectorSession> connect(int contextGroupId,
|
||||
V8Inspector::Channel*,
|
||||
StringView state,
|
||||
ClientTrustLevel) override;
|
||||
ClientTrustLevel,
|
||||
SessionPauseState) override;
|
||||
void contextCreated(const V8ContextInfo&) override;
|
||||
void contextDestroyed(v8::Local<v8::Context>) override;
|
||||
v8::MaybeLocal<v8::Context> contextById(int contextId) override;
|
||||
@ -178,6 +180,8 @@ class V8InspectorImpl : public V8Inspector {
|
||||
|
||||
// contextGroupId -> sessionId -> session
|
||||
std::unordered_map<int, std::map<int, V8InspectorSessionImpl*>> m_sessions;
|
||||
// contextGroupId -> debugger barrier
|
||||
std::unordered_map<int, std::weak_ptr<V8DebuggerBarrier>> m_debuggerBarriers;
|
||||
|
||||
using ConsoleStorageMap =
|
||||
std::unordered_map<int, std::unique_ptr<V8ConsoleMessageStorage>>;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "src/inspector/string-util.h"
|
||||
#include "src/inspector/v8-console-agent-impl.h"
|
||||
#include "src/inspector/v8-debugger-agent-impl.h"
|
||||
#include "src/inspector/v8-debugger-barrier.h"
|
||||
#include "src/inspector/v8-debugger.h"
|
||||
#include "src/inspector/v8-heap-profiler-agent-impl.h"
|
||||
#include "src/inspector/v8-inspector-impl.h"
|
||||
@ -90,15 +91,18 @@ int V8ContextInfo::executionContextId(v8::Local<v8::Context> context) {
|
||||
std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(
|
||||
V8InspectorImpl* inspector, int contextGroupId, int sessionId,
|
||||
V8Inspector::Channel* channel, StringView state,
|
||||
V8Inspector::ClientTrustLevel clientTrustLevel) {
|
||||
V8Inspector::ClientTrustLevel clientTrustLevel,
|
||||
std::shared_ptr<V8DebuggerBarrier> debuggerBarrier) {
|
||||
return std::unique_ptr<V8InspectorSessionImpl>(new V8InspectorSessionImpl(
|
||||
inspector, contextGroupId, sessionId, channel, state, clientTrustLevel));
|
||||
inspector, contextGroupId, sessionId, channel, state, clientTrustLevel,
|
||||
std::move(debuggerBarrier)));
|
||||
}
|
||||
|
||||
V8InspectorSessionImpl::V8InspectorSessionImpl(
|
||||
V8InspectorImpl* inspector, int contextGroupId, int sessionId,
|
||||
V8Inspector::Channel* channel, StringView savedState,
|
||||
V8Inspector::ClientTrustLevel clientTrustLevel)
|
||||
V8Inspector::ClientTrustLevel clientTrustLevel,
|
||||
std::shared_ptr<V8DebuggerBarrier> debuggerBarrier)
|
||||
: m_contextGroupId(contextGroupId),
|
||||
m_sessionId(sessionId),
|
||||
m_inspector(inspector),
|
||||
@ -116,7 +120,8 @@ V8InspectorSessionImpl::V8InspectorSessionImpl(
|
||||
m_state->getBoolean("use_binary_protocol", &use_binary_protocol_);
|
||||
|
||||
m_runtimeAgent.reset(new V8RuntimeAgentImpl(
|
||||
this, this, agentState(protocol::Runtime::Metainfo::domainName)));
|
||||
this, this, agentState(protocol::Runtime::Metainfo::domainName),
|
||||
std::move(debuggerBarrier)));
|
||||
protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());
|
||||
|
||||
m_debuggerAgent.reset(new V8DebuggerAgentImpl(
|
||||
|
@ -21,6 +21,7 @@ class InjectedScript;
|
||||
class RemoteObjectIdBase;
|
||||
class V8ConsoleAgentImpl;
|
||||
class V8DebuggerAgentImpl;
|
||||
class V8DebuggerBarrier;
|
||||
class V8InspectorImpl;
|
||||
class V8HeapProfilerAgentImpl;
|
||||
class V8ProfilerAgentImpl;
|
||||
@ -35,7 +36,8 @@ class V8InspectorSessionImpl : public V8InspectorSession,
|
||||
static std::unique_ptr<V8InspectorSessionImpl> create(
|
||||
V8InspectorImpl*, int contextGroupId, int sessionId,
|
||||
V8Inspector::Channel*, StringView state,
|
||||
v8_inspector::V8Inspector::ClientTrustLevel);
|
||||
v8_inspector::V8Inspector::ClientTrustLevel,
|
||||
std::shared_ptr<V8DebuggerBarrier>);
|
||||
~V8InspectorSessionImpl() override;
|
||||
V8InspectorSessionImpl(const V8InspectorSessionImpl&) = delete;
|
||||
V8InspectorSessionImpl& operator=(const V8InspectorSessionImpl&) = delete;
|
||||
@ -105,7 +107,8 @@ class V8InspectorSessionImpl : public V8InspectorSession,
|
||||
private:
|
||||
V8InspectorSessionImpl(V8InspectorImpl*, int contextGroupId, int sessionId,
|
||||
V8Inspector::Channel*, StringView state,
|
||||
V8Inspector::ClientTrustLevel);
|
||||
V8Inspector::ClientTrustLevel,
|
||||
std::shared_ptr<V8DebuggerBarrier>);
|
||||
protocol::DictionaryValue* agentState(const String16& name);
|
||||
|
||||
// protocol::FrontendChannel implementation.
|
||||
|
@ -241,11 +241,13 @@ Response ensureContext(V8InspectorImpl* inspector, int contextGroupId,
|
||||
|
||||
V8RuntimeAgentImpl::V8RuntimeAgentImpl(
|
||||
V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel,
|
||||
protocol::DictionaryValue* state)
|
||||
protocol::DictionaryValue* state,
|
||||
std::shared_ptr<V8DebuggerBarrier> debuggerBarrier)
|
||||
: m_session(session),
|
||||
m_state(state),
|
||||
m_frontend(FrontendChannel),
|
||||
m_inspector(session->inspector()),
|
||||
m_debuggerBarrier(debuggerBarrier),
|
||||
m_enabled(false) {}
|
||||
|
||||
V8RuntimeAgentImpl::~V8RuntimeAgentImpl() = default;
|
||||
@ -491,11 +493,13 @@ Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) {
|
||||
}
|
||||
|
||||
Response V8RuntimeAgentImpl::runIfWaitingForDebugger() {
|
||||
if (m_runIfWaitingForDebuggerCalled) return Response::Success();
|
||||
m_runIfWaitingForDebuggerCalled = true;
|
||||
// The client implementation is resposible for checking if the session is
|
||||
// actually waiting for debugger. m_runIfWaitingForDebuggerCalled only makes
|
||||
// sure that the client implementation is invoked once per agent instance.
|
||||
if (m_debuggerBarrier) {
|
||||
m_debuggerBarrier.reset();
|
||||
return Response::Success();
|
||||
}
|
||||
// TODO(chromium:1352175): the below is provisional until client-side changes
|
||||
// land. The call should come through the barrier only once client properly
|
||||
// communicates whether the session is waiting for debugger.
|
||||
m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId());
|
||||
return Response::Success();
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class InjectedScript;
|
||||
class InspectedContext;
|
||||
class RemoteObjectIdBase;
|
||||
class V8ConsoleMessage;
|
||||
class V8DebuggerBarrier;
|
||||
class V8InspectorImpl;
|
||||
class V8InspectorSessionImpl;
|
||||
|
||||
@ -58,7 +59,8 @@ using protocol::Maybe;
|
||||
class V8RuntimeAgentImpl : public protocol::Runtime::Backend {
|
||||
public:
|
||||
V8RuntimeAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
|
||||
protocol::DictionaryValue* state);
|
||||
protocol::DictionaryValue* state,
|
||||
std::shared_ptr<V8DebuggerBarrier>);
|
||||
~V8RuntimeAgentImpl() override;
|
||||
V8RuntimeAgentImpl(const V8RuntimeAgentImpl&) = delete;
|
||||
V8RuntimeAgentImpl& operator=(const V8RuntimeAgentImpl&) = delete;
|
||||
@ -155,12 +157,12 @@ class V8RuntimeAgentImpl : public protocol::Runtime::Backend {
|
||||
protocol::DictionaryValue* m_state;
|
||||
protocol::Runtime::Frontend m_frontend;
|
||||
V8InspectorImpl* m_inspector;
|
||||
std::shared_ptr<V8DebuggerBarrier> m_debuggerBarrier;
|
||||
bool m_enabled;
|
||||
std::unordered_map<String16, std::unique_ptr<v8::Global<v8::Script>>>
|
||||
m_compiledScripts;
|
||||
// Binding name -> executionContextIds mapping.
|
||||
std::unordered_map<String16, std::unordered_set<int>> m_activeBindings;
|
||||
bool m_runIfWaitingForDebuggerCalled = false;
|
||||
};
|
||||
|
||||
} // namespace v8_inspector
|
||||
|
@ -104,6 +104,9 @@ class UtilsExtension : public InspectorIsolateData::SetupGlobalTask {
|
||||
utils->Set(isolate, "interruptForMessages",
|
||||
v8::FunctionTemplate::New(
|
||||
isolate, &UtilsExtension::InterruptForMessages));
|
||||
utils->Set(
|
||||
isolate, "waitForDebugger",
|
||||
v8::FunctionTemplate::New(isolate, &UtilsExtension::WaitForDebugger));
|
||||
global->Set(isolate, "utils", utils);
|
||||
}
|
||||
|
||||
@ -404,6 +407,19 @@ class UtilsExtension : public InspectorIsolateData::SetupGlobalTask {
|
||||
backend_runner_->InterruptForMessages();
|
||||
}
|
||||
|
||||
static void WaitForDebugger(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (args.Length() != 2 || !args[0]->IsInt32() || !args[1]->IsFunction()) {
|
||||
FATAL("Internal error: waitForDebugger(context_group_id, callback).");
|
||||
}
|
||||
int context_group_id = args[0].As<v8::Int32>()->Value();
|
||||
RunSimpleAsyncTask(
|
||||
backend_runner_,
|
||||
[context_group_id](InspectorIsolateData* data) {
|
||||
data->WaitForDebugger(context_group_id);
|
||||
},
|
||||
args[1].As<v8::Function>());
|
||||
}
|
||||
|
||||
static std::map<int, std::unique_ptr<FrontendChannelImpl>> channels_;
|
||||
};
|
||||
|
||||
|
@ -163,9 +163,12 @@ int InspectorIsolateData::ConnectSession(
|
||||
v8_inspector::V8Inspector::Channel* channel) {
|
||||
v8::SealHandleScope seal_handle_scope(isolate());
|
||||
int session_id = ++last_session_id_;
|
||||
sessions_[session_id] =
|
||||
inspector_->connect(context_group_id, channel, state,
|
||||
v8_inspector::V8Inspector::kFullyTrusted);
|
||||
sessions_[session_id] = inspector_->connect(
|
||||
context_group_id, channel, state,
|
||||
v8_inspector::V8Inspector::kFullyTrusted,
|
||||
waiting_for_debugger_
|
||||
? v8_inspector::V8Inspector::kWaitingForDebugger
|
||||
: v8_inspector::V8Inspector::kNotWaitingForDebugger);
|
||||
context_group_by_session_[sessions_[session_id].get()] = context_group_id;
|
||||
return session_id;
|
||||
}
|
||||
@ -438,6 +441,10 @@ void InspectorIsolateData::runMessageLoopOnPause(int) {
|
||||
task_runner_->RunMessageLoop(true);
|
||||
}
|
||||
|
||||
void InspectorIsolateData::runIfWaitingForDebugger(int) {
|
||||
quitMessageLoopOnPause();
|
||||
}
|
||||
|
||||
void InspectorIsolateData::quitMessageLoopOnPause() {
|
||||
v8::SealHandleScope seal_handle_scope(isolate());
|
||||
task_runner_->QuitMessageLoop();
|
||||
@ -507,6 +514,13 @@ bool InspectorIsolateData::AssociateExceptionData(
|
||||
this->isolate()->GetCurrentContext(), exception, key, value);
|
||||
}
|
||||
|
||||
void InspectorIsolateData::WaitForDebugger(int context_group_id) {
|
||||
DCHECK(!waiting_for_debugger_);
|
||||
waiting_for_debugger_ = true;
|
||||
runMessageLoopOnPause(context_group_id);
|
||||
waiting_for_debugger_ = false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class StringBufferImpl : public v8_inspector::StringBuffer {
|
||||
public:
|
||||
|
@ -106,6 +106,7 @@ class InspectorIsolateData : public v8_inspector::V8InspectorClient {
|
||||
bool AssociateExceptionData(v8::Local<v8::Value> exception,
|
||||
v8::Local<v8::Name> key,
|
||||
v8::Local<v8::Value> value);
|
||||
void WaitForDebugger(int context_group_id);
|
||||
|
||||
private:
|
||||
static v8::MaybeLocal<v8::Module> ModuleResolveCallback(
|
||||
@ -126,6 +127,7 @@ class InspectorIsolateData : public v8_inspector::V8InspectorClient {
|
||||
v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Context>) override;
|
||||
void runMessageLoopOnPause(int context_group_id) override;
|
||||
void runIfWaitingForDebugger(int context_group_id) override;
|
||||
void quitMessageLoopOnPause() override;
|
||||
void installAdditionalCommandLineAPI(v8::Local<v8::Context>,
|
||||
v8::Local<v8::Object>) override;
|
||||
@ -169,6 +171,7 @@ class InspectorIsolateData : public v8_inspector::V8InspectorClient {
|
||||
double current_time_ = 0.0;
|
||||
bool log_console_api_message_calls_ = false;
|
||||
bool log_max_async_call_stack_depth_changed_ = false;
|
||||
bool waiting_for_debugger_ = false;
|
||||
v8::Global<v8::Private> not_inspectable_private_;
|
||||
v8::Global<v8::String> resource_name_prefix_;
|
||||
v8::Global<v8::String> additional_console_api_;
|
||||
|
@ -142,6 +142,12 @@ InspectorTest.ContextGroup = class {
|
||||
this.id = utils.createContextGroup();
|
||||
}
|
||||
|
||||
waitForDebugger() {
|
||||
return new Promise(resolve => {
|
||||
utils.waitForDebugger(this.id, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
createContext(name) {
|
||||
utils.createContext(this.id, name || '');
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
|
||||
Running test: testTwoSessions
|
||||
Tests Runtime.runIfWaitingForDebugger
|
||||
session 1 resumed
|
||||
session 2 resumed
|
||||
execution resumed
|
||||
|
||||
Running test: testSessionDisconnect
|
||||
Tests Runtime.runIfWaitingForDebugger
|
||||
session 1 resumed
|
||||
session 2 disconnected
|
||||
execution resumed
|
35
test/inspector/runtime/run-if-waiting-for-debugger.js
Normal file
35
test/inspector/runtime/run-if-waiting-for-debugger.js
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
InspectorTest.runAsyncTestSuite([
|
||||
async function testTwoSessions() {
|
||||
InspectorTest.log('Tests Runtime.runIfWaitingForDebugger');
|
||||
|
||||
const contextGroup = new InspectorTest.ContextGroup();
|
||||
const resumed = contextGroup.waitForDebugger().then(() => InspectorTest.log('execution resumed'));
|
||||
|
||||
const session1 = contextGroup.connect();
|
||||
const session2 = contextGroup.connect();
|
||||
await session1.Protocol.Runtime.runIfWaitingForDebugger();
|
||||
InspectorTest.log('session 1 resumed');
|
||||
await session2.Protocol.Runtime.runIfWaitingForDebugger();
|
||||
InspectorTest.log('session 2 resumed');
|
||||
await resumed;
|
||||
},
|
||||
|
||||
async function testSessionDisconnect() {
|
||||
InspectorTest.log('Tests Runtime.runIfWaitingForDebugger');
|
||||
|
||||
const contextGroup = new InspectorTest.ContextGroup();
|
||||
const resumed = contextGroup.waitForDebugger().then(() => InspectorTest.log('execution resumed'));
|
||||
|
||||
const session1 = contextGroup.connect();
|
||||
const session2 = contextGroup.connect();
|
||||
await session1.Protocol.Runtime.runIfWaitingForDebugger();
|
||||
InspectorTest.log('session 1 resumed');
|
||||
session2.disconnect();
|
||||
InspectorTest.log('session 2 disconnected');
|
||||
await resumed;
|
||||
}
|
||||
]);
|
@ -14,6 +14,90 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
void RunSyncTask(TaskRunner* task_runner,
|
||||
std::function<void(InspectorIsolateData*)> callback) {
|
||||
class SyncTask : public TaskRunner::Task {
|
||||
public:
|
||||
SyncTask(v8::base::Semaphore* ready_semaphore,
|
||||
std::function<void(InspectorIsolateData*)> callback)
|
||||
: ready_semaphore_(ready_semaphore), callback_(callback) {}
|
||||
~SyncTask() override = default;
|
||||
bool is_priority_task() final { return true; }
|
||||
|
||||
private:
|
||||
void Run(InspectorIsolateData* data) override {
|
||||
callback_(data);
|
||||
if (ready_semaphore_) ready_semaphore_->Signal();
|
||||
}
|
||||
|
||||
v8::base::Semaphore* ready_semaphore_;
|
||||
std::function<void(InspectorIsolateData*)> callback_;
|
||||
};
|
||||
|
||||
v8::base::Semaphore ready_semaphore(0);
|
||||
task_runner->Append(std::make_unique<SyncTask>(&ready_semaphore, callback));
|
||||
ready_semaphore.Wait();
|
||||
}
|
||||
|
||||
void RunSimpleAsyncTask(TaskRunner* task_runner,
|
||||
std::function<void(InspectorIsolateData* data)> task,
|
||||
v8::Local<v8::Function> callback) {
|
||||
class DispatchResponseTask : public TaskRunner::Task {
|
||||
public:
|
||||
explicit DispatchResponseTask(v8::Local<v8::Function> callback)
|
||||
: context_(callback->GetIsolate(),
|
||||
callback->GetIsolate()->GetCurrentContext()),
|
||||
client_callback_(callback->GetIsolate(), callback) {}
|
||||
~DispatchResponseTask() override = default;
|
||||
|
||||
private:
|
||||
bool is_priority_task() final { return true; }
|
||||
void Run(InspectorIsolateData* data) override {
|
||||
v8::HandleScope handle_scope(data->isolate());
|
||||
v8::Local<v8::Context> context = context_.Get(data->isolate());
|
||||
v8::MicrotasksScope microtasks_scope(context,
|
||||
v8::MicrotasksScope::kRunMicrotasks);
|
||||
v8::Context::Scope context_scope(context);
|
||||
(void)client_callback_.Get(data->isolate())
|
||||
->Call(context, context->Global(), 0, nullptr);
|
||||
}
|
||||
v8::Global<v8::Context> context_;
|
||||
v8::Global<v8::Function> client_callback_;
|
||||
};
|
||||
|
||||
using TaskCallback = std::function<void(InspectorIsolateData * data)>;
|
||||
|
||||
class TaskWrapper : public TaskRunner::Task {
|
||||
public:
|
||||
TaskWrapper(TaskCallback task, TaskRunner* client_task_runner,
|
||||
std::unique_ptr<TaskRunner::Task> response_task)
|
||||
: task_(std::move(task)),
|
||||
client_task_runner_(client_task_runner),
|
||||
response_task_(std::move(response_task)) {}
|
||||
|
||||
~TaskWrapper() override = default;
|
||||
|
||||
private:
|
||||
bool is_priority_task() final { return true; }
|
||||
void Run(InspectorIsolateData* data) override {
|
||||
task_(data);
|
||||
client_task_runner_->Append(std::move(response_task_));
|
||||
}
|
||||
|
||||
TaskCallback task_;
|
||||
TaskRunner* client_task_runner_;
|
||||
std::unique_ptr<TaskRunner::Task> response_task_;
|
||||
};
|
||||
|
||||
v8::Local<v8::Context> context = callback->GetIsolate()->GetCurrentContext();
|
||||
TaskRunner* response_task_runner =
|
||||
InspectorIsolateData::FromContext(context)->task_runner();
|
||||
|
||||
auto response_task = std::make_unique<DispatchResponseTask>(callback);
|
||||
task_runner->Append(std::make_unique<TaskWrapper>(
|
||||
std::move(task), response_task_runner, std::move(response_task)));
|
||||
}
|
||||
|
||||
void ExecuteStringTask::Run(InspectorIsolateData* data) {
|
||||
v8::HandleScope handle_scope(data->isolate());
|
||||
v8::Local<v8::Context> context = data->GetDefaultContext(context_group_id_);
|
||||
|
@ -20,29 +20,11 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
void RunSyncTask(TaskRunner* task_runner, T callback) {
|
||||
class SyncTask : public TaskRunner::Task {
|
||||
public:
|
||||
SyncTask(v8::base::Semaphore* ready_semaphore, T callback)
|
||||
: ready_semaphore_(ready_semaphore), callback_(callback) {}
|
||||
~SyncTask() override = default;
|
||||
bool is_priority_task() final { return true; }
|
||||
|
||||
private:
|
||||
void Run(InspectorIsolateData* data) override {
|
||||
callback_(data);
|
||||
if (ready_semaphore_) ready_semaphore_->Signal();
|
||||
}
|
||||
|
||||
v8::base::Semaphore* ready_semaphore_;
|
||||
T callback_;
|
||||
};
|
||||
|
||||
v8::base::Semaphore ready_semaphore(0);
|
||||
task_runner->Append(std::make_unique<SyncTask>(&ready_semaphore, callback));
|
||||
ready_semaphore.Wait();
|
||||
}
|
||||
void RunSyncTask(TaskRunner* task_runner,
|
||||
std::function<void(InspectorIsolateData*)> callback);
|
||||
void RunSimpleAsyncTask(TaskRunner* task_runner,
|
||||
std::function<void(InspectorIsolateData* data)> task,
|
||||
v8::Local<v8::Function> callback);
|
||||
|
||||
class SendMessageToBackendTask : public TaskRunner::Task {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user