Skip to content

Commit 4c5dc6e

Browse files
eugeneorvagg
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 8f7e373 commit 4c5dc6e

File tree

2 files changed

+143
-67
lines changed

2 files changed

+143
-67
lines changed

src/inspector/main_thread_interface.cc

+123-62
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
namespace node {
@@ -13,56 +14,72 @@ using v8_inspector::StringView;
1314
using v8_inspector::StringBuffer;
1415

1516
template <typename T>
16-
class DeleteRequest : public Request {
17+
class DeletableWrapper : public Deletable {
1718
public:
18-
explicit DeleteRequest(T* object) : object_(object) {}
19-
void Call() override {
20-
delete object_;
19+
explicit DeletableWrapper(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-
class SingleArgumentFunctionCall : public Request {
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+
class CreateObjectRequest : public Request {
40+
public:
41+
CreateObjectRequest(int object_id, Factory factory)
42+
: object_id_(object_id), factory_(std::move(factory)) {}
3643

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

4148
private:
42-
template <typename Element>
43-
void Apply(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+
class DeleteRequest : public Request {
60+
public:
61+
explicit DeleteRequest(int object_id) : object_id_(object_id) {}
62+
63+
void Call(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-
class PostMessageRequest : public Request {
71+
template <typename Target, typename Fn>
72+
class CallRequest : public Request {
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-
void Call() override {
60-
delegate_->SendMessageToFrontend(message_->string());
76+
void Call(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
class DispatchMessagesTask : public v8::Task {
@@ -88,45 +105,63 @@ void DisposePairCallback(uv_handle_t* ref) {
88105
template <typename T>
89106
class AnotherThreadObjectReference {
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>(new DeleteRequest(object_id_)));
104124
}
105125

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

112-
T* get() const {
113-
return object_;
133+
template <typename Arg>
134+
void Call(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+
static void Apply(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+
const int object_id_;
119149
};
120150

121151
class MainThreadSessionState {
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+
new MainThreadSessionState(thread, prevent_shutdown));
161+
}
127162

128163
void Connect(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
void Dispatch(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
class ThreadSafeDelegate : public InspectorSessionDelegate {
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
void SendMessageToFrontend(const v8_inspector::StringView& message) override {
171-
thread_->Post(std::unique_ptr<Request>(
172-
new PostMessageRequest(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+
void MainThreadInterface::AddObject(int id,
307+
std::unique_ptr<Deletable> object) {
308+
CHECK_NE(nullptr, object);
309+
managed_objects_[id] = std::move(object);
310+
}
311+
312+
void MainThreadInterface::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-
new ThreadSafeDelegate(shared_from_this(), std::move(delegate)));
370+
new ThreadSafeDelegate(shared_from_this(), id));
310371
}
311372

312373
bool MainThreadHandle::Expired() {

src/inspector/main_thread_interface.h

+20-5
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
namespace v8_inspector {
1818
class StringBuffer;
@@ -21,31 +21,41 @@ class StringView;
2121

2222
namespace node {
2323
namespace inspector {
24+
class MainThreadInterface;
25+
2426
class Request {
2527
public:
26-
virtual void Call() = 0;
28+
virtual void Call(MainThreadInterface*) = 0;
2729
virtual ~Request() {}
2830
};
2931

32+
class Deletable {
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-
class MainThreadInterface;
3541

3642
class MainThreadHandle : public std::enable_shared_from_this<MainThreadHandle> {
3743
public:
3844
explicit MainThreadHandle(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+
int newObjectId() {
54+
return ++next_object_id_;
55+
}
4656
bool Post(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
bool Expired();
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
friend class MainThreadInterface;
6071
};
@@ -72,6 +83,9 @@ class MainThreadInterface {
7283
Agent* inspector_agent() {
7384
return agent_;
7485
}
86+
void AddObject(int handle, std::unique_ptr<Deletable> object);
87+
Deletable* GetObject(int id);
88+
void RemoveObject(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)