• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
Flag()31   Flag() { flag_ = false; }
32 
Set()33   void Set() {
34     AutoLock locked(lock_);
35     flag_ = true;
36   }
37 
value() const38   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:
AsyncWaiter(MessageLoop * message_loop,Task * task,Flag * flag)54   AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag)
55       : message_loop_(message_loop),
56         cb_task_(task),
57         flag_(flag) { }
58 
Fire(WaitableEvent * event)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
Compare(void * tag)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:
AsyncCallbackTask(Flag * flag,WaitableEventWatcher::Delegate * delegate,WaitableEvent * event)95   AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate,
96                     WaitableEvent* event)
97       : flag_(flag),
98         delegate_(delegate),
99         event_(event) {
100   }
101 
Run()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 
WaitableEventWatcher()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 
~WaitableEventWatcher()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 // -----------------------------------------------------------------------------
StartWatching(WaitableEvent * event,WaitableEventWatcher::Delegate * delegate)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 
StopWatching()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 
GetWatchedEvent()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 // -----------------------------------------------------------------------------
WillDestroyCurrentMessageLoop()276 void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
277   StopWatching();
278 }
279 
280 }  // namespace base
281