[Libreoffice-commits] online.git: 242 commits - common/Common.hpp common/FileUtil.cpp common/IoUtil.cpp common/Log.cpp common/MessageQueue.cpp common/Protocol.cpp common/Session.cpp common/Session.hpp common/SigUtil.cpp common/SigUtil.hpp common/SpookyV2.cpp common/Unit.cpp common/UnitHTTP.cpp common/Util.cpp common/Util.hpp .gitignore kit/ChildSession.cpp kit/DummyLibreOfficeKit.cpp kit/Kit.cpp Makefile.am net/clientnb.cpp net/loolnb.cpp net/ServerSocket.hpp net/Socket.cpp net/Socket.hpp net/Ssl.cpp net/Ssl.hpp net/SslSocket.hpp net/WebSocketHandler.hpp test/helpers.hpp test/httpcrashtest.cpp test/httpwserror.cpp test/httpwstest.cpp test/Makefile.am test/test.cpp test/UnitAdmin.cpp test/UnitFonts.cpp test/UnitFuzz.cpp test/UnitMinSocketBufferSize.cpp test/UnitOOB.cpp test/UnitPrefork.cpp test/UnitStorage.cpp test/UnitTileCache.cpp test/UnitTimeout.cpp tools/KitClient.cpp tools/map.cpp tools/mount.cpp tools/Stress.cpp tools/Tool.cpp wsd/Admin.cpp wsd/AdminModel.cpp wsd/Auth.cpp wsd/ClientSession. cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/FileServer.cpp wsd/FileServer.hpp wsd/LOOLWebSocket.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp wsd/SenderQueue.cpp wsd/SenderQueue.hpp wsd/Storage.cpp wsd/TileCache.cpp

Jan Holesovsky kendy at collabora.com
Fri Mar 10 09:48:20 UTC 2017


 .gitignore                       |    2 
 Makefile.am                      |   46 
 common/Common.hpp                |    3 
 common/FileUtil.cpp              |    3 
 common/IoUtil.cpp                |    3 
 common/Log.cpp                   |    2 
 common/MessageQueue.cpp          |    2 
 common/Protocol.cpp              |    3 
 common/Session.cpp               |   91 -
 common/Session.hpp               |   20 
 common/SigUtil.cpp               |   90 -
 common/SigUtil.hpp               |   20 
 common/SpookyV2.cpp              |    2 
 common/Unit.cpp                  |    3 
 common/UnitHTTP.cpp              |    4 
 common/Util.cpp                  |    3 
 common/Util.hpp                  |    3 
 kit/ChildSession.cpp             |    5 
 kit/DummyLibreOfficeKit.cpp      |    2 
 kit/Kit.cpp                      |   12 
 net/ServerSocket.hpp             |  113 +
 net/Socket.cpp                   |  144 +
 net/Socket.hpp                   |  823 +++++++++
 net/Ssl.cpp                      |  276 +++
 net/Ssl.hpp                      |   83 
 net/SslSocket.hpp                |  264 +++
 net/WebSocketHandler.hpp         |  359 ++++
 net/clientnb.cpp                 |  281 +++
 net/loolnb.cpp                   |  265 +++
 test/Makefile.am                 |    7 
 test/UnitAdmin.cpp               |    4 
 test/UnitFonts.cpp               |    2 
 test/UnitFuzz.cpp                |    2 
 test/UnitMinSocketBufferSize.cpp |    4 
 test/UnitOOB.cpp                 |    2 
 test/UnitPrefork.cpp             |    2 
 test/UnitStorage.cpp             |    4 
 test/UnitTileCache.cpp           |    4 
 test/UnitTimeout.cpp             |    4 
 test/helpers.hpp                 |    8 
 test/httpcrashtest.cpp           |    5 
 test/httpwserror.cpp             |    2 
 test/httpwstest.cpp              |   84 
 test/test.cpp                    |    2 
 tools/KitClient.cpp              |    2 
 tools/Stress.cpp                 |    2 
 tools/Tool.cpp                   |    2 
 tools/map.cpp                    |    2 
 tools/mount.cpp                  |    2 
 wsd/Admin.cpp                    |    3 
 wsd/AdminModel.cpp               |    3 
 wsd/Auth.cpp                     |    3 
 wsd/ClientSession.cpp            |   57 
 wsd/ClientSession.hpp            |    7 
 wsd/DocumentBroker.cpp           |  351 +++-
 wsd/DocumentBroker.hpp           |  140 +
 wsd/FileServer.cpp               |   91 -
 wsd/FileServer.hpp               |   35 
 wsd/LOOLWSD.cpp                  | 3401 ++++++++++++++++++++-------------------
 wsd/LOOLWSD.hpp                  |   13 
 wsd/LOOLWebSocket.hpp            |    2 
 wsd/SenderQueue.cpp              |  159 -
 wsd/SenderQueue.hpp              |  113 -
 wsd/Storage.cpp                  |    3 
 wsd/TileCache.cpp                |    3 
 65 files changed, 5123 insertions(+), 2334 deletions(-)

New commits:
commit 4ba2325c81cafe342d19f186b55a91e248124ddc
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Mar 10 10:44:06 2017 +0100

    Move Replay.hpp to headers.
    
    Change-Id: Id65e81ce2b8a9b77d910524a00d9f42164365ab2

diff --git a/Makefile.am b/Makefile.am
index 4992917..1ed2f25 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -54,8 +54,7 @@ shared_sources = common/FileUtil.cpp \
                  common/Unit.cpp \
                  common/UnitHTTP.cpp \
                  common/Util.cpp \
-                 net/Socket.cpp \
-                 tools/Replay.hpp
+                 net/Socket.cpp
 if ENABLE_SSL
 shared_sources += net/Ssl.cpp
 endif
@@ -163,7 +162,8 @@ shared_headers = common/Common.hpp \
                  common/security.h \
                  common/SpookyV2.h \
                  net/Socket.hpp \
-                 net/WebSocketHandler.hpp
+                 net/WebSocketHandler.hpp \
+                 tools/Replay.hpp
 if ENABLE_SSL
 shared_headers += net/Ssl.hpp \
                   net/SslSocket.hpp
commit 5677eb30b704cce6f3987d17ed712e956a31859a
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 23:50:36 2017 -0500

    nb: fix saving when last client disconnects
    
    When the last client session is disconnected
    docBroker must first issue a save and wait
    until the kit processes the save and sends
    back notfication. Since said notification
    goes to the ChildSession (which is the last)
    and said ChildSession is the one that signals
    to docBroker to persist the doc to the Storage,
    we need to keep all components alive and kicking
    during this final saving.
    
    As such, when the last session is to be removed
    from docBroker, we instead issue an autosave and
    continue everything as normal. When the save
    notification even arrives and ChildSession signals
    docBroker to persist the doc, we check if we were
    destroying and in that even remove that last session
    and stop the polling thread.
    
    The docBroker instance itself will get cleaned up
    in due time.
    
    Change-Id: Ie84e784284e1ec12b0b201d6bf75170b31f66147

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e10bda8..d2b563d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -185,6 +185,11 @@ void DocumentBroker::startThread()
 // The inner heart of the DocumentBroker - our poll loop.
 void DocumentBroker::pollThread()
 {
+    static std::atomic<unsigned> DocBrokerId(1);
+    Util::setThreadName("docbroker_" + Util::encodeId(DocBrokerId++, 3));
+
+    LOG_INF("Starting docBroker polling thread for docKey [" << _docKey << "].");
+
     // Request a kit process for this doc.
     _childProcess = getNewChild_Blocks();
     if (!_childProcess)
@@ -255,22 +260,7 @@ void DocumentBroker::pollThread()
         }
     }
 
-    // FIXME: probably we should stop listening on
-    // incoming sockets here if we have any.
-
-    auto lastSaveTime = _lastSaveTime;
-    auto saveTimeoutStart = std::chrono::steady_clock::now();
-
-    // Save before exit.
-    autoSave(true);
-
-    // wait 20 seconds for a save notification and quit.
-    while (lastSaveTime < saveTimeoutStart &&
-           std::chrono::duration_cast<std::chrono::seconds>
-           (std::chrono::steady_clock::now() - saveTimeoutStart).count() <= 20)
-    {
-        _poll->poll(SocketPoll::DefaultPollTimeoutMs);
-    }
+    LOG_INF("Finished docBroker polling thread for docKey [" << _docKey << "].");
 }
 
 bool DocumentBroker::isAlive() const
@@ -497,6 +487,28 @@ bool DocumentBroker::load(std::shared_ptr<ClientSession>& session, const std::st
 bool DocumentBroker::saveToStorage(const std::string& sessionId,
                                    bool success, const std::string& result)
 {
+    const bool res = saveToStorageInternal(sessionId, success, result);
+
+    // If marked to destroy, then this was the last session.
+    // FIXME: If during that last save another client connects
+    // to this doc, the _markToDestroy will be reset and we
+    // will leak the last session. Need to mark the session as
+    // dead and cleanup somehow.
+    if (_markToDestroy)
+    {
+        // We've saved and can safely destroy.
+        removeSessionInternal(sessionId);
+
+        // Stop so we get cleaned up and removed.
+        _stop = true;
+    }
+
+    return res;
+}
+
+bool DocumentBroker::saveToStorageInternal(const std::string& sessionId,
+                                           bool success, const std::string& result)
+{
     assert(_poll->isCorrectThread());
 
     // If save requested, but core didn't save because document was unmodified
@@ -774,6 +786,23 @@ size_t DocumentBroker::removeSession(const std::string& id, bool destroyIfLast)
         LOG_INF("Removing session [" << id << "] on docKey [" << _docKey <<
                 "]. Have " << _sessions.size() << " sessions.");
 
+        if (_lastEditableSession)
+            autoSave(true);
+        else
+            return removeSessionInternal(id);
+    }
+    catch (const std::exception& ex)
+    {
+        LOG_ERR("Error while removing session [" << id << "]: " << ex.what());
+    }
+
+    return _sessions.size();
+}
+
+size_t DocumentBroker::removeSessionInternal(const std::string& id)
+{
+    try
+    {
         // remove also from the _newSessions
         _newSessions.erase(std::remove_if(_newSessions.begin(), _newSessions.end(), [&id](NewSession& newSession) { return newSession._session->getId() == id; }),
                            _newSessions.end());
@@ -1110,7 +1139,6 @@ void DocumentBroker::destroyIfLastEditor(const std::string& id)
 
     // Last view going away, can destroy.
     _markToDestroy = (_sessions.size() <= 1);
-    _stop = true;
     LOG_DBG("startDestroy on session [" << id << "] on docKey [" << _docKey <<
             "], markToDestroy: " << _markToDestroy << ", lastEditableSession: " << _lastEditableSession);
 }
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 6c855f6..5594c70 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -344,8 +344,11 @@ private:
     /// Sends the .uno:Save command to LoKit.
     bool sendUnoSave(const bool dontSaveIfUnmodified);
 
-    /// Saves the document to Storage (assuming LO Core saved to local copy).
-    bool saveToStorage();
+    /// Saves the doc to the storage.
+    bool saveToStorageInternal(const std::string& sesionId, bool success, const std::string& result = "");
+
+    /// Removes a session by ID. Returns the new number of sessions.
+    size_t removeSessionInternal(const std::string& id);
 
     /// Forward a message from child session to its respective client session.
     bool forwardToClient(const std::shared_ptr<Message>& payload);
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index e124882..4608c5a 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2287,14 +2287,9 @@ private:
             // Connection terminated. Destroy session.
             LOG_DBG("Client session [" << _id << "] on docKey [" << docKey << "] terminated. Cleaning up.");
 
-
             // We issue a force-save when last editable (non-readonly) session is going away
-            const auto sessionsCount = docBroker->removeSession(_id, true);
-            if (sessionsCount == 0)
-            {
-                // We've supposedly destroyed the last session, now cleanup.
-                removeDocBrokerSession(docBroker);
-            }
+            // and defer destroying the last session and the docBroker.
+            docBroker->removeSession(_id, true);
 
             LOG_INF("Finishing GET request handler for session [" << _id << "].");
         }
commit 41eb0527806d5584e991b5d86d25b584af4c1fd6
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 22:39:18 2017 -0500

    nb: really wait 20 seconds for last save
    
    Change-Id: Ia30beb1e68c55b9987a9730e9acab11bd2871811

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 4ef8cb7..e10bda8 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -267,7 +267,7 @@ void DocumentBroker::pollThread()
     // wait 20 seconds for a save notification and quit.
     while (lastSaveTime < saveTimeoutStart &&
            std::chrono::duration_cast<std::chrono::seconds>
-           (std::chrono::steady_clock::now() - saveTimeoutStart).count() >= 20)
+           (std::chrono::steady_clock::now() - saveTimeoutStart).count() <= 20)
     {
         _poll->poll(SocketPoll::DefaultPollTimeoutMs);
     }
commit 2d621f1a4706aba936f4f2e70ce6de9a329e6aa4
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 22:38:25 2017 -0500

    nb: logging
    
    Change-Id: Ic3b724d5869f75234af2238b96a90c4745155b86

diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 004cb88..ac96973 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -186,7 +186,7 @@ private:
         // Last operation failed. Find out if SSL was trying
         // to do something different that failed, or not.
         const int sslError = SSL_get_error(_ssl, rc);
-        LOG_TRC("SSL error: " << sslError);
+        LOG_TRC("Socket #" << getFD() << " SSL error: " << sslError);
         switch (sslError)
         {
         case SSL_ERROR_ZERO_RETURN:
@@ -239,7 +239,7 @@ private:
                 {
                     char buf[512];
                     ERR_error_string_n(bioError, buf, sizeof(buf));
-                    LOG_ERR("BIO error: " << buf);
+                    LOG_ERR("Socket #" << getFD() << " BIO error: " << buf);
                     throw std::runtime_error(buf);
                 }
             }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e8a4c2c..4ef8cb7 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1263,6 +1263,7 @@ void DocumentBroker::closeDocument(const std::string& reason)
 {
     auto lock = getLock();
 
+    LOG_DBG("Closing DocumentBroker for docKey [" << _docKey << "] with reason: " << reason);
     terminateChild(lock, reason);
 }
 
commit 7a5cc9ce6fa6db3c0e048757929166bc5bdb081f
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 20:56:39 2017 -0500

    nb: correct forkit initialization sequence
    
    This avoids extra kits, avoid logging
    unnecessary errors and adds informative logging.
    
    Change-Id: I7a4bb0b690f9787fc362d0b6aefcc722586eaed1

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index fd402de..e124882 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1085,6 +1085,18 @@ bool LOOLWSD::checkAndRestoreForKit()
 #ifdef KIT_IN_PROCESS
     return false;
 #else
+
+    if (ForKitProcId == -1)
+    {
+        // Fire the ForKit process for the first time.
+        if (!createForKit())
+        {
+            // Should never fail.
+            LOG_FTL("Failed to spawn loolforkit.");
+            return Application::EXIT_SOFTWARE;
+        }
+    }
+
     int status;
     const pid_t pid = waitpid(ForKitProcId, &status, WUNTRACED | WNOHANG);
     if (pid > 0)
@@ -1190,7 +1202,6 @@ void PrisonerPoll::wakeupHook()
     }
 }
 
-
 bool LOOLWSD::createForKit()
 {
 #ifdef KIT_IN_PROCESS
@@ -2609,6 +2620,8 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     if (ClientPortNumber == MasterPortNumber)
         throw IncompatibleOptionsException("port");
 
+    // Start the internal prisoner server and spawn forkit,
+    // which in turn forks first child.
     srv.startPrisoners(MasterPortNumber);
 
 #ifndef KIT_IN_PROCESS
@@ -2626,18 +2639,13 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
             throw std::runtime_error(msg);
         }
 
-        // Check we have at least one
+        // Check we have at least one.
+        LOG_TRC("Have " << NewChildren.size() << " new children.");
         assert(NewChildren.size() > 0);
     }
 #endif
 
-    // Fire the ForKit process; we are ready to get child connections.
-    if (!createForKit())
-    {
-        LOG_FTL("Failed to spawn loolforkit.");
-        return Application::EXIT_SOFTWARE;
-    }
-
+    // Start the server.
     srv.start(ClientPortNumber);
 
 #if ENABLE_DEBUG
commit 0bf6e28f5433635385f34510791d3d893aec82bc
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 20:56:02 2017 -0500

    nb: cleanup old comments and move MAX_CONNECTIONS assertion
    
    Change-Id: I24f3f4321ccc62c26c433d4a709b41c894a0196d

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 7eef0cd..fd402de 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -805,6 +805,7 @@ void LOOLWSD::initialize(Application& self)
     setenv("SAL_DISABLE_OPENCL", "true", 1);
 
     // Log the connection and document limits.
+    static_assert(MAX_CONNECTIONS >= 3, "MAX_CONNECTIONS must be at least 3");
     static_assert(MAX_DOCUMENTS > 0 && MAX_DOCUMENTS <= MAX_CONNECTIONS, "MAX_DOCUMENTS must be positive and no more than MAX_CONNECTIONS");
     LOG_INF("Maximum concurrent open Documents limit: " << MAX_DOCUMENTS);
     LOG_INF("Maximum concurrent client Connections limit: " << MAX_CONNECTIONS);
@@ -2608,18 +2609,6 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     if (ClientPortNumber == MasterPortNumber)
         throw IncompatibleOptionsException("port");
 
-    // Configure the Server.
-    // Note: TCPServer internally uses a ThreadPool to dispatch connections
-    // (the default if not given). The capacity of the ThreadPool is increased
-    // here in proportion to MAX_CONNECTIONS. Each client requests ~10
-    // resources (.js, .css, etc) beyond the main one, which are transient.
-    // The pool must have sufficient available threads to dispatch a new
-    // connection, otherwise will deadlock. So we need to have sufficient
-    // threads to serve new clients while those transients are served.
-    // We provision up to half the limit to connect simultaneously
-    // without loss of performance. This cap is to avoid flooding the server.
-    static_assert(MAX_CONNECTIONS >= 3, "MAX_CONNECTIONS must be at least 3");
-
     srv.startPrisoners(MasterPortNumber);
 
 #ifndef KIT_IN_PROCESS
commit 1da9bc3d694e0501e8292e5cd0827f48f2098933
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 20:54:44 2017 -0500

    nb: set the polling thread name for better logging
    
    Change-Id: Iccc4337827f00af08327a4430f3d40fa69ac71b2

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 37dc3b4..7c48bfc 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -241,6 +241,7 @@ public:
     /// The default implementation of our polling thread
     virtual void pollingThread()
     {
+        Util::setThreadName(_name);
         LOG_INF("Starting polling thread [" << _name << "].");
         while (continuePolling())
         {
commit 3bfc8aa7f333e9ac44ea1a13c4d588f96fb193ce
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Mar 9 21:46:52 2017 +0000

    Use larger socket buffers for serving files to improve efficiency.
    
    The combination of nodelay + minimum buffers is horrific for
    file-serving; speedup is from 3.3s to 33ms to serve bundle.js.

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 5ba861b..37dc3b4 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -780,6 +780,10 @@ namespace HttpHelper
             return;
         }
 
+        const int socketBufferSize = 16 * 1024;
+        if (st.st_size >= socketBufferSize)
+            socket->setSendBufferSize(socketBufferSize);
+
         response.setContentLength(st.st_size);
         response.set("User-Agent", HTTP_AGENT_STRING);
         std::ostringstream oss;
@@ -789,15 +793,17 @@ namespace HttpHelper
         socket->sendHttpResponse(header);
 
         std::ifstream file(path, std::ios::binary);
+        bool flush = true;
         do
         {
-            char buf[16 * 1024];
+            char buf[socketBufferSize];
             file.read(buf, sizeof(buf));
             const int size = file.gcount();
             if (size > 0)
-                socket->send(buf, size);
+                socket->send(buf, size, flush);
             else
                 break;
+            flush = false;
         }
         while (file);
     }
commit 42a19e66e36837713f8a0b858fc7e47e9f556a60
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Mar 9 20:35:04 2017 +0000

    Centralize idle poll time.

diff --git a/net/Socket.cpp b/net/Socket.cpp
index 344d86f..b64f904 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -16,6 +16,8 @@
 #include "Socket.hpp"
 #include "ServerSocket.hpp"
 
+int SocketPoll::DefaultPollTimeoutMs = 5000;
+
 // help with initialization order
 namespace {
     std::vector<int> &getWakeupsArray()
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 4cb4821..5ba861b 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -216,6 +216,9 @@ public:
     SocketPoll(const std::string& threadName);
     ~SocketPoll();
 
+    /// Default poll time - useful to increase for debugging.
+    static int DefaultPollTimeoutMs;
+
     /// Start the polling thread (if desired)
     void startThread();
 
@@ -241,7 +244,7 @@ public:
         LOG_INF("Starting polling thread [" << _name << "].");
         while (continuePolling())
         {
-            poll(5000);
+            poll(DefaultPollTimeoutMs);
         }
     }
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 0a00553..e8a4c2c 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -243,7 +243,7 @@ void DocumentBroker::pollThread()
             _newSessions.pop_front();
         }
 
-        _poll->poll(5000);
+        _poll->poll(SocketPoll::DefaultPollTimeoutMs);
 
         if (!std::getenv("LOOL_NO_AUTOSAVE") &&
             std::chrono::duration_cast<std::chrono::seconds>
@@ -269,7 +269,7 @@ void DocumentBroker::pollThread()
            std::chrono::duration_cast<std::chrono::seconds>
            (std::chrono::steady_clock::now() - saveTimeoutStart).count() >= 20)
     {
-        _poll->poll(5000);
+        _poll->poll(SocketPoll::DefaultPollTimeoutMs);
     }
 }
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 410c1ca..7eef0cd 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2662,7 +2662,7 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     {
         UnitWSD::get().invokeTest();
 
-        mainWait.poll(30 * 1000 /* ms */);
+        mainWait.poll(SocketPoll::DefaultPollTimeoutMs * 10);
 
         std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
         cleanupDocBrokers();
commit 438630c68f33c1274a9d8813c264b09c1772d1be
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Mar 9 19:23:21 2017 +0000

    Start DocBroker thread later - when we've added our 1st session.

diff --git a/net/Socket.cpp b/net/Socket.cpp
index 56adecf..344d86f 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -32,7 +32,8 @@ namespace {
 
 SocketPoll::SocketPoll(const std::string& threadName)
     : _name(threadName),
-      _stop(false)
+      _stop(false),
+      _threadStarted(false)
 {
     // Create the wakeup fd.
     if (::pipe2(_wakeup, O_CLOEXEC | O_NONBLOCK) == -1)
@@ -51,7 +52,7 @@ SocketPoll::SocketPoll(const std::string& threadName)
 SocketPoll::~SocketPoll()
 {
     stop();
-    if (_thread.joinable())
+    if (_threadStarted && _thread.joinable())
         _thread.join();
 
     ::close(_wakeup[0]);
@@ -68,8 +69,12 @@ SocketPoll::~SocketPoll()
 
 void SocketPoll::startThread()
 {
-    _thread = std::thread(&SocketPoll::pollingThread, this);
-    _owner = _thread.get_id();
+    if (!_threadStarted)
+    {
+        _threadStarted = true;
+        _thread = std::thread(&SocketPoll::pollingThread, this);
+        _owner = _thread.get_id();
+    }
 }
 
 void SocketPoll::wakeupWorld()
diff --git a/net/Socket.hpp b/net/Socket.hpp
index b9f61c9..4cb4821 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -453,6 +453,7 @@ protected:
     std::atomic<bool> _stop;
     /// The polling thread.
     std::thread _thread;
+    std::atomic<bool> _threadStarted;
     std::thread::id _owner;
 };
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e6bf33c..0a00553 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -173,11 +173,15 @@ std::shared_ptr<DocumentBroker> DocumentBroker::create(
     std::shared_ptr<DocumentBroker> docBroker = std::make_shared<DocumentBroker>(uri, uriPublic, docKey, childRoot);
 
     docBroker->_poll->setDocumentBroker(docBroker);
-    docBroker->_poll->startThread();
 
     return docBroker;
 }
 
+void DocumentBroker::startThread()
+{
+    _poll->startThread();
+}
+
 // The inner heart of the DocumentBroker - our poll loop.
 void DocumentBroker::pollThread()
 {
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index b3f68f4..6c855f6 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -226,8 +226,12 @@ public:
                    const std::string& docKey,
                    const std::string& childRoot);
 
+
     ~DocumentBroker();
 
+    /// Start processing events
+    void startThread();
+
     /// Loads a document from the public URI into the jail.
     bool load(std::shared_ptr<ClientSession>& session, const std::string& jailId);
     bool isLoaded() const { return _isLoaded; }
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 78c5b51..410c1ca 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2255,6 +2255,7 @@ private:
                     _clientSession->onConnect(socket);
                     docBroker->addSocketToPoll(socket);
                 }
+                docBroker->startThread();
             }
         }
         if (!docBroker || !_clientSession)
commit 33d1fa7cc043591bfd5e6819633960ef5bd4d579
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Mar 9 19:12:28 2017 +0000

    Kill SenderThreadPool.

diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 97f5591..51b810a 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -162,7 +162,6 @@ private:
     std::unique_ptr<WopiStorage::WOPIFileInfo> _wopiFileInfo;
 
     SenderQueue<std::shared_ptr<Message>> _senderQueue;
-    std::thread _senderThread;
     std::atomic<bool> _stop;
 };
 
diff --git a/wsd/SenderQueue.cpp b/wsd/SenderQueue.cpp
deleted file mode 100644
index f614dd0..0000000
--- a/wsd/SenderQueue.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include "config.h"
-
-#include "SenderQueue.hpp"
-
-#include <algorithm>
-
-#include <Protocol.hpp>
-#include <Log.hpp>
-
-SenderQueue SenderQueue::TheQueue;
-SenderThreadPool SenderThreadPool::ThePool;
-
-bool SenderThreadPool::dispatchItem(const size_t timeoutMs)
-{
-    SendItem item;
-    if (SenderQueue::instance().waitDequeue(item, timeoutMs))
-    {
-        auto session = item.Session.lock();
-        if (session)
-        {
-            // Make sure we have extra threads before potentially getting stuck.
-            checkAndGrow();
-
-            try
-            {
-                IdleCountGuard guard(_idleThreadCount);
-
-                const std::vector<char>& data = item.Data->data();
-                if (item.Data->isBinary())
-                {
-                    return session->sendBinaryFrame(data.data(), data.size());
-                }
-                else
-                {
-                    return session->sendTextFrame(data.data(), data.size());
-                }
-            }
-            catch (const std::exception& ex)
-            {
-                LOG_ERR("Failed to send tile to " << session->getName() << ": " << ex.what());
-            }
-        }
-        else
-        {
-            LOG_WRN("Discarding send data for expired session.");
-        }
-    }
-
-    return false;
-}
-
-std::shared_ptr<SenderThreadPool::ThreadData> SenderThreadPool::createThread()
-{
-    if (!stopping())
-    {
-        std::shared_ptr<ThreadData> data(std::make_shared<ThreadData>());
-        std::thread thread([this, data]{ threadFunction(data); });
-        data->swap(thread);
-        return data;
-    }
-
-    return nullptr;
-}
-
-void SenderThreadPool::checkAndGrow()
-{
-    auto queueSize = SenderQueue::instance().size();
-    if (_idleThreadCount <= 1 && queueSize > 1)
-    {
-        std::lock_guard<std::mutex> lock(_mutex);
-
-        // Check again, in case rebalancing already did the trick.
-        queueSize = SenderQueue::instance().size();
-        if (_idleThreadCount <= 1 && queueSize > 1 &&
-            _maxThreadCount > _threads.size() && !stopping())
-        {
-            LOG_TRC("SenderThreadPool: growing. Cur: " << _threads.size() << ", Max: " << _maxThreadCount <<
-                    ", Idles: " << _idleThreadCount << ", Q: " << queueSize);
-
-            // We have room to grow.
-            auto newThreadData = createThread();
-            if (newThreadData)
-            {
-                _threads.push_back(newThreadData);
-            }
-        }
-    }
-}
-
-bool SenderThreadPool::rebalance()
-{
-    std::unique_lock<std::mutex> lock(_mutex, std::defer_lock);
-    if (!lock.try_lock())
-    {
-        // A sibling is likely rebalancing.
-        return false;
-    }
-
-    const auto threadCount = _threads.size();
-    LOG_DBG("SenderThreadPool: rebalancing " << threadCount << " threads.");
-
-    // First cleanup the non-joinables.
-    for (int i = _threads.size() - 1; i >= 0; --i)
-    {
-        if (!_threads[i]->joinable())
-        {
-            _threads.erase(_threads.begin() + i);
-        }
-    }
-
-    const auto threadCountNew = _threads.size();
-    LOG_DBG("SenderThreadPool: removed " << threadCount - threadCountNew <<
-            " dead threads to have " << threadCountNew << ".");
-
-    while (_threads.size() < _optimalThreadCount && !stopping())
-    {
-        auto newThreadData = createThread();
-        if (newThreadData)
-        {
-            _threads.push_back(newThreadData);
-        }
-    }
-
-    // Need to reduce?
-    LOG_DBG("SenderThreadPool: threads: " << _threads.size());
-    return _threads.size() > _optimalThreadCount;
-}
-
-void SenderThreadPool::threadFunction(const std::shared_ptr<ThreadData>& data)
-{
-    LOG_DBG("SenderThread started");
-    ++_idleThreadCount;
-
-    while (!stopping())
-    {
-        if (!dispatchItem(HousekeepIdleIntervalMs) && !stopping())
-        {
-            // We timed out. Seems we have more threads than work.
-            if (rebalance())
-            {
-                // We've been considered expendable.
-                LOG_DBG("SenderThread marked to die");
-                break;
-            }
-        }
-    }
-
-    data->detach();
-    LOG_DBG("SenderThread finished");
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/SenderQueue.hpp b/wsd/SenderQueue.hpp
index 2899611..6debf59 100644
--- a/wsd/SenderQueue.hpp
+++ b/wsd/SenderQueue.hpp
@@ -188,108 +188,6 @@ private:
     std::atomic<bool> _stop;
 };
 
-/// Pool of sender threads.
-/// These are dedicated threads that only dequeue from
-/// the SenderQueue and send to the target Session's WS.
-/// This pool has long-running threads that grow
-/// only on congention and shrink otherwise.
-class SenderThreadPool final
-{
-public:
-    SenderThreadPool() :
-        _optimalThreadCount(std::min(2U, std::thread::hardware_concurrency())),
-        _maxThreadCount(_optimalThreadCount),
-        _idleThreadCount(0),
-        _stop(false)
-    {
-        LOG_INF("Creating SenderThreadPool with " << _optimalThreadCount << " optimal threads.");
-        for (size_t i = 0; i < _optimalThreadCount; ++i)
-        {
-            _threads.push_back(createThread());
-        }
-    }
-
-    ~SenderThreadPool()
-    {
-        // Stop us and the queue.
-        stop();
-        //SenderQueue::instance().stop();
-
-        for (const auto& threadData : _threads)
-        {
-            if (threadData && threadData->joinable())
-            {
-                threadData->join();
-            }
-        }
-    }
-
-    void stop() { _stop = true; }
-    bool stopping() const { return _stop || TerminationFlag; }
-
-    void incMaxThreadCount() { ++_maxThreadCount; }
-    void decMaxThreadCount() { --_maxThreadCount; }
-
-private:
-
-    /// Count idle threads safely.
-    /// Decrements count on ctor, and increments on dtor.
-    class IdleCountGuard final
-    {
-    public:
-        IdleCountGuard(std::atomic<size_t>& var) :
-            _var(var)
-        {
-            --_var;
-        }
-
-        ~IdleCountGuard()
-        {
-            ++_var;
-        }
-
-    private:
-        std::atomic<size_t>& _var;
-    };
-
-    typedef std::thread ThreadData;
-
-    /// Dequeue a SendItem and send it.
-    bool dispatchItem(const size_t timeoutMs);
-
-    /// Create a new thread and add to the pool.
-    std::shared_ptr<ThreadData> createThread();
-
-    /// Rebalance the number of threads.
-    /// Returns true if we need to reduce the threads.
-    bool rebalance();
-
-    /// Grow the pool if congestion is detected.
-    void checkAndGrow();
-
-    /// The worker thread entry function.
-    void threadFunction(const std::shared_ptr<ThreadData>& data);
-
-private:
-    /// A minimum of 2, but ideally as many as cores.
-    const size_t _optimalThreadCount;
-
-    /// Never exceed this number of threads.
-    size_t _maxThreadCount;
-
-    /// The number of threads not sending data.
-    std::atomic<size_t> _idleThreadCount;
-
-    /// Stop condition to take the pool down.
-    std::atomic<bool> _stop;
-
-    std::vector<std::shared_ptr<ThreadData>> _threads;
-    mutable std::mutex _mutex;
-
-    /// How often to do housekeeping when we idle.
-    static constexpr size_t HousekeepIdleIntervalMs = 60000;
-};
-
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit e16fc97c159e690ee024db26732973174c48986b
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Mar 9 19:00:04 2017 +0000

    SocketPoll - better control starting our threads.
    
    Extraordinary trace with a thread starting before the SocketPoll's
    derived classes had started - ie. with wrong vtables etc.

diff --git a/net/Socket.cpp b/net/Socket.cpp
index c5bd094..56adecf 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -30,10 +30,9 @@ namespace {
     }
 }
 
-SocketPoll::SocketPoll(const std::string& threadName,
-                       bool withThread) :
-    _name(threadName),
-    _stop(false)
+SocketPoll::SocketPoll(const std::string& threadName)
+    : _name(threadName),
+      _stop(false)
 {
     // Create the wakeup fd.
     if (::pipe2(_wakeup, O_CLOEXEC | O_NONBLOCK) == -1)
@@ -46,13 +45,7 @@ SocketPoll::SocketPoll(const std::string& threadName,
         getWakeupsArray().push_back(_wakeup[1]);
     }
 
-    if (withThread)
-    {
-        _thread = std::thread(&SocketPoll::pollingThread, this);
-        _owner = _thread.get_id();
-    }
-    else
-        _owner = std::this_thread::get_id();
+    _owner = std::this_thread::get_id();
 }
 
 SocketPoll::~SocketPoll()
@@ -73,6 +66,12 @@ SocketPoll::~SocketPoll()
         getWakeupsArray().erase(it);
 }
 
+void SocketPoll::startThread()
+{
+    _thread = std::thread(&SocketPoll::pollingThread, this);
+    _owner = _thread.get_id();
+}
+
 void SocketPoll::wakeupWorld()
 {
     for (const auto& fd : getWakeupsArray())
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 32d9048..b9f61c9 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -213,9 +213,12 @@ class SocketPoll
 {
 public:
     /// Create a socket poll, called rather infrequently.
-    SocketPoll(const std::string& threadName, bool withThread = true);
+    SocketPoll(const std::string& threadName);
     ~SocketPoll();
 
+    /// Start the polling thread (if desired)
+    void startThread();
+
     /// Stop the polling thread.
     void stop()
     {
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index ab97d17..e6bf33c 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -116,8 +116,6 @@ std::string DocumentBroker::getDocKey(const Poco::URI& uri)
 /// The Document Broker Poll - one of these in a thread per document
 class DocumentBroker::DocumentBrokerPoll : public TerminatingPoll
 {
-    std::mutex _lock;
-    std::condition_variable _start_cv;
     std::shared_ptr<DocumentBroker> _docBroker;
 public:
     DocumentBrokerPoll(const std::string &threadName)
@@ -126,20 +124,13 @@ public:
     }
     void setDocumentBroker(const std::shared_ptr<DocumentBroker> &docBroker)
     {
-        std::unique_lock<std::mutex> lk(_lock);
         _docBroker = docBroker;
-        _start_cv.notify_all();
     }
 
     virtual void pollingThread()
     {
-        {
-            std::unique_lock<std::mutex> lk(_lock);
-            while (!_docBroker && !_stop)
-                _start_cv.wait(lk);
-        }
-        if (_docBroker)
-            _docBroker->pollThread();
+        assert (_docBroker);
+        _docBroker->pollThread();
     }
 };
 
@@ -182,6 +173,7 @@ std::shared_ptr<DocumentBroker> DocumentBroker::create(
     std::shared_ptr<DocumentBroker> docBroker = std::make_shared<DocumentBroker>(uri, uriPublic, docKey, childRoot);
 
     docBroker->_poll->setDocumentBroker(docBroker);
+    docBroker->_poll->startThread();
 
     return docBroker;
 }
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index bcd7814..78c5b51 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2393,11 +2393,14 @@ public:
     void startPrisoners(const int port)
     {
         PrisonerPoll.insertNewSocket(findPrisonerServerPort(port));
+        PrisonerPoll.startThread();
     }
 
     void start(const int port)
     {
         _acceptPoll.insertNewSocket(findServerPort(port));
+        _acceptPoll.startThread();
+        WebServerPoll.startThread();
     }
 
     void stop()
@@ -2652,7 +2655,8 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
 #endif
 
     /// Something of a hack to get woken up on exit.
-    SocketPoll mainWait("main", false);
+
+    SocketPoll mainWait("main");
     while (!TerminationFlag && !ShutdownRequestFlag)
     {
         UnitWSD::get().invokeTest();
commit 842b261d9f88ae7ea3b1334874862e9b0f8750eb
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu Mar 9 18:19:53 2017 +0000

    Move auto-save of document into the DocumentBroker poll more convincingly.

diff --git a/net/Socket.cpp b/net/Socket.cpp
index dc0f294..c5bd094 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -30,7 +30,8 @@ namespace {
     }
 }
 
-SocketPoll::SocketPoll(const std::string& threadName) :
+SocketPoll::SocketPoll(const std::string& threadName,
+                       bool withThread) :
     _name(threadName),
     _stop(false)
 {
@@ -45,16 +46,20 @@ SocketPoll::SocketPoll(const std::string& threadName) :
         getWakeupsArray().push_back(_wakeup[1]);
     }
 
-    _thread = std::thread(&SocketPoll::pollingThread, this);
+    if (withThread)
+    {
+        _thread = std::thread(&SocketPoll::pollingThread, this);
+        _owner = _thread.get_id();
+    }
+    else
+        _owner = std::this_thread::get_id();
 }
 
 SocketPoll::~SocketPoll()
 {
     stop();
     if (_thread.joinable())
-    {
         _thread.join();
-    }
 
     ::close(_wakeup[0]);
     ::close(_wakeup[1]);
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 7a6654e..32d9048 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -213,7 +213,7 @@ class SocketPoll
 {
 public:
     /// Create a socket poll, called rather infrequently.
-    SocketPoll(const std::string& threadName);
+    SocketPoll(const std::string& threadName, bool withThread = true);
     ~SocketPoll();
 
     /// Stop the polling thread.
@@ -245,7 +245,7 @@ public:
     /// Are we running in either shutdown, or the polling thread.
     bool isCorrectThread()
     {
-        return _stop || std::this_thread::get_id() == _thread.get_id();
+        return _stop || std::this_thread::get_id() == _owner;
     }
 
 public:
@@ -450,6 +450,7 @@ protected:
     std::atomic<bool> _stop;
     /// The polling thread.
     std::thread _thread;
+    std::thread::id _owner;
 };
 
 class StreamSocket;
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 8332b8a..467a514 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -504,7 +504,7 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt
                 }
 
                 // Save to Storage and log result.
-                docBroker->save(getId(), success, result);
+                docBroker->saveToStorage(getId(), success, result);
                 return true;
             }
         }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index c046fcb..ab97d17 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -133,9 +133,11 @@ public:
 
     virtual void pollingThread()
     {
-        std::unique_lock<std::mutex> lk(_lock);
-        while (!_docBroker && !_stop)
-            _start_cv.wait(lk);
+        {
+            std::unique_lock<std::mutex> lk(_lock);
+            while (!_docBroker && !_stop)
+                _start_cv.wait(lk);
+        }
         if (_docBroker)
             _docBroker->pollThread();
     }
@@ -184,6 +186,7 @@ std::shared_ptr<DocumentBroker> DocumentBroker::create(
     return docBroker;
 }
 
+// The inner heart of the DocumentBroker - our poll loop.
 void DocumentBroker::pollThread()
 {
     // Request a kit process for this doc.
@@ -207,6 +210,8 @@ void DocumentBroker::pollThread()
 
     _childProcess->setDocumentBroker(shared_from_this());
 
+    auto last30SecCheckTime = std::chrono::steady_clock::now();
+
     // Main polling loop goodness.
     while (!_stop && !TerminationFlag && !ShutdownRequestFlag)
     {
@@ -243,6 +248,32 @@ void DocumentBroker::pollThread()
         }
 
         _poll->poll(5000);
+
+        if (!std::getenv("LOOL_NO_AUTOSAVE") &&
+            std::chrono::duration_cast<std::chrono::seconds>
+            (std::chrono::steady_clock::now() - last30SecCheckTime).count() >= 30)
+        {
+            LOG_TRC("Trigger an autosave ...");
+            autoSave(false);
+            last30SecCheckTime = std::chrono::steady_clock::now();
+        }
+    }
+
+    // FIXME: probably we should stop listening on
+    // incoming sockets here if we have any.
+
+    auto lastSaveTime = _lastSaveTime;
+    auto saveTimeoutStart = std::chrono::steady_clock::now();
+
+    // Save before exit.
+    autoSave(true);
+
+    // wait 20 seconds for a save notification and quit.
+    while (lastSaveTime < saveTimeoutStart &&
+           std::chrono::duration_cast<std::chrono::seconds>
+           (std::chrono::steady_clock::now() - saveTimeoutStart).count() >= 20)
+    {
+        _poll->poll(5000);
     }
 }
 
@@ -467,9 +498,10 @@ bool DocumentBroker::load(std::shared_ptr<ClientSession>& session, const std::st
     return true;
 }
 
-bool DocumentBroker::save(const std::string& sessionId, bool success, const std::string& result)
+bool DocumentBroker::saveToStorage(const std::string& sessionId,
+                                   bool success, const std::string& result)
 {
-    std::unique_lock<std::mutex> lock(_saveMutex);
+    assert(_poll->isCorrectThread());
 
     // If save requested, but core didn't save because document was unmodified
     // notify the waiting thread, if any.
@@ -477,8 +509,7 @@ bool DocumentBroker::save(const std::string& sessionId, bool success, const std:
     {
         LOG_DBG("Save skipped as document [" << _docKey << "] was not modified.");
         _lastSaveTime = std::chrono::steady_clock::now();
-        lock.unlock();
-        _saveCV.notify_all();
+        _poll->wakeup();
         return true;
     }
 
@@ -486,8 +517,6 @@ bool DocumentBroker::save(const std::string& sessionId, bool success, const std:
     if (it == _sessions.end())
     {
         LOG_ERR("Session with sessionId [" << sessionId << "] not found while saving docKey [" << _docKey << "].");
-        lock.unlock();
-        _saveCV.notify_all();
         return false;
     }
 
@@ -503,8 +532,7 @@ bool DocumentBroker::save(const std::string& sessionId, bool success, const std:
         LOG_DBG("Skipping unnecessary saving to URI [" << uri << "] with docKey [" << _docKey <<
                 "]. File last modified " << _lastFileModifiedTime.elapsed() / 1000000 << " seconds ago.");
         _lastSaveTime = std::chrono::steady_clock::now();
-        lock.unlock();
-        _saveCV.notify_all();
+        _poll->wakeup();
         return true;
     }
 
@@ -522,6 +550,7 @@ bool DocumentBroker::save(const std::string& sessionId, bool success, const std:
         _lastFileModifiedTime = newFileModifiedTime;
         _tileCache->saveLastModified(_lastFileModifiedTime);
         _lastSaveTime = std::chrono::steady_clock::now();
+        _poll->wakeup();
 
         // Calling getWOPIFileInfo() or getLocalFileInfo() has the side-effect of updating
         // StorageBase::_fileInfo. Get the timestamp of the document as persisted in its storage
@@ -542,8 +571,6 @@ bool DocumentBroker::save(const std::string& sessionId, bool success, const std:
         LOG_DBG("Saved docKey [" << _docKey << "] to URI [" << uri << "] and updated tile cache. Document modified timestamp: " <<
                 Poco::DateTimeFormatter::format(Poco::DateTime(_documentLastModifiedTime),
                                                                Poco::DateTimeFormat::ISO8601_FORMAT));
-        lock.unlock();
-        _saveCV.notify_all();
         return true;
     }
     else if (storageSaveResult == StorageBase::SaveResult::DISKFULL)
@@ -565,15 +592,11 @@ bool DocumentBroker::save(const std::string& sessionId, bool success, const std:
         it->second->sendTextFrame("error: cmd=storage kind=savefailed");
     }
 
-    lock.unlock();
-    _saveCV.notify_all();
     return false;
 }
 
-bool DocumentBroker::autoSave(const bool force, const size_t waitTimeoutMs, std::unique_lock<std::mutex>& lock)
+bool DocumentBroker::autoSave(const bool force)
 {
-    Util::assertIsLocked(lock);
-
     if (_sessions.empty() || _storage == nullptr || !_isLoaded ||
         !_childProcess->isAlive() || (!_isModified && !force))
     {
@@ -582,8 +605,7 @@ bool DocumentBroker::autoSave(const bool force, const size_t waitTimeoutMs, std:
         return true;
     }
 
-    // Remeber the last save time, since this is the predicate.
-    const auto lastSaveTime = _lastSaveTime;
+    // Remember the last save time, since this is the predicate.
     LOG_TRC("Checking to autosave [" << _docKey << "].");
 
     bool sent = false;
@@ -609,26 +631,12 @@ bool DocumentBroker::autoSave(const bool force, const size_t waitTimeoutMs, std:
         }
     }
 
-    if (sent && waitTimeoutMs > 0)
-    {
-        LOG_TRC("Waiting for save event for [" << _docKey << "].");
-        _saveCV.wait_for(lock, std::chrono::milliseconds(waitTimeoutMs));
-        if (lastSaveTime != _lastSaveTime)
-        {
-            LOG_DBG("Successfully persisted document [" << _docKey << "] or document was not modified.");
-            return true;
-        }
-
-        return false;
-    }
-
     return sent;
 }
 
 bool DocumentBroker::sendUnoSave(const bool dontSaveIfUnmodified)
 {
     LOG_INF("Autosave triggered for doc [" << _docKey << "].");
-    Util::assertIsLocked(_mutex);
 
     std::shared_ptr<ClientSession> savingSession;
     for (auto& sessionIt : _sessions)
@@ -758,9 +766,12 @@ size_t DocumentBroker::addSession(std::shared_ptr<ClientSession>& session)
     return count;
 }
 
-size_t DocumentBroker::removeSession(const std::string& id)
+size_t DocumentBroker::removeSession(const std::string& id, bool destroyIfLast)
 {
-    Util::assertIsLocked(_mutex);
+    auto guard = getLock();
+
+    if (destroyIfLast)
+        destroyIfLastEditor(id);
 
     try
     {
@@ -1072,7 +1083,7 @@ void DocumentBroker::handleTileCombinedResponse(const std::vector<char>& payload
     }
 }
 
-bool DocumentBroker::startDestroy(const std::string& id)
+void DocumentBroker::destroyIfLastEditor(const std::string& id)
 {
     Util::assertIsLocked(_mutex);
 
@@ -1081,7 +1092,7 @@ bool DocumentBroker::startDestroy(const std::string& id)
     {
         // We could be called before adding any sessions.
         // For example when a socket disconnects before loading.
-        return false;
+        return;
     }
 
     // Check if the session being destroyed is the last non-readonly session or not.
@@ -1103,9 +1114,9 @@ bool DocumentBroker::startDestroy(const std::string& id)
 
     // Last view going away, can destroy.
     _markToDestroy = (_sessions.size() <= 1);
+    _stop = true;
     LOG_DBG("startDestroy on session [" << id << "] on docKey [" << _docKey <<
             "], markToDestroy: " << _markToDestroy << ", lastEditableSession: " << _lastEditableSession);
-    return _lastEditableSession;
 }
 
 void DocumentBroker::setModified(const bool value)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 3e327ce..b3f68f4 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -233,19 +233,17 @@ public:
     bool isLoaded() const { return _isLoaded; }
     void setLoaded() { _isLoaded = true; }
 
-    /// Save the document to Storage if needs persisting.
-    bool save(const std::string& sesionId, bool success, const std::string& result = "");
+    /// Save the document to Storage if it needs persisting.
+    bool saveToStorage(const std::string& sesionId, bool success, const std::string& result = "");
     bool isModified() const { return _isModified; }
     void setModified(const bool value);
 
     /// Save the document if the document is modified.
     /// @param force when true, will force saving if there
     /// has been any recent activity after the last save.
-    /// @param waitTimeoutMs when >0 will wait for the save to
-    /// complete before returning, or timeout.
     /// @return true if attempts to save or it also waits
     /// and receives save notification. Otherwise, false.
-    bool autoSave(const bool force, const size_t waitTimeoutMs, std::unique_lock<std::mutex>& lock);
+    bool autoSave(const bool force);
 
     Poco::URI getPublicUri() const { return _uriPublic; }
     Poco::URI getJailedUri() const { return _uriJailed; }
@@ -270,7 +268,7 @@ public:
     size_t queueSession(std::shared_ptr<ClientSession>& session);
 
     /// Removes a session by ID. Returns the new number of sessions.
-    size_t removeSession(const std::string& id);
+    size_t removeSession(const std::string& id, bool destroyIfLast = false);
 
     void addSocketToPoll(const std::shared_ptr<Socket>& socket);
 
@@ -299,11 +297,7 @@ public:
     void handleTileResponse(const std::vector<char>& payload);
     void handleTileCombinedResponse(const std::vector<char>& payload);
 
-    /// Called before destroying any session.
-    /// This method calculates and sets important states of
-    /// session being destroyed. Returns true if session id
-    /// is the last editable session.
-    bool startDestroy(const std::string& id);
+    void destroyIfLastEditor(const std::string& id);
     bool isMarkedToDestroy() const { return _markToDestroy; }
 
     bool handleInput(const std::vector<char>& payload);
@@ -407,8 +401,6 @@ private:
     int _cursorWidth;
     int _cursorHeight;
     mutable std::mutex _mutex;
-    std::condition_variable _saveCV;
-    std::mutex _saveMutex;
     std::unique_ptr<DocumentBrokerPoll> _poll;
     std::atomic<bool> _stop;
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index bf7b90d..bcd7814 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1411,9 +1411,7 @@ static void removeDocBrokerSession(const std::shared_ptr<DocumentBroker>& docBro
     auto lock = docBroker->getLock();
 
     if (!id.empty())
-    {
         docBroker->removeSession(id);
-    }
 
     if (docBroker->getSessionsCount() == 0 || !docBroker->isAlive())
     {
@@ -1677,9 +1675,7 @@ private:
     void onDisconnect() override
     {
         if (_clientSession)
-        {
-            saveDocument();
-        }
+            disposeSession();
 
         const size_t curConnections = --LOOLWSD::NumConnections;
         LOG_TRC("Disconnected connection #" << _connectionNum << " of " <<
@@ -2265,7 +2261,8 @@ private:
             LOG_WRN("Failed to connect DocBroker and Client Session.");
     }
 
-    void saveDocument()
+    // this session went away - cleanup now.
+    void disposeSession()
     {
         LOG_CHECK_RET(_clientSession && "Null ClientSession instance", );
         const auto docBroker = _clientSession->getDocumentBroker();
@@ -2277,25 +2274,9 @@ private:
             // Connection terminated. Destroy session.
             LOG_DBG("Client session [" << _id << "] on docKey [" << docKey << "] terminated. Cleaning up.");
 
-            auto docLock = docBroker->getLock();
 
             // We issue a force-save when last editable (non-readonly) session is going away
-            const bool forceSave = docBroker->startDestroy(_id);
-            if (forceSave)
-            {
-                LOG_INF("Shutdown of the last editable (non-readonly) session, saving the document before tearing down.");
-            }
-
-            // We need to wait until the save notification reaches us
-            // and Storage persists the document.
-            if (!docBroker->autoSave(forceSave, COMMAND_TIMEOUT_MS, docLock))
-            {
-                LOG_ERR("Auto-save before closing failed.");
-            }
-
-            const auto sessionsCount = docBroker->removeSession(_id);
-            docLock.unlock();
-
+            const auto sessionsCount = docBroker->removeSession(_id, true);
             if (sessionsCount == 0)
             {
                 // We've supposedly destroyed the last session, now cleanup.
@@ -2428,9 +2409,13 @@ public:
     void dumpState()
     {
         std::cerr << "LOOLWSDServer:\n"
+                  << "   Ports: server " << ClientPortNumber
+                  <<          " prisoner " << MasterPortNumber << "\n"
                   << "  stop: " << _stop << "\n"
                   << "  TerminationFlag: " << TerminationFlag << "\n"
-                  << "  isShuttingDown: " << ShutdownRequestFlag << "\n";
+                  << "  isShuttingDown: " << ShutdownRequestFlag << "\n"
+                  << "  NewChildren: " << NewChildren.size() << "\n"
+                  << "  OutstandingForks: " << OutstandingForks << "\n";
 
         std::cerr << "Server poll:\n";
         _acceptPoll.dumpState();
@@ -2441,7 +2426,8 @@ public:
         std::cerr << "Prisoner poll:\n";
         PrisonerPoll.dumpState();
 
-        std::cerr << "Document Broker polls:\n";
+        std::cerr << "Document Broker polls "
+                  << "[ " << DocBrokers.size() << " ]:\n";
         for (auto &i : DocBrokers)
             i.second->dumpState();
     }
@@ -2665,49 +2651,16 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     time_t startTimeSpan = time(nullptr);
 #endif
 
-    // FIXME: all of this needs cleaning up and putting in the
-    // relevant polls.
-
-    auto last30SecCheckTime = std::chrono::steady_clock::now();
+    /// Something of a hack to get woken up on exit.
+    SocketPoll mainWait("main", false);
     while (!TerminationFlag && !ShutdownRequestFlag)
     {
         UnitWSD::get().invokeTest();
-        if (TerminationFlag || handleShutdownRequest())
-        {
-            break;
-        }
 
-        if (!std::getenv("LOOL_NO_AUTOSAVE") &&
-            std::chrono::duration_cast<std::chrono::seconds>
-            (std::chrono::steady_clock::now() - last30SecCheckTime).count() >= 30)
-        {
-            try
-            {
-#if 0
-                std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
-                cleanupDocBrokers();
-                for (auto& pair : DocBrokers)
-                {
-                    auto docLock = pair.second->getDeferredLock();
-                    if (doclock.try_lock())
-                    {
-                        pair.second->autosave(false, 0, doclock);
-                    }
-                }
-#endif
-            }
-            catch (const std::exception& exc)
-            {
-                LOG_ERR("Exception: " << exc.what());
-            }
+        mainWait.poll(30 * 1000 /* ms */);
 
-            last30SecCheckTime = std::chrono::steady_clock::now();
-        }
-        else
-        {
-            // Wait if we had done no work.
-            std::this_thread::sleep_for(std::chrono::milliseconds(CHILD_REBALANCE_INTERVAL_MS));
-        }
+        std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
+        cleanupDocBrokers();
 
 #if ENABLE_DEBUG
         if (careerSpanSeconds > 0 && time(nullptr) > startTimeSpan + careerSpanSeconds)
commit c097925e510bf2da0d80b9a66716e18a4ed09a34
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed Mar 8 18:48:07 2017 +0000

    Move Kit / prisoner re-balancing into the PrisonerPoll thread.

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index f1d6692..bf7b90d 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -189,9 +189,12 @@ int MasterPortNumber = DEFAULT_MASTER_PORT_NUMBER;
 /// New LOK child processes ready to host documents.
 //TODO: Move to a more sensible namespace.
 static bool DisplayVersion = false;
-static std::vector<std::shared_ptr<ChildProcess> > NewChildren;
+
+// Tracks the set of prisoners / children waiting to be used.
 static std::mutex NewChildrenMutex;
 static std::condition_variable NewChildrenCV;
+static std::vector<std::shared_ptr<ChildProcess> > NewChildren;
+
 static std::chrono::steady_clock::time_point LastForkRequestTime = std::chrono::steady_clock::now();
 static std::atomic<int> OutstandingForks(0);
 static std::map<std::string, std::shared_ptr<DocumentBroker> > DocBrokers;
@@ -347,6 +350,7 @@ static bool cleanupChildren()
         if (!NewChildren[i]->isAlive())
         {
             LOG_WRN("Removing dead spare child [" << NewChildren[i]->getPid() << "].");
+
             NewChildren.erase(NewChildren.begin() + i);
             removed = true;
         }
@@ -393,46 +397,12 @@ static int rebalanceChildren(int balance)
     return 0;
 }
 
-#ifndef KIT_IN_PROCESS
-/// Called on startup only.
-static void preForkChildren(std::unique_lock<std::mutex>& lock)
-{
-    Util::assertIsLocked(DocBrokersMutex);
-    Util::assertIsLocked(lock);
-
-    int numPreSpawn = LOOLWSD::NumPreSpawnedChildren;
-    UnitWSD::get().preSpawnCount(numPreSpawn);
-
-    // Wait until we have at least one child.
-    // With valgrind we need extended time to spawn kits.
-#ifdef KIT_IN_PROCESS
-    const auto timeoutMs = CHILD_TIMEOUT_MS * 3;
-#else
-    const auto timeoutMs = CHILD_TIMEOUT_MS * (LOOLWSD::NoCapsForKit ? 150 : 3);
-#endif
-    const auto timeout = std::chrono::milliseconds(timeoutMs);
-    LOG_TRC("Waiting for a new child for a max of " << timeoutMs << " ms.");
-    NewChildrenCV.wait_for(lock, timeout, []() { return !NewChildren.empty(); });
-
-    // Now spawn more, as needed.
-    rebalanceChildren(numPreSpawn);
-
-    // Make sure we have at least one before moving forward.
-    LOG_TRC("Waiting for a new child for a max of " << timeoutMs << " ms.");
-    if (!NewChildrenCV.wait_for(lock, timeout, []() { return !NewChildren.empty(); }))
-    {
-        const auto msg = "Failed to fork child processes.";
-        LOG_FTL(msg);
-        throw std::runtime_error(msg);
-    }
-}
-#endif
-
 /// Proactively spawn children processes
 /// to load documents with alacrity.
 /// Returns true only if at least one child was requested to spawn.
 static bool prespawnChildren()
 {
+#if 1 // FIXME: why re-balance DockBrokers here ? ...
     // First remove dead DocBrokers, if possible.
     std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex, std::defer_lock);
     if (!docBrokersLock.try_lock())
@@ -442,6 +412,7 @@ static bool prespawnChildren()
     }
 
     cleanupDocBrokers();
+#endif
 
     std::unique_lock<std::mutex> lock(NewChildrenMutex, std::defer_lock);
     if (!lock.try_lock())
@@ -646,8 +617,17 @@ std::unique_ptr<TraceFileWriter> LOOLWSD::TraceDumper;
 /// relevant DocumentBroker poll instead.
 TerminatingPoll WebServerPoll("websrv_poll");
 
+class PrisonerPoll : public TerminatingPoll {
+public:
+    PrisonerPoll() : TerminatingPoll("prisoner_poll") {}
+
+    /// Check prisoners are still alive and balaned.
+    void wakeupHook() override;
+};
+
 /// This thread listens for and accepts prisoner kit processes.
-TerminatingPoll PrisonerPoll("prison_poll");
+/// And also cleans up and balances the correct number of childen.
+PrisonerPoll PrisonerPoll;
 
 /// Helper class to hold default configuration entries.
 class AppConfigMap final : public Poco::Util::MapConfiguration
@@ -1173,6 +1153,43 @@ bool LOOLWSD::checkAndRestoreForKit()
 #endif
 }
 
+void PrisonerPoll::wakeupHook()
+{
+    /// FIXME: we should do this less frequently
+    /// currently the prisoner poll wakes up quite
+    /// a lot.
+    if (!LOOLWSD::checkAndRestoreForKit())
+    {
+        // No children have died.
+        // Make sure we have sufficient reserves.
+        if (prespawnChildren())
+        {
+            // Nothing more to do this round, unless we are fuzzing
+#if FUZZER
+            if (!LOOLWSD::FuzzFileName.empty())
+            {
+                std::unique_ptr<Replay> replay(new Replay(
+#if ENABLE_SSL
+                        "https://127.0.0.1:" + std::to_string(ClientPortNumber),
+#else
+                        "http://127.0.0.1:" + std::to_string(ClientPortNumber),
+#endif
+                        LOOLWSD::FuzzFileName));
+
+                std::unique_ptr<Thread> replayThread(new Thread());
+                replayThread->start(*replay);
+
+                // block until the replay finishes
+                replayThread->join();
+
+                TerminationFlag = true;
+            }
+#endif
+        }
+    }
+}
+
+
 bool LOOLWSD::createForKit()
 {
 #ifdef KIT_IN_PROCESS
@@ -1238,8 +1255,9 @@ bool LOOLWSD::createForKit()
     // Init the Admin manager
     Admin::instance().setForKitPid(ForKitProcId);
 
-    // Spawn some children, if necessary.
-    preForkChildren(newChildrenLock);
+    // Wake the prisoner poll to spawn some children, if necessary.
+    PrisonerPoll.wakeup();
+    // FIXME: horrors with try_lock in prespawnChildren ...
 
     return (ForKitProcId != -1);
 #endif
@@ -2614,6 +2632,26 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
 
     srv.startPrisoners(MasterPortNumber);
 
+#ifndef KIT_IN_PROCESS
+    {
+        std::unique_lock<std::mutex> lock(NewChildrenMutex);
+
+        const auto timeoutMs = CHILD_TIMEOUT_MS * (LOOLWSD::NoCapsForKit ? 150 : 3);
+        const auto timeout = std::chrono::milliseconds(timeoutMs);
+        // Make sure we have at least one before moving forward.
+        LOG_TRC("Waiting for a new child for a max of " << timeoutMs << " ms.");
+        if (!NewChildrenCV.wait_for(lock, timeout, []() { return !NewChildren.empty(); }))
+        {
+            const auto msg = "Failed to fork child processes.";
+            LOG_FTL(msg);
+            throw std::runtime_error(msg);
+        }
+
+        // Check we have at least one
+        assert(NewChildren.size() > 0);
+    }
+#endif
+
     // Fire the ForKit process; we are ready to get child connections.
     if (!createForKit())
     {
@@ -2627,6 +2665,9 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     time_t startTimeSpan = time(nullptr);
 #endif
 
+    // FIXME: all of this needs cleaning up and putting in the
+    // relevant polls.
+
     auto last30SecCheckTime = std::chrono::steady_clock::now();
     while (!TerminationFlag && !ShutdownRequestFlag)
     {
@@ -2636,65 +2677,36 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
             break;
         }
 
-        if (!checkAndRestoreForKit())
+        if (!std::getenv("LOOL_NO_AUTOSAVE") &&
+            std::chrono::duration_cast<std::chrono::seconds>
+            (std::chrono::steady_clock::now() - last30SecCheckTime).count() >= 30)
         {
-            // No children have died.
-            // Make sure we have sufficient reserves.
-            if (prespawnChildren())
-            {
-                // Nothing more to do this round, unless we are fuzzing
-#if FUZZER
-                if (!FuzzFileName.empty())
-                {
-                    std::unique_ptr<Replay> replay(new Replay(
-#if ENABLE_SSL
-                            "https://127.0.0.1:" + std::to_string(ClientPortNumber),
-#else
-                            "http://127.0.0.1:" + std::to_string(ClientPortNumber),
-#endif
-                            FuzzFileName));
-
-                    std::unique_ptr<Thread> replayThread(new Thread());
-                    replayThread->start(*replay);
-
-                    // block until the replay finishes
-                    replayThread->join();
-
-                    TerminationFlag = true;
-                }
-#endif
-            }
-            else if (!std::getenv("LOOL_NO_AUTOSAVE") &&
-                     std::chrono::duration_cast<std::chrono::seconds>
-                        (std::chrono::steady_clock::now() - last30SecCheckTime).count() >= 30)
+            try
             {
-                try
-                {
 #if 0
-                    std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
-                    cleanupDocBrokers();
-                    for (auto& pair : DocBrokers)
+                std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex);
+                cleanupDocBrokers();
+                for (auto& pair : DocBrokers)
+                {
+                    auto docLock = pair.second->getDeferredLock();
+                    if (doclock.try_lock())
                     {
-                        auto docLock = pair.second->getDeferredLock();
-                        if (doclock.try_lock())
-                        {
-                            pair.second->autosave(false, 0, doclock);
-                        }
+                        pair.second->autosave(false, 0, doclock);
                     }
-#endif
-                }
-                catch (const std::exception& exc)
-                {
-                    LOG_ERR("Exception: " << exc.what());
                 }
-
-                last30SecCheckTime = std::chrono::steady_clock::now();
+#endif
             }
-            else
+            catch (const std::exception& exc)
             {
-                // Wait if we had done no work.
-                std::this_thread::sleep_for(std::chrono::milliseconds(CHILD_REBALANCE_INTERVAL_MS));
+                LOG_ERR("Exception: " << exc.what());
             }
+
+            last30SecCheckTime = std::chrono::steady_clock::now();
+        }
+        else
+        {
+            // Wait if we had done no work.
+            std::this_thread::sleep_for(std::chrono::milliseconds(CHILD_REBALANCE_INTERVAL_MS));
         }
 
 #if ENABLE_DEBUG
commit 4eccd4d7b4d753997a838f96d3d097453f1b5d88
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu Mar 9 17:32:55 2017 +0100

    nb: Check threads only when LOOL_CHECK_THREADS is set.
    
    Change-Id: I4137685eb956469d419bded318b83de2b10ce19d

diff --git a/net/Socket.hpp b/net/Socket.hpp
index c45dc24..7a6654e 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -163,7 +163,7 @@ public:
     virtual bool isCorrectThread()
     {
 #if ENABLE_DEBUG
-        return std::this_thread::get_id() == _owner;
+        return !getenv("LOOL_CHECK_THREADS") || std::this_thread::get_id() == _owner;
 #else
         return true;
 #endif
commit b895393ceadc7a908e226fe88b64a94f764b0a10
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu Mar 9 17:20:34 2017 +0100

    nb: Perform the socket shutdown asynchronously after the data is served.
    
    Change-Id: I642e26abf4ef9c8d2be1be428b5786692dfea2c7

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 06ac4a0..c45dc24 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -487,7 +487,8 @@ public:
     StreamSocket(const int fd, std::unique_ptr<SocketHandlerInterface> socketHandler) :
         Socket(fd),
         _socketHandler(std::move(socketHandler)),
-        _closed(false)
+        _closed(false),
+        _shutdownSignalled(false)
     {
         LOG_DBG("StreamSocket ctor #" << fd);
 
@@ -502,11 +503,29 @@ public:
 
         if (!_closed)
             _socketHandler->onDisconnect();
+
+        if (!_shutdownSignalled)
+        {
+            _shutdownSignalled = true;
+            closeConnection();
+        }
+    }
+
+    /// Just trigger the async shutdown.
+    virtual void shutdown()
+    {
+        _shutdownSignalled = true;
+    }
+
+    /// Perform the real shutdown.
+    virtual void closeConnection()
+    {
+        Socket::shutdown();
     }
 
     int getPollEvents() override
     {
-        if (!_outBuffer.empty() || _socketHandler->hasQueuedWrites())
+        if (!_outBuffer.empty() || _socketHandler->hasQueuedWrites() || _shutdownSignalled)
             return POLLIN | POLLOUT;
         else
             return POLLIN;
@@ -531,29 +550,20 @@ public:
         send(str.data(), str.size(), flush);
     }
 
-    /// Sends synchronous response data.
+    /// Sends HTTP response data.
     void sendHttpResponse(const char* data, const int len)
     {
-        // Set to blocking.
-        int opts;
-        opts = fcntl(getFD(), F_GETFL);
-        if (opts != -1)
-        {
-            opts = (opts & ~O_NONBLOCK);
-            opts = fcntl(getFD(), F_SETFL, opts);
-        }
-
         // Send the data and flush.
         send(data, len, true);
     }
 
-    /// Sends synchronous HTTP response string.
+    /// Sends HTTP response string.
     void sendHttpResponse(const std::string& str)
     {
         sendHttpResponse(str.data(), str.size());
     }
 
-    /// Sends synchronous HTTP response.
+    /// Sends HTTP response.
     void sendHttpResponse(Poco::Net::HTTPResponse& response)
     {
         response.set("User-Agent", HTTP_AGENT_STRING);
@@ -640,6 +650,10 @@ protected:
         if ((events & POLLOUT) && _outBuffer.empty())
             _socketHandler->performWrites();
 
+        // perform the shutdown if we have sent everything.
+        if (_shutdownSignalled && _outBuffer.empty())
+            closeConnection();
+
         // SSL might want to do handshake,
         // even if we have no data to write.
         if ((events & POLLOUT) || !_outBuffer.empty())
@@ -728,6 +742,9 @@ protected:
     /// True if we are already closed.
     bool _closed;
 
+    /// True when shutdown was requested via shutdown().
+    bool _shutdownSignalled;
+
     std::vector< char > _inBuffer;
     std::vector< char > _outBuffer;
 
diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 7390093..004cb88 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -52,13 +52,19 @@ public:
     {
         LOG_DBG("SslStreamSocket dtor #" << getFD());
 
-        shutdown();
+        if (!_shutdownSignalled)
+        {
+            _shutdownSignalled = true;
+            closeConnection();
+        }
+
         SSL_free(_ssl);
     }
 
     /// Shutdown the TLS/SSL connection properly.
-    void shutdown() override
+    void closeConnection() override
     {
+        LOG_DBG("SslStreamSocket::performShutdown() #" << getFD());
         if (SSL_shutdown(_ssl) == 0)
         {
             // Complete the bidirectional shutdown.
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 54a1ad1..f1d6692 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1820,8 +1820,6 @@ private:
         // FIXME: - the session should be owning the fd in DocumentBroker's _poll
         if (_clientSession)
             return _clientSession->performWrites();
-
-        assert (false && "performWrites doesn't have client session");
     }
 
     void handleFileServerRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message)
commit b8af4709188c41d8be611613c3ddda9a2e4de335
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Mar 9 01:32:28 2017 -0500

    nb: serve files synchronously
    
    As there isn't support (yet) to send files
    asynchronously, when the socket native buffer
    is small, asynchronous writes naturally return
    EWOULDBLOCK. As a temp solution, we send files
    synchronously, so there is no need to poll.
    
    This should be replaced witha file-server
    polling/serving thread that is dedicated to
    sending files only (which closes the connection
    when done).
    
    Change-Id: I062fea44bfe54ab8d147b745da97bd499bf00657

diff --git a/net/Socket.hpp b/net/Socket.hpp
index d8fa26f..06ac4a0 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -43,7 +43,7 @@ public:
     Socket() :
         _fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0))
     {
-        init();
+        setNoDelay();
     }
 
     virtual ~Socket()
@@ -184,7 +184,12 @@ protected:
         setNoDelay();
 #if ENABLE_DEBUG
         _owner = std::this_thread::get_id();
+
+        const int oldSize = getSendBufferSize();
+        setSendBufferSize(0);
+        LOG_TRC("Socket #" << _fd << " buffer size: " << getSendBufferSize() << " (was " << oldSize << ")");
 #endif
+
     }
 
 private:
@@ -526,13 +531,35 @@ public:
         send(str.data(), str.size(), flush);
     }
 
-    void send(Poco::Net::HTTPResponse& response)
+    /// Sends synchronous response data.
+    void sendHttpResponse(const char* data, const int len)
+    {
+        // Set to blocking.
+        int opts;
+        opts = fcntl(getFD(), F_GETFL);
+        if (opts != -1)
+        {
+            opts = (opts & ~O_NONBLOCK);
+            opts = fcntl(getFD(), F_SETFL, opts);
+        }
+
+        // Send the data and flush.
+        send(data, len, true);
+    }
+
+    /// Sends synchronous HTTP response string.
+    void sendHttpResponse(const std::string& str)
+    {
+        sendHttpResponse(str.data(), str.size());
+    }
+
+    /// Sends synchronous HTTP response.
+    void sendHttpResponse(Poco::Net::HTTPResponse& response)
     {
         response.set("User-Agent", HTTP_AGENT_STRING);
         std::ostringstream oss;
         response.write(oss);
-        LOG_INF(oss.str());
-        send(oss.str());
+        sendHttpResponse(oss.str());
     }
 
     /// Reads data by invoking readData() and buffering.
@@ -628,6 +655,7 @@ protected:
 
         if (closed)
         {
+            LOG_TRC("#" << getFD() << ": closed.");
             _closed = true;
             _socketHandler->onDisconnect();
         }
@@ -731,8 +759,9 @@ namespace HttpHelper
         response.set("User-Agent", HTTP_AGENT_STRING);
         std::ostringstream oss;
         response.write(oss);
-        LOG_INF(oss.str());
-        socket->send(oss.str());
+        const std::string header = oss.str();
+        LOG_TRC("Sending file [" << path << "]: " << header);
+        socket->sendHttpResponse(header);
 
         std::ifstream file(path, std::ios::binary);
         do
diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 77ef0b4..7390093 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -213,7 +213,6 @@ private:
             {
                 // The error is comming from BIO. Find out what happened.
                 const long bioError = ERR_get_error();
-                LOG_TRC("BIO error: " << bioError);
                 if (bioError == 0)
                 {
                     if (rc == 0)
@@ -234,6 +233,7 @@ private:
                 {
                     char buf[512];
                     ERR_error_string_n(bioError, buf, sizeof(buf));
+                    LOG_ERR("BIO error: " << buf);
                     throw std::runtime_error(buf);
                 }
             }
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 4c07c56..5331005 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -219,7 +219,7 @@ public:
 
     void performWrites() override
     {
-        assert(false);
+        assert(false && "performWrites not implemented");
     }
 
     void sendFrame(const std::string& msg) const
diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index c4f83f5..f1d35cf 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -185,7 +185,7 @@ void FileServerRequestHandler::handleRequest(const HTTPRequest& request, Poco::M
             << "Content-Length: 0\r\n"
             << "WWW-Authenticate: Basic realm=\"online\"\r\n"
             << "\r\n";
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
     }
     catch (const Poco::FileAccessDeniedException& exc)
     {
@@ -198,7 +198,7 @@ void FileServerRequestHandler::handleRequest(const HTTPRequest& request, Poco::M
             << "User-Agent: LOOLWSD WOPI Agent\r\n"
             << "Content-Length: 0\r\n"
             << "\r\n";
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
     }
     catch (const Poco::FileNotFoundException& exc)
     {
@@ -211,7 +211,7 @@ void FileServerRequestHandler::handleRequest(const HTTPRequest& request, Poco::M
             << "User-Agent: LOOLWSD WOPI Agent\r\n"
             << "Content-Length: 0\r\n"
             << "\r\n";
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
     }
 }
 
@@ -246,7 +246,7 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
             << "User-Agent: LOOLWSD WOPI Agent\r\n"
             << "Content-Length: 0\r\n"
             << "\r\n";
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
         return;
     }
 
@@ -307,7 +307,7 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
         << "\r\n"
         << preprocess;
 
-    socket->send(oss.str());
+    socket->sendHttpResponse(oss.str());
     LOG_DBG("Sent file: " << path.toString() << ": " << preprocess);
 }
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 5735e49..54a1ad1 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1789,7 +1789,7 @@ private:
                         << "User-Agent: LOOLWSD WOPI Agent\r\n"
                         << "Content-Length: 0\r\n"
                         << "\r\n";
-                    socket->send(oss.str());
+                    socket->sendHttpResponse(oss.str());
                     socket->shutdown();
                 }
             }
@@ -1821,7 +1821,7 @@ private:
         if (_clientSession)
             return _clientSession->performWrites();
 
-        assert (false);
+        assert (false && "performWrites doesn't have client session");
     }
 
     void handleFileServerRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message)
@@ -1834,7 +1834,7 @@ private:
     void handleAdminRequest(const Poco::Net::HTTPRequest& request)
     {
         LOG_ERR("Admin request: " << request.getURI());
-        // requestHandler = Admin::createRequestHandler();
+        // FIXME: implement admin support.
     }
 
     void handleRootRequest(const Poco::Net::HTTPRequest& request)
@@ -1857,7 +1857,7 @@ private:
         }
 
         auto socket = _socket.lock();
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
         socket->shutdown();
         LOG_INF("Sent / response successfully.");
     }
@@ -1923,7 +1923,7 @@ private:
             << xml;
 
         auto socket = _socket.lock();
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
         socket->shutdown();
         LOG_INF("Sent discovery.xml successfully.");
     }
@@ -2109,7 +2109,7 @@ private:
                     std::string fileName = dirPath + "/" + form.get("name");
                     File(tmpPath).moveTo(fileName);
                     response.setContentLength(0);
-                    socket->send(response);
+                    socket->sendHttpResponse(response);
                     return;
                 }
             }
commit 37de43bb58097e6aece528d977a1fd4dcfb09ec3
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed Mar 8 18:14:53 2017 +0000

    Tie sockets to threads, and assert that we're being used in the right one.
    
    This shows up some problems around cleaning up prisoners etc. that will
    need fixing subsequently.

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 87860ac..d8fa26f 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -34,6 +34,7 @@
 #include "Common.hpp"
 #include "Log.hpp"
 #include "Util.hpp"
+#include "SigUtil.hpp"
 
 /// A non-blocking, streaming socket.
 class Socket
@@ -42,7 +43,7 @@ public:
     Socket() :
         _fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0))
     {
-        setNoDelay();
+        init();
     }
 
     virtual ~Socket()
@@ -149,6 +150,25 @@ public:
 
     virtual void dumpState() {}
 
+    /// Set the thread-id we're bound to
+    void setThreadOwner(const std::thread::id &id)
+    {
+#if ENABLE_DEBUG
+       _owner = id;
+#else
+       (void)id;
+#endif
+    }
+
+    virtual bool isCorrectThread()
+    {
+#if ENABLE_DEBUG
+        return std::this_thread::get_id() == _owner;
+#else
+        return true;
+#endif
+    }
+
 protected:
 
     /// Construct based on an existing socket fd.
@@ -156,11 +176,21 @@ protected:
     Socket(const int fd) :
         _fd(fd)
     {
+        init();
+    }
+
+    void init()
+    {
         setNoDelay();
+#if ENABLE_DEBUG
+        _owner = std::this_thread::get_id();
+#endif
     }
 
 private:
     const int _fd;
+    // always enabled to avoid ABI change in debug mode ...
+    std::thread::id _owner;
 };
 
 
@@ -185,6 +215,7 @@ public:
     void stop()
     {
         _stop = true;
+        wakeup();
     }
 
     /// Check if we should continue polling
@@ -206,10 +237,18 @@ public:
         }
     }
 
+    /// Are we running in either shutdown, or the polling thread.
+    bool isCorrectThread()
+    {
+        return _stop || std::this_thread::get_id() == _thread.get_id();
+    }
+
 public:
     /// Poll the sockets for available data to read or buffer to write.
     void poll(const int timeoutMaxMs)
     {
+        assert(isCorrectThread());
+
         Poco::Timestamp now;
         Poco::Timestamp timeout = now;
         timeout += Poco::Timespan(0 /* s */, timeoutMaxMs * 1000 /* us */);
@@ -315,6 +354,7 @@ public:
         if (newSocket)
         {
             std::lock_guard<std::mutex> lock(_mutex);
+            newSocket->setThreadOwner(_thread.get_id());
             LOG_DBG("Inserting socket #" << newSocket->getFD() << " into " << _name);
             _newSockets.emplace_back(newSocket);
             wakeup();
@@ -336,6 +376,7 @@ public:
     /// Removes a socket from this poller.
     void releaseSocket(const std::shared_ptr<Socket>& socket)
     {
+        // assert(isCorrectThread());
         if (socket)
         {
             std::lock_guard<std::mutex> lock(_mutex);
@@ -469,6 +510,7 @@ public:
     /// Send data to the socket peer.
     void send(const char* data, const int len, const bool flush = true)
     {
+        assert(isCorrectThread());
         if (data != nullptr && len > 0)
         {
             auto lock = getWriteLock();
@@ -497,6 +539,8 @@ public:
     /// Return false iff the socket is closed.
     virtual bool readIncomingData()
     {
+        assert(isCorrectThread());
+
         // SSL decodes blocks of 16Kb, so for efficiency we use the same.
         char buf[16 * 1024];
         ssize_t len;
@@ -542,6 +586,8 @@ protected:
     HandleResult handlePoll(const Poco::Timestamp & /* now */,
                             const int events) override
     {
+        assert(isCorrectThread());
+
         // FIXME: need to close input, but not output (?)
         bool closed = (events & (POLLHUP | POLLERR | POLLNVAL));
 
@@ -593,6 +639,8 @@ protected:
     /// Override to write data out to socket.
     virtual void writeOutgoingData()
     {
+        assert(isCorrectThread());
+
         Util::assertIsLocked(_writeMutex);
         assert(!_outBuffer.empty());
         do
@@ -629,12 +677,14 @@ protected:
     /// Override to handle reading of socket data differently.
     virtual int readData(char* buf, int len)
     {
+        assert(isCorrectThread());
         return ::read(getFD(), buf, len);
     }
 
     /// Override to handle writing data to socket differently.
     virtual int writeData(const char* buf, const int len)
     {
+        assert(isCorrectThread());
         return ::write(getFD(), buf, len);
     }
 
diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 073d7c6..77ef0b4 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -68,6 +68,8 @@ public:
 
     bool readIncomingData() override
     {
+        assert(isCorrectThread());
+
         const int rc = doHandshake();
         if (rc <= 0)
         {
@@ -80,6 +82,8 @@ public:
 
     void writeOutgoingData() override
     {
+        assert(isCorrectThread());
+
         const int rc = doHandshake();
         if (rc <= 0)
         {
@@ -92,11 +96,15 @@ public:
 
     virtual int readData(char* buf, int len)
     {
+        assert(isCorrectThread());
+
         return handleSslState(SSL_read(_ssl, buf, len));
     }
 
     virtual int writeData(const char* buf, const int len)
     {
+        assert(isCorrectThread());
+
         assert (len > 0); // Never write 0 bytes.
         return handleSslState(SSL_write(_ssl, buf, len));
     }
@@ -130,6 +138,8 @@ private:
 
     int doHandshake()
     {
+        assert(isCorrectThread());
+
         if (_doHandshake)
         {
             int rc;
@@ -158,6 +168,8 @@ private:
     /// Handles the state of SSL after read or write.
     int handleSslState(const int rc)
     {
+        assert(isCorrectThread());
+
         if (rc > 0)
         {
             // Success: Reset so we can do either.
commit 0eaef6c8968888417adec3e810797468785746a3
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed Mar 8 16:38:22 2017 +0000

    config.h - get includes right: must always be the first include.

diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp
index 52dd3ec..02d2e70 100644
--- a/common/FileUtil.cpp
+++ b/common/FileUtil.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "FileUtil.hpp"
 #include "config.h"
 
+#include "FileUtil.hpp"
+
 #include <ftw.h>
 #include <sys/stat.h>
 #include <sys/vfs.h>
diff --git a/common/IoUtil.cpp b/common/IoUtil.cpp
index 3991fc3..f94d0b3 100644
--- a/common/IoUtil.cpp
+++ b/common/IoUtil.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "IoUtil.hpp"
 #include "config.h"
 
+#include "IoUtil.hpp"
+
 #include <sys/poll.h>
 
 #include <cassert>
diff --git a/common/Log.cpp b/common/Log.cpp
index f4c4589..eaf101f 100644
--- a/common/Log.cpp
+++ b/common/Log.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <sys/prctl.h>
 #include <sys/syscall.h>
 #include <unistd.h>
diff --git a/common/MessageQueue.cpp b/common/MessageQueue.cpp
index 5c4a843..f293fe4 100644
--- a/common/MessageQueue.cpp
+++ b/common/MessageQueue.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include "MessageQueue.hpp"
 
 #include <algorithm>
diff --git a/common/Protocol.cpp b/common/Protocol.cpp
index 030517c..0aa50c4 100644
--- a/common/Protocol.cpp
+++ b/common/Protocol.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "Protocol.hpp"
 #include "config.h"
 
+#include "Protocol.hpp"
+
 #include <cassert>
 #include <cstring>
 #include <map>
diff --git a/common/Session.cpp b/common/Session.cpp
index f80d1d3..78161f1 100644
--- a/common/Session.cpp
+++ b/common/Session.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "Session.hpp"
 #include "config.h"
 
+#include "Session.hpp"
+
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <ftw.h>
diff --git a/common/SigUtil.cpp b/common/SigUtil.cpp
index 5de5721..c6c4ffe 100644
--- a/common/SigUtil.cpp
+++ b/common/SigUtil.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "SigUtil.hpp"
 #include "config.h"
 
+#include "SigUtil.hpp"
+
 #include <execinfo.h>
 #include <csignal>
 #include <sys/poll.h>
diff --git a/common/SpookyV2.cpp b/common/SpookyV2.cpp
index 5a4b0f3..9d6ea64 100644
--- a/common/SpookyV2.cpp
+++ b/common/SpookyV2.cpp
@@ -9,6 +9,8 @@
 //   July 30 2012: I reintroduced the buffer overflow
 //   August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash
 
+#include "config.h"
+
 #include <memory.h>
 #include "SpookyV2.h"
 
diff --git a/common/Unit.cpp b/common/Unit.cpp
index 41469be..c275b53 100644
--- a/common/Unit.cpp
+++ b/common/Unit.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <iostream>
 #include "Unit.hpp"
-#include "config.h"
 
 #include <cassert>
 #include <dlfcn.h>
diff --git a/common/UnitHTTP.cpp b/common/UnitHTTP.cpp
index 8e0c53e..b4b652b 100644
--- a/common/UnitHTTP.cpp
+++ b/common/UnitHTTP.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <iostream>
 #include "UnitHTTP.hpp"
 
diff --git a/common/Util.cpp b/common/Util.cpp
index b959029..ce1730b 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "Util.hpp"
 #include "config.h"
 
+#include "Util.hpp"
+
 #include <execinfo.h>
 #include <csignal>
 #include <sys/poll.h>
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 1cb8627..de84a39 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "ChildSession.hpp"
 #include "config.h"
 
+#include "ChildSession.hpp"
+
 #include <sstream>
 
 #include <Poco/JSON/Object.h>
diff --git a/kit/DummyLibreOfficeKit.cpp b/kit/DummyLibreOfficeKit.cpp
index 93c7f49..cad7aec 100644
--- a/kit/DummyLibreOfficeKit.cpp
+++ b/kit/DummyLibreOfficeKit.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include "DummyLibreOfficeKit.hpp"
 
 #include <cstring>
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index a190669..c14007a 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -11,6 +11,8 @@
  * a document editing session.
  */
 
+#include "config.h"
+
 #include <dlfcn.h>
 #include <ftw.h>
 #include <malloc.h>
diff --git a/net/ServerSocket.hpp b/net/ServerSocket.hpp
index cbd320f..8f98dc6 100644
--- a/net/ServerSocket.hpp
+++ b/net/ServerSocket.hpp
@@ -10,8 +10,6 @@
 #ifndef INCLUDED_SERVERSOCKET_HPP
 #define INCLUDED_SERVERSOCKET_HPP
 
-#include "config.h"
-
 #include "memory"
 
 #include "Socket.hpp"
diff --git a/net/Socket.cpp b/net/Socket.cpp
index 602b1a0..dc0f294 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <ctype.h>
 
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 884d205..87860ac 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -10,8 +10,6 @@
 #ifndef INCLUDED_SOCKET_HPP
 #define INCLUDED_SOCKET_HPP
 
-#include "config.h"
-
 #include <poll.h>
 #include <unistd.h>
 #include <sys/stat.h>
diff --git a/net/Ssl.cpp b/net/Ssl.cpp
index 82bf64f..6e266c7 100644
--- a/net/Ssl.cpp
+++ b/net/Ssl.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "Ssl.hpp"
 #include "config.h"
 
+#include "Ssl.hpp"
+
 #include <sys/syscall.h>
 
 #include "Util.hpp"
diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 54e7be7..073d7c6 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -10,8 +10,6 @@
 #ifndef INCLUDED_SSLSOCKET_HPP
 #define INCLUDED_SSLSOCKET_HPP
 
-#include "config.h"
-
 #include <cerrno>
 
 #include "Ssl.hpp"
diff --git a/test/UnitFonts.cpp b/test/UnitFonts.cpp
index 96c86c7..17062ef 100644
--- a/test/UnitFonts.cpp
+++ b/test/UnitFonts.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <dlfcn.h>
 #include <ftw.h>
 
diff --git a/test/UnitFuzz.cpp b/test/UnitFuzz.cpp
index 422aa81..18ee753 100644
--- a/test/UnitFuzz.cpp
+++ b/test/UnitFuzz.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <cassert>
 #include <iostream>
 #include <random>
diff --git a/test/UnitMinSocketBufferSize.cpp b/test/UnitMinSocketBufferSize.cpp
index 4910683..6bfb678 100644
--- a/test/UnitMinSocketBufferSize.cpp
+++ b/test/UnitMinSocketBufferSize.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include "Log.hpp"
 #include "Protocol.hpp"
 #include "Unit.hpp"
diff --git a/test/UnitOOB.cpp b/test/UnitOOB.cpp
index 2ee524c..e1b83dc 100644
--- a/test/UnitOOB.cpp
+++ b/test/UnitOOB.cpp
@@ -12,6 +12,8 @@
  * UnitFuzz.
  */
 
+#include "config.h"
+
 #include "Common.hpp"
 #include "IoUtil.hpp"
 #include "Protocol.hpp"
diff --git a/test/UnitPrefork.cpp b/test/UnitPrefork.cpp
index bd56337..7a205b3 100644
--- a/test/UnitPrefork.cpp
+++ b/test/UnitPrefork.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <dirent.h>
 #include <dlfcn.h>
 #include <ftw.h>
diff --git a/test/UnitStorage.cpp b/test/UnitStorage.cpp
index 1a2f717..c2cd31c 100644
--- a/test/UnitStorage.cpp
+++ b/test/UnitStorage.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <iostream>
 
 #include "Exceptions.hpp"
diff --git a/test/UnitTileCache.cpp b/test/UnitTileCache.cpp
index 65cf37c..a61d297 100644
--- a/test/UnitTileCache.cpp
+++ b/test/UnitTileCache.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <fstream>
 #include <thread>
 
diff --git a/test/UnitTimeout.cpp b/test/UnitTimeout.cpp
index 4b65d43..c5df874 100644
--- a/test/UnitTimeout.cpp
+++ b/test/UnitTimeout.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <cassert>
 
 #include <Poco/Util/Application.h>
diff --git a/test/helpers.hpp b/test/helpers.hpp
index 16142e7..a950de9 100644
--- a/test/helpers.hpp
+++ b/test/helpers.hpp
@@ -10,8 +10,6 @@
 #ifndef INCLUDED_TEST_HELPERS_HPP
 #define INCLUDED_TEST_HELPERS_HPP
 
-#include "config.h"
-
 #include <algorithm>
 #include <condition_variable>
 #include <cstdlib>
diff --git a/test/test.cpp b/test/test.cpp
index 0d30f3a..9ad21cd 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <cstdlib>
 #include <iostream>
 
diff --git a/tools/KitClient.cpp b/tools/KitClient.cpp
index 5066386..bff3384 100644
--- a/tools/KitClient.cpp
+++ b/tools/KitClient.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <cassert>
 #include <cstdlib>
 #include <cstring>
diff --git a/tools/Stress.cpp b/tools/Stress.cpp
index 5f9b7ea..1acbb9f 100644
--- a/tools/Stress.cpp
+++ b/tools/Stress.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <unistd.h>
 
 #include <algorithm>
diff --git a/tools/Tool.cpp b/tools/Tool.cpp
index 3bb6b77..545641a 100644
--- a/tools/Tool.cpp
+++ b/tools/Tool.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <unistd.h>
 
 #include <algorithm>
diff --git a/tools/map.cpp b/tools/map.cpp
index 83043a1..9ed4719 100644
--- a/tools/map.cpp
+++ b/tools/map.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
diff --git a/tools/mount.cpp b/tools/mount.cpp
index 89df406..40c64eb 100644
--- a/tools/mount.cpp
+++ b/tools/mount.cpp
@@ -10,6 +10,8 @@
  * This is a very tiny helper to allow overlay mounting.
  */
 
+#include "config.h"
+
 #include <sys/mount.h>
 
 #include "security.h"
diff --git a/wsd/AdminModel.cpp b/wsd/AdminModel.cpp
index f06573c..3b81ff3 100644
--- a/wsd/AdminModel.cpp
+++ b/wsd/AdminModel.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "AdminModel.hpp"
 #include "config.h"
 
+#include "AdminModel.hpp"
+
 #include <memory>
 #include <set>
 #include <sstream>
diff --git a/wsd/Auth.cpp b/wsd/Auth.cpp
index 39b1d7a..96765c9 100644
--- a/wsd/Auth.cpp
+++ b/wsd/Auth.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "Auth.hpp"
 #include "config.h"
 
+#include "Auth.hpp"
+
 #include <cstdlib>
 #include <string>
 
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index c98b020..8332b8a 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "ClientSession.hpp"
 #include "config.h"
 
+#include "ClientSession.hpp"
+
 #include <fstream>
 
 #include <Poco/URI.h>
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 7f0d722..c046fcb 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "DocumentBroker.hpp"
 #include "config.h"
 
+#include "DocumentBroker.hpp"
+
 #include <cassert>
 #include <ctime>
 #include <fstream>
diff --git a/wsd/FileServer.hpp b/wsd/FileServer.hpp
index a1ad497..64d3e04 100644
--- a/wsd/FileServer.hpp
+++ b/wsd/FileServer.hpp
@@ -10,8 +10,6 @@
 #ifndef INCLUDED_FILESERVER_HPP
 #define INCLUDED_FILESERVER_HPP
 
-#include "config.h"
-
 #include <string>
 
 #include <Poco/MemoryStream.h>
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 74eec41..5735e49 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "LOOLWSD.hpp"
 #include "config.h"
 
+#include "LOOLWSD.hpp"
+
 /* Default host used in the start test URI */
 #define LOOLWSD_TEST_HOST "localhost"
 
diff --git a/wsd/LOOLWebSocket.hpp b/wsd/LOOLWebSocket.hpp
index 1408622..24bc7a0 100644
--- a/wsd/LOOLWebSocket.hpp
+++ b/wsd/LOOLWebSocket.hpp
@@ -10,8 +10,6 @@
 #ifndef INCLUDED_LOOLWEBSOCKET_HPP
 #define INCLUDED_LOOLWEBSOCKET_HPP
 
-#include "config.h"
-
 #include <cstdlib>
 #include <mutex>
 #include <thread>
diff --git a/wsd/SenderQueue.cpp b/wsd/SenderQueue.cpp
index 0feb463..f614dd0 100644
--- a/wsd/SenderQueue.cpp
+++ b/wsd/SenderQueue.cpp
@@ -7,6 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include "config.h"
+
 #include "SenderQueue.hpp"
 
 #include <algorithm>
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index d4e5709..8e7868f 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "Storage.hpp"
 #include "config.h"
 
+#include "Storage.hpp"
+
 #include <algorithm>
 #include <cassert>
 #include <fstream>
diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp
index 270031c..c8cb0a6 100644
--- a/wsd/TileCache.cpp
+++ b/wsd/TileCache.cpp
@@ -7,9 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "TileCache.hpp"
 #include "config.h"
 
+#include "TileCache.hpp"
+
 #include <cassert>
 #include <climits>
 #include <cstdio>
commit c4c8d93bba04e930606e9e5137b17ec95fc8f548
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Mar 8 14:48:33 2017 +0100

    nb: Blind attempt to fix running inside docker.
    
    If nothing, it will at least improve the logging.
    
    Change-Id: I35a8eb620a210aea9cf55c0eb47bda7b0d777cf8

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index ea5ee48..74eec41 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2457,10 +2457,16 @@ private:
     {
         std::shared_ptr<ServerSocket> serverSocket = std::make_shared<ServerSocket>(clientSocket, factory);
 
-        if (serverSocket->bind(addr) &&
-            serverSocket->listen())
+        if (!serverSocket->bind(addr))
+        {
+            LOG_ERR("Failed to bind to: " << addr.toString());
+            return nullptr;
+        }
+
+        if (serverSocket->listen())
             return serverSocket;
 
+        LOG_ERR("Failed to listen on: " << addr.toString());
         return nullptr;
     }
 
@@ -2499,13 +2505,13 @@ private:
 #endif
             factory = std::make_shared<PlainSocketFactory>();
 
-        std::shared_ptr<ServerSocket> socket = getServerSocket(SocketAddress("127.0.0.1", port),
+        std::shared_ptr<ServerSocket> socket = getServerSocket(SocketAddress(port),
                                                                WebServerPoll, factory);
         while (!socket)
         {
             ++port;
             LOG_INF("Client port " << (port - 1) << " is busy, trying " << port << ".");
-            socket = getServerSocket(SocketAddress("127.0.0.1", port),
+            socket = getServerSocket(SocketAddress(port),
                                      WebServerPoll, factory);
         }
 
commit 59c5eeec4c73265bede9ecc05d490f33cbb2a35b
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Mar 8 12:08:17 2017 +0100

    Fix build.
    
    Change-Id: I218895b6ba314250f0009fa465499f38953b49d8

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 1615d11..ea5ee48 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2455,7 +2455,7 @@ private:
                                                   SocketPoll &clientSocket,
                                                   std::shared_ptr<SocketFactory> factory)
     {
-        std::shared_ptr<ServerSocket> serverSocket = std::make_shared<ServerSocket>(poll, factory);
+        std::shared_ptr<ServerSocket> serverSocket = std::make_shared<ServerSocket>(clientSocket, factory);
 
         if (serverSocket->bind(addr) &&
             serverSocket->listen())
commit d807db08f21c84e25cae59a042e1fe46fd81196a
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed Mar 8 10:04:44 2017 +0000

    More descriptive comment.

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index d4efdd8..1615d11 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2449,8 +2449,10 @@ private:
     /// This thread & poll accepts incoming connections.
     AcceptPoll _acceptPoll;
 
+    /// Create a new server socket - accepted sockets will be added
+    /// to the @clientSockets' poll when created with @factory.
     std::shared_ptr<ServerSocket> getServerSocket(const Poco::Net::SocketAddress& addr,
-                                                  SocketPoll &poll,
+                                                  SocketPoll &clientSocket,
                                                   std::shared_ptr<SocketFactory> factory)
     {
         std::shared_ptr<ServerSocket> serverSocket = std::make_shared<ServerSocket>(poll, factory);
commit ec3e7958139457f514df1e2f002bed6a2ef26a2e
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Wed Mar 8 00:05:53 2017 -0500

    nb: remove superfluous assertion
    
    Change-Id: I22e0e031768e52f83b6e0f062df61aaf6e881279

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 3fa629d..d4efdd8 100644

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list