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