1 // Copyright 2015 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/scoped_interface_endpoint_handle.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/synchronization/lock.h"
10 #include "base/threading/sequenced_task_runner_handle.h"
11 #include "mojo/public/cpp/bindings/associated_group_controller.h"
12 #include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
13
14 namespace mojo {
15
16 // ScopedInterfaceEndpointHandle::State ----------------------------------------
17
18 // State could be called from multiple sequences.
19 class ScopedInterfaceEndpointHandle::State
20 : public base::RefCountedThreadSafe<State> {
21 public:
22 State() = default;
23
State(InterfaceId id,scoped_refptr<AssociatedGroupController> group_controller)24 State(InterfaceId id,
25 scoped_refptr<AssociatedGroupController> group_controller)
26 : id_(id), group_controller_(group_controller) {}
27
InitPendingState(scoped_refptr<State> peer)28 void InitPendingState(scoped_refptr<State> peer) {
29 DCHECK(!lock_);
30 DCHECK(!pending_association_);
31
32 lock_.emplace();
33 pending_association_ = true;
34 peer_state_ = std::move(peer);
35 }
36
Close(const base::Optional<DisconnectReason> & reason)37 void Close(const base::Optional<DisconnectReason>& reason) {
38 scoped_refptr<AssociatedGroupController> cached_group_controller;
39 InterfaceId cached_id = kInvalidInterfaceId;
40 scoped_refptr<State> cached_peer_state;
41
42 {
43 internal::MayAutoLock locker(&lock_);
44
45 if (!association_event_handler_.is_null()) {
46 association_event_handler_.Reset();
47 runner_ = nullptr;
48 }
49
50 if (!pending_association_) {
51 if (IsValidInterfaceId(id_)) {
52 // Intentionally keep |group_controller_| unchanged.
53 // That is because the callback created by
54 // CreateGroupControllerGetter() could still be used after this point,
55 // potentially from another sequence. We would like it to continue
56 // returning the same group controller.
57 //
58 // Imagine there is a ThreadSafeForwarder A:
59 // (1) On the IO thread, A's underlying associated interface pointer
60 // is closed.
61 // (2) On the proxy thread, the user makes a call on A to pass an
62 // associated request B_asso_req. The callback returned by
63 // CreateGroupControllerGetter() is used to associate B_asso_req.
64 // (3) On the proxy thread, the user immediately binds B_asso_ptr_info
65 // to B_asso_ptr and makes calls on it.
66 //
67 // If we reset |group_controller_| in step (1), step (2) won't be able
68 // to associate B_asso_req. Therefore, in step (3) B_asso_ptr won't be
69 // able to serialize associated endpoints or send message because it
70 // is still in "pending_association" state and doesn't have a group
71 // controller.
72 //
73 // We could "address" this issue by ignoring messages if there isn't a
74 // group controller. But the side effect is that we cannot detect
75 // programming errors of "using associated interface pointer before
76 // sending associated request".
77
78 cached_group_controller = group_controller_;
79 cached_id = id_;
80 id_ = kInvalidInterfaceId;
81 }
82 } else {
83 pending_association_ = false;
84 cached_peer_state = std::move(peer_state_);
85 }
86 }
87
88 if (cached_group_controller) {
89 cached_group_controller->CloseEndpointHandle(cached_id, reason);
90 } else if (cached_peer_state) {
91 cached_peer_state->OnPeerClosedBeforeAssociation(reason);
92 }
93 }
94
SetAssociationEventHandler(AssociationEventCallback handler)95 void SetAssociationEventHandler(AssociationEventCallback handler) {
96 internal::MayAutoLock locker(&lock_);
97
98 if (!pending_association_ && !IsValidInterfaceId(id_))
99 return;
100
101 association_event_handler_ = std::move(handler);
102 if (association_event_handler_.is_null()) {
103 runner_ = nullptr;
104 return;
105 }
106
107 runner_ = base::SequencedTaskRunnerHandle::Get();
108 if (!pending_association_) {
109 runner_->PostTask(
110 FROM_HERE,
111 base::Bind(
112 &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
113 this, runner_, ASSOCIATED));
114 } else if (!peer_state_) {
115 runner_->PostTask(
116 FROM_HERE,
117 base::Bind(
118 &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
119 this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
120 }
121 }
122
NotifyAssociation(InterfaceId id,scoped_refptr<AssociatedGroupController> peer_group_controller)123 bool NotifyAssociation(
124 InterfaceId id,
125 scoped_refptr<AssociatedGroupController> peer_group_controller) {
126 scoped_refptr<State> cached_peer_state;
127 {
128 internal::MayAutoLock locker(&lock_);
129
130 DCHECK(pending_association_);
131 pending_association_ = false;
132 cached_peer_state = std::move(peer_state_);
133 }
134
135 if (cached_peer_state) {
136 cached_peer_state->OnAssociated(id, std::move(peer_group_controller));
137 return true;
138 }
139 return false;
140 }
141
is_valid() const142 bool is_valid() const {
143 internal::MayAutoLock locker(&lock_);
144 return pending_association_ || IsValidInterfaceId(id_);
145 }
146
pending_association() const147 bool pending_association() const {
148 internal::MayAutoLock locker(&lock_);
149 return pending_association_;
150 }
151
id() const152 InterfaceId id() const {
153 internal::MayAutoLock locker(&lock_);
154 return id_;
155 }
156
group_controller() const157 AssociatedGroupController* group_controller() const {
158 internal::MayAutoLock locker(&lock_);
159 return group_controller_.get();
160 }
161
disconnect_reason() const162 const base::Optional<DisconnectReason>& disconnect_reason() const {
163 internal::MayAutoLock locker(&lock_);
164 return disconnect_reason_;
165 }
166
167 private:
168 friend class base::RefCountedThreadSafe<State>;
169
~State()170 ~State() {
171 DCHECK(!pending_association_);
172 DCHECK(!IsValidInterfaceId(id_));
173 }
174
175 // Called by the peer, maybe from a different sequence.
OnAssociated(InterfaceId id,scoped_refptr<AssociatedGroupController> group_controller)176 void OnAssociated(InterfaceId id,
177 scoped_refptr<AssociatedGroupController> group_controller) {
178 AssociationEventCallback handler;
179 {
180 internal::MayAutoLock locker(&lock_);
181
182 // There may be race between Close() of endpoint A and
183 // NotifyPeerAssociation() of endpoint A_peer on different sequences.
184 // Therefore, it is possible that endpoint A has been closed but it
185 // still gets OnAssociated() call from its peer.
186 if (!pending_association_)
187 return;
188
189 pending_association_ = false;
190 peer_state_ = nullptr;
191 id_ = id;
192 group_controller_ = std::move(group_controller);
193
194 if (!association_event_handler_.is_null()) {
195 if (runner_->RunsTasksInCurrentSequence()) {
196 handler = std::move(association_event_handler_);
197 runner_ = nullptr;
198 } else {
199 runner_->PostTask(FROM_HERE,
200 base::Bind(&ScopedInterfaceEndpointHandle::State::
201 RunAssociationEventHandler,
202 this, runner_, ASSOCIATED));
203 }
204 }
205 }
206
207 if (!handler.is_null())
208 std::move(handler).Run(ASSOCIATED);
209 }
210
211 // Called by the peer, maybe from a different sequence.
OnPeerClosedBeforeAssociation(const base::Optional<DisconnectReason> & reason)212 void OnPeerClosedBeforeAssociation(
213 const base::Optional<DisconnectReason>& reason) {
214 AssociationEventCallback handler;
215 {
216 internal::MayAutoLock locker(&lock_);
217
218 // There may be race between Close()/NotifyPeerAssociation() of endpoint
219 // A and Close() of endpoint A_peer on different sequences.
220 // Therefore, it is possible that endpoint A is not in pending association
221 // state but still gets OnPeerClosedBeforeAssociation() call from its
222 // peer.
223 if (!pending_association_)
224 return;
225
226 disconnect_reason_ = reason;
227 // NOTE: This handle itself is still pending.
228 peer_state_ = nullptr;
229
230 if (!association_event_handler_.is_null()) {
231 if (runner_->RunsTasksInCurrentSequence()) {
232 handler = std::move(association_event_handler_);
233 runner_ = nullptr;
234 } else {
235 runner_->PostTask(
236 FROM_HERE,
237 base::Bind(&ScopedInterfaceEndpointHandle::State::
238 RunAssociationEventHandler,
239 this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
240 }
241 }
242 }
243
244 if (!handler.is_null())
245 std::move(handler).Run(PEER_CLOSED_BEFORE_ASSOCIATION);
246 }
247
RunAssociationEventHandler(scoped_refptr<base::SequencedTaskRunner> posted_to_runner,AssociationEvent event)248 void RunAssociationEventHandler(
249 scoped_refptr<base::SequencedTaskRunner> posted_to_runner,
250 AssociationEvent event) {
251 AssociationEventCallback handler;
252
253 {
254 internal::MayAutoLock locker(&lock_);
255 if (posted_to_runner == runner_) {
256 runner_ = nullptr;
257 handler = std::move(association_event_handler_);
258 }
259 }
260
261 if (!handler.is_null())
262 std::move(handler).Run(event);
263 }
264
265 // Protects the following members if the handle is initially set to pending
266 // association.
267 mutable base::Optional<base::Lock> lock_;
268
269 bool pending_association_ = false;
270 base::Optional<DisconnectReason> disconnect_reason_;
271
272 scoped_refptr<State> peer_state_;
273
274 AssociationEventCallback association_event_handler_;
275 scoped_refptr<base::SequencedTaskRunner> runner_;
276
277 InterfaceId id_ = kInvalidInterfaceId;
278 scoped_refptr<AssociatedGroupController> group_controller_;
279
280 DISALLOW_COPY_AND_ASSIGN(State);
281 };
282
283 // ScopedInterfaceEndpointHandle -----------------------------------------------
284
285 // static
CreatePairPendingAssociation(ScopedInterfaceEndpointHandle * handle0,ScopedInterfaceEndpointHandle * handle1)286 void ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(
287 ScopedInterfaceEndpointHandle* handle0,
288 ScopedInterfaceEndpointHandle* handle1) {
289 ScopedInterfaceEndpointHandle result0;
290 ScopedInterfaceEndpointHandle result1;
291 result0.state_->InitPendingState(result1.state_);
292 result1.state_->InitPendingState(result0.state_);
293
294 *handle0 = std::move(result0);
295 *handle1 = std::move(result1);
296 }
297
ScopedInterfaceEndpointHandle()298 ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle()
299 : state_(new State) {}
300
ScopedInterfaceEndpointHandle(ScopedInterfaceEndpointHandle && other)301 ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
302 ScopedInterfaceEndpointHandle&& other)
303 : state_(new State) {
304 state_.swap(other.state_);
305 }
306
~ScopedInterfaceEndpointHandle()307 ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() {
308 state_->Close(base::nullopt);
309 }
310
operator =(ScopedInterfaceEndpointHandle && other)311 ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=(
312 ScopedInterfaceEndpointHandle&& other) {
313 reset();
314 state_.swap(other.state_);
315 return *this;
316 }
317
is_valid() const318 bool ScopedInterfaceEndpointHandle::is_valid() const {
319 return state_->is_valid();
320 }
321
pending_association() const322 bool ScopedInterfaceEndpointHandle::pending_association() const {
323 return state_->pending_association();
324 }
325
id() const326 InterfaceId ScopedInterfaceEndpointHandle::id() const {
327 return state_->id();
328 }
329
group_controller() const330 AssociatedGroupController* ScopedInterfaceEndpointHandle::group_controller()
331 const {
332 return state_->group_controller();
333 }
334
335 const base::Optional<DisconnectReason>&
disconnect_reason() const336 ScopedInterfaceEndpointHandle::disconnect_reason() const {
337 return state_->disconnect_reason();
338 }
339
SetAssociationEventHandler(AssociationEventCallback handler)340 void ScopedInterfaceEndpointHandle::SetAssociationEventHandler(
341 AssociationEventCallback handler) {
342 state_->SetAssociationEventHandler(std::move(handler));
343 }
344
reset()345 void ScopedInterfaceEndpointHandle::reset() {
346 ResetInternal(base::nullopt);
347 }
348
ResetWithReason(uint32_t custom_reason,const std::string & description)349 void ScopedInterfaceEndpointHandle::ResetWithReason(
350 uint32_t custom_reason,
351 const std::string& description) {
352 ResetInternal(DisconnectReason(custom_reason, description));
353 }
354
ScopedInterfaceEndpointHandle(InterfaceId id,scoped_refptr<AssociatedGroupController> group_controller)355 ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
356 InterfaceId id,
357 scoped_refptr<AssociatedGroupController> group_controller)
358 : state_(new State(id, std::move(group_controller))) {
359 DCHECK(!IsValidInterfaceId(state_->id()) || state_->group_controller());
360 }
361
NotifyAssociation(InterfaceId id,scoped_refptr<AssociatedGroupController> peer_group_controller)362 bool ScopedInterfaceEndpointHandle::NotifyAssociation(
363 InterfaceId id,
364 scoped_refptr<AssociatedGroupController> peer_group_controller) {
365 return state_->NotifyAssociation(id, peer_group_controller);
366 }
367
ResetInternal(const base::Optional<DisconnectReason> & reason)368 void ScopedInterfaceEndpointHandle::ResetInternal(
369 const base::Optional<DisconnectReason>& reason) {
370 scoped_refptr<State> new_state(new State);
371 state_->Close(reason);
372 state_.swap(new_state);
373 }
374
375 base::Callback<AssociatedGroupController*()>
CreateGroupControllerGetter() const376 ScopedInterfaceEndpointHandle::CreateGroupControllerGetter() const {
377 // We allow this callback to be run on any sequence. If this handle is created
378 // in non-pending state, we don't have a lock but it should still be safe
379 // because the group controller never changes.
380 return base::Bind(&State::group_controller, state_);
381 }
382
383 } // namespace mojo
384