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 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 ~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 ~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. 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 controller_->OnIOComplete(this); 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 controller_->OnIOComplete(this); 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 NewRunnableMethod(operation.get(), &FileBackgroundIO::Read), true); 119 OnOperationPosted(operation); 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 NewRunnableMethod(operation.get(), &FileBackgroundIO::Write), true); 131 OnOperationPosted(operation); 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 tha 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::PlatformFile file) 170 : init_(true), 171 mixed_(true), 172 platform_file_(file), 173 sync_platform_file_(base::kInvalidPlatformFileValue) { 174} 175 176bool 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 193base::PlatformFile File::platform_file() const { 194 return platform_file_; 195} 196 197bool File::IsValid() const { 198 if (!init_) 199 return false; 200 return (base::kInvalidPlatformFileValue != platform_file_); 201} 202 203bool 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 212bool 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). 224bool 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 242bool 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 254bool 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 262size_t File::GetLength() { 263 DCHECK(init_); 264 size_t ret = lseek(platform_file_, 0, SEEK_END); 265 return ret; 266} 267 268// Static. 269void 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 276File::~File() { 277 if (IsValid()) 278 close(platform_file_); 279} 280 281bool 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