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 |