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