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