| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net/disk_cache/blockfile/file.h" | |
| #include <limits> | |
| #include <utility> | |
| #include "base/bind.h" | |
| #include "base/location.h" | |
| #include "base/logging.h" | |
| #include "base/run_loop.h" | |
| #include "base/task/post_task.h" | |
| #include "base/task/task_scheduler/task_scheduler.h" | |
| #include "net/base/net_errors.h" | |
| #include "net/disk_cache/disk_cache.h" | |
| #include "starboard/types.h" | |
| namespace disk_cache { | |
| File::File(base::File file) | |
| : init_(true), mixed_(true), base_file_(std::move(file)) {} | |
| bool File::Init(const base::FilePath& name) { | |
| if (base_file_.IsValid()) | |
| return false; | |
| int flags = base::File::FLAG_OPEN | base::File::FLAG_READ | | |
| base::File::FLAG_WRITE; | |
| base_file_.Initialize(name, flags); | |
| return base_file_.IsValid(); | |
| } | |
| bool File::IsValid() const { | |
| return base_file_.IsValid(); | |
| } | |
| bool File::Read(void* buffer, size_t buffer_len, size_t offset) { | |
| DCHECK(base_file_.IsValid()); | |
| if (buffer_len > static_cast<size_t>(std::numeric_limits<int32_t>::max()) || | |
| offset > static_cast<size_t>(std::numeric_limits<int32_t>::max())) { | |
| return false; | |
| } | |
| int ret = base_file_.Read(offset, static_cast<char*>(buffer), buffer_len); | |
| return (static_cast<size_t>(ret) == buffer_len); | |
| } | |
| bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { | |
| DCHECK(base_file_.IsValid()); | |
| if (buffer_len > static_cast<size_t>(std::numeric_limits<int32_t>::max()) || | |
| offset > static_cast<size_t>(std::numeric_limits<int32_t>::max())) { | |
| return false; | |
| } | |
| int ret = base_file_.Write(offset, static_cast<const char*>(buffer), | |
| buffer_len); | |
| return (static_cast<size_t>(ret) == buffer_len); | |
| } | |
| bool File::Read(void* buffer, size_t buffer_len, size_t offset, | |
| FileIOCallback* callback, bool* completed) { | |
| DCHECK(base_file_.IsValid()); | |
| if (!callback) { | |
| if (completed) | |
| *completed = true; | |
| return Read(buffer, buffer_len, offset); | |
| } | |
| if (buffer_len > static_cast<size_t>(std::numeric_limits<int32_t>::max()) || | |
| offset > static_cast<size_t>(std::numeric_limits<int32_t>::max())) { | |
| return false; | |
| } | |
| base::PostTaskWithTraitsAndReplyWithResult( | |
| FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()}, | |
| base::BindOnce(&File::DoRead, base::Unretained(this), buffer, buffer_len, | |
| offset), | |
| base::BindOnce(&File::OnOperationComplete, this, callback)); | |
| *completed = false; | |
| return true; | |
| } | |
| bool File::Write(const void* buffer, size_t buffer_len, size_t offset, | |
| FileIOCallback* callback, bool* completed) { | |
| DCHECK(base_file_.IsValid()); | |
| if (!callback) { | |
| if (completed) | |
| *completed = true; | |
| return Write(buffer, buffer_len, offset); | |
| } | |
| if (buffer_len > static_cast<size_t>(std::numeric_limits<int32_t>::max()) || | |
| offset > static_cast<size_t>(std::numeric_limits<int32_t>::max())) { | |
| return false; | |
| } | |
| // The priority is USER_BLOCKING because the cache waits for the write to | |
| // finish before it reads from the network again. | |
| // TODO(fdoray): Consider removing this from the critical path of network | |
| // requests and changing the priority to BACKGROUND. | |
| base::PostTaskWithTraitsAndReplyWithResult( | |
| FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()}, | |
| base::BindOnce(&File::DoWrite, base::Unretained(this), buffer, buffer_len, | |
| offset), | |
| base::BindOnce(&File::OnOperationComplete, this, callback)); | |
| *completed = false; | |
| return true; | |
| } | |
| bool File::SetLength(size_t length) { | |
| DCHECK(base_file_.IsValid()); | |
| if (length > std::numeric_limits<uint32_t>::max()) | |
| return false; | |
| return base_file_.SetLength(length); | |
| } | |
| size_t File::GetLength() { | |
| DCHECK(base_file_.IsValid()); | |
| int64_t len = base_file_.GetLength(); | |
| if (len < 0) | |
| return 0; | |
| if (len > static_cast<int64_t>(std::numeric_limits<uint32_t>::max())) | |
| return std::numeric_limits<uint32_t>::max(); | |
| return static_cast<size_t>(len); | |
| } | |
| // Static. | |
| void File::WaitForPendingIO(int* num_pending_io) { | |
| // We are running unit tests so we should wait for all callbacks. | |
| // This waits for callbacks running on worker threads. | |
| base::TaskScheduler::GetInstance()->FlushForTesting(); | |
| // This waits for the "Reply" tasks running on the current MessageLoop. | |
| base::RunLoop().RunUntilIdle(); | |
| } | |
| // Static. | |
| void File::DropPendingIO() { | |
| } | |
| File::~File() = default; | |
| base::PlatformFile File::platform_file() const { | |
| return base_file_.GetPlatformFile(); | |
| } | |
| // Runs on a worker thread. | |
| int File::DoRead(void* buffer, size_t buffer_len, size_t offset) { | |
| if (Read(const_cast<void*>(buffer), buffer_len, offset)) | |
| return static_cast<int>(buffer_len); | |
| return net::ERR_CACHE_READ_FAILURE; | |
| } | |
| // Runs on a worker thread. | |
| int File::DoWrite(const void* buffer, size_t buffer_len, size_t offset) { | |
| if (Write(const_cast<void*>(buffer), buffer_len, offset)) | |
| return static_cast<int>(buffer_len); | |
| return net::ERR_CACHE_WRITE_FAILURE; | |
| } | |
| // This method actually makes sure that the last reference to the file doesn't | |
| // go away on the worker pool. | |
| void File::OnOperationComplete(FileIOCallback* callback, int result) { | |
| callback->OnFileIOComplete(result); | |
| } | |
| } // namespace disk_cache |