| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/disk_cache/file.h" | 5 #include "net/disk_cache/file.h" |
| 6 | 6 |
| 7 #include "base/message_loop.h" |
| 8 #include "base/singleton.h" |
| 7 #include "net/disk_cache/disk_cache.h" | 9 #include "net/disk_cache/disk_cache.h" |
| 8 | 10 |
| 9 namespace { | 11 namespace { |
| 10 | 12 |
| 11 // This class implements FileIOCallback to perform IO operations | 13 // Structure used for asynchronous operations. |
| 12 // when the callback parameter of the operation is NULL. | 14 struct MyOverlapped { |
| 13 class SyncCallback: public disk_cache::FileIOCallback { | 15 MyOverlapped(disk_cache::File* file, size_t offset, |
| 14 public: | 16 disk_cache::FileIOCallback* callback); |
| 15 SyncCallback() : called_(false) {} | 17 ~MyOverlapped(); |
| 16 ~SyncCallback() {} | 18 OVERLAPPED* overlapped() { |
| 19 return &context_.overlapped; |
| 20 } |
| 17 | 21 |
| 18 virtual void OnFileIOComplete(int bytes_copied); | 22 MessageLoopForIO::IOContext context_; |
| 19 void WaitForResult(int* bytes_copied); | 23 scoped_refptr<disk_cache::File> file_; |
| 20 private: | 24 disk_cache::FileIOCallback* callback_; |
| 21 bool called_; | 25 const void* buffer_; |
| 22 int actual_; | 26 bool delete_buffer_; // Delete the user buffer at completion. |
| 23 }; | 27 }; |
| 24 | 28 |
| 25 void SyncCallback::OnFileIOComplete(int bytes_copied) { | 29 COMPILE_ASSERT(!offsetof(MyOverlapped, context_), starts_with_overlapped); |
| 26 actual_ = bytes_copied; | |
| 27 called_ = true; | |
| 28 } | |
| 29 | 30 |
| 30 // Waits for the IO operation to complete. | 31 // Helper class to handle the IO completion notifications from the message loop. |
| 31 void SyncCallback::WaitForResult(int* bytes_copied) { | 32 class CompletionHandler : public MessageLoopForIO::IOHandler { |
| 32 for (;;) { | 33 virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, |
| 33 SleepEx(INFINITE, TRUE); | 34 DWORD actual_bytes, DWORD error); |
| 34 if (called_) | |
| 35 break; | |
| 36 } | |
| 37 *bytes_copied = actual_; | |
| 38 } | |
| 39 | |
| 40 // Structure used for asynchronous operations. | |
| 41 struct MyOverlapped { | |
| 42 OVERLAPPED overlapped; | |
| 43 disk_cache::File* file; | |
| 44 disk_cache::FileIOCallback* callback; | |
| 45 const void* buffer; | |
| 46 DWORD actual_bytes; | |
| 47 bool async; // Invoke the callback form the completion. | |
| 48 bool called; // Completion received. | |
| 49 bool delete_buffer; // Delete the user buffer at completion. | |
| 50 }; | 35 }; |
| 51 | 36 |
| 52 COMPILE_ASSERT(!offsetof(MyOverlapped, overlapped), starts_with_overlapped); | 37 void CompletionHandler::OnIOCompleted(MessageLoopForIO::IOContext* context, |
| 53 | 38 DWORD actual_bytes, DWORD error) { |
| 54 } // namespace | 39 MyOverlapped* data = reinterpret_cast<MyOverlapped*>(context); |
| 55 | |
| 56 namespace disk_cache { | |
| 57 | |
| 58 // SyncCallback to be invoked as an APC when the asynchronous operation | |
| 59 // completes. | |
| 60 void CALLBACK IoCompletion(DWORD error, DWORD actual_bytes, | |
| 61 OVERLAPPED* overlapped) { | |
| 62 MyOverlapped* data = reinterpret_cast<MyOverlapped*>(overlapped); | |
| 63 | 40 |
| 64 if (error) { | 41 if (error) { |
| 65 DCHECK(!actual_bytes); | 42 DCHECK(!actual_bytes); |
| 66 actual_bytes = static_cast<DWORD>(-1); | 43 actual_bytes = static_cast<DWORD>(-1); |
| 67 NOTREACHED(); | 44 NOTREACHED(); |
| 68 } | 45 } |
| 69 | 46 |
| 70 if (data->delete_buffer) { | 47 if (data->callback_) |
| 71 DCHECK(!data->callback); | 48 data->callback_->OnFileIOComplete(static_cast<int>(actual_bytes)); |
| 72 data->file->Release(); | |
| 73 delete data->buffer; | |
| 74 delete data; | |
| 75 return; | |
| 76 } | |
| 77 | 49 |
| 78 if (data->async) { | 50 delete data; |
| 79 data->callback->OnFileIOComplete(static_cast<int>(actual_bytes)); | 51 } |
| 80 data->file->Release(); | 52 |
| 81 delete data; | 53 MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset, |
| 82 } else { | 54 disk_cache::FileIOCallback* callback) { |
| 83 // Somebody is waiting for this so don't delete data and instead notify | 55 memset(this, 0, sizeof(*this)); |
| 84 // that we were called. | 56 context_.handler = Singleton<CompletionHandler>::get(); |
| 85 data->actual_bytes = actual_bytes; | 57 context_.overlapped.Offset = static_cast<DWORD>(offset); |
| 86 data->file->Release(); | 58 file_ = file; |
| 87 data->called = true; | 59 callback_ = callback; |
| 60 } |
| 61 |
| 62 MyOverlapped::~MyOverlapped() { |
| 63 if (delete_buffer_) { |
| 64 DCHECK(!callback_); |
| 65 delete buffer_; |
| 88 } | 66 } |
| 89 } | 67 } |
| 90 | 68 |
| 69 } // namespace |
| 70 |
| 71 namespace disk_cache { |
| 72 |
| 73 // Used from WaitForPendingIO() when the cache is being destroyed. |
| 74 MessageLoopForIO::IOHandler* GetFileIOHandler() { |
| 75 return Singleton<CompletionHandler>::get(); |
| 76 } |
| 77 |
| 91 File::File(base::PlatformFile file) | 78 File::File(base::PlatformFile file) |
| 92 : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE), | 79 : init_(true), platform_file_(INVALID_HANDLE_VALUE), |
| 93 sync_platform_file_(file) { | 80 sync_platform_file_(file) { |
| 94 } | 81 } |
| 95 | 82 |
| 96 bool File::Init(const std::wstring& name) { | 83 bool File::Init(const std::wstring& name) { |
| 97 DCHECK(!init_); | 84 DCHECK(!init_); |
| 98 if (init_) | 85 if (init_) |
| 99 return false; | 86 return false; |
| 100 | 87 |
| 101 platform_file_ = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, | 88 platform_file_ = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, |
| 102 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | 89 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| 103 FILE_FLAG_OVERLAPPED, NULL); | 90 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); |
| 104 | 91 |
| 105 if (INVALID_HANDLE_VALUE == platform_file_) | 92 if (INVALID_HANDLE_VALUE == platform_file_) |
| 106 return false; | 93 return false; |
| 107 | 94 |
| 95 MessageLoopForIO::current()->RegisterIOHandler( |
| 96 platform_file_, Singleton<CompletionHandler>::get()); |
| 97 |
| 108 init_ = true; | 98 init_ = true; |
| 109 if (mixed_) { | 99 sync_platform_file_ = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, |
| 110 sync_platform_file_ = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, | 100 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| 111 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 101 OPEN_EXISTING, 0, NULL); |
| 112 OPEN_EXISTING, 0, NULL); | |
| 113 | 102 |
| 114 if (INVALID_HANDLE_VALUE == sync_platform_file_) | 103 if (INVALID_HANDLE_VALUE == sync_platform_file_) |
| 115 return false; | 104 return false; |
| 116 } else { | |
| 117 sync_platform_file_ = INVALID_HANDLE_VALUE; | |
| 118 } | |
| 119 | 105 |
| 120 return true; | 106 return true; |
| 121 } | 107 } |
| 122 | 108 |
| 123 File::~File() { | 109 File::~File() { |
| 124 if (!init_) | 110 if (!init_) |
| 125 return; | 111 return; |
| 126 | 112 |
| 127 if (INVALID_HANDLE_VALUE != platform_file_) | 113 if (INVALID_HANDLE_VALUE != platform_file_) |
| 128 CloseHandle(platform_file_); | 114 CloseHandle(platform_file_); |
| 129 if (mixed_ && INVALID_HANDLE_VALUE != sync_platform_file_) | 115 if (INVALID_HANDLE_VALUE != sync_platform_file_) |
| 130 CloseHandle(sync_platform_file_); | 116 CloseHandle(sync_platform_file_); |
| 131 } | 117 } |
| 132 | 118 |
| 133 base::PlatformFile File::platform_file() const { | 119 base::PlatformFile File::platform_file() const { |
| 134 DCHECK(init_); | 120 DCHECK(init_); |
| 135 return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ : | 121 return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ : |
| 136 platform_file_; | 122 platform_file_; |
| 137 } | 123 } |
| 138 | 124 |
| 139 bool File::IsValid() const { | 125 bool File::IsValid() const { |
| 140 if (!init_) | 126 if (!init_) |
| 141 return false; | 127 return false; |
| 142 return (INVALID_HANDLE_VALUE != platform_file_ || | 128 return (INVALID_HANDLE_VALUE != platform_file_ || |
| 143 INVALID_HANDLE_VALUE != sync_platform_file_); | 129 INVALID_HANDLE_VALUE != sync_platform_file_); |
| 144 } | 130 } |
| 145 | 131 |
| 146 bool File::Read(void* buffer, size_t buffer_len, size_t offset) { | 132 bool File::Read(void* buffer, size_t buffer_len, size_t offset) { |
| 147 DCHECK(init_); | 133 DCHECK(init_); |
| 148 if (!mixed_ || buffer_len > ULONG_MAX || offset > LONG_MAX) | 134 if (buffer_len > ULONG_MAX || offset > LONG_MAX) |
| 149 return false; | 135 return false; |
| 150 | 136 |
| 151 DWORD ret = SetFilePointer(sync_platform_file_, | 137 DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset), |
| 152 static_cast<LONG>(offset), | 138 NULL, FILE_BEGIN); |
| 153 NULL, | |
| 154 FILE_BEGIN); | |
| 155 if (INVALID_SET_FILE_POINTER == ret) | 139 if (INVALID_SET_FILE_POINTER == ret) |
| 156 return false; | 140 return false; |
| 157 | 141 |
| 158 DWORD actual; | 142 DWORD actual; |
| 159 DWORD size = static_cast<DWORD>(buffer_len); | 143 DWORD size = static_cast<DWORD>(buffer_len); |
| 160 if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL)) | 144 if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL)) |
| 161 return false; | 145 return false; |
| 162 return actual == size; | 146 return actual == size; |
| 163 } | 147 } |
| 164 | 148 |
| 165 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { | 149 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { |
| 166 DCHECK(init_); | 150 DCHECK(init_); |
| 167 if (!mixed_ || buffer_len > ULONG_MAX || offset > ULONG_MAX) | 151 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) |
| 168 return false; | 152 return false; |
| 169 | 153 |
| 170 DWORD ret = SetFilePointer(sync_platform_file_, | 154 DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset), |
| 171 static_cast<LONG>(offset), | 155 NULL, FILE_BEGIN); |
| 172 NULL, | |
| 173 FILE_BEGIN); | |
| 174 if (INVALID_SET_FILE_POINTER == ret) | 156 if (INVALID_SET_FILE_POINTER == ret) |
| 175 return false; | 157 return false; |
| 176 | 158 |
| 177 DWORD actual; | 159 DWORD actual; |
| 178 DWORD size = static_cast<DWORD>(buffer_len); | 160 DWORD size = static_cast<DWORD>(buffer_len); |
| 179 if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL)) | 161 if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL)) |
| 180 return false; | 162 return false; |
| 181 return actual == size; | 163 return actual == size; |
| 182 } | 164 } |
| 183 | 165 |
| 184 // We have to increase the ref counter of the file before performing the IO to | 166 // We have to increase the ref counter of the file before performing the IO to |
| 185 // prevent the completion to happen with an invalid handle (if the file is | 167 // prevent the completion to happen with an invalid handle (if the file is |
| 186 // closed while the IO is in flight). | 168 // closed while the IO is in flight). |
| 187 bool File::Read(void* buffer, size_t buffer_len, size_t offset, | 169 bool File::Read(void* buffer, size_t buffer_len, size_t offset, |
| 188 FileIOCallback* callback, bool* completed) { | 170 FileIOCallback* callback, bool* completed) { |
| 189 DCHECK(init_); | 171 DCHECK(init_); |
| 172 if (!callback) |
| 173 return Read(buffer, buffer_len, offset); |
| 174 |
| 190 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) | 175 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) |
| 191 return false; | 176 return false; |
| 192 | 177 |
| 193 MyOverlapped* data = new MyOverlapped; | 178 MyOverlapped* data = new MyOverlapped(this, offset, callback); |
| 194 memset(data, 0, sizeof(*data)); | 179 DWORD size = static_cast<DWORD>(buffer_len); |
| 195 | 180 |
| 196 SyncCallback local_callback; | 181 DWORD actual; |
| 197 data->overlapped.Offset = static_cast<DWORD>(offset); | 182 if (!ReadFile(platform_file_, buffer, size, &actual, data->overlapped())) { |
| 198 data->callback = callback ? callback : &local_callback; | 183 *completed = false; |
| 199 data->file = this; | 184 if (GetLastError() == ERROR_IO_PENDING) |
| 200 | 185 return true; |
| 201 DWORD size = static_cast<DWORD>(buffer_len); | |
| 202 AddRef(); | |
| 203 | |
| 204 if (!ReadFileEx(platform_file_, buffer, size, &data->overlapped, | |
| 205 &IoCompletion)) { | |
| 206 Release(); | |
| 207 delete data; | 186 delete data; |
| 208 return false; | 187 return false; |
| 209 } | 188 } |
| 210 | 189 |
| 211 if (callback) { | 190 // The operation completed already. We'll be called back anyway. |
| 212 *completed = false; | 191 *completed = (actual == size); |
| 213 // Let's check if the operation is already finished. | 192 DCHECK(actual == size); |
| 214 SleepEx(0, TRUE); | 193 data->callback_ = NULL; |
| 215 if (data->called) { | 194 data->file_ = NULL; // There is no reason to hold on to this anymore. |
| 216 *completed = (data->actual_bytes == size); | 195 return *completed; |
| 217 DCHECK(data->actual_bytes == size); | |
| 218 delete data; | |
| 219 return *completed; | |
| 220 } | |
| 221 data->async = true; | |
| 222 } else { | |
| 223 // Invoke the callback and perform cleanup on the APC. | |
| 224 data->async = true; | |
| 225 int bytes_copied; | |
| 226 local_callback.WaitForResult(&bytes_copied); | |
| 227 if (static_cast<int>(buffer_len) != bytes_copied) { | |
| 228 NOTREACHED(); | |
| 229 return false; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 return true; | |
| 234 } | 196 } |
| 235 | 197 |
| 236 bool File::Write(const void* buffer, size_t buffer_len, size_t offset, | 198 bool File::Write(const void* buffer, size_t buffer_len, size_t offset, |
| 237 FileIOCallback* callback, bool* completed) { | 199 FileIOCallback* callback, bool* completed) { |
| 238 DCHECK(init_); | 200 DCHECK(init_); |
| 201 if (!callback) |
| 202 return Write(buffer, buffer_len, offset); |
| 203 |
| 239 return AsyncWrite(buffer, buffer_len, offset, true, callback, completed); | 204 return AsyncWrite(buffer, buffer_len, offset, true, callback, completed); |
| 240 } | 205 } |
| 241 | 206 |
| 242 bool File::PostWrite(const void* buffer, size_t buffer_len, size_t offset) { | 207 bool File::PostWrite(const void* buffer, size_t buffer_len, size_t offset) { |
| 243 DCHECK(init_); | 208 DCHECK(init_); |
| 244 return AsyncWrite(buffer, buffer_len, offset, false, NULL, NULL); | 209 return AsyncWrite(buffer, buffer_len, offset, false, NULL, NULL); |
| 245 } | 210 } |
| 246 | 211 |
| 247 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset, | 212 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset, |
| 248 bool notify, FileIOCallback* callback, bool* completed) { | 213 bool notify, FileIOCallback* callback, bool* completed) { |
| 249 DCHECK(init_); | 214 DCHECK(init_); |
| 250 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) | 215 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) |
| 251 return false; | 216 return false; |
| 252 | 217 |
| 253 MyOverlapped* data = new MyOverlapped; | 218 MyOverlapped* data = new MyOverlapped(this, offset, callback); |
| 254 memset(data, 0, sizeof(*data)); | 219 bool dummy_completed; |
| 255 | 220 if (!callback) { |
| 256 SyncCallback local_callback; | 221 DCHECK(!notify); |
| 257 data->overlapped.Offset = static_cast<DWORD>(offset); | 222 data->delete_buffer_ = true; |
| 258 data->callback = callback ? callback : &local_callback; | 223 data->buffer_ = buffer; |
| 259 data->file = this; | 224 completed = &dummy_completed; |
| 260 if (!callback && !notify) { | |
| 261 data->delete_buffer = true; | |
| 262 data->callback = NULL; | |
| 263 data->buffer = buffer; | |
| 264 } | 225 } |
| 265 | 226 |
| 266 DWORD size = static_cast<DWORD>(buffer_len); | 227 DWORD size = static_cast<DWORD>(buffer_len); |
| 267 AddRef(); | |
| 268 | 228 |
| 269 if (!WriteFileEx(platform_file_, buffer, size, &data->overlapped, | 229 DWORD actual; |
| 270 &IoCompletion)) { | 230 if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) { |
| 271 Release(); | 231 *completed = false; |
| 232 if (GetLastError() == ERROR_IO_PENDING) |
| 233 return true; |
| 272 delete data; | 234 delete data; |
| 273 return false; | 235 return false; |
| 274 } | 236 } |
| 275 | 237 |
| 276 if (callback) { | 238 // The operation completed already. We'll be called back anyway. |
| 277 *completed = false; | 239 *completed = (actual == size); |
| 278 SleepEx(0, TRUE); | 240 DCHECK(actual == size); |
| 279 if (data->called) { | 241 data->callback_ = NULL; |
| 280 *completed = (data->actual_bytes == size); | 242 data->file_ = NULL; // There is no reason to hold on to this anymore. |
| 281 DCHECK(data->actual_bytes == size); | 243 return *completed; |
| 282 delete data; | |
| 283 return *completed; | |
| 284 } | |
| 285 data->async = true; | |
| 286 } else if (notify) { | |
| 287 data->async = true; | |
| 288 int bytes_copied; | |
| 289 local_callback.WaitForResult(&bytes_copied); | |
| 290 if (static_cast<int>(buffer_len) != bytes_copied) { | |
| 291 NOTREACHED(); | |
| 292 return false; | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 return true; | |
| 297 } | 244 } |
| 298 | 245 |
| 299 bool File::SetLength(size_t length) { | 246 bool File::SetLength(size_t length) { |
| 300 DCHECK(init_); | 247 DCHECK(init_); |
| 301 if (length > ULONG_MAX) | 248 if (length > ULONG_MAX) |
| 302 return false; | 249 return false; |
| 303 | 250 |
| 304 DWORD size = static_cast<DWORD>(length); | 251 DWORD size = static_cast<DWORD>(length); |
| 305 HANDLE file = platform_file(); | 252 HANDLE file = platform_file(); |
| 306 if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN)) | 253 if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN)) |
| 307 return false; | 254 return false; |
| 308 | 255 |
| 309 return TRUE == SetEndOfFile(file); | 256 return TRUE == SetEndOfFile(file); |
| 310 } | 257 } |
| 311 | 258 |
| 312 size_t File::GetLength() { | 259 size_t File::GetLength() { |
| 313 DCHECK(init_); | 260 DCHECK(init_); |
| 314 LARGE_INTEGER size; | 261 LARGE_INTEGER size; |
| 315 HANDLE file = platform_file(); | 262 HANDLE file = platform_file(); |
| 316 if (!GetFileSizeEx(file, &size)) | 263 if (!GetFileSizeEx(file, &size)) |
| 317 return 0; | 264 return 0; |
| 318 if (size.HighPart) | 265 if (size.HighPart) |
| 319 return ULONG_MAX; | 266 return ULONG_MAX; |
| 320 | 267 |
| 321 return static_cast<size_t>(size.LowPart); | 268 return static_cast<size_t>(size.LowPart); |
| 322 } | 269 } |
| 323 | 270 |
| 324 } // namespace disk_cache | 271 } // namespace disk_cache |
| 325 | 272 |
| OLD | NEW |