• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/disk_cache/blockfile/file.h"
6 
7 #include <limits.h>
8 #include <stdint.h>
9 
10 #include <limits>
11 #include <utility>
12 
13 #include "base/check.h"
14 #include "base/compiler_specific.h"
15 #include "base/functional/bind.h"
16 #include "base/location.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/task/thread_pool.h"
19 #include "net/base/net_errors.h"
20 #include "net/disk_cache/blockfile/in_flight_io.h"
21 #include "net/disk_cache/disk_cache.h"
22 
23 namespace {
24 
25 // This class represents a single asynchronous IO operation while it is being
26 // bounced between threads.
27 class FileBackgroundIO : public disk_cache::BackgroundIO {
28  public:
29   // Other than the actual parameters for the IO operation (including the
30   // |callback| that must be notified at the end), we need the controller that
31   // is keeping track of all operations. When done, we notify the controller
32   // (we do NOT invoke the callback), in the worker thead that completed the
33   // operation.
FileBackgroundIO(disk_cache::File * file,const void * buf,size_t buf_len,size_t offset,disk_cache::FileIOCallback * callback,disk_cache::InFlightIO * controller)34   FileBackgroundIO(disk_cache::File* file, const void* buf, size_t buf_len,
35                    size_t offset, disk_cache::FileIOCallback* callback,
36                    disk_cache::InFlightIO* controller)
37       : disk_cache::BackgroundIO(controller), callback_(callback), file_(file),
38         buf_(buf), buf_len_(buf_len), offset_(offset) {
39   }
40 
41   FileBackgroundIO(const FileBackgroundIO&) = delete;
42   FileBackgroundIO& operator=(const FileBackgroundIO&) = delete;
43 
callback()44   disk_cache::FileIOCallback* callback() {
45     return callback_;
46   }
47 
file()48   disk_cache::File* file() {
49     return file_;
50   }
51 
52   // Read and Write are the operations that can be performed asynchronously.
53   // The actual parameters for the operation are setup in the constructor of
54   // the object. Both methods should be called from a worker thread, by posting
55   // a task to the WorkerPool (they are RunnableMethods). When finished,
56   // controller->OnIOComplete() is called.
57   void Read();
58   void Write();
59 
60  private:
~FileBackgroundIO()61   ~FileBackgroundIO() override {}
62 
63   raw_ptr<disk_cache::FileIOCallback> callback_;
64 
65   raw_ptr<disk_cache::File> file_;
66   raw_ptr<const void> buf_;
67   size_t buf_len_;
68   size_t offset_;
69 };
70 
71 
72 // The specialized controller that keeps track of current operations.
73 class FileInFlightIO : public disk_cache::InFlightIO {
74  public:
75   FileInFlightIO() = default;
76 
77   FileInFlightIO(const FileInFlightIO&) = delete;
78   FileInFlightIO& operator=(const FileInFlightIO&) = delete;
79 
80   ~FileInFlightIO() override = default;
81 
82   // These methods start an asynchronous operation. The arguments have the same
83   // semantics of the File asynchronous operations, with the exception that the
84   // operation never finishes synchronously.
85   void PostRead(disk_cache::File* file, void* buf, size_t buf_len,
86                 size_t offset, disk_cache::FileIOCallback* callback);
87   void PostWrite(disk_cache::File* file, const void* buf, size_t buf_len,
88                  size_t offset, disk_cache::FileIOCallback* callback);
89 
90  protected:
91   // Invokes the users' completion callback at the end of the IO operation.
92   // |cancel| is true if the actual task posted to the thread is still
93   // queued (because we are inside WaitForPendingIO), and false if said task is
94   // the one performing the call.
95   void OnOperationComplete(disk_cache::BackgroundIO* operation,
96                            bool cancel) override;
97 };
98 
99 // ---------------------------------------------------------------------------
100 
101 // Runs on a worker thread.
Read()102 void FileBackgroundIO::Read() {
103   if (file_->Read(const_cast<void*>(buf_.get()), buf_len_, offset_)) {
104     result_ = static_cast<int>(buf_len_);
105   } else {
106     result_ = net::ERR_CACHE_READ_FAILURE;
107   }
108   NotifyController();
109 }
110 
111 // Runs on a worker thread.
Write()112 void FileBackgroundIO::Write() {
113   bool rv = file_->Write(buf_, buf_len_, offset_);
114 
115   result_ = rv ? static_cast<int>(buf_len_) : net::ERR_CACHE_WRITE_FAILURE;
116   NotifyController();
117 }
118 
119 // ---------------------------------------------------------------------------
120 
PostRead(disk_cache::File * file,void * buf,size_t buf_len,size_t offset,disk_cache::FileIOCallback * callback)121 void FileInFlightIO::PostRead(disk_cache::File *file, void* buf, size_t buf_len,
122                           size_t offset, disk_cache::FileIOCallback *callback) {
123   auto operation = base::MakeRefCounted<FileBackgroundIO>(
124       file, buf, buf_len, offset, callback, this);
125   file->AddRef();  // Balanced on OnOperationComplete()
126 
127   base::ThreadPool::PostTask(
128       FROM_HERE,
129       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
130       base::BindOnce(&FileBackgroundIO::Read, operation.get()));
131   OnOperationPosted(operation.get());
132 }
133 
PostWrite(disk_cache::File * file,const void * buf,size_t buf_len,size_t offset,disk_cache::FileIOCallback * callback)134 void FileInFlightIO::PostWrite(disk_cache::File* file, const void* buf,
135                            size_t buf_len, size_t offset,
136                            disk_cache::FileIOCallback* callback) {
137   auto operation = base::MakeRefCounted<FileBackgroundIO>(
138       file, buf, buf_len, offset, callback, this);
139   file->AddRef();  // Balanced on OnOperationComplete()
140 
141   base::ThreadPool::PostTask(
142       FROM_HERE,
143       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
144       base::BindOnce(&FileBackgroundIO::Write, operation.get()));
145   OnOperationPosted(operation.get());
146 }
147 
148 // Runs on the IO thread.
OnOperationComplete(disk_cache::BackgroundIO * operation,bool cancel)149 void FileInFlightIO::OnOperationComplete(disk_cache::BackgroundIO* operation,
150                                          bool cancel) {
151   FileBackgroundIO* op = static_cast<FileBackgroundIO*>(operation);
152 
153   disk_cache::FileIOCallback* callback = op->callback();
154   int bytes = operation->result();
155 
156   // Release the references acquired in PostRead / PostWrite.
157   op->file()->Release();
158   callback->OnFileIOComplete(bytes);
159 }
160 
161 // A static object that will broker all async operations.
162 FileInFlightIO* s_file_operations = nullptr;
163 
164 // Returns the current FileInFlightIO.
GetFileInFlightIO()165 FileInFlightIO* GetFileInFlightIO() {
166   if (!s_file_operations) {
167     s_file_operations = new FileInFlightIO;
168   }
169   return s_file_operations;
170 }
171 
172 // Deletes the current FileInFlightIO.
DeleteFileInFlightIO()173 void DeleteFileInFlightIO() {
174   DCHECK(s_file_operations);
175   delete s_file_operations;
176   s_file_operations = nullptr;
177 }
178 
179 }  // namespace
180 
181 namespace disk_cache {
182 
File(base::File file)183 File::File(base::File file)
184     : init_(true), mixed_(true), base_file_(std::move(file)) {}
185 
Init(const base::FilePath & name)186 bool File::Init(const base::FilePath& name) {
187   if (base_file_.IsValid())
188     return false;
189 
190   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
191               base::File::FLAG_WRITE;
192   base_file_.Initialize(name, flags);
193   return base_file_.IsValid();
194 }
195 
IsValid() const196 bool File::IsValid() const {
197   return base_file_.IsValid();
198 }
199 
Read(void * buffer,size_t buffer_len,size_t offset)200 bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
201   DCHECK(base_file_.IsValid());
202   if (buffer_len > static_cast<size_t>(std::numeric_limits<int32_t>::max()) ||
203       offset > static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
204     return false;
205   }
206 
207   int ret = UNSAFE_TODO(
208       base_file_.Read(offset, static_cast<char*>(buffer), buffer_len));
209   return (static_cast<size_t>(ret) == buffer_len);
210 }
211 
Write(const void * buffer,size_t buffer_len,size_t offset)212 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
213   DCHECK(base_file_.IsValid());
214   if (buffer_len > static_cast<size_t>(std::numeric_limits<int32_t>::max()) ||
215       offset > static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
216     return false;
217   }
218 
219   int ret = UNSAFE_TODO(
220       base_file_.Write(offset, static_cast<const char*>(buffer), buffer_len));
221   return (static_cast<size_t>(ret) == buffer_len);
222 }
223 
224 // We have to increase the ref counter of the file before performing the IO to
225 // prevent the completion to happen with an invalid handle (if the file is
226 // closed while the IO is in flight).
Read(void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)227 bool File::Read(void* buffer, size_t buffer_len, size_t offset,
228                 FileIOCallback* callback, bool* completed) {
229   DCHECK(base_file_.IsValid());
230   if (!callback) {
231     if (completed)
232       *completed = true;
233     return Read(buffer, buffer_len, offset);
234   }
235 
236   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
237     return false;
238 
239   GetFileInFlightIO()->PostRead(this, buffer, buffer_len, offset, callback);
240 
241   *completed = false;
242   return true;
243 }
244 
Write(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)245 bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
246                  FileIOCallback* callback, bool* completed) {
247   DCHECK(base_file_.IsValid());
248   if (!callback) {
249     if (completed)
250       *completed = true;
251     return Write(buffer, buffer_len, offset);
252   }
253 
254   return AsyncWrite(buffer, buffer_len, offset, callback, completed);
255 }
256 
SetLength(size_t length)257 bool File::SetLength(size_t length) {
258   DCHECK(base_file_.IsValid());
259   if (length > std::numeric_limits<uint32_t>::max())
260     return false;
261 
262   return base_file_.SetLength(length);
263 }
264 
GetLength()265 size_t File::GetLength() {
266   DCHECK(base_file_.IsValid());
267   int64_t len = base_file_.GetLength();
268 
269   if (len < 0)
270     return 0;
271   if (len > static_cast<int64_t>(std::numeric_limits<uint32_t>::max()))
272     return std::numeric_limits<uint32_t>::max();
273 
274   return static_cast<size_t>(len);
275 }
276 
277 // Static.
WaitForPendingIOForTesting(int * num_pending_io)278 void File::WaitForPendingIOForTesting(int* num_pending_io) {
279   // We may be running unit tests so we should allow be able to reset the
280   // message loop.
281   GetFileInFlightIO()->WaitForPendingIO();
282   DeleteFileInFlightIO();
283 }
284 
285 // Static.
DropPendingIO()286 void File::DropPendingIO() {
287   GetFileInFlightIO()->DropPendingIO();
288   DeleteFileInFlightIO();
289 }
290 
291 File::~File() = default;
292 
platform_file() const293 base::PlatformFile File::platform_file() const {
294   return base_file_.GetPlatformFile();
295 }
296 
AsyncWrite(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)297 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
298                       FileIOCallback* callback, bool* completed) {
299   DCHECK(base_file_.IsValid());
300   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
301     return false;
302 
303   GetFileInFlightIO()->PostWrite(this, buffer, buffer_len, offset, callback);
304 
305   if (completed)
306     *completed = false;
307   return true;
308 }
309 
310 }  // namespace disk_cache
311