• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "main_thread_interface.h"
2 
3 #include "env-inl.h"
4 #include "simdutf.h"
5 #include "v8-inspector.h"
6 
7 #include <functional>
8 #include <memory>
9 
10 namespace node {
11 namespace inspector {
12 namespace {
13 
14 using v8_inspector::StringBuffer;
15 using v8_inspector::StringView;
16 
17 template <typename T>
18 class DeletableWrapper : public Deletable {
19  public:
DeletableWrapper(std::unique_ptr<T> object)20   explicit DeletableWrapper(std::unique_ptr<T> object)
21                         : object_(std::move(object)) {}
22   ~DeletableWrapper() override = default;
23 
get(MainThreadInterface * thread,int id)24   static T* get(MainThreadInterface* thread, int id) {
25     return
26         static_cast<DeletableWrapper<T>*>(thread->GetObject(id))->object_.get();
27   }
28 
29  private:
30   std::unique_ptr<T> object_;
31 };
32 
33 template <typename T>
WrapInDeletable(std::unique_ptr<T> object)34 std::unique_ptr<Deletable> WrapInDeletable(std::unique_ptr<T> object) {
35   return std::unique_ptr<DeletableWrapper<T>>(
36       new DeletableWrapper<T>(std::move(object)));
37 }
38 
39 template <typename Factory>
40 class CreateObjectRequest : public Request {
41  public:
CreateObjectRequest(int object_id,Factory factory)42   CreateObjectRequest(int object_id, Factory factory)
43                       : object_id_(object_id), factory_(std::move(factory)) {}
44 
Call(MainThreadInterface * thread)45   void Call(MainThreadInterface* thread) override {
46     thread->AddObject(object_id_, WrapInDeletable(factory_(thread)));
47   }
48 
49  private:
50   int object_id_;
51   Factory factory_;
52 };
53 
54 template <typename Factory>
NewCreateRequest(int object_id,Factory factory)55 std::unique_ptr<Request> NewCreateRequest(int object_id, Factory factory) {
56   return std::unique_ptr<Request>(
57       new CreateObjectRequest<Factory>(object_id, std::move(factory)));
58 }
59 
60 class DeleteRequest : public Request {
61  public:
DeleteRequest(int object_id)62   explicit DeleteRequest(int object_id) : object_id_(object_id) {}
63 
Call(MainThreadInterface * thread)64   void Call(MainThreadInterface* thread) override {
65     thread->RemoveObject(object_id_);
66   }
67 
68  private:
69   int object_id_;
70 };
71 
72 template <typename Target, typename Fn>
73 class CallRequest : public Request {
74  public:
CallRequest(int id,Fn fn)75   CallRequest(int id, Fn fn) : id_(id), fn_(std::move(fn)) {}
76 
Call(MainThreadInterface * thread)77   void Call(MainThreadInterface* thread) override {
78     fn_(DeletableWrapper<Target>::get(thread, id_));
79   }
80 
81  private:
82   int id_;
83   Fn fn_;
84 };
85 
86 template <typename T>
87 class AnotherThreadObjectReference {
88  public:
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,int object_id)89   AnotherThreadObjectReference(
90       std::shared_ptr<MainThreadHandle> thread, int object_id)
91       : thread_(thread), object_id_(object_id) {}
92 
93   template <typename Factory>
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,Factory factory)94   AnotherThreadObjectReference(
95       std::shared_ptr<MainThreadHandle> thread, Factory factory)
96       : AnotherThreadObjectReference(thread, thread->newObjectId()) {
97     thread_->Post(NewCreateRequest(object_id_, std::move(factory)));
98   }
99   AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete;
100 
~AnotherThreadObjectReference()101   ~AnotherThreadObjectReference() {
102     // Disappearing thread may cause a memory leak
103     thread_->Post(std::make_unique<DeleteRequest>(object_id_));
104   }
105 
106   template <typename Fn>
Call(Fn fn) const107   void Call(Fn fn) const {
108     using Request = CallRequest<T, Fn>;
109     thread_->Post(std::unique_ptr<Request>(
110         new Request(object_id_, std::move(fn))));
111   }
112 
113   template <typename Arg>
Call(void (T::* fn)(Arg),Arg argument) const114   void Call(void (T::*fn)(Arg), Arg argument) const {
115     Call(std::bind(Apply<Arg>, std::placeholders::_1, fn, std::move(argument)));
116   }
117 
118  private:
119   // This has to use non-const reference to support std::bind with non-copyable
120   // types
121   template <typename Argument>
Apply(T * target,void (T::* fn)(Argument),Argument & argument)122   static void Apply(T* target, void (T::*fn)(Argument),
123     /* NOLINT (runtime/references) */ Argument& argument) {
124     (target->*fn)(std::move(argument));
125   }
126 
127   std::shared_ptr<MainThreadHandle> thread_;
128   const int object_id_;
129 };
130 
131 class MainThreadSessionState {
132  public:
MainThreadSessionState(MainThreadInterface * thread,bool prevent_shutdown)133   MainThreadSessionState(MainThreadInterface* thread, bool prevent_shutdown)
134                          : thread_(thread),
135                            prevent_shutdown_(prevent_shutdown) {}
136 
Create(MainThreadInterface * thread,bool prevent_shutdown)137   static std::unique_ptr<MainThreadSessionState> Create(
138       MainThreadInterface* thread, bool prevent_shutdown) {
139     return std::make_unique<MainThreadSessionState>(thread, prevent_shutdown);
140   }
141 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate)142   void Connect(std::unique_ptr<InspectorSessionDelegate> delegate) {
143     Agent* agent = thread_->inspector_agent();
144     if (agent != nullptr)
145       session_ = agent->Connect(std::move(delegate), prevent_shutdown_);
146   }
147 
Dispatch(std::unique_ptr<StringBuffer> message)148   void Dispatch(std::unique_ptr<StringBuffer> message) {
149     session_->Dispatch(message->string());
150   }
151 
152  private:
153   MainThreadInterface* thread_;
154   bool prevent_shutdown_;
155   std::unique_ptr<InspectorSession> session_;
156 };
157 
158 class CrossThreadInspectorSession : public InspectorSession {
159  public:
CrossThreadInspectorSession(int id,std::shared_ptr<MainThreadHandle> thread,std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)160   CrossThreadInspectorSession(
161       int id,
162       std::shared_ptr<MainThreadHandle> thread,
163       std::unique_ptr<InspectorSessionDelegate> delegate,
164       bool prevent_shutdown)
165       : state_(thread, std::bind(MainThreadSessionState::Create,
166                                  std::placeholders::_1,
167                                  prevent_shutdown)) {
168     state_.Call(&MainThreadSessionState::Connect, std::move(delegate));
169   }
170 
Dispatch(const StringView & message)171   void Dispatch(const StringView& message) override {
172     state_.Call(&MainThreadSessionState::Dispatch,
173                 StringBuffer::create(message));
174   }
175 
176  private:
177   AnotherThreadObjectReference<MainThreadSessionState> state_;
178 };
179 
180 class ThreadSafeDelegate : public InspectorSessionDelegate {
181  public:
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread,int object_id)182   ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int object_id)
183                      : thread_(thread), delegate_(thread, object_id) {}
184 
SendMessageToFrontend(const v8_inspector::StringView & message)185   void SendMessageToFrontend(const v8_inspector::StringView& message) override {
186     delegate_.Call(
187         [m = StringBuffer::create(message)]
188         (InspectorSessionDelegate* delegate) {
189       delegate->SendMessageToFrontend(m->string());
190     });
191   }
192 
193  private:
194   std::shared_ptr<MainThreadHandle> thread_;
195   AnotherThreadObjectReference<InspectorSessionDelegate> delegate_;
196 };
197 }  // namespace
198 
199 
MainThreadInterface(Agent * agent)200 MainThreadInterface::MainThreadInterface(Agent* agent) : agent_(agent) {}
201 
~MainThreadInterface()202 MainThreadInterface::~MainThreadInterface() {
203   if (handle_)
204     handle_->Reset();
205 }
206 
Post(std::unique_ptr<Request> request)207 void MainThreadInterface::Post(std::unique_ptr<Request> request) {
208   CHECK_NOT_NULL(agent_);
209   Mutex::ScopedLock scoped_lock(requests_lock_);
210   bool needs_notify = requests_.empty();
211   requests_.push_back(std::move(request));
212   if (needs_notify) {
213     std::weak_ptr<MainThreadInterface> weak_self {shared_from_this()};
214     agent_->env()->RequestInterrupt([weak_self](Environment*) {
215       if (auto iface = weak_self.lock()) iface->DispatchMessages();
216     });
217   }
218   incoming_message_cond_.Broadcast(scoped_lock);
219 }
220 
WaitForFrontendEvent()221 bool MainThreadInterface::WaitForFrontendEvent() {
222   // We allow DispatchMessages reentry as we enter the pause. This is important
223   // to support debugging the code invoked by an inspector call, such
224   // as Runtime.evaluate
225   dispatching_messages_ = false;
226   if (dispatching_message_queue_.empty()) {
227     Mutex::ScopedLock scoped_lock(requests_lock_);
228     while (requests_.empty()) incoming_message_cond_.Wait(scoped_lock);
229   }
230   return true;
231 }
232 
DispatchMessages()233 void MainThreadInterface::DispatchMessages() {
234   if (dispatching_messages_)
235     return;
236   dispatching_messages_ = true;
237   bool had_messages = false;
238   do {
239     if (dispatching_message_queue_.empty()) {
240       Mutex::ScopedLock scoped_lock(requests_lock_);
241       requests_.swap(dispatching_message_queue_);
242     }
243     had_messages = !dispatching_message_queue_.empty();
244     while (!dispatching_message_queue_.empty()) {
245       MessageQueue::value_type task;
246       std::swap(dispatching_message_queue_.front(), task);
247       dispatching_message_queue_.pop_front();
248 
249       v8::SealHandleScope seal_handle_scope(agent_->env()->isolate());
250       task->Call(this);
251     }
252   } while (had_messages);
253   dispatching_messages_ = false;
254 }
255 
GetHandle()256 std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle() {
257   if (handle_ == nullptr)
258     handle_ = std::make_shared<MainThreadHandle>(this);
259   return handle_;
260 }
261 
AddObject(int id,std::unique_ptr<Deletable> object)262 void MainThreadInterface::AddObject(int id,
263                                     std::unique_ptr<Deletable> object) {
264   CHECK_NOT_NULL(object);
265   managed_objects_[id] = std::move(object);
266 }
267 
RemoveObject(int id)268 void MainThreadInterface::RemoveObject(int id) {
269   CHECK_EQ(1, managed_objects_.erase(id));
270 }
271 
GetObject(int id)272 Deletable* MainThreadInterface::GetObject(int id) {
273   Deletable* pointer = GetObjectIfExists(id);
274   // This would mean the object is requested after it was disposed, which is
275   // a coding error.
276   CHECK_NOT_NULL(pointer);
277   return pointer;
278 }
279 
GetObjectIfExists(int id)280 Deletable* MainThreadInterface::GetObjectIfExists(int id) {
281   auto iterator = managed_objects_.find(id);
282   if (iterator == managed_objects_.end()) {
283     return nullptr;
284   }
285   return iterator->second.get();
286 }
287 
Utf8ToStringView(const std::string_view message)288 std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string_view message) {
289   size_t expected_u16_length =
290       simdutf::utf16_length_from_utf8(message.data(), message.length());
291   MaybeStackBuffer<char16_t> buffer(expected_u16_length);
292   size_t utf16_length = simdutf::convert_utf8_to_utf16(
293       message.data(), message.length(), buffer.out());
294   StringView view(reinterpret_cast<uint16_t*>(buffer.out()), utf16_length);
295   return StringBuffer::create(view);
296 }
297 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)298 std::unique_ptr<InspectorSession> MainThreadHandle::Connect(
299     std::unique_ptr<InspectorSessionDelegate> delegate,
300     bool prevent_shutdown) {
301   return std::unique_ptr<InspectorSession>(
302       new CrossThreadInspectorSession(++next_session_id_,
303                                       shared_from_this(),
304                                       std::move(delegate),
305                                       prevent_shutdown));
306 }
307 
Post(std::unique_ptr<Request> request)308 bool MainThreadHandle::Post(std::unique_ptr<Request> request) {
309   Mutex::ScopedLock scoped_lock(block_lock_);
310   if (!main_thread_)
311     return false;
312   main_thread_->Post(std::move(request));
313   return true;
314 }
315 
Reset()316 void MainThreadHandle::Reset() {
317   Mutex::ScopedLock scoped_lock(block_lock_);
318   main_thread_ = nullptr;
319 }
320 
321 std::unique_ptr<InspectorSessionDelegate>
MakeDelegateThreadSafe(std::unique_ptr<InspectorSessionDelegate> delegate)322 MainThreadHandle::MakeDelegateThreadSafe(
323     std::unique_ptr<InspectorSessionDelegate> delegate) {
324   int id = newObjectId();
325   main_thread_->AddObject(id, WrapInDeletable(std::move(delegate)));
326   return std::unique_ptr<InspectorSessionDelegate>(
327       new ThreadSafeDelegate(shared_from_this(), id));
328 }
329 
Expired()330 bool MainThreadHandle::Expired() {
331   Mutex::ScopedLock scoped_lock(block_lock_);
332   return main_thread_ == nullptr;
333 }
334 }  // namespace inspector
335 }  // namespace node
336