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