LCOV - code coverage report
Current view: directory - ipc/chromium/src/base - waitable_event_watcher_posix.cc (source / functions) Found Hit Coverage
Test: app.info Lines: 90 3 3.3 %
Date: 2012-06-02 Functions: 19 1 5.3 %

       1                 : // Copyright (c) 2006-2008 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/waitable_event_watcher.h"
       6                 : 
       7                 : #include "base/condition_variable.h"
       8                 : #include "base/lock.h"
       9                 : #include "base/message_loop.h"
      10                 : #include "base/waitable_event.h"
      11                 : 
      12                 : namespace base {
      13                 : 
      14                 : // -----------------------------------------------------------------------------
      15                 : // WaitableEventWatcher (async waits).
      16                 : //
      17                 : // The basic design is that we add an AsyncWaiter to the wait-list of the event.
      18                 : // That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it.
      19                 : // The MessageLoop ends up running the task, which calls the delegate.
      20                 : //
      21                 : // Since the wait can be canceled, we have a thread-safe Flag object which is
      22                 : // set when the wait has been canceled. At each stage in the above, we check the
      23                 : // flag before going onto the next stage. Since the wait may only be canceled in
      24                 : // the MessageLoop which runs the Task, we are assured that the delegate cannot
      25                 : // be called after canceling...
      26                 : 
      27                 : // -----------------------------------------------------------------------------
      28                 : // A thread-safe, reference-counted, write-once flag.
      29                 : // -----------------------------------------------------------------------------
      30               0 : class Flag : public RefCountedThreadSafe<Flag> {
      31                 :  public:
      32               0 :   Flag() { flag_ = false; }
      33                 : 
      34               0 :   void Set() {
      35               0 :     AutoLock locked(lock_);
      36               0 :     flag_ = true;
      37               0 :   }
      38                 : 
      39               0 :   bool value() const {
      40               0 :     AutoLock locked(lock_);
      41               0 :     return flag_;
      42                 :   }
      43                 : 
      44                 :  private:
      45                 :   mutable Lock lock_;
      46                 :   bool flag_;
      47                 : };
      48                 : 
      49                 : // -----------------------------------------------------------------------------
      50                 : // This is an asynchronous waiter which posts a task to a MessageLoop when
      51                 : // fired. An AsyncWaiter may only be in a single wait-list.
      52                 : // -----------------------------------------------------------------------------
      53               0 : class AsyncWaiter : public WaitableEvent::Waiter {
      54                 :  public:
      55               0 :   AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag)
      56                 :       : message_loop_(message_loop),
      57                 :         cb_task_(task),
      58               0 :         flag_(flag) { }
      59                 : 
      60               0 :   bool Fire(WaitableEvent* event) {
      61               0 :     if (flag_->value()) {
      62                 :       // If the callback has been canceled, we don't enqueue the task, we just
      63                 :       // delete it instead.
      64               0 :       delete cb_task_;
      65                 :     } else {
      66               0 :       message_loop_->PostTask(FROM_HERE, cb_task_);
      67                 :     }
      68                 : 
      69                 :     // We are removed from the wait-list by the WaitableEvent itself. It only
      70                 :     // remains to delete ourselves.
      71               0 :     delete this;
      72                 : 
      73                 :     // We can always return true because an AsyncWaiter is never in two
      74                 :     // different wait-lists at the same time.
      75               0 :     return true;
      76                 :   }
      77                 : 
      78                 :   // See StopWatching for discussion
      79               0 :   bool Compare(void* tag) {
      80               0 :     return tag == flag_.get();
      81                 :   }
      82                 : 
      83                 :  private:
      84                 :   MessageLoop *const message_loop_;
      85                 :   Task *const cb_task_;
      86                 :   scoped_refptr<Flag> flag_;
      87                 : };
      88                 : 
      89                 : // -----------------------------------------------------------------------------
      90                 : // For async waits we need to make a callback in a MessageLoop thread. We do
      91                 : // this by posting this task, which calls the delegate and keeps track of when
      92                 : // the event is canceled.
      93                 : // -----------------------------------------------------------------------------
      94               0 : class AsyncCallbackTask : public Task {
      95                 :  public:
      96               0 :   AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate,
      97                 :                     WaitableEvent* event)
      98                 :       : flag_(flag),
      99                 :         delegate_(delegate),
     100               0 :         event_(event) {
     101               0 :   }
     102                 : 
     103               0 :   void Run() {
     104                 :     // Runs in MessageLoop thread.
     105               0 :     if (!flag_->value()) {
     106                 :       // This is to let the WaitableEventWatcher know that the event has occured
     107                 :       // because it needs to be able to return NULL from GetWatchedObject
     108               0 :       flag_->Set();
     109               0 :       delegate_->OnWaitableEventSignaled(event_);
     110                 :     }
     111                 : 
     112                 :     // We are deleted by the MessageLoop
     113               0 :   }
     114                 : 
     115                 :  private:
     116                 :   scoped_refptr<Flag> flag_;
     117                 :   WaitableEventWatcher::Delegate *const delegate_;
     118                 :   WaitableEvent *const event_;
     119                 : };
     120                 : 
     121               1 : WaitableEventWatcher::WaitableEventWatcher()
     122                 :     : event_(NULL),
     123                 :       message_loop_(NULL),
     124                 :       cancel_flag_(NULL),
     125               1 :       callback_task_(NULL) {
     126               1 : }
     127                 : 
     128               0 : WaitableEventWatcher::~WaitableEventWatcher() {
     129               0 :   StopWatching();
     130               0 : }
     131                 : 
     132                 : // -----------------------------------------------------------------------------
     133                 : // The Handle is how the user cancels a wait. After deleting the Handle we
     134                 : // insure that the delegate cannot be called.
     135                 : // -----------------------------------------------------------------------------
     136               0 : bool WaitableEventWatcher::StartWatching
     137                 :     (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) {
     138               0 :   MessageLoop *const current_ml = MessageLoop::current();
     139               0 :   DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a "
     140               0 :                         "current MessageLoop";
     141                 : 
     142                 :   // A user may call StartWatching from within the callback function. In this
     143                 :   // case, we won't know that we have finished watching, expect that the Flag
     144                 :   // will have been set in AsyncCallbackTask::Run()
     145               0 :   if (cancel_flag_.get() && cancel_flag_->value()) {
     146               0 :     if (message_loop_) {
     147               0 :       message_loop_->RemoveDestructionObserver(this);
     148               0 :       message_loop_ = NULL;
     149                 :     }
     150                 : 
     151               0 :     cancel_flag_ = NULL;
     152                 :   }
     153                 : 
     154               0 :   DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching";
     155                 : 
     156               0 :   cancel_flag_ = new Flag;
     157               0 :   callback_task_ = new AsyncCallbackTask(cancel_flag_, delegate, event);
     158               0 :   WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get();
     159                 : 
     160               0 :   AutoLock locked(kernel->lock_);
     161                 : 
     162               0 :   if (kernel->signaled_) {
     163               0 :     if (!kernel->manual_reset_)
     164               0 :       kernel->signaled_ = false;
     165                 : 
     166                 :     // No hairpinning - we can't call the delegate directly here. We have to
     167                 :     // enqueue a task on the MessageLoop as normal.
     168               0 :     current_ml->PostTask(FROM_HERE, callback_task_);
     169               0 :     return true;
     170                 :   }
     171                 : 
     172               0 :   message_loop_ = current_ml;
     173               0 :   current_ml->AddDestructionObserver(this);
     174                 : 
     175               0 :   event_ = event;
     176               0 :   kernel_ = kernel;
     177               0 :   waiter_ = new AsyncWaiter(current_ml, callback_task_, cancel_flag_);
     178               0 :   event->Enqueue(waiter_);
     179                 : 
     180               0 :   return true;
     181                 : }
     182                 : 
     183               0 : void WaitableEventWatcher::StopWatching() {
     184               0 :   if (message_loop_) {
     185               0 :     message_loop_->RemoveDestructionObserver(this);
     186               0 :     message_loop_ = NULL;
     187                 :   }
     188                 : 
     189               0 :   if (!cancel_flag_.get())  // if not currently watching...
     190               0 :     return;
     191                 : 
     192               0 :   if (cancel_flag_->value()) {
     193                 :     // In this case, the event has fired, but we haven't figured that out yet.
     194                 :     // The WaitableEvent may have been deleted too.
     195               0 :     cancel_flag_ = NULL;
     196               0 :     return;
     197                 :   }
     198                 : 
     199               0 :   if (!kernel_.get()) {
     200                 :     // We have no kernel. This means that we never enqueued a Waiter on an
     201                 :     // event because the event was already signaled when StartWatching was
     202                 :     // called.
     203                 :     //
     204                 :     // In this case, a task was enqueued on the MessageLoop and will run.
     205                 :     // We set the flag in case the task hasn't yet run. The flag will stop the
     206                 :     // delegate getting called. If the task has run then we have the last
     207                 :     // reference to the flag and it will be deleted immedately after.
     208               0 :     cancel_flag_->Set();
     209               0 :     cancel_flag_ = NULL;
     210               0 :     return;
     211                 :   }
     212                 : 
     213               0 :   AutoLock locked(kernel_->lock_);
     214                 :   // We have a lock on the kernel. No one else can signal the event while we
     215                 :   // have it.
     216                 : 
     217                 :   // We have a possible ABA issue here. If Dequeue was to compare only the
     218                 :   // pointer values then it's possible that the AsyncWaiter could have been
     219                 :   // fired, freed and the memory reused for a different Waiter which was
     220                 :   // enqueued in the same wait-list. We would think that that waiter was our
     221                 :   // AsyncWaiter and remove it.
     222                 :   //
     223                 :   // To stop this, Dequeue also takes a tag argument which is passed to the
     224                 :   // virtual Compare function before the two are considered a match. So we need
     225                 :   // a tag which is good for the lifetime of this handle: the Flag. Since we
     226                 :   // have a reference to the Flag, its memory cannot be reused while this object
     227                 :   // still exists. So if we find a waiter with the correct pointer value, and
     228                 :   // which shares a Flag pointer, we have a real match.
     229               0 :   if (kernel_->Dequeue(waiter_, cancel_flag_.get())) {
     230                 :     // Case 2: the waiter hasn't been signaled yet; it was still on the wait
     231                 :     // list. We've removed it, thus we can delete it and the task (which cannot
     232                 :     // have been enqueued with the MessageLoop because the waiter was never
     233                 :     // signaled)
     234               0 :     delete waiter_;
     235               0 :     delete callback_task_;
     236               0 :     cancel_flag_ = NULL;
     237                 :     return;
     238                 :   }
     239                 : 
     240                 :   // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may
     241                 :   // not have run yet, so we set the flag to tell it not to bother enqueuing the
     242                 :   // task on the MessageLoop, but to delete it instead. The Waiter deletes
     243                 :   // itself once run.
     244               0 :   cancel_flag_->Set();
     245               0 :   cancel_flag_ = NULL;
     246                 : 
     247                 :   // If the waiter has already run then the task has been enqueued. If the Task
     248                 :   // hasn't yet run, the flag will stop the delegate from getting called. (This
     249                 :   // is thread safe because one may only delete a Handle from the MessageLoop
     250                 :   // thread.)
     251                 :   //
     252                 :   // If the delegate has already been called then we have nothing to do. The
     253                 :   // task has been deleted by the MessageLoop.
     254                 : }
     255                 : 
     256               0 : WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
     257               0 :   if (!cancel_flag_.get())
     258               0 :     return NULL;
     259                 : 
     260               0 :   if (cancel_flag_->value())
     261               0 :     return NULL;
     262                 : 
     263               0 :   return event_;
     264                 : }
     265                 : 
     266                 : // -----------------------------------------------------------------------------
     267                 : // This is called when the MessageLoop which the callback will be run it is
     268                 : // deleted. We need to cancel the callback as if we had been deleted, but we
     269                 : // will still be deleted at some point in the future.
     270                 : // -----------------------------------------------------------------------------
     271               0 : void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
     272               0 :   StopWatching();
     273               0 : }
     274                 : 
     275                 : }  // namespace base

Generated by: LCOV version 1.7