mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-13 09:54:32 +00:00
FileSystem: Add LockedFile helper class
This commit is contained in:
@@ -1464,6 +1464,98 @@ s64 FileSystem::GetPathFileSize(const char* path)
|
||||
return sd.Size;
|
||||
}
|
||||
|
||||
FileSystem::LockedFile FileSystem::OpenLockedFile(const char* path, bool for_write, Error* error /* = nullptr */)
|
||||
{
|
||||
static constexpr u32 DEFAULT_FILE_LOCK_TIMEOUT = 100;
|
||||
return OpenLockedFile(path, for_write, DEFAULT_FILE_LOCK_TIMEOUT, error);
|
||||
}
|
||||
|
||||
FileSystem::LockedFile FileSystem::OpenLockedFile(const char* path, bool for_write, u32 timeout_ms, Error* error)
|
||||
{
|
||||
const FileSystem::FileShareMode share_mode =
|
||||
for_write ? FileSystem::FileShareMode::DenyReadWrite : FileSystem::FileShareMode::DenyWrite;
|
||||
#ifdef _WIN32
|
||||
const char* mode = for_write ? "r+b" : "rb";
|
||||
#else
|
||||
// Always open read/write on Linux, since we need it for flock().
|
||||
const char* mode = "r+b";
|
||||
#endif
|
||||
|
||||
std::FILE* fp = FileSystem::OpenSharedCFile(path, mode, share_mode, error);
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
// Doesn't exist? Create it.
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (!for_write)
|
||||
return {};
|
||||
|
||||
mode = "w+b";
|
||||
fp = FileSystem::OpenSharedCFile(path, mode, share_mode, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
// If there's a sharing violation, try again for 100ms.
|
||||
if (errno != EACCES)
|
||||
return {};
|
||||
|
||||
Timer timer;
|
||||
while (timer.GetTimeMilliseconds() <= static_cast<float>(timeout_ms))
|
||||
{
|
||||
fp = FileSystem::OpenSharedCFile(path, mode, share_mode, error);
|
||||
if (fp)
|
||||
break;
|
||||
|
||||
if (errno != EACCES)
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
Error::SetStringFmt(error, "Timed out while trying to open file", Path::GetFileTitle(path));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Error lock_error;
|
||||
LockedFile ret(fp, &lock_error);
|
||||
if (!ret.IsLocked())
|
||||
ERROR_LOG("Failed to lock file {}: {}", Path::GetFileTitle(path), lock_error.GetDescription());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FileSystem::LockedFile::LockedFile(std::FILE* fp, Error* lock_error)
|
||||
: ManagedCFilePtr(fp)
|
||||
#ifdef HAS_POSIX_FILE_LOCK
|
||||
,
|
||||
m_lock(fp, true, lock_error)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
std::FILE* FileSystem::LockedFile::release()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef HAS_POSIX_FILE_LOCK
|
||||
|
||||
void FileSystem::LockedFile::reset()
|
||||
{
|
||||
// avoid race where the file isn't flushed before it's unlocked
|
||||
if (*this)
|
||||
std::fflush(get());
|
||||
|
||||
m_lock.Unlock();
|
||||
ManagedCFilePtr::reset();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::optional<DynamicHeapArray<u8>> FileSystem::ReadBinaryFile(const char* path, Error* error)
|
||||
{
|
||||
std::optional<DynamicHeapArray<u8>> ret;
|
||||
|
||||
@@ -183,6 +183,39 @@ private:
|
||||
|
||||
#endif
|
||||
|
||||
/// Provides a simple RAII wrapper around a locked file, where only a single process can have it open at a time.
|
||||
class LockedFile : public ManagedCFilePtr
|
||||
{
|
||||
public:
|
||||
LockedFile() = default;
|
||||
LockedFile(LockedFile&& move) = default;
|
||||
LockedFile(const LockedFile&) = delete;
|
||||
LockedFile(std::FILE* fp, Error* lock_error);
|
||||
|
||||
~LockedFile() = default;
|
||||
LockedFile& operator=(LockedFile&& move) = default;
|
||||
LockedFile& operator=(const LockedFile&) = delete;
|
||||
|
||||
#ifdef HAS_POSIX_FILE_LOCK
|
||||
ALWAYS_INLINE bool IsLocked() const { return m_lock.IsLocked(); }
|
||||
|
||||
void reset();
|
||||
|
||||
#else
|
||||
ALWAYS_INLINE bool IsLocked() const { return true; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Hide release(), it doesn't make sense here.
|
||||
std::FILE* release();
|
||||
|
||||
#ifdef HAS_POSIX_FILE_LOCK
|
||||
POSIXLock m_lock;
|
||||
#endif
|
||||
};
|
||||
LockedFile OpenLockedFile(const char* path, bool for_write, Error* error = nullptr);
|
||||
LockedFile OpenLockedFile(const char* path, bool for_write, u32 timeout_ms, Error* error);
|
||||
|
||||
std::optional<DynamicHeapArray<u8>> ReadBinaryFile(const char* path, Error* error = nullptr);
|
||||
std::optional<DynamicHeapArray<u8>> ReadBinaryFile(std::FILE* fp, Error* error = nullptr);
|
||||
std::optional<std::string> ReadFileToString(const char* path, Error* error = nullptr);
|
||||
|
||||
Reference in New Issue
Block a user