From e4b293fe3ba15879056d0fd3740793ec5455c616 Mon Sep 17 00:00:00 2001
From: Sergey Linev <S.Linev@gsi.de>
Date: Thu, 27 Jul 2017 12:12:31 +0200
Subject: [PATCH] webgui: implement sync and async mode for Canvas::SaveAs()
 method

When synchronous mode specified (defualt),
method blocked until file is produced.
With asynchronous mode method returned and file will be produced in
background.
---
 graf2d/gpad/v7/inc/ROOT/TCanvas.hxx           |  2 +-
 .../v7/inc/ROOT/TVirtualCanvasPainter.hxx     |  2 +-
 graf2d/gpad/v7/src/TCanvas.cxx                |  6 +--
 gui/canvaspainter/v7/src/TCanvasPainter.cxx   | 39 +++++++++++++++++--
 4 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx b/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx
index f96904f2979..9a98b8b2d02 100644
--- a/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx
+++ b/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx
@@ -139,7 +139,7 @@ public:
    void Update(bool async = false);
 
    /// Save canvas in image file
-   void SaveAs(const std::string &filename);
+   void SaveAs(const std::string &filename, bool async = false);
 
    /// Get the canvas's title.
    const std::string &GetTitle() const { return fTitle; }
diff --git a/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx b/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx
index 1c0195acbde..446dcbf936b 100644
--- a/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx
+++ b/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx
@@ -60,7 +60,7 @@ public:
    virtual bool IsCanvasModified(uint64_t) const = 0;
 
    /// perform special action when drawing is ready
-   virtual void DoWhenReady(const std::string &, const std::string &) = 0;
+   virtual void DoWhenReady(const std::string &, const std::string &, bool) = 0;
 
    virtual void NewDisplay(const std::string &where) = 0;
 
diff --git a/graf2d/gpad/v7/src/TCanvas.cxx b/graf2d/gpad/v7/src/TCanvas.cxx
index 79db04fce18..38c2e50e015 100644
--- a/graf2d/gpad/v7/src/TCanvas.cxx
+++ b/graf2d/gpad/v7/src/TCanvas.cxx
@@ -101,14 +101,14 @@ void ROOT::Experimental::TCanvas::Show(const std::string &where)
    }
 }
 
-void ROOT::Experimental::TCanvas::SaveAs(const std::string &filename)
+void ROOT::Experimental::TCanvas::SaveAs(const std::string &filename, bool async)
 {
    if (!fPainter)
       fPainter = Internal::TVirtualCanvasPainter::Create(*this, true);
    if (filename.find(".svg") != std::string::npos)
-      fPainter->DoWhenReady("SVG", filename);
+      fPainter->DoWhenReady("SVG", filename, async);
    else if (filename.find(".png") != std::string::npos)
-      fPainter->DoWhenReady("PNG", filename);
+      fPainter->DoWhenReady("PNG", filename, async);
 }
 
 // TODO: removal from GetHeldCanvases().
diff --git a/gui/canvaspainter/v7/src/TCanvasPainter.cxx b/gui/canvaspainter/v7/src/TCanvasPainter.cxx
index 16fa8cfc58e..43f68f1dc01 100644
--- a/gui/canvaspainter/v7/src/TCanvasPainter.cxx
+++ b/gui/canvaspainter/v7/src/TCanvasPainter.cxx
@@ -195,6 +195,7 @@ private:
    ROOT::Experimental::TPadDisplayItem fDisplayList; ///!< full list of items to display
    WebCommandsList fCmds;                            ///!< list of submitted commands
    uint64_t fCmdsCnt;                                ///!< commands counter
+   std::string fWaitingCmdId;                        ///!< command id waited for complition
 
    uint64_t fSnapshotVersion;   ///!< version of snapshot
    std::string fSnapshot;       ///!< last produced snapshot
@@ -232,7 +233,7 @@ public:
    /// The painter observes it; it needs to know should the TCanvas be deleted.
    TCanvasPainter(const std::string &name, const ROOT::Experimental::TCanvas &canv, bool batch_mode)
       : THttpWSHandler(name.c_str(), "title"), fCanvas(canv), fBatchMode(batch_mode), fWebConn(), fDisplayList(),
-        fCmds(), fCmdsCnt(0), fSnapshotVersion(0), fSnapshot(), fSnapshotDelivered(0)
+        fCmds(), fCmdsCnt(0), fWaitingCmdId(), fSnapshotVersion(0), fSnapshot(), fSnapshotDelivered(0)
    {
       CreateHttpServer();
       gServer->Register("/web7gui", this);
@@ -247,7 +248,7 @@ public:
    virtual bool IsCanvasModified(uint64_t) const override;
 
    /// perform special action when drawing is ready
-   virtual void DoWhenReady(const std::string &cmd, const std::string &arg) final;
+   virtual void DoWhenReady(const std::string &cmd, const std::string &arg, bool async) final;
 
    // open new display for the canvas
    virtual void NewDisplay(const std::string &where) override;
@@ -429,15 +430,42 @@ bool TCanvasPainter::WaitWhenCanvasPainted(uint64_t ver)
    return false;
 }
 
-void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg)
+void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg, bool async)
 {
+   if (!async && !fWaitingCmdId.empty()) {
+      Error("DoWhenReady", "Fail to submit sync command when previous is still awaited - use async");
+      async = true;
+   }
+
    WebCommand cmd;
-   cmd.fId = TString::ULLtoa(fCmdsCnt++, 10);
+   cmd.fId = TString::ULLtoa(++fCmdsCnt, 10);
    cmd.fName = name;
    cmd.fArg = arg;
    cmd.fRunning = false;
    fCmds.push_back(cmd);
+
+   if (!async) fWaitingCmdId = cmd.fId;
+
    CheckDataToSend();
+
+   if (async) return;
+
+   uint64_t cnt = 0;
+   bool had_connection = false;
+
+   while (true) {
+      if (fWebConn.size() > 0)
+         had_connection = true;
+      if ((fWebConn.size() == 0) && (had_connection || (cnt > 1000)))
+         return; // wait ~1 min if no new connection established
+      if (fWaitingCmdId.empty()) {
+         printf("Command %s waiting READY!!!\n", name.c_str());
+         return;
+      }
+      gSystem->ProcessEvents();
+      gSystem->Sleep((++cnt < 500) ? 1 : 100); // increase sleep interval when do very often
+   }
+
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////////
@@ -448,6 +476,9 @@ void TCanvasPainter::PopFrontCommand(bool)
 {
    if (fCmds.size() == 0) return;
 
+   // simple condition, which will be checked in waiting loop
+   if (!fWaitingCmdId.empty() && (fWaitingCmdId == fCmds.front().fId)) fWaitingCmdId.clear();
+
    fCmds.pop_front();
 }
 
-- 
GitLab