• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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