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 <utility> 8 9#include "base/bind.h" 10#include "base/logging.h" 11#include "base/synchronization/lock.h" 12#include "base/threading/sequenced_task_runner_handle.h" 13 14namespace 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 SequencedTaskRunner, and a Task to be 21// posted to it. The task ends up calling the callback when it runs on the 22// sequence. 23// 24// Since the wait can be canceled, we have a thread-safe Flag object which is 25// set when the wait has been canceled. At each stage in the above, we check the 26// flag before going onto the next stage. Since the wait may only be canceled in 27// the sequence which runs the Task, we are assured that the callback cannot be 28// called after canceling... 29 30// ----------------------------------------------------------------------------- 31// A thread-safe, reference-counted, write-once flag. 32// ----------------------------------------------------------------------------- 33class Flag : public RefCountedThreadSafe<Flag> { 34 public: 35 Flag() { flag_ = false; } 36 37 void Set() { 38 AutoLock locked(lock_); 39 flag_ = true; 40 } 41 42 bool value() const { 43 AutoLock locked(lock_); 44 return flag_; 45 } 46 47 private: 48 friend class RefCountedThreadSafe<Flag>; 49 ~Flag() {} 50 51 mutable Lock lock_; 52 bool flag_; 53 54 DISALLOW_COPY_AND_ASSIGN(Flag); 55}; 56 57// ----------------------------------------------------------------------------- 58// This is an asynchronous waiter which posts a task to a SequencedTaskRunner 59// when fired. An AsyncWaiter may only be in a single wait-list. 60// ----------------------------------------------------------------------------- 61class AsyncWaiter : public WaitableEvent::Waiter { 62 public: 63 AsyncWaiter(scoped_refptr<SequencedTaskRunner> task_runner, 64 const base::Closure& callback, 65 Flag* flag) 66 : task_runner_(std::move(task_runner)), 67 callback_(callback), 68 flag_(flag) {} 69 70 bool Fire(WaitableEvent* event) override { 71 // Post the callback if we haven't been cancelled. 72 if (!flag_->value()) 73 task_runner_->PostTask(FROM_HERE, callback_); 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 const scoped_refptr<SequencedTaskRunner> task_runner_; 89 const base::Closure callback_; 90 const scoped_refptr<Flag> flag_; 91}; 92 93// ----------------------------------------------------------------------------- 94// For async waits we need to run a callback on a sequence. We do this by 95// posting an AsyncCallbackHelper task, which calls the callback and keeps track 96// of when the event is canceled. 97// ----------------------------------------------------------------------------- 98void AsyncCallbackHelper(Flag* flag, 99 const WaitableEventWatcher::EventCallback& callback, 100 WaitableEvent* event) { 101 // Runs on the sequence that called StartWatching(). 102 if (!flag->value()) { 103 // This is to let the WaitableEventWatcher know that the event has occured. 104 flag->Set(); 105 callback.Run(event); 106 } 107} 108 109WaitableEventWatcher::WaitableEventWatcher() { 110 sequence_checker_.DetachFromSequence(); 111} 112 113WaitableEventWatcher::~WaitableEventWatcher() { 114 // The destructor may be called from a different sequence than StartWatching() 115 // when there is no active watch. To avoid triggering a DCHECK in 116 // StopWatching(), do not call it when there is no active watch. 117 if (cancel_flag_ && !cancel_flag_->value()) 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// ----------------------------------------------------------------------------- 125bool WaitableEventWatcher::StartWatching( 126 WaitableEvent* event, 127 const EventCallback& callback) { 128 DCHECK(sequence_checker_.CalledOnValidSequence()); 129 DCHECK(SequencedTaskRunnerHandle::Get()); 130 131 // A user may call StartWatching from within the callback function. In this 132 // case, we won't know that we have finished watching, expect that the Flag 133 // will have been set in AsyncCallbackHelper(). 134 if (cancel_flag_.get() && cancel_flag_->value()) 135 cancel_flag_ = nullptr; 136 137 DCHECK(!cancel_flag_) << "StartWatching called while still watching"; 138 139 cancel_flag_ = new Flag; 140 const Closure internal_callback = base::Bind( 141 &AsyncCallbackHelper, base::RetainedRef(cancel_flag_), callback, event); 142 WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get(); 143 144 AutoLock locked(kernel->lock_); 145 146 if (kernel->signaled_) { 147 if (!kernel->manual_reset_) 148 kernel->signaled_ = false; 149 150 // No hairpinning - we can't call the delegate directly here. We have to 151 // post a task to the SequencedTaskRunnerHandle as usual. 152 SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, internal_callback); 153 return true; 154 } 155 156 kernel_ = kernel; 157 waiter_ = new AsyncWaiter(SequencedTaskRunnerHandle::Get(), internal_callback, 158 cancel_flag_.get()); 159 event->Enqueue(waiter_); 160 161 return true; 162} 163 164void WaitableEventWatcher::StopWatching() { 165 DCHECK(sequence_checker_.CalledOnValidSequence()); 166 167 if (!cancel_flag_.get()) // if not currently watching... 168 return; 169 170 if (cancel_flag_->value()) { 171 // In this case, the event has fired, but we haven't figured that out yet. 172 // The WaitableEvent may have been deleted too. 173 cancel_flag_ = NULL; 174 return; 175 } 176 177 if (!kernel_.get()) { 178 // We have no kernel. This means that we never enqueued a Waiter on an 179 // event because the event was already signaled when StartWatching was 180 // called. 181 // 182 // In this case, a task was enqueued on the MessageLoop and will run. 183 // We set the flag in case the task hasn't yet run. The flag will stop the 184 // delegate getting called. If the task has run then we have the last 185 // reference to the flag and it will be deleted immedately after. 186 cancel_flag_->Set(); 187 cancel_flag_ = NULL; 188 return; 189 } 190 191 AutoLock locked(kernel_->lock_); 192 // We have a lock on the kernel. No one else can signal the event while we 193 // have it. 194 195 // We have a possible ABA issue here. If Dequeue was to compare only the 196 // pointer values then it's possible that the AsyncWaiter could have been 197 // fired, freed and the memory reused for a different Waiter which was 198 // enqueued in the same wait-list. We would think that that waiter was our 199 // AsyncWaiter and remove it. 200 // 201 // To stop this, Dequeue also takes a tag argument which is passed to the 202 // virtual Compare function before the two are considered a match. So we need 203 // a tag which is good for the lifetime of this handle: the Flag. Since we 204 // have a reference to the Flag, its memory cannot be reused while this object 205 // still exists. So if we find a waiter with the correct pointer value, and 206 // which shares a Flag pointer, we have a real match. 207 if (kernel_->Dequeue(waiter_, cancel_flag_.get())) { 208 // Case 2: the waiter hasn't been signaled yet; it was still on the wait 209 // list. We've removed it, thus we can delete it and the task (which cannot 210 // have been enqueued with the MessageLoop because the waiter was never 211 // signaled) 212 delete waiter_; 213 cancel_flag_ = NULL; 214 return; 215 } 216 217 // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may not 218 // have run yet, so we set the flag to tell it not to bother enqueuing the 219 // task on the SequencedTaskRunner, but to delete it instead. The Waiter 220 // deletes itself once run. 221 cancel_flag_->Set(); 222 cancel_flag_ = NULL; 223 224 // If the waiter has already run then the task has been enqueued. If the Task 225 // hasn't yet run, the flag will stop the delegate from getting called. (This 226 // is thread safe because one may only delete a Handle from the sequence that 227 // called StartWatching()). 228 // 229 // If the delegate has already been called then we have nothing to do. The 230 // task has been deleted by the MessageLoop. 231} 232 233} // namespace base 234