void TileCache::saveTileAndNotify(const TileDesc& tile, const char *data, const size_t size, const bool priority) { std::unique_lock<std::mutex> lock(_tilesBeingRenderedMutex); std::shared_ptr<TileBeingRendered> tileBeingRendered = findTileBeingRendered(tile); if (!priority && tileBeingRendered && tileBeingRendered->getVersion() != tile.getVersion()) { Log::trace() << "Skipping unexpected tile ver: " << tile.getVersion() << ", waiting for ver " << tileBeingRendered->getVersion() << Log::end; return; } // Save to disk. const auto cachedName = (tileBeingRendered ? tileBeingRendered->getCacheName() : cacheFileName(tile)); const auto fileName = _cacheDir + "/" + cachedName; Log::trace() << "Saving cache tile: " << fileName << Log::end; std::fstream outStream(fileName, std::ios::out); outStream.write(data, size); outStream.close(); // Notify subscribers, if any. if (tileBeingRendered) { if (!tileBeingRendered->_subscribers.empty()) { const std::string message = tile.serialize("tile"); Log::debug("Sending tile message to subscribers: " + message); for (const auto& i: tileBeingRendered->_subscribers) { auto subscriber = i.lock(); if (subscriber) { //FIXME: This is inefficient; should just send directly to each client (although that is risky as well! // Re-emit the tile command in the other thread(s) to re-check and hit // the cache. Construct the message from scratch to contain only the // mandatory parts of the message. subscriber->sendToInputQueue(message); } } } // Remove subscriptions. if (tileBeingRendered->getVersion() == tile.getVersion()) { Log::debug() << "STATISTICS: tile internal roundtrip " << tileBeingRendered->getElapsedTimeMs() << " ms." << Log::end; _tilesBeingRendered.erase(cachedName); } } }
void TileCache::notifyAndRemoveSubscribers(const TileDesc& tile) { std::unique_lock<std::mutex> lock(_tilesBeingRenderedMutex); std::shared_ptr<TileBeingRendered> tileBeingRendered = findTileBeingRendered(tile); if (!tileBeingRendered) return; const std::string message = tile.serialize("tile"); Log::debug("Sending tile message to subscribers: " + message); for (const auto& i: tileBeingRendered->_subscribers) { auto subscriber = i.lock(); if (subscriber) { //FIXME: This is inefficient; should just send directly to each client (although that is risky as well! // Re-emit the tile command in the other thread(s) to re-check and hit // the cache. Construct the message from scratch to contain only the // mandatory parts of the message. subscriber->sendToInputQueue(message); } } forgetTileBeingRendered(tile); }
std::string TileCache::cacheFileName(const TileDesc& tile) { std::ostringstream oss; oss << tile.getPart() << '_' << tile.getWidth() << 'x' << tile.getHeight() << '.' << tile.getTilePosX() << ',' << tile.getTilePosY() << '.' << tile.getTileWidth() << 'x' << tile.getTileHeight() << ".png"; return oss.str(); }
// FIXME: to be further simplified when we centralize tile messages. int TileCache::isTileBeingRenderedIfSoSubscribe(const TileDesc& tile, const std::shared_ptr<ClientSession> &subscriber) { std::unique_lock<std::mutex> lock(_tilesBeingRenderedMutex); std::shared_ptr<TileBeingRendered> tileBeingRendered = findTileBeingRendered(tile); if (tileBeingRendered) { Log::debug() << "Tile (" << tile.getPart() << ',' << tile.getTilePosX() << ',' << tile.getTilePosY() << ") is already being rendered, subscribing." << Log::end; assert(subscriber->getKind() == LOOLSession::Kind::ToClient); for (const auto &s : tileBeingRendered->_subscribers) { if (s.lock().get() == subscriber.get()) { Log::debug("Redundant request to re-subscribe on a tile"); return 0; } } tileBeingRendered->_subscribers.push_back(subscriber); const auto duration = (std::chrono::steady_clock::now() - tileBeingRendered->getStartTime()); if (std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() > COMMAND_TIMEOUT_MS) { // Tile painting has stalled. Reissue. return tileBeingRendered->getVersion(); } return 0; } else { Log::debug() << "Tile (" << tile.getPart() << ',' << tile.getTilePosX() << ',' << tile.getTilePosY() << ") needs rendering, subscribing for ver: " << tile.getVersion() << "." << Log::end; const std::string cachedName = cacheFileName(tile); assert(_tilesBeingRendered.find(cachedName) == _tilesBeingRendered.end()); tileBeingRendered = std::make_shared<TileBeingRendered>(cachedName, tile.getVersion()); tileBeingRendered->_subscribers.push_back(subscriber); _tilesBeingRendered[cachedName] = tileBeingRendered; return tileBeingRendered->getVersion(); } }
std::unique_ptr<std::fstream> TileCache::lookupTile(const TileDesc& tile) { const std::string fileName = _cacheDir + "/" + cacheFileName(tile); std::unique_ptr<std::fstream> result(new std::fstream(fileName, std::ios::in)); UnitWSD::get().lookupTile(tile.getPart(), tile.getWidth(), tile.getHeight(), tile.getTilePosX(), tile.getTilePosY(), tile.getTileWidth(), tile.getTileHeight(), result); if (result && result->is_open()) { Log::trace("Found cache tile: " + fileName); return result; } return nullptr; }