Skip to content

Commit b229129

Browse files
Eugene Ostroukhovtargos
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: #21182Fixes: #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‎

Lines changed: 3 additions & 1 deletion
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)'
Lines changed: 317 additions & 0 deletions
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+
namespacenode{
9+
namespaceinspector{
10+
namespace{
11+
12+
using v8_inspector::StringView;
13+
using v8_inspector::StringBuffer;
14+
15+
template <typename T>
16+
classDeleteRequest : publicRequest{
17+
public:
18+
explicitDeleteRequest(T* object) : object_(object){}
19+
voidCall() override{
20+
delete object_;
21+
}
22+
23+
private:
24+
T* object_;
25+
};
26+
27+
template <typename Target, typename Arg>
28+
classSingleArgumentFunctionCall : publicRequest{
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+
voidCall() override{
38+
Apply(target_, fn_, std::move(arg_));
39+
}
40+
41+
private:
42+
template <typename Element>
43+
voidApply(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+
classPostMessageRequest : publicRequest{
53+
public:
54+
PostMessageRequest(InspectorSessionDelegate* delegate,
55+
StringView message)
56+
: delegate_(delegate),
57+
message_(StringBuffer::create(message)){}
58+
59+
voidCall() override{
60+
delegate_->SendMessageToFrontend(message_->string());
61+
}
62+
63+
private:
64+
InspectorSessionDelegate* delegate_;
65+
std::unique_ptr<StringBuffer> message_;
66+
};
67+
68+
classDispatchMessagesTask : publicv8::Task{
69+
public:
70+
explicitDispatchMessagesTask(MainThreadInterface* thread)
71+
: thread_(thread){}
72+
73+
voidRun() override{
74+
thread_->DispatchMessages();
75+
}
76+
77+
private:
78+
MainThreadInterface* thread_;
79+
};
80+
81+
voidDisposePairCallback(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+
classAnotherThreadObjectReference{
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+
voidPost(Fn fn, Arg argument) const{
108+
using R = SingleArgumentFunctionCall<T, Arg>
109+
thread_->Post(std::unique_ptr<R>(newR(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+
classMainThreadSessionState{
122+
public:
123+
MainThreadSessionState(
124+
std::shared_ptr<MainThreadHandle> thread,
125+
bool prevent_shutdown) : thread_(thread),
126+
prevent_shutdown_(prevent_shutdown){}
127+
128+
voidConnect(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+
voidDispatch(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+
classCrossThreadInspectorSession : publicInspectorSession{
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+
voidDispatch(const StringView& message) override{
156+
state_.Post(&MainThreadSessionState::Dispatch,
157+
StringBuffer::create(message));
158+
}
159+
160+
private:
161+
AnotherThreadObjectReference<MainThreadSessionState> state_;
162+
};
163+
164+
classThreadSafeDelegate : publicInspectorSessionDelegate{
165+
public:
166+
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread,
167+
std::unique_ptr<InspectorSessionDelegate> delegate)
168+
: thread_(thread), delegate_(thread, delegate.release()){}
169+
170+
voidSendMessageToFrontend(const v8_inspector::StringView& message) override{
171+
thread_->Post(std::unique_ptr<Request>(
172+
newPostMessageRequest(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(newAsyncAndInterface(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+
voidMainThreadInterface::DispatchMessagesAsyncCallback(uv_async_t* async){
201+
AsyncAndInterface* asyncAndInterface =
202+
node::ContainerOf(&AsyncAndInterface::first, async);
203+
asyncAndInterface->second->DispatchMessages();
204+
}
205+
206+
// static
207+
voidMainThreadInterface::CloseAsync(AsyncAndInterface* pair){
208+
uv_close(reinterpret_cast<uv_handle_t*>(&pair->first), DisposePairCallback);
209+
}
210+
211+
voidMainThreadInterface::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+
newDispatchMessagesTask(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+
boolMainThreadInterface::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+
returntrue;
238+
}
239+
240+
voidMainThreadInterface::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<constuint16_t*>(utf16.getBuffer()),
271+
utf16.length());
272+
returnStringBuffer::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+
newCrossThreadInspectorSession(++next_session_id_,
280+
shared_from_this(),
281+
std::move(delegate),
282+
prevent_shutdown));
283+
}
284+
285+
boolMainThreadHandle::Post(std::unique_ptr<Request> request){
286+
Mutex::ScopedLock scoped_lock(block_lock_);
287+
if (!main_thread_)
288+
returnfalse;
289+
main_thread_->Post(std::move(request));
290+
returntrue;
291+
}
292+
293+
voidMainThreadHandle::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+
returnnullptr;
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+
newThreadSafeDelegate(shared_from_this(), std::move(delegate)));
310+
}
311+
312+
boolMainThreadHandle::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)