1 // Copyright (c) 2011 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 "base/synchronization/waitable_event_watcher.h" 6 7 #include "base/message_loop.h" 8 #include "base/synchronization/lock.h" 9 #include "base/synchronization/waitable_event.h" 10 11 namespace base { 12 13 // ----------------------------------------------------------------------------- 14 // WaitableEventWatcher (async waits). 15 // 16 // The basic design is that we add an AsyncWaiter to the wait-list of the event. 17 // That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it. 18 // The MessageLoop ends up running the task, which calls the delegate. 19 // 20 // Since the wait can be canceled, we have a thread-safe Flag object which is 21 // set when the wait has been canceled. At each stage in the above, we check the 22 // flag before going onto the next stage. Since the wait may only be canceled in 23 // the MessageLoop which runs the Task, we are assured that the delegate cannot 24 // be called after canceling... 25 26 // ----------------------------------------------------------------------------- 27 // A thread-safe, reference-counted, write-once flag. 28 // ----------------------------------------------------------------------------- 29 class Flag : public RefCountedThreadSafe<Flag> { 30 public: 31 Flag() { flag_ = false; } 32 33 void Set() { 34 AutoLock locked(lock_); 35 flag_ = true; 36 } 37 38 bool value() const { 39 AutoLock locked(lock_); 40 return flag_; 41 } 42 43 private: 44 mutable Lock lock_; 45 bool flag_; 46 }; 47 48 // ----------------------------------------------------------------------------- 49 // This is an asynchronous waiter which posts a task to a MessageLoop when 50 // fired. An AsyncWaiter may only be in a single wait-list. 51 // ----------------------------------------------------------------------------- 52 class AsyncWaiter : public WaitableEvent::Waiter { 53 public: 54 AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag) 55 : message_loop_(message_loop), 56 cb_task_(task), 57 flag_(flag) { } 58 59 bool Fire(WaitableEvent* event) { 60 if (flag_->value()) { 61 // If the callback has been canceled, we don't enqueue the task, we just 62 // delete it instead. 63 delete cb_task_; 64 } else { 65 message_loop_->PostTask(FROM_HERE, cb_task_); 66 } 67 68 // We are removed from the wait-list by the WaitableEvent itself. It only 69 // remains to delete ourselves. 70 delete this; 71 72 // We can always return true because an AsyncWaiter is never in two 73 // different wait-lists at the same time. 74 return true; 75 } 76 77 // See StopWatching for discussion 78 bool Compare(void* tag) { 79 return tag == flag_.get(); 80 } 81 82 private: 83 MessageLoop *const message_loop_; 84 Task *const cb_task_; 85 scoped_refptr<Flag> flag_; 86 }; 87 88 // ----------------------------------------------------------------------------- 89 // For async waits we need to make a callback in a MessageLoop thread. We do 90 // this by posting this task, which calls the delegate and keeps track of when 91 // the event is canceled. 92 // ----------------------------------------------------------------------------- 93 class AsyncCallbackTask : public Task { 94 public: 95 AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate, 96 WaitableEvent* event) 97 : flag_(flag), 98 delegate_(delegate), 99 event_(event) { 100 } 101 102 void Run() { 103 // Runs in MessageLoop thread. 104 if (!flag_->value()) { 105 // This is to let the WaitableEventWatcher know that the event has occured 106 // because it needs to be able to return NULL from GetWatchedObject 107 flag_->Set(); 108 delegate_->OnWaitableEventSignaled(event_); 109 } 110 111 // We are deleted by the MessageLoop 112 } 113 114 private: 115 scoped_refptr<Flag> flag_; 116 WaitableEventWatcher::Delegate *const delegate_; 117 WaitableEvent *const event_; 118 }; 119 120 WaitableEventWatcher::WaitableEventWatcher() 121 : message_loop_(NULL), 122 cancel_flag_(NULL), 123 waiter_(NULL), 124 callback_task_(NULL), 125 event_(NULL), 126 delegate_(NULL) { 127 } 128 129 WaitableEventWatcher::~WaitableEventWatcher() { 130 StopWatching(); 131 } 132 133 // ----------------------------------------------------------------------------- 134 // The Handle is how the user cancels a wait. After deleting the Handle we 135 // insure that the delegate cannot be called. 136 // ----------------------------------------------------------------------------- 137 bool WaitableEventWatcher::StartWatching 138 (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) { 139 MessageLoop *const current_ml = MessageLoop::current(); 140 DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a " 141 "current MessageLoop"; 142 143 // A user may call StartWatching from within the callback function. In this 144 // case, we won't know that we have finished watching, expect that the Flag 145 // will have been set in AsyncCallbackTask::Run() 146 if (cancel_flag_.get() && cancel_flag_->value()) { 147 if (message_loop_) { 148 message_loop_->RemoveDestructionObserver(this); 149 message_loop_ = NULL; 150 } 151 152 cancel_flag_ = NULL; 153 } 154 155 DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching"; 156 157 cancel_flag_ = new Flag; 158 callback_task_ = new AsyncCallbackTask(cancel_flag_, delegate, event); 159 WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get(); 160 161 AutoLock locked(kernel->lock_); 162 163 delegate_ = delegate; 164 event_ = event; 165 166 if (kernel->signaled_) { 167 if (!kernel->manual_reset_) 168 kernel->signaled_ = false; 169 170 // No hairpinning - we can't call the delegate directly here. We have to 171 // enqueue a task on the MessageLoop as normal. 172 current_ml->PostTask(FROM_HERE, callback_task_); 173 return true; 174 } 175 176 message_loop_ = current_ml; 177 current_ml->AddDestructionObserver(this); 178 179 kernel_ = kernel; 180 waiter_ = new AsyncWaiter(current_ml, callback_task_, cancel_flag_); 181 event->Enqueue(waiter_); 182 183 return true; 184 } 185 186 void WaitableEventWatcher::StopWatching() { 187 delegate_ = NULL; 188 189 if (message_loop_) { 190 message_loop_->RemoveDestructionObserver(this); 191 message_loop_ = NULL; 192 } 193 194 if (!cancel_flag_.get()) // if not currently watching... 195 return; 196 197 if (cancel_flag_->value()) { 198 // In this case, the event has fired, but we haven't figured that out yet. 199 // The WaitableEvent may have been deleted too. 200 cancel_flag_ = NULL; 201 return; 202 } 203 204 if (!kernel_.get()) { 205 // We have no kernel. This means that we never enqueued a Waiter on an 206 // event because the event was already signaled when StartWatching was 207 // called. 208 // 209 // In this case, a task was enqueued on the MessageLoop and will run. 210 // We set the flag in case the task hasn't yet run. The flag will stop the 211 // delegate getting called. If the task has run then we have the last 212 // reference to the flag and it will be deleted immedately after. 213 cancel_flag_->Set(); 214 cancel_flag_ = NULL; 215 return; 216 } 217 218 AutoLock locked(kernel_->lock_); 219 // We have a lock on the kernel. No one else can signal the event while we 220 // have it. 221 222 // We have a possible ABA issue here. If Dequeue was to compare only the 223 // pointer values then it's possible that the AsyncWaiter could have been 224 // fired, freed and the memory reused for a different Waiter which was 225 // enqueued in the same wait-list. We would think that that waiter was our 226 // AsyncWaiter and remove it. 227 // 228 // To stop this, Dequeue also takes a tag argument which is passed to the 229 // virtual Compare function before the two are considered a match. So we need 230 // a tag which is good for the lifetime of this handle: the Flag. Since we 231 // have a reference to the Flag, its memory cannot be reused while this object 232 // still exists. So if we find a waiter with the correct pointer value, and 233 // which shares a Flag pointer, we have a real match. 234 if (kernel_->Dequeue(waiter_, cancel_flag_.get())) { 235 // Case 2: the waiter hasn't been signaled yet; it was still on the wait 236 // list. We've removed it, thus we can delete it and the task (which cannot 237 // have been enqueued with the MessageLoop because the waiter was never 238 // signaled) 239 delete waiter_; 240 delete callback_task_; 241 cancel_flag_ = NULL; 242 return; 243 } 244 245 // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may 246 // not have run yet, so we set the flag to tell it not to bother enqueuing the 247 // task on the MessageLoop, but to delete it instead. The Waiter deletes 248 // itself once run. 249 cancel_flag_->Set(); 250 cancel_flag_ = NULL; 251 252 // If the waiter has already run then the task has been enqueued. If the Task 253 // hasn't yet run, the flag will stop the delegate from getting called. (This 254 // is thread safe because one may only delete a Handle from the MessageLoop 255 // thread.) 256 // 257 // If the delegate has already been called then we have nothing to do. The 258 // task has been deleted by the MessageLoop. 259 } 260 261 WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { 262 if (!cancel_flag_.get()) 263 return NULL; 264 265 if (cancel_flag_->value()) 266 return NULL; 267 268 return event_; 269 } 270 271 // ----------------------------------------------------------------------------- 272 // This is called when the MessageLoop which the callback will be run it is 273 // deleted. We need to cancel the callback as if we had been deleted, but we 274 // will still be deleted at some point in the future. 275 // ----------------------------------------------------------------------------- 276 void WaitableEventWatcher::WillDestroyCurrentMessageLoop() { 277 StopWatching(); 278 } 279 280 } // namespace base 281