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
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 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// -----------------------------------------------------------------------------
32class 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// -----------------------------------------------------------------------------
60class 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// -----------------------------------------------------------------------------
98void 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
110WaitableEventWatcher::WaitableEventWatcher()
111    : message_loop_(NULL),
112      cancel_flag_(NULL),
113      waiter_(NULL),
114      event_(NULL) {
115}
116
117WaitableEventWatcher::~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// -----------------------------------------------------------------------------
125bool 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_ = base::Bind(
149      &AsyncCallbackHelper, base::RetainedRef(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
176void 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
251WaitableEvent* 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// -----------------------------------------------------------------------------
266void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
267  StopWatching();
268}
269
270}  // namespace base
271