• 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/win/object_watcher.h"
6 
7 #include "base/logging.h"
8 
9 namespace base {
10 namespace win {
11 
12 //-----------------------------------------------------------------------------
13 
14 struct ObjectWatcher::Watch : public Task {
15   ObjectWatcher* watcher;    // The associated ObjectWatcher instance
16   HANDLE object;             // The object being watched
17   HANDLE wait_object;        // Returned by RegisterWaitForSingleObject
18   MessageLoop* origin_loop;  // Used to get back to the origin thread
19   Delegate* delegate;        // Delegate to notify when signaled
20   bool did_signal;           // DoneWaiting was called
21 
Runbase::win::ObjectWatcher::Watch22   virtual void Run() {
23     // The watcher may have already been torn down, in which case we need to
24     // just get out of dodge.
25     if (!watcher)
26       return;
27 
28     DCHECK(did_signal);
29     watcher->StopWatching();
30 
31     delegate->OnObjectSignaled(object);
32   }
33 };
34 
35 //-----------------------------------------------------------------------------
36 
ObjectWatcher()37 ObjectWatcher::ObjectWatcher() : watch_(NULL) {
38 }
39 
~ObjectWatcher()40 ObjectWatcher::~ObjectWatcher() {
41   StopWatching();
42 }
43 
StartWatching(HANDLE object,Delegate * delegate)44 bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
45   if (watch_) {
46     NOTREACHED() << "Already watching an object";
47     return false;
48   }
49 
50   Watch* watch = new Watch;
51   watch->watcher = this;
52   watch->object = object;
53   watch->origin_loop = MessageLoop::current();
54   watch->delegate = delegate;
55   watch->did_signal = false;
56 
57   // Since our job is to just notice when an object is signaled and report the
58   // result back to this thread, we can just run on a Windows wait thread.
59   DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;
60 
61   if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting,
62                                    watch, INFINITE, wait_flags)) {
63     NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
64     delete watch;
65     return false;
66   }
67 
68   watch_ = watch;
69 
70   // We need to know if the current message loop is going away so we can
71   // prevent the wait thread from trying to access a dead message loop.
72   MessageLoop::current()->AddDestructionObserver(this);
73   return true;
74 }
75 
StopWatching()76 bool ObjectWatcher::StopWatching() {
77   if (!watch_)
78     return false;
79 
80   // Make sure ObjectWatcher is used in a single-threaded fashion.
81   DCHECK(watch_->origin_loop == MessageLoop::current());
82 
83   // If DoneWaiting is in progress, we wait for it to finish.  We know whether
84   // DoneWaiting happened or not by inspecting the did_signal flag.
85   if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) {
86     NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError();
87     return false;
88   }
89 
90   // Make sure that we see any mutation to did_signal.  This should be a no-op
91   // since we expect that UnregisterWaitEx resulted in a memory barrier, but
92   // just to be sure, we're going to be explicit.
93   MemoryBarrier();
94 
95   // If the watch has been posted, then we need to make sure it knows not to do
96   // anything once it is run.
97   watch_->watcher = NULL;
98 
99   // If DoneWaiting was called, then the watch would have been posted as a
100   // task, and will therefore be deleted by the MessageLoop.  Otherwise, we
101   // need to take care to delete it here.
102   if (!watch_->did_signal)
103     delete watch_;
104 
105   watch_ = NULL;
106 
107   MessageLoop::current()->RemoveDestructionObserver(this);
108   return true;
109 }
110 
GetWatchedObject()111 HANDLE ObjectWatcher::GetWatchedObject() {
112   if (!watch_)
113     return NULL;
114 
115   return watch_->object;
116 }
117 
118 // static
DoneWaiting(void * param,BOOLEAN timed_out)119 void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
120   DCHECK(!timed_out);
121 
122   Watch* watch = static_cast<Watch*>(param);
123 
124   // Record that we ran this function.
125   watch->did_signal = true;
126 
127   // We rely on the locking in PostTask() to ensure that a memory barrier is
128   // provided, which in turn ensures our change to did_signal can be observed
129   // on the target thread.
130   watch->origin_loop->PostTask(FROM_HERE, watch);
131 }
132 
WillDestroyCurrentMessageLoop()133 void ObjectWatcher::WillDestroyCurrentMessageLoop() {
134   // Need to shutdown the watch so that we don't try to access the MessageLoop
135   // after this point.
136   StopWatching();
137 }
138 
139 }  // namespace win
140 }  // namespace base
141