object_watcher.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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
9namespace base {
10namespace win {
11
12//-----------------------------------------------------------------------------
13
14struct 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
22  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
37ObjectWatcher::ObjectWatcher() : watch_(NULL) {
38}
39
40ObjectWatcher::~ObjectWatcher() {
41  StopWatching();
42}
43
44bool 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
76bool 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
111HANDLE ObjectWatcher::GetWatchedObject() {
112  if (!watch_)
113    return NULL;
114
115  return watch_->object;
116}
117
118// static
119void 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
133void 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