1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
7 #include "base/object_watcher.h"
9 #include "base/logging.h"
13 //-----------------------------------------------------------------------------
15 class ObjectWatcher::Watch
: public mozilla::Runnable
{
17 ObjectWatcher
* watcher
; // The associated ObjectWatcher instance
18 HANDLE object
; // The object being watched
19 HANDLE wait_object
; // Returned by RegisterWaitForSingleObject
20 MessageLoop
* origin_loop
; // Used to get back to the origin thread
21 Delegate
* delegate
; // Delegate to notify when signaled
22 bool did_signal
; // DoneWaiting was called
24 Watch() : mozilla::Runnable("ObjectWatcher::Watch") {}
26 NS_IMETHOD
Run() override
{
27 // The watcher may have already been torn down, in which case we need to
28 // just get out of dodge.
29 if (!watcher
) return NS_OK
;
32 watcher
->StopWatching();
34 delegate
->OnObjectSignaled(object
);
40 //-----------------------------------------------------------------------------
42 ObjectWatcher::ObjectWatcher() : watch_(nullptr) {}
44 ObjectWatcher::~ObjectWatcher() { StopWatching(); }
46 bool ObjectWatcher::StartWatching(HANDLE object
, Delegate
* delegate
) {
48 NOTREACHED() << "Already watching an object";
52 RefPtr
<Watch
> watch
= new Watch
;
53 watch
->watcher
= this;
54 watch
->object
= object
;
55 watch
->origin_loop
= MessageLoop::current();
56 watch
->delegate
= delegate
;
57 watch
->did_signal
= false;
59 // Since our job is to just notice when an object is signaled and report the
60 // result back to this thread, we can just run on a Windows wait thread.
61 DWORD wait_flags
= WT_EXECUTEDEFAULT
| WT_EXECUTEONLYONCE
;
63 if (!RegisterWaitForSingleObject(&watch
->wait_object
, object
, DoneWaiting
,
64 watch
.get(), INFINITE
, wait_flags
)) {
65 NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
69 watch_
= watch
.forget();
71 // We need to know if the current message loop is going away so we can
72 // prevent the wait thread from trying to access a dead message loop.
73 MessageLoop::current()->AddDestructionObserver(this);
77 bool ObjectWatcher::StopWatching() {
78 if (!watch_
) return false;
80 // Make sure ObjectWatcher is used in a single-threaded fashion.
81 DCHECK(watch_
->origin_loop
== MessageLoop::current());
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();
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.
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
;
101 MessageLoop::current()->RemoveDestructionObserver(this);
105 HANDLE
ObjectWatcher::GetWatchedObject() {
106 if (!watch_
) return NULL
;
108 return watch_
->object
;
112 void CALLBACK
ObjectWatcher::DoneWaiting(void* param
, BOOLEAN timed_out
) {
115 Watch
* watch
= static_cast<Watch
*>(param
);
116 RefPtr
<Watch
> addrefedWatch
= watch
;
118 // Record that we ran this function.
119 watch
->did_signal
= true;
121 // We rely on the locking in PostTask() to ensure that a memory barrier is
122 // provided, which in turn ensures our change to did_signal can be observed
123 // on the target thread.
124 if (watch
->origin_loop
->IsAcceptingTasks()) {
125 watch
->origin_loop
->PostTask(addrefedWatch
.forget());
129 void ObjectWatcher::WillDestroyCurrentMessageLoop() {
130 // Need to shutdown the watch so that we don't try to access the MessageLoop