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