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. 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 tha 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::PlatformFile file) 170 : init_(true), 171 mixed_(true), 172 platform_file_(file), 173 sync_platform_file_(base::kInvalidPlatformFileValue) { 174 } 175 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 193 base::PlatformFile File::platform_file() const { 194 return platform_file_; 195 } 196 197 bool File::IsValid() const { 198 if (!init_) 199 return false; 200 return (base::kInvalidPlatformFileValue != platform_file_); 201 } 202 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 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). 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 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 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 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. 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. 289 void File::DropPendingIO() { 290 GetFileInFlightIO()->DropPendingIO(); 291 DeleteFileInFlightIO(); 292 } 293 294 File::~File() { 295 if (IsValid()) 296 base::ClosePlatformFile(platform_file_); 297 } 298 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