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 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. 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. 64 class 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. 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. 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 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 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. 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 that will broker all async operations. 148 FileInFlightIO* s_file_operations = NULL; 149 150 // Returns the current FileInFlightIO. 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. 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 169 File::File(base::File file) 170 : init_(true), 171 mixed_(true), 172 base_file_(file.Pass()) { 173 } 174 175 bool 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 185 bool File::IsValid() const { 186 return base_file_.IsValid(); 187 } 188 189 bool 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 200 bool 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). 215 bool 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 233 bool 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 245 bool 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 253 size_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. 264 void 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. 272 void File::DropPendingIO() { 273 GetFileInFlightIO()->DropPendingIO(); 274 DeleteFileInFlightIO(); 275 } 276 277 File::~File() { 278 } 279 280 base::PlatformFile File::platform_file() const { 281 return base_file_.GetPlatformFile(); 282 } 283 284 bool 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