1 // Copyright (c) 2006-2010 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 <fcntl.h> 8 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 ~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 ~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); 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 controller_->OnIOComplete(this); 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 controller_->OnIOComplete(this); 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 NewRunnableMethod(operation.get(), &FileBackgroundIO::Read), true); 119 OnOperationPosted(operation); 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 NewRunnableMethod(operation.get(), &FileBackgroundIO::Write), true); 131 OnOperationPosted(operation); 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 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 > ULONG_MAX || offset > LONG_MAX) 206 return false; 207 208 int ret = pread(platform_file_, buffer, buffer_len, offset); 209 return (static_cast<size_t>(ret) == buffer_len); 210 } 211 212 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { 213 DCHECK(init_); 214 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) 215 return false; 216 217 int ret = pwrite(platform_file_, buffer, buffer_len, offset); 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). 224 bool File::Read(void* buffer, size_t buffer_len, size_t offset, 225 FileIOCallback* callback, bool* completed) { 226 DCHECK(init_); 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 242 bool File::Write(const void* buffer, size_t buffer_len, size_t offset, 243 FileIOCallback* callback, bool* completed) { 244 DCHECK(init_); 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 254 bool File::SetLength(size_t length) { 255 DCHECK(init_); 256 if (length > ULONG_MAX) 257 return false; 258 259 return 0 == ftruncate(platform_file_, length); 260 } 261 262 size_t File::GetLength() { 263 DCHECK(init_); 264 size_t ret = lseek(platform_file_, 0, SEEK_END); 265 return ret; 266 } 267 268 // Static. 269 void File::WaitForPendingIO(int* num_pending_io) { 270 // We may be running unit tests so we should allow be able to reset the 271 // message loop. 272 GetFileInFlightIO()->WaitForPendingIO(); 273 DeleteFileInFlightIO(); 274 } 275 276 File::~File() { 277 if (IsValid()) 278 close(platform_file_); 279 } 280 281 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset, 282 FileIOCallback* callback, bool* completed) { 283 DCHECK(init_); 284 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) 285 return false; 286 287 GetFileInFlightIO()->PostWrite(this, buffer, buffer_len, offset, callback); 288 289 if (completed) 290 *completed = false; 291 return true; 292 } 293 294 } // namespace disk_cache 295