[Libreoffice-commits] online.git: Branch 'private/Ashod/nonblocking' - 45 commits - common/FileUtil.cpp common/IoUtil.cpp common/Log.cpp common/MessageQueue.cpp common/Protocol.cpp common/Session.cpp common/SigUtil.cpp common/SpookyV2.cpp common/Unit.cpp common/UnitHTTP.cpp common/Util.cpp kit/ChildSession.cpp kit/DummyLibreOfficeKit.cpp kit/Kit.cpp Makefile.am net/loolnb.cpp net/ServerSocket.hpp net/Socket.cpp net/Socket.hpp net/Ssl.cpp net/SslSocket.hpp net/WebSocketHandler.hpp test/helpers.hpp test/Makefile.am test/test.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/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/SenderQueue.cpp wsd/S enderQueue.hpp wsd/Storage.cpp wsd/TileCache.cpp

Michael Meeks (via logerrit) logerrit at kemper.freedesktop.org
Sat Dec 7 01:50:27 UTC 2019


Rebased ref, commits from common ancestor:
commit 5ad0df4ec9cfa9fdeaa5ab25d6e7cebb6fba61f4
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Fri Mar 10 10:52:33 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Fri Mar 10 10:52:33 2017 +0000

    Re-work socket buffer sizing and setting.
    
    Avoid writing more data than can be absorbed by our socket buffer.
    It is fine to set socket buffer sizes after bind/accept.

diff --git a/net/Socket.hpp b/net/Socket.hpp
index f1778d00d..89d9b176d 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -39,11 +39,10 @@
 /// A non-blocking, streaming socket.
 class Socket
 {
-    // Guestimates; verify, or read from the kernel ?
-    static const int MaximumSendBufferSize = 128 * 1024;
-    static const int DefaultSendBufferSize = 16 * 1024;
-    static const int MinimumSendBufferSize = 4 * 1024;
 public:
+    static const int DefaultSendBufferSize = 16 * 1024;
+    static const int MaximumSendBufferSize = 128 * 1024;
+
     Socket() :
         _fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)),
         _sendBufferSize(DefaultSendBufferSize)
@@ -87,40 +86,55 @@ public:
                     (char *) &val, sizeof(val));
     }
 
-    /// Sets the send buffer in size bytes.
-    /// Must be called before accept or connect.
+    /// Sets the kernel socket send buffer in size bytes.
     /// Note: TCP will allocate twice this size for admin purposes,
     /// so a subsequent call to getSendBufferSize will return
     /// the larger (actual) buffer size, if this succeeds.
     /// Note: the upper limit is set via /proc/sys/net/core/wmem_max,
     /// and there is an unconfigurable lower limit as well.
     /// Returns true on success only.
-    bool setSendBufferSize(const int size)
+    bool setSocketBufferSize(const int size)
     {
-        int rc = ::setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, size, sizeof(size));
-        bool success = (rc == 0);
-
-        _sendBufferSize = std::min(std::max(size, MinimumSendBufferSize), // old guess.
-                                   MaximumSendBufferSize);
-        int readSize = _sendBufferSize;
-        const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &readSize, sizeof(size));
-        if (rc == 0)
-            _sendBufferSize = readSize;
+        int rc = ::setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
 
-        return success && rc == 0;
+        _sendBufferSize = getSocketBufferSize();
+        if (rc != 0 || _sendBufferSize < 0 )
+        {
+            LOG_ERR("Error getting socket buffer size " << errno);
+            _sendBufferSize = DefaultSendBufferSize;
+            return false;
+        }
+        else
+        {
+            if (_sendBufferSize > MaximumSendBufferSize * 2)
+            {
+                LOG_TRC("Clamped send buffer size to " << MaximumSendBufferSize << " from " << _sendBufferSize);
+                _sendBufferSize = MaximumSendBufferSize;
+            }
+            else
+                LOG_TRC("Set socket buffer size to " << _sendBufferSize);
+            return true;
+        }
     }
 
     /// Gets the actual send buffer size in bytes, -1 for failure.
+    int getSocketBufferSize() const
+    {
+        int size;
+        unsigned int len = sizeof(size);
+        const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, &len);
+        return (rc == 0 ? size : -1);
+    }
+
+    /// Gets our fast cache of the socket buffer size
     int getSendBufferSize() const
     {
         return _sendBufferSize;
-        return (rc == 0 ? size : -1);
     }
 
     /// Sets the receive buffer size in bytes.
-    /// Must be called before accept or connect.
     /// Note: TCP will allocate twice this size for admin purposes,
-    /// so a subsequent call to getSendBufferSize will return
+    /// so a subsequent call to getReceieveBufferSize will return
     /// the larger (actual) buffer size, if this succeeds.
     /// Note: the upper limit is set via /proc/sys/net/core/rmem_max,
     /// and there is an unconfigurable lower limit as well.
@@ -196,8 +210,8 @@ protected:
 #if ENABLE_DEBUG
         _owner = std::this_thread::get_id();
 
-        const int oldSize = getSendBufferSize();
-        setSendBufferSize(0);
+        const int oldSize = getSocketBufferSize();
+        setSocketBufferSize(0);
         LOG_TRC("Socket #" << _fd << " buffer size: " << getSendBufferSize() << " (was " << oldSize << ")");
 #endif
 
@@ -711,8 +725,9 @@ protected:
             ssize_t len;
             do
             {
-                len = writeData(&_outBuffer[0], std::max((int)_outBuffer.size(),
-                                                         _sendBufferSize));
+                // Writing more than we can absorb in the kernel causes SSL wasteage.
+                len = writeData(&_outBuffer[0], std::min((int)_outBuffer.size(),
+                                                         getSendBufferSize()));
 
                 auto& log = Log::logger();
                 if (log.trace() && len > 0) {
@@ -794,9 +809,12 @@ namespace HttpHelper
             return;
         }
 
-        const int socketBufferSize = 16 * 1024;
-        if (st.st_size >= socketBufferSize)
-            socket->setSendBufferSize(socketBufferSize);
+        int bufferSize = std::min(st.st_size, (off_t)Socket::MaximumSendBufferSize);
+        if (st.st_size >= socket->getSendBufferSize())
+        {
+            socket->setSocketBufferSize(bufferSize);
+            bufferSize = socket->getSendBufferSize();
+        }
 
         response.setContentLength(st.st_size);
         response.set("User-Agent", HTTP_AGENT_STRING);
@@ -810,7 +828,7 @@ namespace HttpHelper
         bool flush = true;
         do
         {
-            char buf[socketBufferSize];
+            char buf[bufferSize];
             file.read(buf, sizeof(buf));
             const int size = file.gcount();
             if (size > 0)
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 9af0f329b..9694cf058 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -351,7 +351,7 @@ protected:
 
         // Want very low latency sockets.
         socket->setNoDelay();
-        socket->setSendBufferSize(0);
+        socket->setSocketBufferSize(0);
 
         socket->send(oss.str());
         _wsState = WSState::WS;
commit 86833a6a14df2d1a2d441d99281ed9a2ddeaff3e
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Fri Mar 10 09:55:28 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Fri Mar 10 09:55:28 2017 +0000

    foo.

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 7c48bfc4d..f1778d00d 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -39,11 +39,16 @@
 /// A non-blocking, streaming socket.
 class Socket
 {
+    // Guestimates; verify, or read from the kernel ?
+    static const int MaximumSendBufferSize = 128 * 1024;
+    static const int DefaultSendBufferSize = 16 * 1024;
+    static const int MinimumSendBufferSize = 4 * 1024;
 public:
     Socket() :
-        _fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0))
+        _fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)),
+        _sendBufferSize(DefaultSendBufferSize)
     {
-        setNoDelay();
+        init();
     }
 
     virtual ~Socket()
@@ -92,17 +97,23 @@ public:
     /// Returns true on success only.
     bool setSendBufferSize(const int size)
     {
-        constexpr unsigned int len = sizeof(size);
-        const int rc = ::setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, len);
-        return (rc == 0);
+        int rc = ::setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, size, sizeof(size));
+        bool success = (rc == 0);
+
+        _sendBufferSize = std::min(std::max(size, MinimumSendBufferSize), // old guess.
+                                   MaximumSendBufferSize);
+        int readSize = _sendBufferSize;
+        const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &readSize, sizeof(size));
+        if (rc == 0)
+            _sendBufferSize = readSize;
+
+        return success && rc == 0;
     }
 
     /// Gets the actual send buffer size in bytes, -1 for failure.
     int getSendBufferSize() const
     {
-        int size;
-        unsigned int len = sizeof(size);
-        const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, &len);
+        return _sendBufferSize;
         return (rc == 0 ? size : -1);
     }
 
@@ -194,6 +205,7 @@ protected:
 
 private:
     const int _fd;
+    int _sendBufferSize;
     // always enabled to avoid ABI change in debug mode ...
     std::thread::id _owner;
 };
@@ -699,7 +711,8 @@ protected:
             ssize_t len;
             do
             {
-                len = writeData(&_outBuffer[0], _outBuffer.size());
+                len = writeData(&_outBuffer[0], std::max((int)_outBuffer.size(),
+                                                         _sendBufferSize));
 
                 auto& log = Log::logger();
                 if (log.trace() && len > 0) {
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 5331005ed..9af0f329b 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -349,6 +349,10 @@ protected:
             << "Sec-WebSocket-Accept: " << PublicComputeAccept::doComputeAccept(wsKey) << "\r\n"
             << "\r\n";
 
+        // Want very low latency sockets.
+        socket->setNoDelay();
+        socket->setSendBufferSize(0);
+
         socket->send(oss.str());
         _wsState = WSState::WS;
     }
commit 21acbf1093d7a06749231ce08a29fda931e2a665
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 23:50:36 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: Thu Mar 9 23:58:09 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 e10bda83e..d2b563dda 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 6c855f6fd..5594c70e0 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 a8ffcfbf1..7c42ba983 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2283,14 +2283,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 9093f4212e0383d82cfd3a72eccec1c348a9737e
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 22:39:18 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: 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 4ef8cb7b3..e10bda83e 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 2b5461705c39a6db50b2b611c543dc29554ec39d
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 22:38:25 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: Thu Mar 9 22:38:25 2017 -0500

    nb: logging
    
    Change-Id: Ic3b724d5869f75234af2238b96a90c4745155b86

diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 004cb88a7..ac969732e 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 e8a4c2c3c..4ef8cb7b3 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 b273194d4136eb1475df67b7b47a71540dff80bb
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 20:56:39 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: Thu Mar 9 20:56:43 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 ce68ebb16..a8ffcfbf1 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1081,6 +1081,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)
@@ -1186,7 +1198,6 @@ void PrisonerPoll::wakeupHook()
     }
 }
 
-
 bool LOOLWSD::createForKit()
 {
 #ifdef KIT_IN_PROCESS
@@ -2605,6 +2616,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
@@ -2622,18 +2635,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 c768d4cedc16c34d2d78e6e340ad5df2d0accee1
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 20:56:02 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: 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 d6c427ae0..ce68ebb16 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -801,6 +801,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);
@@ -2604,18 +2605,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 10bb7167dd1f03165f3750c6f82d9f0a5933b148
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 20:54:44 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: 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 37dc3b470..7c48bfc4d 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 3e65ec30e4941b0e9f2687a21ca6b124caba0d8e
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu Mar 9 21:46:52 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: 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 5ba861bb7..37dc3b470 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 61cee9696a32196875329dae39d6d7eae67caeb4
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu Mar 9 20:35:04 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu Mar 9 20:35:04 2017 +0000

    Centralize idle poll time.

diff --git a/net/Socket.cpp b/net/Socket.cpp
index 344d86ffa..b64f90401 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 4cb482184..5ba861bb7 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 0a005530a..e8a4c2c3c 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 9bf429708..d6c427ae0 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2658,7 +2658,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 e72eba8a6058236033ffd1d2cd2e3c8764995520
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu Mar 9 19:23:21 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: 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 56adecfca..344d86ffa 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 b9f61c9b3..4cb482184 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 e6bf33cad..0a005530a 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 b3f68f484..6c855f6fd 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 a5231e4f5..9bf429708 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2251,6 +2251,7 @@ private:
                     _clientSession->onConnect(socket);
                     docBroker->addSocketToPoll(socket);
                 }
+                docBroker->startThread();
             }
         }
         if (!docBroker || !_clientSession)
commit 16f0e703a607d461babba8a533bae17e92477467
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu Mar 9 19:12:28 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu Mar 9 19:12:28 2017 +0000

    Kill SenderThreadPool.

diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 97f5591e3..51b810a18 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 f614dd014..000000000
--- 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 28996112a..6debf5956 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 3da5c5f6e3300ffc9cfc9e4330a13cb1c83cc81b
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu Mar 9 19:00:04 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: 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 c5bd09477..56adecfca 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 32d904882..b9f61c9b3 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 ab97d178e..e6bf33cad 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 dba432782..a5231e4f5 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2389,11 +2389,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()
@@ -2648,7 +2651,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 cbf71949fc4bb5ae47415860dd08c961b7cb970e
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu Mar 9 18:19:53 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu Mar 9 18:53:09 2017 +0000

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

diff --git a/net/Socket.cpp b/net/Socket.cpp
index dc0f294ba..c5bd09477 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 7a6654e48..32d904882 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 8332b8a23..467a51440 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 c046fcb1b..ab97d178e 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 3e327ce5d..b3f68f484 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 c91b3212b..dba432782 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1407,9 +1407,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())
     {
@@ -1673,9 +1671,7 @@ private:
     void onDisconnect() override
     {
         if (_clientSession)
-        {
-            saveDocument();
-        }
+            disposeSession();
 
         const size_t curConnections = --LOOLWSD::NumConnections;
         LOG_TRC("Disconnected connection #" << _connectionNum << " of " <<
@@ -2261,7 +2257,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();
@@ -2273,25 +2270,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.
@@ -2424,9 +2405,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();
@@ -2437,7 +2422,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();
     }
@@ -2661,49 +2647,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 4e2571091a8bcc40da194efb64b31140cde3e31b
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Mar 8 18:48:07 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu Mar 9 17:48:41 2017 +0000

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

diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index ae341f5c1..c91b3212b 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -185,9 +185,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;
@@ -343,6 +346,7 @@ static bool cleanupChildren()
         if (!NewChildren[i]->isAlive())
         {
             LOG_WRN("Removing dead spare child [" << NewChildren[i]->getPid() << "].");
+
             NewChildren.erase(NewChildren.begin() + i);
             removed = true;
         }
@@ -389,46 +393,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())
@@ -438,6 +408,7 @@ static bool prespawnChildren()
     }
 
     cleanupDocBrokers();
+#endif
 
     std::unique_lock<std::mutex> lock(NewChildrenMutex, std::defer_lock);
     if (!lock.try_lock())
@@ -642,8 +613,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
@@ -1169,6 +1149,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
@@ -1234,8 +1251,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
@@ -2610,6 +2628,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())
     {
@@ -2623,6 +2661,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)
     {
@@ -2632,65 +2673,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 97824bdc4cb51aee47314083305129c379192a62
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Thu Mar 9 17:32:55 2017 +0100
Commit:     Jan Holesovsky <kendy at collabora.com>
CommitDate: 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 c45dc241d..7a6654e48 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 cd4b6ea69a2b45cb24a6b4eebc50d71f700da38c
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Thu Mar 9 17:20:34 2017 +0100
Commit:     Jan Holesovsky <kendy at collabora.com>
CommitDate: Thu Mar 9 17:27:20 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 06ac4a0c8..c45dc241d 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 739009320..004cb88a7 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 0e09faa29..ae341f5c1 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1816,8 +1816,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 3bec255fb1c1f444b196fd1445f52243ef547c8c
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Mar 9 01:32:28 2017 -0500
Commit:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
CommitDate: Thu Mar 9 01:39:19 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 d8fa26f9b..06ac4a0c8 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 77ef0b49c..739009320 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 4c07c5621..5331005ed 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 c4f83f568..f1d35cf92 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 2d6c134c4..0e09faa29 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1785,7 +1785,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();
                 }
             }
@@ -1817,7 +1817,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)
@@ -1830,7 +1830,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)
@@ -1853,7 +1853,7 @@ private:
         }
 
         auto socket = _socket.lock();
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
         socket->shutdown();
         LOG_INF("Sent / response successfully.");
     }
@@ -1919,7 +1919,7 @@ private:
             << xml;
 
         auto socket = _socket.lock();
-        socket->send(oss.str());
+        socket->sendHttpResponse(oss.str());
         socket->shutdown();
         LOG_INF("Sent discovery.xml successfully.");
     }
@@ -2105,7 +2105,7 @@ private:
                     std::string fileName = dirPath + "/" + form.get("name");
                     File(tmpPath).moveTo(fileName);
                     response.setContentLength(0);
-                    socket->send(response);
+                    socket->sendHttpResponse(response);
                     return;
                 }
             }
commit 9d50f6a3fae44d2c70655559d72d579e6edeb689
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Mar 8 18:14:53 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: 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 87860ac74..d8fa26f9b 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 073d7c687..77ef0b49c 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 953152beb42d18da07da2fa13465de5b55f18979
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Mar 8 16:38:22 2017 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Mar 8 16:40:30 2017 +0000

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

diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp
index 52dd3ec1a..02d2e705f 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 3991fc317..f94d0b3cf 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 f4c45891e..eaf101f19 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 5c4a843a1..f293fe49d 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 030517ced..0aa50c470 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 f80d1d359..78161f112 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 5de572181..c6c4ffe46 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 5a4b0f303..9d6ea6498 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 41469bee4..c275b533b 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 8e0c53e46..b4b652b11 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 b95902936..ce1730b0c 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 1cb8627dc..de84a398e 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 91bb4fa5e..1c470f4ed 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 a1906690e..c14007a6e 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 cbd320ff6..8f98dc660 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 602b1a00a..dc0f294ba 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 884d20572..87860ac74 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 82bf64fdc..6e266c70f 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 54e7be780..073d7c687 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 96c86c7db..17062ef18 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 422aa8147..18ee75335 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 4910683a5..6bfb678b2 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"

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list