diff --git a/gui/webdisplay/inc/ROOT/RWebWindow.hxx b/gui/webdisplay/inc/ROOT/RWebWindow.hxx index 9e4f2e80550b80862650e86257dc8d873a33d9d1..b50d7690e2e257148bb7addab5988a85b7e5fa93 100644 --- a/gui/webdisplay/inc/ROOT/RWebWindow.hxx +++ b/gui/webdisplay/inc/ROOT/RWebWindow.hxx @@ -110,7 +110,7 @@ private: QueueEntry(unsigned connid, EQueueEntryKind kind, std::string &&data) : fConnId(connid), fKind(kind), fData(data) {} }; - typedef std::vector<std::shared_ptr<WebConn>> ConnectionsList; + using ConnectionsList_t = std::vector<std::shared_ptr<WebConn>>; std::shared_ptr<RWebWindowsManager> fMgr; ///<! display manager std::shared_ptr<RWebWindow> fMaster; ///<! master window where this window is embeded @@ -123,8 +123,8 @@ private: bool fSendMT{false}; ///<! true is special threads should be used for sending data std::shared_ptr<RWebWindowWSHandler> fWSHandler; ///<! specialize websocket handler for all incoming connections unsigned fConnCnt{0}; ///<! counter of new connections to assign ids - ConnectionsList fPendingConn; ///<! list of pending connection with pre-assigned keys - ConnectionsList fConn; ///<! list of all accepted connections + ConnectionsList_t fPendingConn; ///<! list of pending connection with pre-assigned keys + ConnectionsList_t fConn; ///<! list of all accepted connections mutable std::mutex fConnMutex; ///<! mutex used to protect connection list unsigned fConnLimit{1}; ///<! number of allowed active connections bool fNativeOnlyConn{false}; ///<! only native connection are allowed, created by Show() method @@ -153,7 +153,7 @@ private: void CompleteWSSend(unsigned wsid); - ConnectionsList GetConnections(unsigned connid = 0, bool only_active = false) const; + ConnectionsList_t GetConnections(unsigned connid = 0, bool only_active = false) const; std::shared_ptr<WebConn> FindOrCreateConnection(unsigned wsid, bool make_new, const char *query); @@ -183,6 +183,8 @@ private: unsigned AddEmbedWindow(std::shared_ptr<RWebWindow> window, int channel); + void RemoveEmbedWindow(unsigned connid, int channel); + bool ProcessBatchHolder(std::shared_ptr<THttpCallArg> &arg); void AssignCallbackThreadId(); diff --git a/gui/webdisplay/src/RWebWindow.cxx b/gui/webdisplay/src/RWebWindow.cxx index 4613203abd2746fa72a0412514cd4696eea5226f..d89a6370c4990405ed85bffaf3b043b4bafb38e5 100644 --- a/gui/webdisplay/src/RWebWindow.cxx +++ b/gui/webdisplay/src/RWebWindow.cxx @@ -77,6 +77,9 @@ ROOT::Experimental::RWebWindow::RWebWindow() = default; ROOT::Experimental::RWebWindow::~RWebWindow() { + if (fMaster) + fMaster->RemoveEmbedWindow(fMasterConnId, fMasterChannel); + if (fWSHandler) fWSHandler->SetDisabled(); @@ -92,8 +95,11 @@ ROOT::Experimental::RWebWindow::~RWebWindow() fPendingConn.clear(); } - for (auto &conn : lst) + for (auto &conn : lst) { conn->fActive = false; + for (auto &elem: conn->fEmbed) + elem.second->fMaster.reset(); + } fMgr->Unregister(*this); } @@ -275,17 +281,26 @@ std::shared_ptr<ROOT::Experimental::RWebWindow::WebConn> ROOT::Experimental::RWe std::shared_ptr<ROOT::Experimental::RWebWindow::WebConn> ROOT::Experimental::RWebWindow::RemoveConnection(unsigned wsid) { - std::lock_guard<std::mutex> grd(fConnMutex); - for (size_t n=0; n<fConn.size();++n) - if (fConn[n]->fWSId == wsid) { - std::shared_ptr<WebConn> res = std::move(fConn[n]); - fConn.erase(fConn.begin() + n); - res->fActive = false; - return res; - } + std::shared_ptr<WebConn> res; - return nullptr; + { + std::lock_guard<std::mutex> grd(fConnMutex); + + for (size_t n = 0; n < fConn.size(); ++n) + if (fConn[n]->fWSId == wsid) { + res = std::move(fConn[n]); + fConn.erase(fConn.begin() + n); + res->fActive = false; + break; + } + } + + if (res) + for (auto &elem: res->fEmbed) + elem.second->fMaster.reset(); + + return res; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -446,7 +461,7 @@ void ROOT::Experimental::RWebWindow::CheckPendingConnections() float tmout = fMgr->GetLaunchTmout(); - ConnectionsList selected; + ConnectionsList_t selected; { std::lock_guard<std::mutex> grd(fConnMutex); @@ -645,6 +660,13 @@ bool ROOT::Experimental::RWebWindow::ProcessWS(THttpCallArg &arg) ProvideQueueEntry(conn->fConnId, kind_Connect, ""s); conn->fReady = 10; } + } else if (cdata.compare(0,8,"CLOSECH=") == 0) { + int channel = std::stoi(cdata.substr(8)); + auto iter = conn->fEmbed.find(channel); + if (iter != conn->fEmbed.end()) { + iter->second->ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s); + conn->fEmbed.erase(iter); + } } } else if (fPanelName.length() && (conn->fReady < 10)) { if (cdata == "PANEL_READY") { @@ -970,9 +992,9 @@ void ROOT::Experimental::RWebWindow::CloseConnection(unsigned connid) /////////////////////////////////////////////////////////////////////////////////// /// returns connection (or all active connections) -ROOT::Experimental::RWebWindow::ConnectionsList ROOT::Experimental::RWebWindow::GetConnections(unsigned connid, bool only_active) const +ROOT::Experimental::RWebWindow::ConnectionsList_t ROOT::Experimental::RWebWindow::GetConnections(unsigned connid, bool only_active) const { - ConnectionsList arr; + ConnectionsList_t arr; { std::lock_guard<std::mutex> grd(fConnMutex); @@ -1275,6 +1297,20 @@ unsigned ROOT::Experimental::RWebWindow::AddEmbedWindow(std::shared_ptr<RWebWind return arr[0]->fConnId; } +///////////////////////////////////////////////////////////////////////////////// +/// Remove RWebWindow associated with the channel + +void ROOT::Experimental::RWebWindow::RemoveEmbedWindow(unsigned connid, int channel) +{ + auto arr = GetConnections(connid); + + for (auto &conn : arr) { + auto iter = conn->fEmbed.find(channel); + if (iter != conn->fEmbed.end()) + conn->fEmbed.erase(iter); + } +} + ///////////////////////////////////////////////////////////////////////////////// /// Create new RWebWindow diff --git a/js/scripts/JSRootPainter.js b/js/scripts/JSRootPainter.js index 6b1fa3e1ecd89136895cb8e9f0e4aa10d56f05cf..d9a68d816da2e03c27d40d745a6fe7de729d0c79 100644 --- a/js/scripts/JSRootPainter.js +++ b/js/scripts/JSRootPainter.js @@ -1917,6 +1917,7 @@ /** Close connection. */ WebWindowHandle.prototype.Close = function(force) { if (this.master) { + this.master.Send("CLOSECH=" + this.channelid, 0); delete this.master.channels[this.channelid]; delete this.master; return;