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