1 // Copyright 2017 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 "mojo/public/cpp/system/handle_signal_tracker.h"
6 
7 #include "base/bind.h"
8 #include "base/synchronization/lock.h"
9 #include "mojo/public/cpp/system/handle_signals_state.h"
10 
11 namespace mojo {
12 
HandleSignalTracker(Handle handle,MojoHandleSignals signals,scoped_refptr<base::SequencedTaskRunner> task_runner)13 HandleSignalTracker::HandleSignalTracker(
14     Handle handle,
15     MojoHandleSignals signals,
16     scoped_refptr<base::SequencedTaskRunner> task_runner)
17     : high_watcher_(FROM_HERE,
18                     SimpleWatcher::ArmingPolicy::MANUAL,
19                     task_runner),
20       low_watcher_(FROM_HERE,
21                    SimpleWatcher::ArmingPolicy::MANUAL,
22                    std::move(task_runner)) {
23   MojoResult rv = high_watcher_.Watch(
24       handle, signals, MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
25       base::BindRepeating(&HandleSignalTracker::OnNotify,
26                           base::Unretained(this)));
27   DCHECK_EQ(MOJO_RESULT_OK, rv);
28 
29   rv = low_watcher_.Watch(handle, signals,
30                           MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED,
31                           base::BindRepeating(&HandleSignalTracker::OnNotify,
32                                               base::Unretained(this)));
33   DCHECK_EQ(MOJO_RESULT_OK, rv);
34 
35   last_known_state_ = handle.QuerySignalsState();
36 
37   Arm();
38 }
39 
40 HandleSignalTracker::~HandleSignalTracker() = default;
41 
Arm()42 void HandleSignalTracker::Arm() {
43   // Arm either the low watcher or high watcher. We cycle until one of them
44   // succeeds, which should almost always happen within two iterations.
45   bool arm_low_watcher = true;
46   for (;;) {
47     MojoResult ready_result;
48     SimpleWatcher& watcher = arm_low_watcher ? low_watcher_ : high_watcher_;
49     MojoResult result = watcher.Arm(&ready_result, &last_known_state_);
50     if (result == MOJO_RESULT_OK) {
51       // Successfully armed one of the watchers, so we can go back to waiting
52       // for a notification.
53       return;
54     }
55 
56     DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
57     if (ready_result == MOJO_RESULT_FAILED_PRECONDITION && !arm_low_watcher) {
58       // The high watcher failed to arm because the watched signal will never
59       // be satisfied again. We can also return in this case, and
60       // |last_known_state_| will remain with its current value indefinitely.
61       return;
62     }
63     arm_low_watcher = !arm_low_watcher;
64   }
65 }
66 
OnNotify(MojoResult result,const HandleSignalsState & state)67 void HandleSignalTracker::OnNotify(MojoResult result,
68                                    const HandleSignalsState& state) {
69   last_known_state_ = state;
70   Arm();
71   if (notification_callback_)
72     notification_callback_.Run(state);
73 }
74 
75 }  // namespace mojo
76