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 5c978ed5aaa839054b605a96fb298641ef3ce986 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> (cherry picked from commit 9b3a22aafee8d0163212ea0c1b499eb03cb632ba) Reviewed-on: https://gerrit.libreoffice.org/36656 Reviewed-by: Jan Holesovsky <ke...@collabora.com> Tested-by: Jan Holesovsky <ke...@collabora.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