OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 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 | 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 #ifndef NET_DISK_CACHE_IN_FLIGHT_IO_H_ |
6 | 6 #define NET_DISK_CACHE_IN_FLIGHT_IO_H_ |
7 #include <fcntl.h> | |
8 | 7 |
9 #include <set> | 8 #include <set> |
10 | 9 |
11 #include "base/logging.h" | |
12 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
13 #include "base/singleton.h" | |
14 #include "base/waitable_event.h" | 11 #include "base/waitable_event.h" |
15 #include "base/worker_pool.h" | |
16 #include "net/disk_cache/disk_cache.h" | |
17 | 12 |
18 namespace { | 13 namespace disk_cache { |
19 | 14 |
20 class InFlightIO; | 15 class InFlightIO; |
21 | 16 |
22 // This class represents a single asynchronous IO operation while it is being | 17 // This class represents a single asynchronous IO operation while it is being |
23 // bounced between threads. | 18 // bounced between threads. |
24 class BackgroundIO : public base::RefCountedThreadSafe<BackgroundIO> { | 19 class BackgroundIO : public base::RefCountedThreadSafe<BackgroundIO> { |
25 public: | 20 public: |
26 // Other than the actual parameters for the IO operation (including the | 21 // Other than the actual parameters for the IO operation (including the |
27 // |callback| that must be notified at the end), we need the controller that | 22 // |callback| that must be notified at the end), we need the controller that |
28 // is keeping track of all operations. When done, we notify the controller | 23 // is keeping track of all operations. When done, we notify the controller |
29 // (we do NOT invoke the callback), in the worker thead that completed the | 24 // (we do NOT invoke the callback), in the worker thead that completed the |
30 // operation. | 25 // operation. |
31 BackgroundIO(disk_cache::File* file, const void* buf, size_t buf_len, | 26 explicit BackgroundIO(InFlightIO* controller) |
32 size_t offset, disk_cache::FileIOCallback* callback, | 27 : controller_(controller), result_(-1), io_completed_(true, false) {} |
33 InFlightIO* controller) | |
34 : io_completed_(true, false), callback_(callback), file_(file), buf_(buf), | |
35 buf_len_(buf_len), offset_(offset), controller_(controller), | |
36 bytes_(0) {} | |
37 | |
38 // Read and Write are the operations that can be performed asynchronously. | |
39 // The actual parameters for the operation are setup in the constructor of | |
40 // the object, with the exception of |delete_buffer|, that allows a write | |
41 // without a callback. Both methods should be called from a worker thread, by | |
42 // posting a task to the WorkerPool (they are RunnableMethods). When finished, | |
43 // controller->OnIOComplete() is called. | |
44 void Read(); | |
45 void Write(bool delete_buffer); | |
46 | 28 |
47 // This method signals the controller that this operation is finished, in the | 29 // This method signals the controller that this operation is finished, in the |
48 // original thread (presumably the IO-Thread). In practice, this is a | 30 // original thread. In practice, this is a RunableMethod that allows |
49 // RunableMethod that allows cancellation. | 31 // cancellation. |
50 void OnIOSignalled(); | 32 void OnIOSignalled(); |
51 | 33 |
52 // Allows the cancellation of the task to notify the controller (step number 7 | 34 // Allows the cancellation of the task to notify the controller (step number 8 |
53 // in the diagram below). In practice, if the controller waits for the | 35 // in the diagram below). In practice, if the controller waits for the |
54 // operation to finish it doesn't have to wait for the final task to be | 36 // operation to finish it doesn't have to wait for the final task to be |
55 // processed by the message loop so calling this method prevents its delivery. | 37 // processed by the message loop so calling this method prevents its delivery. |
56 // Note that this method is not intended to cancel the actual IO operation or | 38 // Note that this method is not intended to cancel the actual IO operation or |
57 // to prevent the first notification to take place (OnIOComplete). | 39 // to prevent the first notification to take place (OnIOComplete). |
58 void Cancel(); | 40 void Cancel(); |
59 | 41 |
60 // Retrieves the number of bytes transfered. | 42 int result() { return result_; } |
61 int Result(); | |
62 | 43 |
63 base::WaitableEvent* io_completed() { | 44 base::WaitableEvent* io_completed() { |
64 return &io_completed_; | 45 return &io_completed_; |
65 } | 46 } |
66 | 47 |
67 disk_cache::FileIOCallback* callback() { | 48 protected: |
68 return callback_; | 49 virtual ~BackgroundIO() {} |
69 } | |
70 | 50 |
71 disk_cache::File* file() { | 51 InFlightIO* controller_; // The controller that tracks all operations. |
72 return file_; | 52 int result_; // Final operation result. |
73 } | |
74 | 53 |
75 private: | 54 private: |
76 friend class base::RefCountedThreadSafe<BackgroundIO>; | 55 friend class base::RefCountedThreadSafe<BackgroundIO>; |
77 ~BackgroundIO() {} | |
78 | 56 |
79 // An event to signal when the operation completes, and the user callback that | 57 // Notifies the controller about the end of the operation, from the background |
80 // has to be invoked. These members are accessed directly by the controller. | 58 // thread. |
| 59 void NotifyController(); |
| 60 |
| 61 // An event to signal when the operation completes. |
81 base::WaitableEvent io_completed_; | 62 base::WaitableEvent io_completed_; |
82 disk_cache::FileIOCallback* callback_; | |
83 | |
84 disk_cache::File* file_; | |
85 const void* buf_; | |
86 size_t buf_len_; | |
87 size_t offset_; | |
88 InFlightIO* controller_; // The controller that tracks all operations. | |
89 int bytes_; // Final operation result. | |
90 | 63 |
91 DISALLOW_COPY_AND_ASSIGN(BackgroundIO); | 64 DISALLOW_COPY_AND_ASSIGN(BackgroundIO); |
92 }; | 65 }; |
93 | 66 |
94 // This class keeps track of every asynchronous IO operation. A single instance | 67 // This class keeps track of asynchronous IO operations. A single instance |
95 // of this class is meant to be used to start an asynchronous operation (using | 68 // of this class is meant to be used to start an asynchronous operation (using |
96 // PostRead/PostWrite). This class will post the operation to a worker thread, | 69 // PostXX, exposed by a derived class). This class will post the operation to a |
97 // hanlde the notification when the operation finishes and perform the callback | 70 // worker thread, hanlde the notification when the operation finishes and |
98 // on the same thread that was used to start the operation. | 71 // perform the callback on the same thread that was used to start the operation. |
99 // | 72 // |
100 // The regular sequence of calls is: | 73 // The regular sequence of calls is: |
101 // Thread_1 Worker_thread | 74 // Thread_1 Worker_thread |
102 // 1. InFlightIO::PostRead() | 75 // 1. DerivedInFlightIO::PostXX() |
103 // 2. -> PostTask -> | 76 // 2. -> PostTask -> |
104 // 3. BackgroundIO::Read() | 77 // 3. InFlightIO::OnOperationPosted() |
105 // 4. IO operation completes | 78 // 4. DerivedBackgroundIO::XX() |
106 // 5. InFlightIO::OnIOComplete() | 79 // 5. IO operation completes |
107 // 6. <- PostTask <- | 80 // 6. InFlightIO::OnIOComplete() |
108 // 7. BackgroundIO::OnIOSignalled() | 81 // 7. <- PostTask <- |
109 // 8. InFlightIO::InvokeCallback() | 82 // 8. BackgroundIO::OnIOSignalled() |
110 // 9. invoke callback | 83 // 9. InFlightIO::InvokeCallback() |
| 84 // 10. DerivedInFlightIO::OnOperationComplete() |
| 85 // 11. invoke callback |
111 // | 86 // |
112 // Shutdown is a special case that is handled though WaitForPendingIO() instead | 87 // Shutdown is a special case that is handled though WaitForPendingIO() instead |
113 // of just waiting for step 7. | 88 // of just waiting for step 7. |
114 class InFlightIO { | 89 class InFlightIO { |
115 public: | 90 public: |
116 InFlightIO() : callback_thread_(MessageLoop::current()) {} | 91 InFlightIO() |
117 ~InFlightIO() {} | 92 : callback_thread_(MessageLoop::current()), running_(false), |
118 | 93 single_thread_(false) {} |
119 // These methods start an asynchronous operation. The arguments have the same | 94 virtual ~InFlightIO() {} |
120 // semantics of the File asynchronous operations, with the exception that the | |
121 // operation never finishes synchronously. | |
122 void PostRead(disk_cache::File* file, void* buf, size_t buf_len, | |
123 size_t offset, disk_cache::FileIOCallback* callback); | |
124 void PostWrite(disk_cache::File* file, const void* buf, size_t buf_len, | |
125 size_t offset, disk_cache::FileIOCallback* callback, | |
126 bool delete_buffer); | |
127 | 95 |
128 // Blocks the current thread until all IO operations tracked by this object | 96 // Blocks the current thread until all IO operations tracked by this object |
129 // complete. | 97 // complete. |
130 void WaitForPendingIO(); | 98 void WaitForPendingIO(); |
131 | 99 |
132 // Called on a worker thread when |operation| completes. | 100 // Called on a background thread when |operation| completes. |
133 void OnIOComplete(BackgroundIO* operation); | 101 void OnIOComplete(BackgroundIO* operation); |
134 | 102 |
135 // Invokes the users' completion callback at the end of the IO operation. | 103 // Invokes the users' completion callback at the end of the IO operation. |
136 // |cancel_task| is true if the actual task posted to the thread is still | 104 // |cancel_task| is true if the actual task posted to the thread is still |
137 // queued (because we are inside WaitForPendingIO), and false if said task is | 105 // queued (because we are inside WaitForPendingIO), and false if said task is |
138 // the one performing the call. | 106 // the one performing the call. |
139 void InvokeCallback(BackgroundIO* operation, bool cancel_task); | 107 void InvokeCallback(BackgroundIO* operation, bool cancel_task); |
140 | 108 |
| 109 protected: |
| 110 // This method is called to signal the completion of the |operation|. |cancel| |
| 111 // is true if the operation is being cancelled. This method is called on the |
| 112 // thread that created this object. |
| 113 virtual void OnOperationComplete(BackgroundIO* operation, bool cancel) = 0; |
| 114 |
| 115 // Signals this object that the derived class just posted the |operation| to |
| 116 // be executed on a background thread. This method must be called on the same |
| 117 // thread used to create this object. |
| 118 void OnOperationPosted(BackgroundIO* operation); |
| 119 |
141 private: | 120 private: |
142 typedef std::set<scoped_refptr<BackgroundIO> > IOList; | 121 typedef std::set<scoped_refptr<BackgroundIO> > IOList; |
143 | 122 |
144 IOList io_list_; // List of pending io operations. | 123 IOList io_list_; // List of pending, in-flight io operations. |
145 MessageLoop* callback_thread_; | 124 MessageLoop* callback_thread_; |
| 125 |
| 126 bool running_; // True after the first posted operation completes. |
| 127 bool single_thread_; // True if we only have one thread. |
| 128 |
| 129 DISALLOW_COPY_AND_ASSIGN(InFlightIO); |
146 }; | 130 }; |
147 | 131 |
148 // --------------------------------------------------------------------------- | 132 } // namespace disk_cache |
149 | 133 |
150 // Runs on a worker thread. | 134 #endif // NET_DISK_CACHE_IN_FLIGHT_IO_H_ |
151 void BackgroundIO::Read() { | |
152 if (file_->Read(const_cast<void*>(buf_), buf_len_, offset_)) { | |
153 bytes_ = static_cast<int>(buf_len_); | |
154 } else { | |
155 bytes_ = -1; | |
156 } | |
157 controller_->OnIOComplete(this); | |
158 } | |
159 | |
160 int BackgroundIO::Result() { | |
161 return bytes_; | |
162 } | |
163 | |
164 void BackgroundIO::Cancel() { | |
165 DCHECK(controller_); | |
166 controller_ = NULL; | |
167 } | |
168 | |
169 // Runs on a worker thread. | |
170 void BackgroundIO::Write(bool delete_buffer) { | |
171 bool rv = file_->Write(buf_, buf_len_, offset_); | |
172 if (delete_buffer) { | |
173 // TODO(rvargas): remove or update this code. | |
174 delete[] reinterpret_cast<const char*>(buf_); | |
175 } | |
176 | |
177 bytes_ = rv ? static_cast<int>(buf_len_) : -1; | |
178 controller_->OnIOComplete(this); | |
179 } | |
180 | |
181 // Runs on the IO thread. | |
182 void BackgroundIO::OnIOSignalled() { | |
183 if (controller_) | |
184 controller_->InvokeCallback(this, false); | |
185 } | |
186 | |
187 // --------------------------------------------------------------------------- | |
188 | |
189 void InFlightIO::PostRead(disk_cache::File *file, void* buf, size_t buf_len, | |
190 size_t offset, disk_cache::FileIOCallback *callback) { | |
191 scoped_refptr<BackgroundIO> operation = | |
192 new BackgroundIO(file, buf, buf_len, offset, callback, this); | |
193 io_list_.insert(operation.get()); | |
194 file->AddRef(); // Balanced on InvokeCallback() | |
195 | |
196 WorkerPool::PostTask(FROM_HERE, | |
197 NewRunnableMethod(operation.get(), &BackgroundIO::Read), | |
198 true); | |
199 } | |
200 | |
201 void InFlightIO::PostWrite(disk_cache::File* file, const void* buf, | |
202 size_t buf_len, size_t offset, | |
203 disk_cache::FileIOCallback* callback, | |
204 bool delete_buffer) { | |
205 scoped_refptr<BackgroundIO> operation = | |
206 new BackgroundIO(file, buf, buf_len, offset, callback, this); | |
207 io_list_.insert(operation.get()); | |
208 file->AddRef(); // Balanced on InvokeCallback() | |
209 | |
210 WorkerPool::PostTask(FROM_HERE, | |
211 NewRunnableMethod(operation.get(), &BackgroundIO::Write, | |
212 delete_buffer), | |
213 true); | |
214 } | |
215 | |
216 void InFlightIO::WaitForPendingIO() { | |
217 while (!io_list_.empty()) { | |
218 // Block the current thread until all pending IO completes. | |
219 IOList::iterator it = io_list_.begin(); | |
220 InvokeCallback(*it, true); | |
221 } | |
222 } | |
223 | |
224 // Runs on a worker thread. | |
225 void InFlightIO::OnIOComplete(BackgroundIO* operation) { | |
226 callback_thread_->PostTask(FROM_HERE, | |
227 NewRunnableMethod(operation, | |
228 &BackgroundIO::OnIOSignalled)); | |
229 operation->io_completed()->Signal(); | |
230 } | |
231 | |
232 // Runs on the IO thread. | |
233 void InFlightIO::InvokeCallback(BackgroundIO* operation, bool cancel_task) { | |
234 operation->io_completed()->Wait(); | |
235 | |
236 if (cancel_task) | |
237 operation->Cancel(); | |
238 | |
239 disk_cache::FileIOCallback* callback = operation->callback(); | |
240 int bytes = operation->Result(); | |
241 | |
242 // Release the references acquired in PostRead / PostWrite. | |
243 operation->file()->Release(); | |
244 io_list_.erase(operation); | |
245 callback->OnFileIOComplete(bytes); | |
246 } | |
247 | |
248 } // namespace | |
249 | |
250 namespace disk_cache { | |
251 | |
252 File::File(base::PlatformFile file) | |
253 : init_(true), mixed_(true), platform_file_(file) { | |
254 } | |
255 | |
256 bool File::Init(const FilePath& name) { | |
257 if (init_) | |
258 return false; | |
259 | |
260 int flags = base::PLATFORM_FILE_OPEN | | |
261 base::PLATFORM_FILE_READ | | |
262 base::PLATFORM_FILE_WRITE; | |
263 platform_file_ = base::CreatePlatformFile(name, flags, NULL); | |
264 if (platform_file_ < 0) { | |
265 platform_file_ = 0; | |
266 return false; | |
267 } | |
268 | |
269 init_ = true; | |
270 return true; | |
271 } | |
272 | |
273 File::~File() { | |
274 if (platform_file_) | |
275 close(platform_file_); | |
276 } | |
277 | |
278 base::PlatformFile File::platform_file() const { | |
279 return platform_file_; | |
280 } | |
281 | |
282 bool File::IsValid() const { | |
283 if (!init_) | |
284 return false; | |
285 return (base::kInvalidPlatformFileValue != platform_file_); | |
286 } | |
287 | |
288 bool File::Read(void* buffer, size_t buffer_len, size_t offset) { | |
289 DCHECK(init_); | |
290 if (buffer_len > ULONG_MAX || offset > LONG_MAX) | |
291 return false; | |
292 | |
293 int ret = pread(platform_file_, buffer, buffer_len, offset); | |
294 return (static_cast<size_t>(ret) == buffer_len); | |
295 } | |
296 | |
297 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { | |
298 DCHECK(init_); | |
299 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) | |
300 return false; | |
301 | |
302 int ret = pwrite(platform_file_, buffer, buffer_len, offset); | |
303 return (static_cast<size_t>(ret) == buffer_len); | |
304 } | |
305 | |
306 // We have to increase the ref counter of the file before performing the IO to | |
307 // prevent the completion to happen with an invalid handle (if the file is | |
308 // closed while the IO is in flight). | |
309 bool File::Read(void* buffer, size_t buffer_len, size_t offset, | |
310 FileIOCallback* callback, bool* completed) { | |
311 DCHECK(init_); | |
312 if (!callback) { | |
313 if (completed) | |
314 *completed = true; | |
315 return Read(buffer, buffer_len, offset); | |
316 } | |
317 | |
318 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) | |
319 return false; | |
320 | |
321 InFlightIO* io_operations = Singleton<InFlightIO>::get(); | |
322 io_operations->PostRead(this, buffer, buffer_len, offset, callback); | |
323 | |
324 *completed = false; | |
325 return true; | |
326 } | |
327 | |
328 bool File::Write(const void* buffer, size_t buffer_len, size_t offset, | |
329 FileIOCallback* callback, bool* completed) { | |
330 DCHECK(init_); | |
331 if (!callback) { | |
332 if (completed) | |
333 *completed = true; | |
334 return Write(buffer, buffer_len, offset); | |
335 } | |
336 | |
337 return AsyncWrite(buffer, buffer_len, offset, true, callback, completed); | |
338 } | |
339 | |
340 bool File::PostWrite(const void* buffer, size_t buffer_len, size_t offset) { | |
341 DCHECK(init_); | |
342 return AsyncWrite(buffer, buffer_len, offset, false, NULL, NULL); | |
343 } | |
344 | |
345 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset, | |
346 bool notify, FileIOCallback* callback, bool* completed) { | |
347 DCHECK(init_); | |
348 if (buffer_len > ULONG_MAX || offset > ULONG_MAX) | |
349 return false; | |
350 | |
351 InFlightIO* io_operations = Singleton<InFlightIO>::get(); | |
352 io_operations->PostWrite(this, buffer, buffer_len, offset, callback, !notify); | |
353 | |
354 if (completed) | |
355 *completed = false; | |
356 return true; | |
357 } | |
358 | |
359 bool File::SetLength(size_t length) { | |
360 DCHECK(init_); | |
361 if (length > ULONG_MAX) | |
362 return false; | |
363 | |
364 return 0 == ftruncate(platform_file_, length); | |
365 } | |
366 | |
367 size_t File::GetLength() { | |
368 DCHECK(init_); | |
369 size_t ret = lseek(platform_file_, 0, SEEK_END); | |
370 return ret; | |
371 } | |
372 | |
373 // Static. | |
374 void File::WaitForPendingIO(int* num_pending_io) { | |
375 if (*num_pending_io) | |
376 Singleton<InFlightIO>::get()->WaitForPendingIO(); | |
377 } | |
378 | |
379 } // namespace disk_cache | |
OLD | NEW |