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