Skip to content

Commit b229129

Browse files
eugeneotargos
authored andcommitted
inspector: split main thread interface from transport
Workers debugging will require interfacing between the "main" inspector and per-worker isolate inspectors. This is consistent with what WS interface does. This change is a refactoring change and does not change the functionality. PR-URL: #21182 Fixes: #21725 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 42d7539 commit b229129

14 files changed

+890
-634
lines changed

node.gyp

+3-1
Original file line numberDiff line numberDiff line change
@@ -490,12 +490,14 @@
490490
'src/inspector_js_api.cc',
491491
'src/inspector_socket.cc',
492492
'src/inspector_socket_server.cc',
493-
'src/inspector/tracing_agent.cc',
493+
'src/inspector/main_thread_interface.cc',
494494
'src/inspector/node_string.cc',
495+
'src/inspector/tracing_agent.cc',
495496
'src/inspector_agent.h',
496497
'src/inspector_io.h',
497498
'src/inspector_socket.h',
498499
'src/inspector_socket_server.h',
500+
'src/inspector/main_thread_interface.h',
499501
'src/inspector/node_string.h',
500502
'src/inspector/tracing_agent.h',
501503
'<@(node_inspector_generated_sources)'
+317
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#include "main_thread_interface.h"
2+
3+
#include "node_mutex.h"
4+
#include "v8-inspector.h"
5+
6+
#include <unicode/unistr.h>
7+
8+
namespace node {
9+
namespace inspector {
10+
namespace {
11+
12+
using v8_inspector::StringView;
13+
using v8_inspector::StringBuffer;
14+
15+
template <typename T>
16+
class DeleteRequest : public Request {
17+
public:
18+
explicit DeleteRequest(T* object) : object_(object) {}
19+
void Call() override {
20+
delete object_;
21+
}
22+
23+
private:
24+
T* object_;
25+
};
26+
27+
template <typename Target, typename Arg>
28+
class SingleArgumentFunctionCall : public Request {
29+
public:
30+
using Fn = void (Target::*)(Arg);
31+
32+
SingleArgumentFunctionCall(Target* target, Fn fn, Arg argument)
33+
: target_(target),
34+
fn_(fn),
35+
arg_(std::move(argument)) {}
36+
37+
void Call() override {
38+
Apply(target_, fn_, std::move(arg_));
39+
}
40+
41+
private:
42+
template <typename Element>
43+
void Apply(Element* target, Fn fn, Arg arg) {
44+
(target->*fn)(std::move(arg));
45+
}
46+
47+
Target* target_;
48+
Fn fn_;
49+
Arg arg_;
50+
};
51+
52+
class PostMessageRequest : public Request {
53+
public:
54+
PostMessageRequest(InspectorSessionDelegate* delegate,
55+
StringView message)
56+
: delegate_(delegate),
57+
message_(StringBuffer::create(message)) {}
58+
59+
void Call() override {
60+
delegate_->SendMessageToFrontend(message_->string());
61+
}
62+
63+
private:
64+
InspectorSessionDelegate* delegate_;
65+
std::unique_ptr<StringBuffer> message_;
66+
};
67+
68+
class DispatchMessagesTask : public v8::Task {
69+
public:
70+
explicit DispatchMessagesTask(MainThreadInterface* thread)
71+
: thread_(thread) {}
72+
73+
void Run() override {
74+
thread_->DispatchMessages();
75+
}
76+
77+
private:
78+
MainThreadInterface* thread_;
79+
};
80+
81+
void DisposePairCallback(uv_handle_t* ref) {
82+
using AsyncAndInterface = std::pair<uv_async_t, MainThreadInterface*>;
83+
AsyncAndInterface* pair = node::ContainerOf(
84+
&AsyncAndInterface::first, reinterpret_cast<uv_async_t*>(ref));
85+
delete pair;
86+
}
87+
88+
template <typename T>
89+
class AnotherThreadObjectReference {
90+
public:
91+
// We create it on whatever thread, just make sure it gets disposed on the
92+
// proper thread.
93+
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,
94+
T* object)
95+
: thread_(thread), object_(object) {
96+
}
97+
AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete;
98+
99+
~AnotherThreadObjectReference() {
100+
// Disappearing thread may cause a memory leak
101+
CHECK(thread_->Post(
102+
std::unique_ptr<DeleteRequest<T>>(new DeleteRequest<T>(object_))));
103+
object_ = nullptr;
104+
}
105+
106+
template <typename Fn, typename Arg>
107+
void Post(Fn fn, Arg argument) const {
108+
using R = SingleArgumentFunctionCall<T, Arg>;
109+
thread_->Post(std::unique_ptr<R>(new R(object_, fn, std::move(argument))));
110+
}
111+
112+
T* get() const {
113+
return object_;
114+
}
115+
116+
private:
117+
std::shared_ptr<MainThreadHandle> thread_;
118+
T* object_;
119+
};
120+
121+
class MainThreadSessionState {
122+
public:
123+
MainThreadSessionState(
124+
std::shared_ptr<MainThreadHandle> thread,
125+
bool prevent_shutdown) : thread_(thread),
126+
prevent_shutdown_(prevent_shutdown) {}
127+
128+
void Connect(std::unique_ptr<InspectorSessionDelegate> delegate) {
129+
Agent* agent = thread_->GetInspectorAgent();
130+
if (agent != nullptr)
131+
session_ = agent->Connect(std::move(delegate), prevent_shutdown_);
132+
}
133+
134+
void Dispatch(std::unique_ptr<StringBuffer> message) {
135+
session_->Dispatch(message->string());
136+
}
137+
138+
private:
139+
std::shared_ptr<MainThreadHandle> thread_;
140+
bool prevent_shutdown_;
141+
std::unique_ptr<InspectorSession> session_;
142+
};
143+
144+
class CrossThreadInspectorSession : public InspectorSession {
145+
public:
146+
CrossThreadInspectorSession(
147+
int id,
148+
std::shared_ptr<MainThreadHandle> thread,
149+
std::unique_ptr<InspectorSessionDelegate> delegate,
150+
bool prevent_shutdown)
151+
: state_(thread, new MainThreadSessionState(thread, prevent_shutdown)) {
152+
state_.Post(&MainThreadSessionState::Connect, std::move(delegate));
153+
}
154+
155+
void Dispatch(const StringView& message) override {
156+
state_.Post(&MainThreadSessionState::Dispatch,
157+
StringBuffer::create(message));
158+
}
159+
160+
private:
161+
AnotherThreadObjectReference<MainThreadSessionState> state_;
162+
};
163+
164+
class ThreadSafeDelegate : public InspectorSessionDelegate {
165+
public:
166+
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread,
167+
std::unique_ptr<InspectorSessionDelegate> delegate)
168+
: thread_(thread), delegate_(thread, delegate.release()) {}
169+
170+
void SendMessageToFrontend(const v8_inspector::StringView& message) override {
171+
thread_->Post(std::unique_ptr<Request>(
172+
new PostMessageRequest(delegate_.get(), message)));
173+
}
174+
175+
private:
176+
std::shared_ptr<MainThreadHandle> thread_;
177+
AnotherThreadObjectReference<InspectorSessionDelegate> delegate_;
178+
};
179+
} // namespace
180+
181+
182+
MainThreadInterface::MainThreadInterface(Agent* agent, uv_loop_t* loop,
183+
v8::Isolate* isolate,
184+
v8::Platform* platform)
185+
: agent_(agent), isolate_(isolate),
186+
platform_(platform) {
187+
main_thread_request_.reset(new AsyncAndInterface(uv_async_t(), this));
188+
CHECK_EQ(0, uv_async_init(loop, &main_thread_request_->first,
189+
DispatchMessagesAsyncCallback));
190+
// Inspector uv_async_t should not prevent main loop shutdown.
191+
uv_unref(reinterpret_cast<uv_handle_t*>(&main_thread_request_->first));
192+
}
193+
194+
MainThreadInterface::~MainThreadInterface() {
195+
if (handle_)
196+
handle_->Reset();
197+
}
198+
199+
// static
200+
void MainThreadInterface::DispatchMessagesAsyncCallback(uv_async_t* async) {
201+
AsyncAndInterface* asyncAndInterface =
202+
node::ContainerOf(&AsyncAndInterface::first, async);
203+
asyncAndInterface->second->DispatchMessages();
204+
}
205+
206+
// static
207+
void MainThreadInterface::CloseAsync(AsyncAndInterface* pair) {
208+
uv_close(reinterpret_cast<uv_handle_t*>(&pair->first), DisposePairCallback);
209+
}
210+
211+
void MainThreadInterface::Post(std::unique_ptr<Request> request) {
212+
Mutex::ScopedLock scoped_lock(requests_lock_);
213+
bool needs_notify = requests_.empty();
214+
requests_.push_back(std::move(request));
215+
if (needs_notify) {
216+
CHECK_EQ(0, uv_async_send(&main_thread_request_->first));
217+
if (isolate_ != nullptr && platform_ != nullptr) {
218+
platform_->CallOnForegroundThread(isolate_,
219+
new DispatchMessagesTask(this));
220+
isolate_->RequestInterrupt([](v8::Isolate* isolate, void* thread) {
221+
static_cast<MainThreadInterface*>(thread)->DispatchMessages();
222+
}, this);
223+
}
224+
}
225+
incoming_message_cond_.Broadcast(scoped_lock);
226+
}
227+
228+
bool MainThreadInterface::WaitForFrontendEvent() {
229+
// We allow DispatchMessages reentry as we enter the pause. This is important
230+
// to support debugging the code invoked by an inspector call, such
231+
// as Runtime.evaluate
232+
dispatching_messages_ = false;
233+
if (dispatching_message_queue_.empty()) {
234+
Mutex::ScopedLock scoped_lock(requests_lock_);
235+
while (requests_.empty()) incoming_message_cond_.Wait(scoped_lock);
236+
}
237+
return true;
238+
}
239+
240+
void MainThreadInterface::DispatchMessages() {
241+
if (dispatching_messages_)
242+
return;
243+
dispatching_messages_ = true;
244+
bool had_messages = false;
245+
do {
246+
if (dispatching_message_queue_.empty()) {
247+
Mutex::ScopedLock scoped_lock(requests_lock_);
248+
requests_.swap(dispatching_message_queue_);
249+
}
250+
had_messages = !dispatching_message_queue_.empty();
251+
while (!dispatching_message_queue_.empty()) {
252+
MessageQueue::value_type task;
253+
std::swap(dispatching_message_queue_.front(), task);
254+
dispatching_message_queue_.pop_front();
255+
task->Call();
256+
}
257+
} while (had_messages);
258+
dispatching_messages_ = false;
259+
}
260+
261+
std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle() {
262+
if (handle_ == nullptr)
263+
handle_ = std::make_shared<MainThreadHandle>(this);
264+
return handle_;
265+
}
266+
267+
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message) {
268+
icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(
269+
icu::StringPiece(message.data(), message.length()));
270+
StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()),
271+
utf16.length());
272+
return StringBuffer::create(view);
273+
}
274+
275+
std::unique_ptr<InspectorSession> MainThreadHandle::Connect(
276+
std::unique_ptr<InspectorSessionDelegate> delegate,
277+
bool prevent_shutdown) {
278+
return std::unique_ptr<InspectorSession>(
279+
new CrossThreadInspectorSession(++next_session_id_,
280+
shared_from_this(),
281+
std::move(delegate),
282+
prevent_shutdown));
283+
}
284+
285+
bool MainThreadHandle::Post(std::unique_ptr<Request> request) {
286+
Mutex::ScopedLock scoped_lock(block_lock_);
287+
if (!main_thread_)
288+
return false;
289+
main_thread_->Post(std::move(request));
290+
return true;
291+
}
292+
293+
void MainThreadHandle::Reset() {
294+
Mutex::ScopedLock scoped_lock(block_lock_);
295+
main_thread_ = nullptr;
296+
}
297+
298+
Agent* MainThreadHandle::GetInspectorAgent() {
299+
Mutex::ScopedLock scoped_lock(block_lock_);
300+
if (main_thread_ == nullptr)
301+
return nullptr;
302+
return main_thread_->inspector_agent();
303+
}
304+
305+
std::unique_ptr<InspectorSessionDelegate>
306+
MainThreadHandle::MakeThreadSafeDelegate(
307+
std::unique_ptr<InspectorSessionDelegate> delegate) {
308+
return std::unique_ptr<InspectorSessionDelegate>(
309+
new ThreadSafeDelegate(shared_from_this(), std::move(delegate)));
310+
}
311+
312+
bool MainThreadHandle::Expired() {
313+
Mutex::ScopedLock scoped_lock(block_lock_);
314+
return main_thread_ == nullptr;
315+
}
316+
} // namespace inspector
317+
} // namespace node

0 commit comments

Comments
 (0)