1 // Copyright 2016 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/bindings/sync_handle_registry.h"
6
7 #include <algorithm>
8
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/threading/sequence_local_storage_slot.h"
13 #include "base/threading/sequenced_task_runner_handle.h"
14 #include "mojo/public/c/system/core.h"
15
16 namespace mojo {
17 namespace {
18
19 base::LazyInstance<
20 base::SequenceLocalStorageSlot<scoped_refptr<SyncHandleRegistry>>>::Leaky
21 g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER;
22
23 } // namespace
24
25 // static
current()26 scoped_refptr<SyncHandleRegistry> SyncHandleRegistry::current() {
27 // SyncMessageFilter can be used on threads without sequence-local storage
28 // being available. Those receive a unique, standalone SyncHandleRegistry.
29 if (!base::SequencedTaskRunnerHandle::IsSet())
30 return new SyncHandleRegistry();
31
32 scoped_refptr<SyncHandleRegistry> result =
33 g_current_sync_handle_watcher.Get().Get();
34 if (!result) {
35 result = new SyncHandleRegistry();
36 g_current_sync_handle_watcher.Get().Set(result);
37 }
38 return result;
39 }
40
RegisterHandle(const Handle & handle,MojoHandleSignals handle_signals,const HandleCallback & callback)41 bool SyncHandleRegistry::RegisterHandle(const Handle& handle,
42 MojoHandleSignals handle_signals,
43 const HandleCallback& callback) {
44 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
45
46 if (base::ContainsKey(handles_, handle))
47 return false;
48
49 MojoResult result = wait_set_.AddHandle(handle, handle_signals);
50 if (result != MOJO_RESULT_OK)
51 return false;
52
53 handles_[handle] = callback;
54 return true;
55 }
56
UnregisterHandle(const Handle & handle)57 void SyncHandleRegistry::UnregisterHandle(const Handle& handle) {
58 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
59 if (!base::ContainsKey(handles_, handle))
60 return;
61
62 MojoResult result = wait_set_.RemoveHandle(handle);
63 DCHECK_EQ(MOJO_RESULT_OK, result);
64 handles_.erase(handle);
65 }
66
RegisterEvent(base::WaitableEvent * event,const base::Closure & callback)67 void SyncHandleRegistry::RegisterEvent(base::WaitableEvent* event,
68 const base::Closure& callback) {
69 auto it = events_.find(event);
70 if (it == events_.end()) {
71 auto result = events_.emplace(event, EventCallbackList{});
72 it = result.first;
73 }
74
75 // The event may already be in the WaitSet, but we don't care. This will be a
76 // no-op in that case, which is more efficient than scanning the list of
77 // callbacks to see if any are valid.
78 wait_set_.AddEvent(event);
79
80 it->second.container().push_back(callback);
81 }
82
UnregisterEvent(base::WaitableEvent * event,const base::Closure & callback)83 void SyncHandleRegistry::UnregisterEvent(base::WaitableEvent* event,
84 const base::Closure& callback) {
85 auto it = events_.find(event);
86 if (it == events_.end())
87 return;
88
89 bool has_valid_callbacks = false;
90 auto& callbacks = it->second.container();
91 if (is_dispatching_event_callbacks_) {
92 // Not safe to remove any elements from |callbacks| here since an outer
93 // stack frame is currently iterating over it in Wait().
94 for (auto& cb : callbacks) {
95 if (cb.Equals(callback))
96 cb.Reset();
97 else if (cb)
98 has_valid_callbacks = true;
99 }
100 remove_invalid_event_callbacks_after_dispatch_ = true;
101 } else {
102 callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
103 [&callback](const base::Closure& cb) {
104 return cb.Equals(callback);
105 }),
106 callbacks.end());
107 if (callbacks.empty())
108 events_.erase(it);
109 else
110 has_valid_callbacks = true;
111 }
112
113 if (!has_valid_callbacks) {
114 // Regardless of whether or not we're nested within a Wait(), we need to
115 // ensure that |event| is removed from the WaitSet before returning if this
116 // was the last callback registered for it.
117 MojoResult rv = wait_set_.RemoveEvent(event);
118 DCHECK_EQ(MOJO_RESULT_OK, rv);
119 }
120 }
121
Wait(const bool * should_stop[],size_t count)122 bool SyncHandleRegistry::Wait(const bool* should_stop[], size_t count) {
123 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
124
125 size_t num_ready_handles;
126 Handle ready_handle;
127 MojoResult ready_handle_result;
128
129 scoped_refptr<SyncHandleRegistry> preserver(this);
130 while (true) {
131 for (size_t i = 0; i < count; ++i) {
132 if (*should_stop[i])
133 return true;
134 }
135
136 // TODO(yzshen): Theoretically it can reduce sync call re-entrancy if we
137 // give priority to the handle that is waiting for sync response.
138 base::WaitableEvent* ready_event = nullptr;
139 num_ready_handles = 1;
140 wait_set_.Wait(&ready_event, &num_ready_handles, &ready_handle,
141 &ready_handle_result);
142 if (num_ready_handles) {
143 DCHECK_EQ(1u, num_ready_handles);
144 const auto iter = handles_.find(ready_handle);
145 iter->second.Run(ready_handle_result);
146 }
147
148 if (ready_event) {
149 const auto iter = events_.find(ready_event);
150 DCHECK(iter != events_.end());
151 bool was_dispatching_event_callbacks = is_dispatching_event_callbacks_;
152 is_dispatching_event_callbacks_ = true;
153
154 // NOTE: It's possible for the container to be extended by any of these
155 // callbacks if they call RegisterEvent, so we are careful to iterate by
156 // index. Also note that conversely, elements cannot be *removed* from the
157 // container, by any of these callbacks, so it is safe to assume the size
158 // only stays the same or increases, with no elements changing position.
159 auto& callbacks = iter->second.container();
160 for (size_t i = 0; i < callbacks.size(); ++i) {
161 auto& callback = callbacks[i];
162 if (callback)
163 callback.Run();
164 }
165
166 is_dispatching_event_callbacks_ = was_dispatching_event_callbacks;
167 if (!was_dispatching_event_callbacks &&
168 remove_invalid_event_callbacks_after_dispatch_) {
169 // If we've had events unregistered within any callback dispatch, now is
170 // a good time to prune them from the map.
171 RemoveInvalidEventCallbacks();
172 remove_invalid_event_callbacks_after_dispatch_ = false;
173 }
174 }
175 };
176
177 return false;
178 }
179
180 SyncHandleRegistry::SyncHandleRegistry() = default;
181
182 SyncHandleRegistry::~SyncHandleRegistry() = default;
183
RemoveInvalidEventCallbacks()184 void SyncHandleRegistry::RemoveInvalidEventCallbacks() {
185 for (auto it = events_.begin(); it != events_.end();) {
186 auto& callbacks = it->second.container();
187 callbacks.erase(
188 std::remove_if(callbacks.begin(), callbacks.end(),
189 [](const base::Closure& callback) { return !callback; }),
190 callbacks.end());
191 if (callbacks.empty())
192 events_.erase(it++);
193 else
194 ++it;
195 }
196 }
197
198 } // namespace mojo
199