1// Copyright (c) 2012 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/bind.h"
8#include "base/logging.h"
9
10namespace base {
11namespace win {
12
13//-----------------------------------------------------------------------------
14
15ObjectWatcher::ObjectWatcher()
16    : weak_factory_(this),
17      object_(NULL),
18      wait_object_(NULL),
19      origin_loop_(NULL) {
20}
21
22ObjectWatcher::~ObjectWatcher() {
23  StopWatching();
24}
25
26bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
27  CHECK(delegate);
28  if (wait_object_) {
29    NOTREACHED() << "Already watching an object";
30    return false;
31  }
32
33  // Since our job is to just notice when an object is signaled and report the
34  // result back to this thread, we can just run on a Windows wait thread.
35  DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;
36
37  // DoneWaiting can be synchronously called from RegisterWaitForSingleObject,
38  // so set up all state now.
39  callback_ = base::Bind(&ObjectWatcher::Signal, weak_factory_.GetWeakPtr(),
40                         delegate);
41  object_ = object;
42  origin_loop_ = MessageLoop::current();
43
44  if (!RegisterWaitForSingleObject(&wait_object_, object, DoneWaiting,
45                                   this, INFINITE, wait_flags)) {
46    DLOG_GETLASTERROR(FATAL) << "RegisterWaitForSingleObject failed";
47    object_ = NULL;
48    wait_object_ = NULL;
49    return false;
50  }
51
52  // We need to know if the current message loop is going away so we can
53  // prevent the wait thread from trying to access a dead message loop.
54  MessageLoop::current()->AddDestructionObserver(this);
55  return true;
56}
57
58bool ObjectWatcher::StopWatching() {
59  if (!wait_object_)
60    return false;
61
62  // Make sure ObjectWatcher is used in a single-threaded fashion.
63  DCHECK_EQ(origin_loop_, MessageLoop::current());
64
65  // Blocking call to cancel the wait. Any callbacks already in progress will
66  // finish before we return from this call.
67  if (!UnregisterWaitEx(wait_object_, INVALID_HANDLE_VALUE)) {
68    DLOG_GETLASTERROR(FATAL) << "UnregisterWaitEx failed";
69    return false;
70  }
71
72  weak_factory_.InvalidateWeakPtrs();
73  object_ = NULL;
74  wait_object_ = NULL;
75
76  MessageLoop::current()->RemoveDestructionObserver(this);
77  return true;
78}
79
80HANDLE ObjectWatcher::GetWatchedObject() {
81  return object_;
82}
83
84// static
85void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
86  DCHECK(!timed_out);
87
88  // The destructor blocks on any callbacks that are in flight, so we know that
89  // that is always a pointer to a valid ObjectWater.
90  ObjectWatcher* that = static_cast<ObjectWatcher*>(param);
91  that->origin_loop_->PostTask(FROM_HERE, that->callback_);
92  that->callback_.Reset();
93}
94
95void ObjectWatcher::Signal(Delegate* delegate) {
96  // Signaling the delegate may result in our destruction or a nested call to
97  // StartWatching(). As a result, we save any state we need and clear previous
98  // watcher state before signaling the delegate.
99  HANDLE object = object_;
100  StopWatching();
101  delegate->OnObjectSignaled(object);
102}
103
104void ObjectWatcher::WillDestroyCurrentMessageLoop() {
105  // Need to shutdown the watch so that we don't try to access the MessageLoop
106  // after this point.
107  StopWatching();
108}
109
110}  // namespace win
111}  // namespace base
112