@@ -189,9 +189,9 @@ const int CONTEXT_GROUP_ID = 1;
189189
190190class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
191191 public:
192- explicit ChannelImpl (V8Inspector* inspector,
193- InspectorSessionDelegate* delegate)
194- : delegate_(delegate) {
192+ explicit ChannelImpl (const std::unique_ptr< V8Inspector>& inspector,
193+ std::unique_ptr< InspectorSessionDelegate> delegate)
194+ : delegate_(std::move( delegate) ) {
195195 session_ = inspector->connect (1 , this , StringView ());
196196 }
197197
@@ -201,19 +201,11 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
201201 session_->dispatchProtocolMessage (message);
202202 }
203203
204- bool waitForFrontendMessage () {
205- return delegate_->WaitForFrontendMessageWhilePaused ();
206- }
207-
208204 void schedulePauseOnNextStatement (const std::string& reason) {
209205 std::unique_ptr<StringBuffer> buffer = Utf8ToStringView (reason);
210206 session_->schedulePauseOnNextStatement (buffer->string (), buffer->string ());
211207 }
212208
213- InspectorSessionDelegate* delegate () {
214- return delegate_;
215- }
216-
217209 private:
218210 void sendResponse (
219211 int callId,
@@ -232,7 +224,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
232224 delegate_->SendMessageToFrontend (message);
233225 }
234226
235- InspectorSessionDelegate* const delegate_;
227+ std::unique_ptr< InspectorSessionDelegate> delegate_;
236228 std::unique_ptr<v8_inspector::V8InspectorSession> session_;
237229};
238230
@@ -300,8 +292,7 @@ class InspectorTimerHandle {
300292class NodeInspectorClient : public V8InspectorClient {
301293 public:
302294 NodeInspectorClient (node::Environment* env, node::NodePlatform* platform)
303- : env_(env), platform_(platform), terminated_(false ),
304- running_nested_loop_ (false ) {
295+ : env_(env), platform_(platform) {
305296 client_ = V8Inspector::create (env->isolate (), this );
306297 // TODO(bnoordhuis) Make name configurable from src/node.cc.
307298 ContextInfo info (GetHumanReadableProcessName ());
@@ -310,18 +301,28 @@ class NodeInspectorClient : public V8InspectorClient {
310301 }
311302
312303 void runMessageLoopOnPause (int context_group_id) override {
313- CHECK_NE (channel_, nullptr );
304+ runMessageLoop (false );
305+ }
306+
307+ void runMessageLoop (bool ignore_terminated) {
314308 if (running_nested_loop_)
315309 return ;
316310 terminated_ = false ;
317311 running_nested_loop_ = true ;
318- while (!terminated_ && channel_-> waitForFrontendMessage ()) {
312+ while ((ignore_terminated || !terminated_) && waitForFrontendEvent ()) {
319313 while (platform_->FlushForegroundTasks (env_->isolate ())) {}
320314 }
321315 terminated_ = false ;
322316 running_nested_loop_ = false ;
323317 }
324318
319+ bool waitForFrontendEvent () {
320+ InspectorIo* io = env_->inspector_agent ()->io ();
321+ if (io == nullptr )
322+ return false ;
323+ return io->WaitForFrontendEvent ();
324+ }
325+
325326 double currentTimeMS () override {
326327 return uv_hrtime () * 1.0 / NANOS_PER_MSEC;
327328 }
@@ -363,20 +364,22 @@ class NodeInspectorClient : public V8InspectorClient {
363364 terminated_ = true ;
364365 }
365366
366- void connectFrontend (InspectorSessionDelegate* delegate) {
367- CHECK_EQ (channel_, nullptr );
368- channel_ = std::unique_ptr<ChannelImpl>(
369- new ChannelImpl (client_.get (), delegate));
367+ int connectFrontend (std::unique_ptr<InspectorSessionDelegate> delegate) {
368+ events_dispatched_ = true ;
369+ int session_id = next_session_id_++;
370+ channels_[session_id] =
371+ std::make_unique<ChannelImpl>(client_, std::move (delegate));
372+ return session_id;
370373 }
371374
372- void disconnectFrontend () {
373- quitMessageLoopOnPause () ;
374- channel_. reset ( );
375+ void disconnectFrontend (int session_id ) {
376+ events_dispatched_ = true ;
377+ channels_. erase (session_id );
375378 }
376379
377- void dispatchMessageFromFrontend (const StringView& message) {
378- CHECK_NE (channel_, nullptr ) ;
379- channel_ ->dispatchProtocolMessage (message);
380+ void dispatchMessageFromFrontend (int session_id, const StringView& message) {
381+ events_dispatched_ = true ;
382+ channels_[session_id] ->dispatchProtocolMessage (message);
380383 }
381384
382385 Local<Context> ensureDefaultContextInGroup (int contextGroupId) override {
@@ -426,10 +429,6 @@ class NodeInspectorClient : public V8InspectorClient {
426429 script_id);
427430 }
428431
429- ChannelImpl* channel () {
430- return channel_.get ();
431- }
432-
433432 void startRepeatingTimer (double interval_s,
434433 TimerCallback callback,
435434 void * data) override {
@@ -464,20 +463,31 @@ class NodeInspectorClient : public V8InspectorClient {
464463 client_->allAsyncTasksCanceled ();
465464 }
466465
466+ void schedulePauseOnNextStatement (const std::string& reason) {
467+ for (const auto & id_channel : channels_) {
468+ id_channel.second ->schedulePauseOnNextStatement (reason);
469+ }
470+ }
471+
472+ bool hasConnectedSessions () {
473+ return !channels_.empty ();
474+ }
475+
467476 private:
468477 node::Environment* env_;
469478 node::NodePlatform* platform_;
470- bool terminated_;
471- bool running_nested_loop_;
479+ bool terminated_ = false ;
480+ bool running_nested_loop_ = false ;
472481 std::unique_ptr<V8Inspector> client_;
473- std::unique_ptr<ChannelImpl> channel_ ;
482+ std::unordered_map< int , std:: unique_ptr<ChannelImpl>> channels_ ;
474483 std::unordered_map<void *, InspectorTimerHandle> timers_;
484+ int next_session_id_ = 1 ;
485+ bool events_dispatched_ = false ;
475486};
476487
477488Agent::Agent (Environment* env) : parent_env_(env),
478489 client_ (nullptr ),
479490 platform_(nullptr ),
480- enabled_(false ),
481491 pending_enable_async_hook_(false ),
482492 pending_disable_async_hook_(false ) {}
483493
@@ -491,7 +501,7 @@ bool Agent::Start(node::NodePlatform* platform, const char* path,
491501 path_ = path == nullptr ? " " : path;
492502 debug_options_ = options;
493503 client_ =
494- std::unique_ptr <NodeInspectorClient>(
504+ std::shared_ptr <NodeInspectorClient>(
495505 new NodeInspectorClient (parent_env_, platform));
496506 platform_ = platform;
497507 CHECK_EQ (0 , uv_async_init (uv_default_loop (),
@@ -515,7 +525,6 @@ bool Agent::StartIoThread(bool wait_for_connect) {
515525
516526 CHECK_NE (client_, nullptr );
517527
518- enabled_ = true ;
519528 io_ = std::unique_ptr<InspectorIo>(
520529 new InspectorIo (parent_env_, platform_, path_, debug_options_,
521530 wait_for_connect));
@@ -554,20 +563,25 @@ void Agent::Stop() {
554563 if (io_ != nullptr ) {
555564 io_->Stop ();
556565 io_.reset ();
557- enabled_ = false ;
558566 }
559567}
560568
561- void Agent::Connect (InspectorSessionDelegate* delegate) {
562- enabled_ = true ;
563- client_->connectFrontend (delegate);
569+ std::unique_ptr<InspectorSession> Agent::Connect (
570+ std::unique_ptr<InspectorSessionDelegate> delegate) {
571+ int session_id = client_->connectFrontend (std::move (delegate));
572+ return std::make_unique<InspectorSession>(session_id, client_);
564573}
565574
566575void Agent::WaitForDisconnect () {
567576 CHECK_NE (client_, nullptr );
568577 client_->contextDestroyed (parent_env_->context ());
569578 if (io_ != nullptr ) {
570579 io_->WaitForDisconnect ();
580+ // There is a bug in V8 Inspector (https://crbug.com/834056) that
581+ // calls V8InspectorClient::quitMessageLoopOnPause when a session
582+ // disconnects. We are using this flag to ignore those calls so the message
583+ // loop is spinning as long as there's a reason to expect inspector messages
584+ client_->runMessageLoop (true );
571585 }
572586}
573587
@@ -578,33 +592,8 @@ void Agent::FatalException(Local<Value> error, Local<v8::Message> message) {
578592 WaitForDisconnect ();
579593}
580594
581- void Agent::Dispatch (const StringView& message) {
582- CHECK_NE (client_, nullptr );
583- client_->dispatchMessageFromFrontend (message);
584- }
585-
586- void Agent::Disconnect () {
587- CHECK_NE (client_, nullptr );
588- client_->disconnectFrontend ();
589- }
590-
591- void Agent::RunMessageLoop () {
592- CHECK_NE (client_, nullptr );
593- client_->runMessageLoopOnPause (CONTEXT_GROUP_ID);
594- }
595-
596- InspectorSessionDelegate* Agent::delegate () {
597- CHECK_NE (client_, nullptr );
598- ChannelImpl* channel = client_->channel ();
599- if (channel == nullptr )
600- return nullptr ;
601- return channel->delegate ();
602- }
603-
604595void Agent::PauseOnNextJavascriptStatement (const std::string& reason) {
605- ChannelImpl* channel = client_->channel ();
606- if (channel != nullptr )
607- channel->schedulePauseOnNextStatement (reason);
596+ client_->schedulePauseOnNextStatement (reason);
608597}
609598
610599void Agent::RegisterAsyncHook (Isolate* isolate,
@@ -699,5 +688,20 @@ bool Agent::IsWaitingForConnect() {
699688 return debug_options_.wait_for_connect ();
700689}
701690
691+ bool Agent::HasConnectedSessions () {
692+ return client_->hasConnectedSessions ();
693+ }
694+
695+ InspectorSession::InspectorSession (int session_id,
696+ std::shared_ptr<NodeInspectorClient> client)
697+ : session_id_(session_id), client_(client) {}
698+
699+ InspectorSession::~InspectorSession () {
700+ client_->disconnectFrontend (session_id_);
701+ }
702+
703+ void InspectorSession::Dispatch (const StringView& message) {
704+ client_->dispatchMessageFromFrontend (session_id_, message);
705+ }
702706} // namespace inspector
703707} // namespace node
0 commit comments