1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/waitable_event_watcher_posix.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,277 @@ 1.4 +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "base/waitable_event_watcher.h" 1.9 + 1.10 +#include "base/condition_variable.h" 1.11 +#include "base/lock.h" 1.12 +#include "base/message_loop.h" 1.13 +#include "base/waitable_event.h" 1.14 + 1.15 +#include "mozilla/Attributes.h" 1.16 + 1.17 +namespace base { 1.18 + 1.19 +// ----------------------------------------------------------------------------- 1.20 +// WaitableEventWatcher (async waits). 1.21 +// 1.22 +// The basic design is that we add an AsyncWaiter to the wait-list of the event. 1.23 +// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it. 1.24 +// The MessageLoop ends up running the task, which calls the delegate. 1.25 +// 1.26 +// Since the wait can be canceled, we have a thread-safe Flag object which is 1.27 +// set when the wait has been canceled. At each stage in the above, we check the 1.28 +// flag before going onto the next stage. Since the wait may only be canceled in 1.29 +// the MessageLoop which runs the Task, we are assured that the delegate cannot 1.30 +// be called after canceling... 1.31 + 1.32 +// ----------------------------------------------------------------------------- 1.33 +// A thread-safe, reference-counted, write-once flag. 1.34 +// ----------------------------------------------------------------------------- 1.35 +class Flag : public RefCountedThreadSafe<Flag> { 1.36 + public: 1.37 + Flag() { flag_ = false; } 1.38 + 1.39 + void Set() { 1.40 + AutoLock locked(lock_); 1.41 + flag_ = true; 1.42 + } 1.43 + 1.44 + bool value() const { 1.45 + AutoLock locked(lock_); 1.46 + return flag_; 1.47 + } 1.48 + 1.49 + private: 1.50 + mutable Lock lock_; 1.51 + bool flag_; 1.52 +}; 1.53 + 1.54 +// ----------------------------------------------------------------------------- 1.55 +// This is an asynchronous waiter which posts a task to a MessageLoop when 1.56 +// fired. An AsyncWaiter may only be in a single wait-list. 1.57 +// ----------------------------------------------------------------------------- 1.58 +class AsyncWaiter MOZ_FINAL : public WaitableEvent::Waiter { 1.59 + public: 1.60 + AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag) 1.61 + : message_loop_(message_loop), 1.62 + cb_task_(task), 1.63 + flag_(flag) { } 1.64 + 1.65 + bool Fire(WaitableEvent* event) { 1.66 + if (flag_->value()) { 1.67 + // If the callback has been canceled, we don't enqueue the task, we just 1.68 + // delete it instead. 1.69 + delete cb_task_; 1.70 + } else { 1.71 + message_loop_->PostTask(FROM_HERE, cb_task_); 1.72 + } 1.73 + 1.74 + // We are removed from the wait-list by the WaitableEvent itself. It only 1.75 + // remains to delete ourselves. 1.76 + delete this; 1.77 + 1.78 + // We can always return true because an AsyncWaiter is never in two 1.79 + // different wait-lists at the same time. 1.80 + return true; 1.81 + } 1.82 + 1.83 + // See StopWatching for discussion 1.84 + bool Compare(void* tag) { 1.85 + return tag == flag_.get(); 1.86 + } 1.87 + 1.88 + private: 1.89 + MessageLoop *const message_loop_; 1.90 + Task *const cb_task_; 1.91 + scoped_refptr<Flag> flag_; 1.92 +}; 1.93 + 1.94 +// ----------------------------------------------------------------------------- 1.95 +// For async waits we need to make a callback in a MessageLoop thread. We do 1.96 +// this by posting this task, which calls the delegate and keeps track of when 1.97 +// the event is canceled. 1.98 +// ----------------------------------------------------------------------------- 1.99 +class AsyncCallbackTask : public Task { 1.100 + public: 1.101 + AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate, 1.102 + WaitableEvent* event) 1.103 + : flag_(flag), 1.104 + delegate_(delegate), 1.105 + event_(event) { 1.106 + } 1.107 + 1.108 + void Run() { 1.109 + // Runs in MessageLoop thread. 1.110 + if (!flag_->value()) { 1.111 + // This is to let the WaitableEventWatcher know that the event has occured 1.112 + // because it needs to be able to return NULL from GetWatchedObject 1.113 + flag_->Set(); 1.114 + delegate_->OnWaitableEventSignaled(event_); 1.115 + } 1.116 + 1.117 + // We are deleted by the MessageLoop 1.118 + } 1.119 + 1.120 + private: 1.121 + scoped_refptr<Flag> flag_; 1.122 + WaitableEventWatcher::Delegate *const delegate_; 1.123 + WaitableEvent *const event_; 1.124 +}; 1.125 + 1.126 +WaitableEventWatcher::WaitableEventWatcher() 1.127 + : event_(NULL), 1.128 + message_loop_(NULL), 1.129 + cancel_flag_(NULL), 1.130 + callback_task_(NULL) { 1.131 +} 1.132 + 1.133 +WaitableEventWatcher::~WaitableEventWatcher() { 1.134 + StopWatching(); 1.135 +} 1.136 + 1.137 +// ----------------------------------------------------------------------------- 1.138 +// The Handle is how the user cancels a wait. After deleting the Handle we 1.139 +// insure that the delegate cannot be called. 1.140 +// ----------------------------------------------------------------------------- 1.141 +bool WaitableEventWatcher::StartWatching 1.142 + (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) { 1.143 + MessageLoop *const current_ml = MessageLoop::current(); 1.144 + DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a " 1.145 + "current MessageLoop"; 1.146 + 1.147 + // A user may call StartWatching from within the callback function. In this 1.148 + // case, we won't know that we have finished watching, expect that the Flag 1.149 + // will have been set in AsyncCallbackTask::Run() 1.150 + if (cancel_flag_.get() && cancel_flag_->value()) { 1.151 + if (message_loop_) { 1.152 + message_loop_->RemoveDestructionObserver(this); 1.153 + message_loop_ = NULL; 1.154 + } 1.155 + 1.156 + cancel_flag_ = NULL; 1.157 + } 1.158 + 1.159 + DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching"; 1.160 + 1.161 + cancel_flag_ = new Flag; 1.162 + callback_task_ = new AsyncCallbackTask(cancel_flag_, delegate, event); 1.163 + WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get(); 1.164 + 1.165 + AutoLock locked(kernel->lock_); 1.166 + 1.167 + if (kernel->signaled_) { 1.168 + if (!kernel->manual_reset_) 1.169 + kernel->signaled_ = false; 1.170 + 1.171 + // No hairpinning - we can't call the delegate directly here. We have to 1.172 + // enqueue a task on the MessageLoop as normal. 1.173 + current_ml->PostTask(FROM_HERE, callback_task_); 1.174 + return true; 1.175 + } 1.176 + 1.177 + message_loop_ = current_ml; 1.178 + current_ml->AddDestructionObserver(this); 1.179 + 1.180 + event_ = event; 1.181 + kernel_ = kernel; 1.182 + waiter_ = new AsyncWaiter(current_ml, callback_task_, cancel_flag_); 1.183 + event->Enqueue(waiter_); 1.184 + 1.185 + return true; 1.186 +} 1.187 + 1.188 +void WaitableEventWatcher::StopWatching() { 1.189 + if (message_loop_) { 1.190 + message_loop_->RemoveDestructionObserver(this); 1.191 + message_loop_ = NULL; 1.192 + } 1.193 + 1.194 + if (!cancel_flag_.get()) // if not currently watching... 1.195 + return; 1.196 + 1.197 + if (cancel_flag_->value()) { 1.198 + // In this case, the event has fired, but we haven't figured that out yet. 1.199 + // The WaitableEvent may have been deleted too. 1.200 + cancel_flag_ = NULL; 1.201 + return; 1.202 + } 1.203 + 1.204 + if (!kernel_.get()) { 1.205 + // We have no kernel. This means that we never enqueued a Waiter on an 1.206 + // event because the event was already signaled when StartWatching was 1.207 + // called. 1.208 + // 1.209 + // In this case, a task was enqueued on the MessageLoop and will run. 1.210 + // We set the flag in case the task hasn't yet run. The flag will stop the 1.211 + // delegate getting called. If the task has run then we have the last 1.212 + // reference to the flag and it will be deleted immedately after. 1.213 + cancel_flag_->Set(); 1.214 + cancel_flag_ = NULL; 1.215 + return; 1.216 + } 1.217 + 1.218 + AutoLock locked(kernel_->lock_); 1.219 + // We have a lock on the kernel. No one else can signal the event while we 1.220 + // have it. 1.221 + 1.222 + // We have a possible ABA issue here. If Dequeue was to compare only the 1.223 + // pointer values then it's possible that the AsyncWaiter could have been 1.224 + // fired, freed and the memory reused for a different Waiter which was 1.225 + // enqueued in the same wait-list. We would think that that waiter was our 1.226 + // AsyncWaiter and remove it. 1.227 + // 1.228 + // To stop this, Dequeue also takes a tag argument which is passed to the 1.229 + // virtual Compare function before the two are considered a match. So we need 1.230 + // a tag which is good for the lifetime of this handle: the Flag. Since we 1.231 + // have a reference to the Flag, its memory cannot be reused while this object 1.232 + // still exists. So if we find a waiter with the correct pointer value, and 1.233 + // which shares a Flag pointer, we have a real match. 1.234 + if (kernel_->Dequeue(waiter_, cancel_flag_.get())) { 1.235 + // Case 2: the waiter hasn't been signaled yet; it was still on the wait 1.236 + // list. We've removed it, thus we can delete it and the task (which cannot 1.237 + // have been enqueued with the MessageLoop because the waiter was never 1.238 + // signaled) 1.239 + delete waiter_; 1.240 + delete callback_task_; 1.241 + cancel_flag_ = NULL; 1.242 + return; 1.243 + } 1.244 + 1.245 + // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may 1.246 + // not have run yet, so we set the flag to tell it not to bother enqueuing the 1.247 + // task on the MessageLoop, but to delete it instead. The Waiter deletes 1.248 + // itself once run. 1.249 + cancel_flag_->Set(); 1.250 + cancel_flag_ = NULL; 1.251 + 1.252 + // If the waiter has already run then the task has been enqueued. If the Task 1.253 + // hasn't yet run, the flag will stop the delegate from getting called. (This 1.254 + // is thread safe because one may only delete a Handle from the MessageLoop 1.255 + // thread.) 1.256 + // 1.257 + // If the delegate has already been called then we have nothing to do. The 1.258 + // task has been deleted by the MessageLoop. 1.259 +} 1.260 + 1.261 +WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { 1.262 + if (!cancel_flag_.get()) 1.263 + return NULL; 1.264 + 1.265 + if (cancel_flag_->value()) 1.266 + return NULL; 1.267 + 1.268 + return event_; 1.269 +} 1.270 + 1.271 +// ----------------------------------------------------------------------------- 1.272 +// This is called when the MessageLoop which the callback will be run it is 1.273 +// deleted. We need to cancel the callback as if we had been deleted, but we 1.274 +// will still be deleted at some point in the future. 1.275 +// ----------------------------------------------------------------------------- 1.276 +void WaitableEventWatcher::WillDestroyCurrentMessageLoop() { 1.277 + StopWatching(); 1.278 +} 1.279 + 1.280 +} // namespace base