Home | History | Annotate | Download | only in synchronization
      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:
     31   Flag() { flag_ = false; }
     32 
     33   void Set() {
     34     AutoLock locked(lock_);
     35     flag_ = true;
     36   }
     37 
     38   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:
     54   AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag)
     55       : message_loop_(message_loop),
     56         cb_task_(task),
     57         flag_(flag) { }
     58 
     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
     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:
     95   AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate,
     96                     WaitableEvent* event)
     97       : flag_(flag),
     98         delegate_(delegate),
     99         event_(event) {
    100   }
    101 
    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 
    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 
    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 // -----------------------------------------------------------------------------
    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 
    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 
    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 // -----------------------------------------------------------------------------
    276 void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
    277   StopWatching();
    278 }
    279 
    280 }  // namespace base
    281