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/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/disk_cache.h"
13 #include "net/disk_cache/in_flight_io.h"
14
15 namespace {
16
17 // This class represents a single asynchronous IO operation while it is being
18 // bounced between threads.
19 class 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.
FileBackgroundIO(disk_cache::File * file,const void * buf,size_t buf_len,size_t offset,disk_cache::FileIOCallback * callback,disk_cache::InFlightIO * controller)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
callback()33 disk_cache::FileIOCallback* callback() {
34 return callback_;
35 }
36
file()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:
~FileBackgroundIO()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.
64 class FileInFlightIO : public disk_cache::InFlightIO {
65 public:
FileInFlightIO()66 FileInFlightIO() {}
~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.
Read()92 void 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.
Write()102 void 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
PostRead(disk_cache::File * file,void * buf,size_t buf_len,size_t offset,disk_cache::FileIOCallback * callback)111 void 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
PostWrite(disk_cache::File * file,const void * buf,size_t buf_len,size_t offset,disk_cache::FileIOCallback * callback)122 void 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.
OnOperationComplete(disk_cache::BackgroundIO * operation,bool cancel)135 void 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 tha will broker all async operations.
148 FileInFlightIO* s_file_operations = NULL;
149
150 // Returns the current FileInFlightIO.
GetFileInFlightIO()151 FileInFlightIO* 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.
DeleteFileInFlightIO()159 void DeleteFileInFlightIO() {
160 DCHECK(s_file_operations);
161 delete s_file_operations;
162 s_file_operations = NULL;
163 }
164
165 } // namespace
166
167 namespace disk_cache {
168
File(base::PlatformFile file)169 File::File(base::PlatformFile file)
170 : init_(true),
171 mixed_(true),
172 platform_file_(file),
173 sync_platform_file_(base::kInvalidPlatformFileValue) {
174 }
175
Init(const base::FilePath & name)176 bool File::Init(const base::FilePath& name) {
177 if (init_)
178 return false;
179
180 int flags = base::PLATFORM_FILE_OPEN |
181 base::PLATFORM_FILE_READ |
182 base::PLATFORM_FILE_WRITE;
183 platform_file_ = base::CreatePlatformFile(name, flags, NULL, NULL);
184 if (platform_file_ < 0) {
185 platform_file_ = 0;
186 return false;
187 }
188
189 init_ = true;
190 return true;
191 }
192
platform_file() const193 base::PlatformFile File::platform_file() const {
194 return platform_file_;
195 }
196
IsValid() const197 bool File::IsValid() const {
198 if (!init_)
199 return false;
200 return (base::kInvalidPlatformFileValue != platform_file_);
201 }
202
Read(void * buffer,size_t buffer_len,size_t offset)203 bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
204 DCHECK(init_);
205 if (buffer_len > static_cast<size_t>(kint32max) ||
206 offset > static_cast<size_t>(kint32max)) {
207 return false;
208 }
209
210 int ret = base::ReadPlatformFile(platform_file_, offset,
211 static_cast<char*>(buffer), buffer_len);
212 return (static_cast<size_t>(ret) == buffer_len);
213 }
214
Write(const void * buffer,size_t buffer_len,size_t offset)215 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
216 DCHECK(init_);
217 if (buffer_len > static_cast<size_t>(kint32max) ||
218 offset > static_cast<size_t>(kint32max)) {
219 return false;
220 }
221
222 int ret = base::WritePlatformFile(platform_file_, offset,
223 static_cast<const char*>(buffer),
224 buffer_len);
225 return (static_cast<size_t>(ret) == buffer_len);
226 }
227
228 // We have to increase the ref counter of the file before performing the IO to
229 // prevent the completion to happen with an invalid handle (if the file is
230 // closed while the IO is in flight).
Read(void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)231 bool File::Read(void* buffer, size_t buffer_len, size_t offset,
232 FileIOCallback* callback, bool* completed) {
233 DCHECK(init_);
234 if (!callback) {
235 if (completed)
236 *completed = true;
237 return Read(buffer, buffer_len, offset);
238 }
239
240 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
241 return false;
242
243 GetFileInFlightIO()->PostRead(this, buffer, buffer_len, offset, callback);
244
245 *completed = false;
246 return true;
247 }
248
Write(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)249 bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
250 FileIOCallback* callback, bool* completed) {
251 DCHECK(init_);
252 if (!callback) {
253 if (completed)
254 *completed = true;
255 return Write(buffer, buffer_len, offset);
256 }
257
258 return AsyncWrite(buffer, buffer_len, offset, callback, completed);
259 }
260
SetLength(size_t length)261 bool File::SetLength(size_t length) {
262 DCHECK(init_);
263 if (length > kuint32max)
264 return false;
265
266 return base::TruncatePlatformFile(platform_file_, length);
267 }
268
GetLength()269 size_t File::GetLength() {
270 DCHECK(init_);
271 int64 len = base::SeekPlatformFile(platform_file_,
272 base::PLATFORM_FILE_FROM_END, 0);
273
274 if (len > static_cast<int64>(kuint32max))
275 return kuint32max;
276
277 return static_cast<size_t>(len);
278 }
279
280 // Static.
WaitForPendingIO(int * num_pending_io)281 void File::WaitForPendingIO(int* num_pending_io) {
282 // We may be running unit tests so we should allow be able to reset the
283 // message loop.
284 GetFileInFlightIO()->WaitForPendingIO();
285 DeleteFileInFlightIO();
286 }
287
288 // Static.
DropPendingIO()289 void File::DropPendingIO() {
290 GetFileInFlightIO()->DropPendingIO();
291 DeleteFileInFlightIO();
292 }
293
~File()294 File::~File() {
295 if (IsValid())
296 base::ClosePlatformFile(platform_file_);
297 }
298
AsyncWrite(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)299 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
300 FileIOCallback* callback, bool* completed) {
301 DCHECK(init_);
302 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
303 return false;
304
305 GetFileInFlightIO()->PostWrite(this, buffer, buffer_len, offset, callback);
306
307 if (completed)
308 *completed = false;
309 return true;
310 }
311
312 } // namespace disk_cache
313