Wait tileprocessed message from client to send new bunch of tiles

We always  one bunch of tiles (e.g. all tiles invalidated) and we
are waiting until client send tileprocessed message back for all
tiles before sending the new tiles.
By canceltiles message we drop every previously requested tiles and
make wsd ready to send new tiles, which will be requested by the client
in theory.

Change-Id: I9901420ada549e962ffaf5e6bd58e52b86bd129d
This commit is contained in:
Tamás Zolnai 2018-06-07 13:13:36 +02:00
parent 57cdd68fcf
commit 15afe2c0fb
6 changed files with 137 additions and 31 deletions

View file

@ -1436,6 +1436,9 @@ L.TileLayer = L.GridLayer.extend({
tile.el.src = img;
}
L.Log.log(textMsg, L.INCOMING, key);
// Send acknowledgment, that the tile message arrived
this._map._socket.sendMessage('tileprocessed tile= ' + key);
},
_tileOnLoad: function (done, tile) {

View file

@ -54,7 +54,8 @@ ClientSession::ClientSession(const std::string& id,
_tileWidthPixel(0),
_tileHeightPixel(0),
_tileWidthTwips(0),
_tileHeightTwips(0)
_tileHeightTwips(0),
_tilesOnFly(0)
{
assert(!creatingPngThumbnail || thumbnailFile != "");
const size_t curConnections = ++LOOLWSD::NumConnections;
@ -138,6 +139,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
return loadDocument(buffer, length, tokens, docBroker);
}
else if (tokens[0] != "canceltiles" &&
tokens[0] != "tileprocessed" &&
tokens[0] != "clientzoom" &&
tokens[0] != "clientvisiblearea" &&
tokens[0] != "outlinestate" &&
@ -329,6 +331,13 @@ bool ClientSession::_handleInput(const char *buffer, int length)
return forwardToChild(std::string(buffer, length), docBroker);
}
}
else if (tokens[0] == "tileprocessed")
{
if(_tilesOnFly > 0) // canceltiles message can zero this value
--_tilesOnFly;
docBroker->sendRequestedTiles(shared_from_this());
return true;
}
else
{
if (tokens[0] == "key")

View file

@ -17,9 +17,11 @@
#include "DocumentBroker.hpp"
#include <Poco/URI.h>
#include <Rectangle.hpp>
#include <boost/optional.hpp>
class DocumentBroker;
/// Represents a session to a LOOL client, in the WSD process.
class ClientSession final : public Session, public std::enable_shared_from_this<ClientSession>
{
@ -107,6 +109,12 @@ public:
/// Set WOPI fileinfo object
void setWopiFileInfo(std::unique_ptr<WopiStorage::WOPIFileInfo>& wopiFileInfo) { _wopiFileInfo = std::move(wopiFileInfo); }
boost::optional<TileCombined>& getRequestedTiles() { return _requestedTiles; }
int getTilesOnFly() const { return _tilesOnFly; }
void setTilesOnFly(int tilesOnFly) { _tilesOnFly = tilesOnFly; }
private:
/// SocketHandler: disconnection event.
@ -195,8 +203,13 @@ private:
// Type of the docuemnt, extracter from status message
std::string _docType;
int _tilesOnFly;
boost::optional<TileCombined> _requestedTiles;
};
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -1290,61 +1290,136 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
LOG_TRC("TileCombined request for " << tileCombined.serialize());
// Satisfy as many tiles from the cache.
std::vector<TileDesc> tiles;
// Check which newly requested tiles needs rendering.
std::vector<TileDesc> tilesNeedsRendering;
for (auto& tile : tileCombined.getTiles())
{
std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(tile);
if (cachedTile)
{
//TODO: Combine the response to reduce latency.
#if ENABLE_DEBUG
const std::string response = tile.serialize("tile:") + " renderid=cached\n";
#else
const std::string response = tile.serialize("tile:") + "\n";
#endif
std::vector<char> output;
output.reserve(static_cast<size_t>(4) * tile.getWidth() * tile.getHeight());
output.resize(response.size());
std::memcpy(output.data(), response.data(), response.size());
assert(cachedTile->is_open());
cachedTile->seekg(0, std::ios_base::end);
const size_t pos = output.size();
std::streamsize size = cachedTile->tellg();
output.resize(pos + size);
cachedTile->seekg(0, std::ios_base::beg);
cachedTile->read(output.data() + pos, size);
if(cachedTile)
cachedTile->close();
session->sendBinaryFrame(output.data(), output.size());
}
else
{
// Not cached, needs rendering.
tile.setVersion(++_tileVersion);
tileCache().subscribeToTileRendering(tile, session);
tiles.push_back(tile);
tilesNeedsRendering.push_back(tile);
_debugRenderedTileCount++;
}
}
if (!tiles.empty())
// Send rendering request
if (!tilesNeedsRendering.empty())
{
TileCombined newTileCombined = TileCombined::create(tiles);
TileCombined newTileCombined = TileCombined::create(tilesNeedsRendering);
// Forward to child to render.
const std::string req = newTileCombined.serialize("tilecombine");
LOG_DBG("Sending residual tilecombine: " << req);
_childProcess->sendTextFrame(req);
}
// Accumulate tiles
boost::optional<TileCombined>& requestedTiles = session->getRequestedTiles();
if(requestedTiles == boost::none)
{
requestedTiles = TileCombined::create(tileCombined.getTiles());
}
// Drop duplicated tiles, but use newer version number
else
{
for (const auto& newTile : tileCombined.getTiles())
{
const TileDesc& firstOldTile = requestedTiles.get().getTiles()[0];
if(newTile.getPart() != firstOldTile.getPart() ||
newTile.getWidth() != firstOldTile.getWidth() ||
newTile.getHeight() != firstOldTile.getHeight() ||
newTile.getTileWidth() != firstOldTile.getTileWidth() ||
newTile.getTileHeight() != firstOldTile.getTileHeight() )
{
LOG_WRN("Different visible area information in tile requests");
}
bool tileFound = false;
for (auto& oldTile : requestedTiles.get().getTiles())
{
if(oldTile.getTilePosX() == newTile.getTilePosX() &&
oldTile.getTilePosY() == newTile.getTilePosY() )
{
oldTile.setVersion(newTile.getVersion());
oldTile.setOldWireId(newTile.getOldWireId());
oldTile.setWireId(newTile.getWireId());
tileFound = true;
break;
}
}
if(!tileFound)
requestedTiles.get().getTiles().push_back(newTile);
}
}
lock.unlock();
lock.release();
sendRequestedTiles(session);
}
void DocumentBroker::sendRequestedTiles(const std::shared_ptr<ClientSession>& session)
{
assert(session->getTilesOnFly() >= 0);
std::unique_lock<std::mutex> lock(_mutex);
// All tiles were processed on client side what we sent last time, so we can send a new banch of tiles
// which was invalidated / requested in the meantime
boost::optional<TileCombined>& requestedTiles = session->getRequestedTiles();
if(session->getTilesOnFly() == 0 && requestedTiles != boost::none && !requestedTiles.get().getTiles().empty())
{
session->setTilesOnFly(requestedTiles.get().getTiles().size());
// Satisfy as many tiles from the cache.
for (auto& tile : requestedTiles.get().getTiles())
{
std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(tile);
if (cachedTile)
{
//TODO: Combine the response to reduce latency.
#if ENABLE_DEBUG
const std::string response = tile.serialize("tile:") + " renderid=cached\n";
#else
const std::string response = tile.serialize("tile:") + "\n";
#endif
std::vector<char> output;
output.reserve(static_cast<size_t>(4) * tile.getWidth() * tile.getHeight());
output.resize(response.size());
std::memcpy(output.data(), response.data(), response.size());
assert(cachedTile->is_open());
cachedTile->seekg(0, std::ios_base::end);
const auto pos = output.size();
std::streamsize size = cachedTile->tellg();
output.resize(pos + size);
cachedTile->seekg(0, std::ios_base::beg);
cachedTile->read(output.data() + pos, size);
cachedTile->close();
session->sendBinaryFrame(output.data(), output.size());
}
else
{
// Not cached, needs rendering. Rendering request was already sent
tileCache().subscribeToTileRendering(tile, session);
}
}
requestedTiles = boost::none;
}
}
void DocumentBroker::cancelTileRequests(const std::shared_ptr<ClientSession>& session)
{
std::unique_lock<std::mutex> lock(_mutex);
// Clear tile requests
session->setTilesOnFly(0);
session->getRequestedTiles() = boost::none;
const std::string canceltiles = tileCache().cancelTiles(session);
if (!canceltiles.empty())
{

View file

@ -301,6 +301,7 @@ public:
void handleDialogRequest(const std::string& dialogCmd);
void handleTileCombinedRequest(TileCombined& tileCombined,
const std::shared_ptr<ClientSession>& session);
void sendRequestedTiles(const std::shared_ptr<ClientSession>& session);
void cancelTileRequests(const std::shared_ptr<ClientSession>& session);
void handleTileResponse(const std::vector<char>& payload);
void handleDialogPaintResponse(const std::vector<char>& payload, bool child);

View file

@ -31,6 +31,11 @@ canceltiles
parameter. There is no guarantee of exactly which tile: messages
might still be sent back to the client.
tileprocessed tile=<tileid>
Previously sent tile (server -> client) arrived and processed by the client.
Tileid has the next stucture : <tile x coord>:<tile y coord>:<zoom level>:<selected part>
downloadas name=<fileName> id=<id> format=<document format> options=<SkipImages, etc>
Exports the current document to the desired format and returns a download URL