Skip to content

Commit b97c73f

Browse files
Eugene Ostroukhovrvagg
authored andcommitted
inspector: tie objects lifetime to the thread they belong to
PR-URL: #22242 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Aleksei Koziatinskii <[email protected]>
1 parent 22b9691 commit b97c73f

File tree

2 files changed

+143
-67
lines changed

2 files changed

+143
-67
lines changed

‎src/inspector/main_thread_interface.cc‎

Lines changed: 123 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include"node_mutex.h"
44
#include"v8-inspector.h"
55

6+
#include<functional>
67
#include<unicode/unistr.h>
78

89
namespacenode{
@@ -13,56 +14,72 @@ using v8_inspector::StringView;
1314
using v8_inspector::StringBuffer;
1415

1516
template <typename T>
16-
classDeleteRequest : publicRequest{
17+
classDeletableWrapper : publicDeletable{
1718
public:
18-
explicitDeleteRequest(T* object) : object_(object){}
19-
voidCall() override{
20-
delete object_;
19+
explicitDeletableWrapper(std::unique_ptr<T> object)
20+
: object_(std::move(object)){}
21+
~DeletableWrapper() override = default;
22+
23+
static T* get(MainThreadInterface* thread, int id){
24+
return
25+
static_cast<DeletableWrapper<T>*>(thread->GetObject(id))->object_.get();
2126
}
2227

2328
private:
24-
T* object_;
29+
std::unique_ptr<T> object_;
2530
};
2631

27-
template <typename Target, typename Arg>
28-
classSingleArgumentFunctionCall : publicRequest{
29-
public:
30-
using Fn = void (Target::*)(Arg);
32+
template <typename T>
33+
std::unique_ptr<Deletable> WrapInDeletable(std::unique_ptr<T> object){
34+
return std::unique_ptr<DeletableWrapper<T>>(
35+
new DeletableWrapper<T>(std::move(object)));
36+
}
3137

32-
SingleArgumentFunctionCall(Target* target, Fn fn, Arg argument)
33-
: target_(target),
34-
fn_(fn),
35-
arg_(std::move(argument)){}
38+
template <typename Factory>
39+
classCreateObjectRequest : publicRequest{
40+
public:
41+
CreateObjectRequest(int object_id, Factory factory)
42+
: object_id_(object_id), factory_(std::move(factory)){}
3643

37-
voidCall() override{
38-
Apply(target_, fn_, std::move(arg_));
44+
voidCall(MainThreadInterface* thread){
45+
thread->AddObject(object_id_, WrapInDeletable(factory_(thread)));
3946
}
4047

4148
private:
42-
template <typename Element>
43-
voidApply(Element* target, Fn fn, Arg arg){
44-
(target->*fn)(std::move(arg));
49+
int object_id_;
50+
Factory factory_;
51+
};
52+
53+
template <typename Factory>
54+
std::unique_ptr<Request> NewCreateRequest(int object_id, Factory factory){
55+
return std::unique_ptr<Request>(
56+
new CreateObjectRequest<Factory>(object_id, std::move(factory)));
57+
}
58+
59+
classDeleteRequest : publicRequest{
60+
public:
61+
explicitDeleteRequest(int object_id) : object_id_(object_id){}
62+
63+
voidCall(MainThreadInterface* thread) override{
64+
thread->RemoveObject(object_id_);
4565
}
4666

47-
Target* target_;
48-
Fn fn_;
49-
Arg arg_;
67+
private:
68+
int object_id_;
5069
};
5170

52-
classPostMessageRequest : publicRequest{
71+
template <typename Target, typename Fn>
72+
classCallRequest : publicRequest{
5373
public:
54-
PostMessageRequest(InspectorSessionDelegate* delegate,
55-
StringView message)
56-
: delegate_(delegate),
57-
message_(StringBuffer::create(message)){}
74+
CallRequest(int id, Fn fn) : id_(id), fn_(std::move(fn)){}
5875

59-
voidCall() override{
60-
delegate_->SendMessageToFrontend(message_->string());
76+
voidCall(MainThreadInterface* thread) override{
77+
fn_(DeletableWrapper<Target>::get(thread, id_));
6178
}
6279

6380
private:
64-
InspectorSessionDelegate* delegate_;
65-
std::unique_ptr<StringBuffer> message_;
81+
int id_;
82+
Fn fn_;
6683
};
6784

6885
classDispatchMessagesTask : publicv8::Task{
@@ -88,45 +105,63 @@ void DisposePairCallback(uv_handle_t* ref){
88105
template <typename T>
89106
classAnotherThreadObjectReference{
90107
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){
108+
AnotherThreadObjectReference(
109+
std::shared_ptr<MainThreadHandle> thread, int object_id)
110+
: thread_(thread), object_id_(object_id){}
111+
112+
template <typename Factory>
113+
AnotherThreadObjectReference(
114+
std::shared_ptr<MainThreadHandle> thread, Factory factory)
115+
: AnotherThreadObjectReference(thread, thread->newObjectId()){
116+
thread_->Post(NewCreateRequest(object_id_, std::move(factory)));
96117
}
97118
AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete;
98119

99120
~AnotherThreadObjectReference(){
100121
// Disappearing thread may cause a memory leak
101-
CHECK(thread_->Post(
102-
std::unique_ptr<DeleteRequest<T>>(new DeleteRequest<T>(object_))));
103-
object_ = nullptr;
122+
thread_->Post(
123+
std::unique_ptr<DeleteRequest>(newDeleteRequest(object_id_)));
104124
}
105125

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))));
126+
template <typename Fn>
127+
voidCall(Fn fn) const{
128+
using Request = CallRequest<T, Fn>
129+
thread_->Post(std::unique_ptr<Request>(
130+
newRequest(object_id_, std::move(fn))));
110131
}
111132

112-
T* get() const{
113-
return object_;
133+
template <typename Arg>
134+
voidCall(void (T::*fn)(Arg), Arg argument) const{
135+
Call(std::bind(Apply<Arg>, std::placeholders::_1, fn, std::move(argument)));
114136
}
115137

116138
private:
139+
// This has to use non-const reference to support std::bind with non-copyable
140+
// types
141+
template <typename Argument>
142+
staticvoidApply(T* target, void (T::*fn)(Argument),
143+
/* NOLINT (runtime/references) */ Argument& argument){
144+
(target->*fn)(std::move(argument));
145+
}
146+
117147
std::shared_ptr<MainThreadHandle> thread_;
118-
T* object_;
148+
constint object_id_;
119149
};
120150

121151
classMainThreadSessionState{
122152
public:
123-
MainThreadSessionState(
124-
std::shared_ptr<MainThreadHandle> thread,
125-
bool prevent_shutdown) : thread_(thread),
126-
prevent_shutdown_(prevent_shutdown){}
153+
MainThreadSessionState(MainThreadInterface* thread, bool prevent_shutdown)
154+
: thread_(thread),
155+
prevent_shutdown_(prevent_shutdown){}
156+
157+
static std::unique_ptr<MainThreadSessionState> Create(
158+
MainThreadInterface* thread, bool prevent_shutdown){
159+
return std::unique_ptr<MainThreadSessionState>(
160+
newMainThreadSessionState(thread, prevent_shutdown));
161+
}
127162

128163
voidConnect(std::unique_ptr<InspectorSessionDelegate> delegate){
129-
Agent* agent = thread_->GetInspectorAgent();
164+
Agent* agent = thread_->inspector_agent();
130165
if (agent != nullptr)
131166
session_ = agent->Connect(std::move(delegate), prevent_shutdown_);
132167
}
@@ -136,7 +171,7 @@ class MainThreadSessionState{
136171
}
137172

138173
private:
139-
std::shared_ptr<MainThreadHandle> thread_;
174+
MainThreadInterface* thread_;
140175
bool prevent_shutdown_;
141176
std::unique_ptr<InspectorSession> session_;
142177
};
@@ -148,12 +183,14 @@ class CrossThreadInspectorSession : public InspectorSession{
148183
std::shared_ptr<MainThreadHandle> thread,
149184
std::unique_ptr<InspectorSessionDelegate> delegate,
150185
bool prevent_shutdown)
151-
: state_(thread, new MainThreadSessionState(thread, prevent_shutdown)){
152-
state_.Post(&MainThreadSessionState::Connect, std::move(delegate));
186+
: state_(thread, std::bind(MainThreadSessionState::Create,
187+
std::placeholders::_1,
188+
prevent_shutdown)){
189+
state_.Call(&MainThreadSessionState::Connect, std::move(delegate));
153190
}
154191

155192
voidDispatch(const StringView& message) override{
156-
state_.Post(&MainThreadSessionState::Dispatch,
193+
state_.Call(&MainThreadSessionState::Dispatch,
157194
StringBuffer::create(message));
158195
}
159196

@@ -163,13 +200,15 @@ class CrossThreadInspectorSession : public InspectorSession{
163200

164201
classThreadSafeDelegate : publicInspectorSessionDelegate{
165202
public:
166-
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread,
167-
std::unique_ptr<InspectorSessionDelegate> delegate)
168-
: thread_(thread), delegate_(thread, delegate.release()){}
203+
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int object_id)
204+
: thread_(thread), delegate_(thread, object_id){}
169205

170206
voidSendMessageToFrontend(const v8_inspector::StringView& message) override{
171-
thread_->Post(std::unique_ptr<Request>(
172-
newPostMessageRequest(delegate_.get(), message)));
207+
delegate_.Call(
208+
[m = StringBuffer::create(message)]
209+
(InspectorSessionDelegate* delegate){
210+
delegate->SendMessageToFrontend(m->string());
211+
});
173212
}
174213

175214
private:
@@ -252,7 +291,7 @@ void MainThreadInterface::DispatchMessages(){
252291
MessageQueue::value_type task;
253292
std::swap(dispatching_message_queue_.front(), task);
254293
dispatching_message_queue_.pop_front();
255-
task->Call();
294+
task->Call(this);
256295
}
257296
} while (had_messages);
258297
dispatching_messages_ = false;
@@ -264,6 +303,26 @@ std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle(){
264303
return handle_;
265304
}
266305

306+
voidMainThreadInterface::AddObject(int id,
307+
std::unique_ptr<Deletable> object){
308+
CHECK_NE(nullptr, object);
309+
managed_objects_[id] = std::move(object);
310+
}
311+
312+
voidMainThreadInterface::RemoveObject(int id){
313+
CHECK_EQ(1, managed_objects_.erase(id));
314+
}
315+
316+
Deletable* MainThreadInterface::GetObject(int id){
317+
auto iterator = managed_objects_.find(id);
318+
// This would mean the object is requested after it was disposed, which is
319+
// a coding error.
320+
CHECK_NE(managed_objects_.end(), iterator);
321+
Deletable* pointer = iterator->second.get();
322+
CHECK_NE(nullptr, pointer);
323+
return pointer;
324+
}
325+
267326
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message){
268327
icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(
269328
icu::StringPiece(message.data(), message.length()));
@@ -303,10 +362,12 @@ Agent* MainThreadHandle::GetInspectorAgent(){
303362
}
304363

305364
std::unique_ptr<InspectorSessionDelegate>
306-
MainThreadHandle::MakeThreadSafeDelegate(
365+
MainThreadHandle::MakeDelegateThreadSafe(
307366
std::unique_ptr<InspectorSessionDelegate> delegate){
367+
int id = newObjectId();
368+
main_thread_->AddObject(id, WrapInDeletable(std::move(delegate)));
308369
return std::unique_ptr<InspectorSessionDelegate>(
309-
newThreadSafeDelegate(shared_from_this(), std::move(delegate)));
370+
newThreadSafeDelegate(shared_from_this(), id));
310371
}
311372

312373
boolMainThreadHandle::Expired(){

‎src/inspector/main_thread_interface.h‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
#include"inspector_agent.h"
1010
#include"node_mutex.h"
1111

12+
#include<atomic>
1213
#include<deque>
1314
#include<memory>
1415
#include<unordered_map>
15-
#include<unordered_set>
1616

1717
namespacev8_inspector{
1818
classStringBuffer;
@@ -21,31 +21,41 @@ class StringView;
2121

2222
namespacenode{
2323
namespaceinspector{
24+
classMainThreadInterface;
25+
2426
classRequest{
2527
public:
26-
virtualvoidCall() = 0;
28+
virtualvoidCall(MainThreadInterface*) = 0;
2729
virtual~Request(){}
2830
};
2931

32+
classDeletable{
33+
public:
34+
virtual~Deletable(){}
35+
};
36+
3037
std::unique_ptr<v8_inspector::StringBuffer> Utf8ToStringView(
3138
const std::string& message);
3239

3340
using MessageQueue = std::deque<std::unique_ptr<Request>>
34-
classMainThreadInterface;
3541

3642
classMainThreadHandle : publicstd::enable_shared_from_this<MainThreadHandle>{
3743
public:
3844
explicitMainThreadHandle(MainThreadInterface* main_thread)
39-
: main_thread_(main_thread){}
45+
: main_thread_(main_thread){
46+
}
4047
~MainThreadHandle(){
4148
CHECK_NULL(main_thread_); // main_thread_ should have called Reset
4249
}
4350
std::unique_ptr<InspectorSession> Connect(
4451
std::unique_ptr<InspectorSessionDelegate> delegate,
4552
bool prevent_shutdown);
53+
intnewObjectId(){
54+
return ++next_object_id_;
55+
}
4656
boolPost(std::unique_ptr<Request> request);
4757
Agent* GetInspectorAgent();
48-
std::unique_ptr<InspectorSessionDelegate> MakeThreadSafeDelegate(
58+
std::unique_ptr<InspectorSessionDelegate> MakeDelegateThreadSafe(
4959
std::unique_ptr<InspectorSessionDelegate> delegate);
5060
boolExpired();
5161

@@ -55,6 +65,7 @@ class MainThreadHandle : public std::enable_shared_from_this<MainThreadHandle>{
5565
MainThreadInterface* main_thread_;
5666
Mutex block_lock_;
5767
int next_session_id_ = 0;
68+
std::atomic_int next_object_id_ ={1};
5869

5970
friendclassMainThreadInterface;
6071
};
@@ -72,6 +83,9 @@ class MainThreadInterface{
7283
Agent* inspector_agent(){
7384
return agent_;
7485
}
86+
voidAddObject(int handle, std::unique_ptr<Deletable> object);
87+
Deletable* GetObject(int id);
88+
voidRemoveObject(int handle);
7589

7690
private:
7791
using AsyncAndInterface = std::pair<uv_async_t, MainThreadInterface*>
@@ -92,6 +106,7 @@ class MainThreadInterface{
92106
v8::Platform* const platform_;
93107
DeleteFnPtr<AsyncAndInterface, CloseAsync> main_thread_request_;
94108
std::shared_ptr<MainThreadHandle> handle_;
109+
std::unordered_map<int, std::unique_ptr<Deletable>> managed_objects_;
95110
};
96111

97112
} // namespace inspector

0 commit comments

Comments
(0)