[Libreoffice-commits] online.git: 3 commits - loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/protocol.txt loolwsd/TileCache.cpp loolwsd/TileCache.hpp

Tor Lillqvist tml at collabora.com
Thu May 28 08:49:25 PDT 2015


 loolwsd/LOOLSession.cpp |   64 ++++++++++++++++++++++++++++++-------
 loolwsd/LOOLSession.hpp |    6 ++-
 loolwsd/TileCache.cpp   |   53 ++++++++++++++++++++++++++++++
 loolwsd/TileCache.hpp   |    6 +++
 loolwsd/protocol.txt    |   83 +++++++++++++++++++++++++++---------------------
 5 files changed, 164 insertions(+), 48 deletions(-)

New commits:
commit bf985624f1cdf5f6299c2b64e1805edb79f719a1
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu May 28 18:39:05 2015 +0300

    Handle LOK_CALLBACK_INVALIDATE_TILES in the server too
    
    Remove any intersecting cached tiles. It is the parent process that handles
    the tile cache, so it must look for invalidatetiles: messages, too, before
    passing them on to the client. To know for which part we should remove tiles,
    add an "ephemeral" curpart: message that the child process sends to the parent
    process before the invalidatetils: message.

diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 9bbca5b..a98a577 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -106,7 +106,8 @@ std::mutex MasterProcessSession::_rngMutex;
 
 MasterProcessSession::MasterProcessSession(std::shared_ptr<WebSocket> ws, Kind kind) :
     LOOLSession(ws, kind),
-    _childId(0)
+    _childId(0),
+    _curPart(0)
 {
     std::cout << Util::logPrefix() << "MasterProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
 }
@@ -134,8 +135,19 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
         // Note that this handles both forwarding requests from the client to the child process, and
         // forwarding replies from the child process to the client. Or does it?
 
-        // Snoop at tile: and status: messages and (re-)cache them
+        // Snoop at some  messages and manipulate tile cache information as needed
         auto peer = _peer.lock();
+
+        if (_kind == Kind::ToPrisoner)
+        {
+            if (tokens[0] == "curpart:" &&
+                tokens.count() == 2 &&
+                getTokenInteger(tokens[1], "part", _curPart))
+            {
+                return true;
+            }
+        }
+
         if (_kind == Kind::ToPrisoner && peer && peer->_tileCache)
         {
             if (tokens[0] == "tile:")
@@ -159,6 +171,11 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
                 assert(firstLine.size() == static_cast<std::string::size_type>(length));
                 peer->_tileCache->saveStatus(firstLine);
             }
+            else if (tokens[0] == "invalidatetiles:")
+            {
+                assert(firstLine.size() == static_cast<std::string::size_type>(length));
+                peer->_tileCache->invalidateTiles(_curPart, firstLine);
+            }
         }
 
         forwardToPeer(buffer, length);
@@ -212,15 +229,16 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
         }
         return loadDocument(buffer, length, tokens);
     }
-    else if (tokens[0] != "status" &&
-             tokens[0] != "tile" &&
+    else if (tokens[0] != "invalidatetiles" &&
              tokens[0] != "key" &&
              tokens[0] != "mouse" &&
-             tokens[0] != "uno" &&
-             tokens[0] != "selecttext" &&
-             tokens[0] != "selectgraphic" &&
              tokens[0] != "resetselection" &&
-             tokens[0] != "saveas")
+             tokens[0] != "saveas" &&
+             tokens[0] != "selectgraphic" &&
+             tokens[0] != "selecttext" &&
+             tokens[0] != "status" &&
+             tokens[0] != "tile" &&
+             tokens[0] != "uno")
     {
         sendTextFrame("error: cmd=" + tokens[0] + " kind=unknown");
         return false;
@@ -230,6 +248,10 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
         sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
         return false;
     }
+    else if (tokens[0] == "invalidatetiles")
+    {
+        return invalidateTiles(buffer, length, tokens);
+    }
     else if (tokens[0] == "status")
     {
         return getStatus(buffer, length);
@@ -406,6 +428,25 @@ void MasterProcessSession::preSpawn()
     _childProcesses[child.id()] = childId;
 }
 
+bool MasterProcessSession::invalidateTiles(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int part, tilePosX, tilePosY, tileWidth, tileHeight;
+
+    if (tokens.count() != 6 ||
+        !getTokenInteger(tokens[1], "part", part) ||
+        !getTokenInteger(tokens[2], "tileposx", tilePosX) ||
+        !getTokenInteger(tokens[3], "tileposy", tilePosY) ||
+        !getTokenInteger(tokens[4], "tilewidth", tileWidth) ||
+        !getTokenInteger(tokens[5], "tileheight", tileHeight))
+    {
+        sendTextFrame("error: cmd=invalidatetiles kind=syntax");
+        return false;
+    }
+
+    _tileCache->invalidateTiles(_curPart, tilePosX, tilePosY, tileWidth, tileHeight);
+    return true;
+}
+
 bool MasterProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
 {
     if (tokens.count() != 2)
@@ -592,8 +633,8 @@ void MasterProcessSession::forwardToPeer(const char *buffer, int length)
 
 ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit) :
     LOOLSession(ws, Kind::ToMaster),
-    _loKit(loKit),
-    _loKitDocument(NULL)
+    _loKitDocument(NULL),
+    _loKit(loKit)
 {
     std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
 }
@@ -690,11 +731,12 @@ extern "C"
 {
     static void myCallback(int nType, const char* pPayload, void* pData)
     {
-        LOOLSession *srv = reinterpret_cast<LOOLSession *>(pData);
+        ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData);
 
         switch ((LibreOfficeKitCallbackType) nType)
         {
         case LOK_CALLBACK_INVALIDATE_TILES:
+            srv->sendTextFrame("curpart: part=" + std::to_string(srv->_loKitDocument->pClass->getPart(srv->_loKitDocument)));
             srv->sendTextFrame("invalidatetiles: " + std::string(pPayload));
             break;
         case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 2d780d7..c73456c 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -109,6 +109,8 @@ public:
     virtual bool getStatus(const char *buffer, int length);
 
  protected:
+    bool invalidateTiles(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
     virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
 
     virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
@@ -141,6 +143,7 @@ private:
     Poco::UInt64 _childId;
     static Poco::Random _rng;
     static std::mutex _rngMutex;
+    int _curPart;
 };
 
 class ChildProcessSession final : public LOOLSession
@@ -153,6 +156,8 @@ public:
 
     virtual bool getStatus(const char *buffer, int length);
 
+    LibreOfficeKitDocument *_loKitDocument;
+
  protected:
     virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
 
@@ -169,7 +174,6 @@ public:
     std::string _jail;
     std::string _loSubPath;
     LibreOfficeKit *_loKit;
-    LibreOfficeKitDocument *_loKitDocument;
 };
 
 #endif
diff --git a/loolwsd/TileCache.cpp b/loolwsd/TileCache.cpp
index 3b673f0..c72888e 100644
--- a/loolwsd/TileCache.cpp
+++ b/loolwsd/TileCache.cpp
@@ -10,11 +10,13 @@
 #include "config.h"
 
 #include <cassert>
+#include <cstdio>
 #include <fstream>
 #include <iostream>
 #include <memory>
 #include <string>
 
+#include <Poco/DirectoryIterator.h>
 #include <Poco/File.h>
 #include <Poco/Path.h>
 #include <Poco/SHA1Engine.h>
@@ -118,6 +120,52 @@ void TileCache::saveStatus(const std::string& status)
     statusStream.close();
 }
 
+void TileCache::invalidateTiles(int part, int x, int y, int width, int height)
+{
+    std::string dirName = cacheDirName();
+
+    std::vector<std::string> toBeRemoved;
+
+    for (auto tileIterator = Poco::DirectoryIterator(dirName); tileIterator != Poco::DirectoryIterator(); ++tileIterator)
+    {
+        std::string baseName = tileIterator.path().getBaseName();
+
+        int tilePart, tilePixelWidth, tilePixelHeight, tilePosX, tilePosY, tileWidth, tileHeight;
+
+        if (parseCacheFileName(baseName, tilePart, tilePixelWidth, tilePixelHeight, tilePosX, tilePosY, tileWidth, tileHeight))
+        {
+            std::cout << "Tile " << baseName << " is " << tileWidth << "x" << tileHeight << "@+" << tilePosX << "+" << tilePosY << std::endl;
+            if (tilePart == part &&
+                tilePosX < x + width && tilePosX + tileWidth >= x &&
+                tilePosY < y + height && tilePosY + tileHeight >= y)
+            {
+                std::cout << "Match!" << std::endl;
+                toBeRemoved.push_back(tileIterator.path().toString());
+            }
+        }
+    }
+
+    for (auto i: toBeRemoved)
+        std::remove(i.c_str());
+}
+
+void TileCache::invalidateTiles(int part, const std::string& tiles)
+{
+    Poco::StringTokenizer tokens(tiles, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
+
+    assert(tokens[0] == "invalidateTiles:");
+
+    if (tokens.count() != 5)
+        return;
+
+    int width(std::stoi(tokens[1]));
+    int height(std::stoi(tokens[2]));
+    int x(std::stoi(tokens[3]));
+    int y(std::stoi(tokens[4]));
+
+    invalidateTiles(part, x, y, width, height);
+}
+
 std::string TileCache::cacheDirName()
 {
     Poco::SHA1Engine digestEngine;
@@ -136,6 +184,11 @@ std::string TileCache::cacheFileName(int part, int width, int height, int tilePo
             std::to_string(tileWidth) + "x" + std::to_string(tileHeight) + ".png");
 }
 
+bool TileCache::parseCacheFileName(std::string& fileName, int& part, int& width, int& height, int& tilePosX, int& tilePosY, int& tileWidth, int& tileHeight)
+{
+    return (std::sscanf(fileName.c_str(), "%d_%dx%d.%d,%d.%dx%d", &part, &width, &height, &tilePosX, &tilePosY, &tileWidth, &tileHeight) == 7);
+}
+
 Poco::Timestamp TileCache::getLastModified()
 {
     std::fstream modTimeFile(cacheDirName() + "/modtime.txt", std::ios::in);
diff --git a/loolwsd/TileCache.hpp b/loolwsd/TileCache.hpp
index 28d8e3a..21704a2 100644
--- a/loolwsd/TileCache.hpp
+++ b/loolwsd/TileCache.hpp
@@ -28,9 +28,15 @@ public:
     // The parameter is a status: message
     void saveStatus(const std::string& status);
 
+    // The tiles parameter is an invalidatetiles: message as sent by the child process
+    void invalidateTiles(int part, const std::string& tiles);
+
+    void invalidateTiles(int part, int x, int y, int width, int height);
+
 private:
     std::string cacheDirName();
     std::string cacheFileName(int part, int width, int height, int tilePosX, int tilePosY, int tileWidth, int tileHeight);
+    bool parseCacheFileName(std::string& fileName, int& part, int& width, int& height, int& tilePosX, int& tilePosY, int& tileWidth, int& tileHeight);
     Poco::Timestamp getLastModified();
 
     const std::string& _docURL;
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 9dce5cf..adb5950 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -108,6 +108,12 @@ child <id>
     parent has passed the id (a 64-bit random number) to the child
     when starting it, so this is how the child identificates itself.
 
+curpart: part=<partNumber>
+
+    Sent to the parent process before certain messages that the parent
+    needs to act on in addition to passing them on to the client, like
+    invalidatetiles:
+
 nextmessage: size=<upperlimit>
 
     each tile: message sent from the child to the parent is preceded
commit 1d75eacd6a2d124b46f601f4353a54980fb3a3e9
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu May 28 17:57:29 2015 +0300

    Add an invalidatetiles message from client to server
    
    Mainly as a debugging aid to make it easier to check the tile cache invalidation
    code, but might be useful for real, too.

diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 30f428e..9dce5cf 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -11,6 +11,11 @@ tiles proactively (guessing what the client might need). Etc.
 client -> server
 ================
 
+invalidatetiles part=<partNumber> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
+
+    All parameters are numbers. Makes the server remove any cached
+    tiles intersecting with the given area (in twips).
+
 key type=<type> char=<charcode> key=<keycode>
 
     <type> is 'input' or 'up', <charcode> and <keycode> are numbers.
commit 5d9f5e715a73f3c2d85ebeeb0a5404606bfcd10f
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu May 28 17:55:49 2015 +0300

    Sort the messages in the sections

diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 10aa581..30f428e 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -11,29 +11,26 @@ tiles proactively (guessing what the client might need). Etc.
 client -> server
 ================
 
+key type=<type> char=<charcode> key=<keycode>
+
+    <type> is 'input' or 'up', <charcode> and <keycode> are numbers.
+
 load <pathname>
 
     Deprecated.
 
 load url=<url>
 
-status
-
-tile part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
-
-    All parameters are numbers.
-
-key type=<type> char=<charcode> key=<keycode>
-
-    <type> is 'input' or 'up', <charcode> and <keycode> are numbers.
-
 mouse type=<type> x=<x> y=<y> count=<count>
 
     <type> is 'buttondown', 'buttonup' or 'move', others are numbers.
 
-uno <command>
+resetselection
 
-    <command> is a line of text.
+saveas url=<url> format=<format> options=<options>
+
+    <url> is a URL, encoded. <format> is also URL-encoded, i.e. spaces as %20
+    options are the whole rest of the line, not URL-encoded, and can be empty
 
 selecttext type=<type> x=<x> y=<y>
 
@@ -43,12 +40,16 @@ selectgraphic type=<type> x=<x> y=<y>
 
     <type> is 'start' or 'end' <x> and <y> are numbers.
 
-resetselection
+status
 
-saveas url=<url> format=<format> options=<options>
+tile part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
+
+    All parameters are numbers.
+
+uno <command>
+
+    <command> is a line of text.
 
-    <url> is a URL, encoded. <format> is also URL-encoded, i.e. spaces as %20
-    options are the whole rest of the line, not URL-encoded, and can be empty
 
 server -> client
 ================
@@ -60,15 +61,6 @@ error: cmd=<command> kind=<kind>
     message that caused the error. <kind> is some single-word
     classification
 
-status: type=<typeName> parts=<numberOfParts> current=<currentPartNumber> width=<width> height=<height>
-
-    <typeName> is 'text, 'spreadsheet', 'presentation', 'drawing' or 'other. Others are numbers.
-
-tile: part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
-<binaryPngImage>
-
-    The parameters from the corresponding 'tile' command.
-
 nextmessage: size=<byteSize>
 
     <byteSize> is the size, in bytes, of the next message, in case it
@@ -78,36 +70,44 @@ nextmessage: size=<byteSize>
     must be handled by clients that cannot (like those using Poco
     1.6.0).
 
+status: type=<typeName> parts=<numberOfParts> current=<currentPartNumber> width=<width> height=<height>
+
+    <typeName> is 'text, 'spreadsheet', 'presentation', 'drawing' or 'other. Others are numbers.
+
+tile: part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
+<binaryPngImage>
+
+    The parameters from the corresponding 'tile' command.
+
 Each LOK_CALLBACK_FOO_BAR callback causes a corresponding message to
 the client, consisting of the FOO_BAR part in lowercase, without
 underscore, followed by a colon, space and the callback payload. For
 instance:
 
-invalidatetiles: <payload>
-
 invalidatecursor:
 
-
+invalidatetiles: <payload>
 
 The communication between the parent process (the one keeping open the
 Websocket connections to the clients) and a child process (handling
 one document through LibreOfficeKit) uses the same protocol, with
 the following additions and changes:
 
+
 child -> parent
 ===============
 
 child <id>
 
-Must be the first message sent from the child to the parent. The
-parent has passed the id (a 64-bit random number) to the child when
-starting it, so this is how the child identificates itself.
+    Must be the first message sent from the child to the parent. The
+    parent has passed the id (a 64-bit random number) to the child
+    when starting it, so this is how the child identificates itself.
 
 nextmessage: size=<upperlimit>
 
-each tile: message sent from the child to the parent is preceded by a
-nextmessage: message that gives an upper limit on the size of the
-tile: message that will follow. (We assume it is only tile: messages
-that can be "large".) Once we depend on Poco 1.6.1, where one doesn't
-need to use a pre-allocated buffer when receiving WebSocket messages,
-this will go away.
+    each tile: message sent from the child to the parent is preceded
+    by a nextmessage: message that gives an upper limit on the size of
+    the tile: message that will follow. (We assume it is only tile:
+    messages that can be "large".) Once we depend on Poco 1.6.1, where
+    one doesn't need to use a pre-allocated buffer when receiving
+    WebSocket messages, this will go away.


More information about the Libreoffice-commits mailing list