net/Socket.hpp         |    3 +
 wsd/DocumentBroker.cpp |   75 +++++++++++++++++++++++++++++++++----------------
 wsd/DocumentBroker.hpp |    3 -
 wsd/LOOLWSD.cpp        |   25 +++++++++++++++-
 4 files changed, 78 insertions(+), 28 deletions(-)

New commits:
commit 9b3a22aafee8d0163212ea0c1b499eb03cb632ba
Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk>
Date:   Wed Apr 19 00:54:42 2017 -0400

    wsd: save document upon server shutdown
    
    The server correctly saves all documents
    and waits to upload them before exiting.
    
    Change-Id: I04dc9ce588bc0fa39a9deb298d0a5efa61a03f1a
    Reviewed-on: https://gerrit.libreoffice.org/36654
    Reviewed-by: Ashod Nakashian <ashnak...@gmail.com>
    Tested-by: Ashod Nakashian <ashnak...@gmail.com>

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 89844912..c0b4b97e 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -438,7 +438,8 @@ public:
     void wakeup()
     {
         if (!isAlive())
-            LOG_WRN("Waking up dead poll thread [" << _name << "]");
+            LOG_WRN("Waking up dead poll thread [" << _name << "], started: " 
<<
+                    _threadStarted << ", finished: " << _threadFinished);
 
         wakeup(_wakeup[1]);
     }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 8ad9571e..94a49a1d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -12,6 +12,7 @@
 #include "DocumentBroker.hpp"
 
 #include <cassert>
+#include <chrono>
 #include <ctime>
 #include <fstream>
 #include <sstream>
@@ -220,29 +221,66 @@ void DocumentBroker::pollThread()
 
     auto last30SecCheckTime = std::chrono::steady_clock::now();
 
+    static const bool AutoSaveEnabled = !std::getenv("LOOL_NO_AUTOSAVE");
+
     // Main polling loop goodness.
-    while (!_stop && _poll->continuePolling() && !TerminationFlag && 
!ShutdownRequestFlag)
+    while (!_stop && _poll->continuePolling() && !TerminationFlag)
     {
         _poll->poll(SocketPoll::DefaultPollTimeoutMs);
 
-        if (!std::getenv("LOOL_NO_AUTOSAVE") && !_stop &&
-            std::chrono::duration_cast<std::chrono::seconds>
-            (std::chrono::steady_clock::now() - last30SecCheckTime).count() >= 
30)
+        const auto now = std::chrono::steady_clock::now();
+        if (_lastSaveTime < _lastSaveRequestTime &&
+            std::chrono::duration_cast<std::chrono::milliseconds>
+                    (now - _lastSaveRequestTime).count() <= COMMAND_TIMEOUT_MS)
+        {
+            // We are saving, nothing more to do but wait.
+            continue;
+        }
+
+        if (ShutdownRequestFlag)
         {
-            LOG_TRC("Trigger an autosave ...");
+            // Shutting down the server: notify clients, save, and stop.
+            static const std::string msg("close: recycling");
+
+            // First copy into local container, since removeSession
+            // will erase from _sessions, but will leave the last.
+            std::vector<std::shared_ptr<ClientSession>> sessions;
+            for (const auto& pair : _sessions)
+            {
+                sessions.push_back(pair.second);
+            }
+
+            for (const std::shared_ptr<ClientSession>& session : sessions)
+            {
+                try
+                {
+                    // Notify the client and disconnect.
+                    session->sendMessage(msg);
+                    
session->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, 
"recycling");
+
+                    // Remove session, save, and mark to destroy.
+                    removeSession(session->getId(), true);
+                }
+                catch (const std::exception& exc)
+                {
+                    LOG_WRN("Error while shutting down client [" <<
+                            session->getName() << "]: " << exc.what());
+                }
+            }
+        }
+        else if (AutoSaveEnabled && !_stop &&
+                 std::chrono::duration_cast<std::chrono::seconds>(now - 
last30SecCheckTime).count() >= 30)
+        {
+            LOG_TRC("Triggering an autosave.");
             autoSave(false);
             last30SecCheckTime = std::chrono::steady_clock::now();
         }
 
-        const bool notSaving = 
(std::chrono::duration_cast<std::chrono::milliseconds>
-                                (std::chrono::steady_clock::now() - 
_lastSaveRequestTime).count() > COMMAND_TIMEOUT_MS);
-
         // Remove idle documents after 1 hour.
         const bool idle = (getIdleTimeSecs() >= 3600);
 
         // If all sessions have been removed, no reason to linger.
-        if ((isLoaded() || _markToDestroy) && notSaving &&
-            (_sessions.empty() || idle))
+        if ((isLoaded() || _markToDestroy) && (_sessions.empty() || idle))
         {
             LOG_INF("Terminating " << (idle ? "idle" : "dead") <<
                     " DocumentBroker for docKey [" << getDocKey() << "].");
@@ -251,17 +289,8 @@ void DocumentBroker::pollThread()
     }
 
     LOG_INF("Finished polling doc [" << _docKey << "]. stop: " << _stop << ", 
continuePolling: " <<
-            _poll->continuePolling() << ", TerminationFlag: " << 
TerminationFlag <<
-            ", ShutdownRequestFlag: " << ShutdownRequestFlag << ".");
-
-    if (ShutdownRequestFlag)
-    {
-        static const std::string msg("close: recycling");
-        for (const auto& pair : _sessions)
-        {
-            pair.second->sendMessage(msg);
-        }
-    }
+            _poll->continuePolling() << ", ShutdownRequestFlag: " << 
ShutdownRequestFlag <<
+            ", TerminationFlag: " << TerminationFlag << ".");
 
     // Terminate properly while we can.
     //TODO: pass some sensible reason.
@@ -309,7 +338,7 @@ DocumentBroker::~DocumentBroker()
 
     Admin::instance().rmDoc(_docKey);
 
-    LOG_INF("~DocumentBroker [" << _uriPublic.toString() <<
+    LOG_INF("~DocumentBroker [" << _docKey <<
             "] destroyed with " << _sessions.size() << " sessions left.");
 
     // Do this early - to avoid operating on _childProcess from two threads.
@@ -317,7 +346,7 @@ DocumentBroker::~DocumentBroker()
 
     if (!_sessions.empty())
     {
-        LOG_WRN("DocumentBroker still has unremoved sessions.");
+        LOG_WRN("DocumentBroker [" << _docKey << "] still has unremoved 
sessions.");
     }
 
     // Need to first make sure the child exited, socket closed,
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 62cb6a95..5b64d7ed 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -48,8 +48,7 @@ public:
 
     bool continuePolling() override
     {
-        return SocketPoll::continuePolling()
-            && !TerminationFlag && !ShutdownRequestFlag;
+        return SocketPoll::continuePolling() && !TerminationFlag;
     }
 };
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index c432a862..d109e5d2 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2466,8 +2466,29 @@ int LOOLWSD::innerMain()
 
     // atexit handlers tend to free Admin before Documents
     LOG_INF("Cleaning up lingering documents.");
-    for (auto& docBrokerIt : DocBrokers)
-        docBrokerIt.second->joinThread();
+    if (ShutdownRequestFlag || TerminationFlag)
+    {
+        // Don't stop the DocBroker, they will exit.
+        const size_t sleepMs = 300;
+        const size_t count = std::max<size_t>(COMMAND_TIMEOUT_MS, 2000) / 
sleepMs;
+        for (size_t i = 0; i < count; ++i)
+        {
+            std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
+            cleanupDocBrokers();
+            if (DocBrokers.empty())
+                break;
+            docBrokersLock.unlock();
+
+            // Give them time to save and cleanup.
+            std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs));
+        }
+    }
+    else
+    {
+        // Stop and join.
+        for (auto& docBrokerIt : DocBrokers)
+            docBrokerIt.second->joinThread();
+    }
 
     // Disable thread checking - we'll now cleanup lots of things if we can
     Socket::InhibitThreadChecks = true;
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to