CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/FileLoaders/RamCachingFileLoader.cpp
Views: 1401
// Copyright (c) 2015- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include <thread>19#include <cstring>20#include <cstdlib>2122#include "Common/Thread/ThreadUtil.h"23#include "Common/TimeUtil.h"24#include "Core/FileLoaders/RamCachingFileLoader.h"2526#include "Common/Log.h"2728// Takes ownership of backend.29RamCachingFileLoader::RamCachingFileLoader(FileLoader *backend)30: ProxiedFileLoader(backend) {31filesize_ = backend->FileSize();32if (filesize_ > 0) {33InitCache();34}35}3637RamCachingFileLoader::~RamCachingFileLoader() {38if (filesize_ > 0) {39ShutdownCache();40}41}4243bool RamCachingFileLoader::Exists() {44if (exists_ == -1) {45exists_ = ProxiedFileLoader::Exists() ? 1 : 0;46}47return exists_ == 1;48}4950bool RamCachingFileLoader::ExistsFast() {51if (exists_ == -1) {52return ProxiedFileLoader::ExistsFast();53}54return exists_ == 1;55}5657bool RamCachingFileLoader::IsDirectory() {58if (isDirectory_ == -1) {59isDirectory_ = ProxiedFileLoader::IsDirectory() ? 1 : 0;60}61return isDirectory_ == 1;62}6364s64 RamCachingFileLoader::FileSize() {65return filesize_;66}6768size_t RamCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) {69size_t readSize = 0;70if (cache_ == nullptr || (flags & Flags::HINT_UNCACHED) != 0) {71readSize = backend_->ReadAt(absolutePos, bytes, data, flags);72} else {73readSize = ReadFromCache(absolutePos, bytes, data);74// While in case the cache size is too small for the entire read.75while (readSize < bytes) {76SaveIntoCache(absolutePos + readSize, bytes - readSize, flags);77size_t bytesFromCache = ReadFromCache(absolutePos + readSize, bytes - readSize, (u8 *)data + readSize);78readSize += bytesFromCache;79if (bytesFromCache == 0) {80// We can't read any more.81break;82}83}8485StartReadAhead(absolutePos + readSize);86}87return readSize;88}8990void RamCachingFileLoader::InitCache() {91std::lock_guard<std::mutex> guard(blocksMutex_);92u32 blockCount = (u32)((filesize_ + BLOCK_SIZE - 1) >> BLOCK_SHIFT);93// Overallocate for the last block.94cache_ = (u8 *)malloc((size_t)blockCount << BLOCK_SHIFT);95if (cache_ == nullptr) {96return;97}98aheadRemaining_ = blockCount;99blocks_.resize(blockCount);100}101102void RamCachingFileLoader::ShutdownCache() {103Cancel();104105// We can't delete while the thread is running, so have to wait.106// This should only happen from the menu.107while (aheadThreadRunning_) {108sleep_ms(1);109}110if (aheadThread_.joinable())111aheadThread_.join();112113std::lock_guard<std::mutex> guard(blocksMutex_);114blocks_.clear();115if (cache_ != nullptr) {116free(cache_);117cache_ = nullptr;118}119}120121void RamCachingFileLoader::Cancel() {122if (aheadThreadRunning_) {123std::lock_guard<std::mutex> guard(blocksMutex_);124aheadCancel_ = true;125}126127ProxiedFileLoader::Cancel();128}129130size_t RamCachingFileLoader::ReadFromCache(s64 pos, size_t bytes, void *data) {131s64 cacheStartPos = pos >> BLOCK_SHIFT;132s64 cacheEndPos = (pos + bytes - 1) >> BLOCK_SHIFT;133if ((size_t)cacheEndPos >= blocks_.size()) {134cacheEndPos = blocks_.size() - 1;135}136137size_t readSize = 0;138size_t offset = (size_t)(pos - (cacheStartPos << BLOCK_SHIFT));139u8 *p = (u8 *)data;140141// Clamp bytes to what's actually available.142if (pos + (s64)bytes > filesize_) {143// Should've been caught above, but just in case.144if (pos >= filesize_) {145return 0;146}147bytes = (size_t)(filesize_ - pos);148}149150std::lock_guard<std::mutex> guard(blocksMutex_);151for (s64 i = cacheStartPos; i <= cacheEndPos; ++i) {152if (blocks_[(size_t)i] == 0) {153return readSize;154}155156size_t toRead = std::min(bytes - readSize, (size_t)BLOCK_SIZE - offset);157s64 cachePos = (i << BLOCK_SHIFT) + offset;158memcpy(p + readSize, &cache_[cachePos], toRead);159readSize += toRead;160161// Don't need an offset after the first read.162offset = 0;163}164return readSize;165}166167void RamCachingFileLoader::SaveIntoCache(s64 pos, size_t bytes, Flags flags) {168s64 cacheStartPos = pos >> BLOCK_SHIFT;169s64 cacheEndPos = (pos + bytes - 1) >> BLOCK_SHIFT;170if ((size_t)cacheEndPos >= blocks_.size()) {171cacheEndPos = blocks_.size() - 1;172}173174size_t blocksToRead = 0;175{176std::lock_guard<std::mutex> guard(blocksMutex_);177for (s64 i = cacheStartPos; i <= cacheEndPos; ++i) {178if (blocks_[(size_t)i] == 0) {179++blocksToRead;180if (blocksToRead >= MAX_BLOCKS_PER_READ) {181break;182}183}184}185}186187s64 cacheFilePos = cacheStartPos << BLOCK_SHIFT;188size_t bytesRead = backend_->ReadAt(cacheFilePos, blocksToRead << BLOCK_SHIFT, &cache_[cacheFilePos], flags);189190// In case there was an error, let's not mark blocks that failed to read as read.191u32 blocksActuallyRead = (u32)((bytesRead + BLOCK_SIZE - 1) >> BLOCK_SHIFT);192{193std::lock_guard<std::mutex> guard(blocksMutex_);194195// In case they were simultaneously read.196u32 blocksRead = 0;197for (size_t i = 0; i < blocksActuallyRead; ++i) {198if (blocks_[(size_t)cacheStartPos + i] == 0) {199blocks_[(size_t)cacheStartPos + i] = 1;200++blocksRead;201}202}203204if (aheadRemaining_ != 0) {205aheadRemaining_ -= blocksRead;206}207}208}209210void RamCachingFileLoader::StartReadAhead(s64 pos) {211if (cache_ == nullptr) {212return;213}214215std::lock_guard<std::mutex> guard(blocksMutex_);216aheadPos_ = pos;217if (aheadThreadRunning_) {218// Already going.219return;220}221222aheadThreadRunning_ = true;223aheadCancel_ = false;224if (aheadThread_.joinable())225aheadThread_.join();226aheadThread_ = std::thread([this] {227SetCurrentThreadName("FileLoaderReadAhead");228229AndroidJNIThreadContext jniContext;230231while (aheadRemaining_ != 0 && !aheadCancel_) {232// Where should we look?233const u32 cacheStartPos = NextAheadBlock();234if (cacheStartPos == 0xFFFFFFFF) {235// Must be full.236break;237}238u32 cacheEndPos = cacheStartPos + BLOCK_READAHEAD - 1;239if (cacheEndPos >= blocks_.size()) {240cacheEndPos = (u32)blocks_.size() - 1;241}242243for (u32 i = cacheStartPos; i <= cacheEndPos; ++i) {244if (blocks_[i] == 0) {245SaveIntoCache((u64)i << BLOCK_SHIFT, BLOCK_SIZE * BLOCK_READAHEAD, Flags::NONE);246break;247}248}249}250251aheadThreadRunning_ = false;252});253}254255u32 RamCachingFileLoader::NextAheadBlock() {256std::lock_guard<std::mutex> guard(blocksMutex_);257258// If we had an aheadPos_ set, start reading from there and go forward.259u32 startFrom = (u32)(aheadPos_ >> BLOCK_SHIFT);260// But next time, start from the beginning again.261aheadPos_ = 0;262263for (u32 i = startFrom; i < blocks_.size(); ++i) {264if (blocks_[i] == 0) {265return i;266}267}268269return 0xFFFFFFFF;270}271272273