aboutsummaryrefslogtreecommitdiff
path: root/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc')
-rw-r--r--mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc370
1 files changed, 30 insertions, 340 deletions
diff --git a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
index c134507..f54c3f7 100644
--- a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
+++ b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
@@ -4,379 +4,69 @@
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
-#include "base/bind.h"
#include "base/logging.h"
-#include "base/synchronization/lock.h"
#include "mojo/public/cpp/bindings/associated_group_controller.h"
-#include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
namespace mojo {
-// ScopedInterfaceEndpointHandle::State ----------------------------------------
-
-// State could be called from multiple threads.
-class ScopedInterfaceEndpointHandle::State
- : public base::RefCountedThreadSafe<State> {
- public:
- State() = default;
-
- State(InterfaceId id,
- scoped_refptr<AssociatedGroupController> group_controller)
- : id_(id), group_controller_(group_controller) {}
-
- void InitPendingState(scoped_refptr<State> peer) {
- DCHECK(!lock_);
- DCHECK(!pending_association_);
-
- lock_.emplace();
- pending_association_ = true;
- peer_state_ = std::move(peer);
- }
-
- void Close(const base::Optional<DisconnectReason>& reason) {
- scoped_refptr<AssociatedGroupController> cached_group_controller;
- InterfaceId cached_id = kInvalidInterfaceId;
- scoped_refptr<State> cached_peer_state;
-
- {
- internal::MayAutoLock locker(&lock_);
-
- if (!association_event_handler_.is_null()) {
- association_event_handler_.Reset();
- runner_ = nullptr;
- }
-
- if (!pending_association_) {
- if (IsValidInterfaceId(id_)) {
- // Intentionally keep |group_controller_| unchanged.
- // That is because the callback created by
- // CreateGroupControllerGetter() could still be used after this point,
- // potentially from another thread. We would like it to continue
- // returning the same group controller.
- //
- // Imagine there is a ThreadSafeForwarder A:
- // (1) On the IO thread, A's underlying associated interface pointer
- // is closed.
- // (2) On the proxy thread, the user makes a call on A to pass an
- // associated request B_asso_req. The callback returned by
- // CreateGroupControllerGetter() is used to associate B_asso_req.
- // (3) On the proxy thread, the user immediately binds B_asso_ptr_info
- // to B_asso_ptr and makes calls on it.
- //
- // If we reset |group_controller_| in step (1), step (2) won't be able
- // to associate B_asso_req. Therefore, in step (3) B_asso_ptr won't be
- // able to serialize associated endpoints or send message because it
- // is still in "pending_association" state and doesn't have a group
- // controller.
- //
- // We could "address" this issue by ignoring messages if there isn't a
- // group controller. But the side effect is that we cannot detect
- // programming errors of "using associated interface pointer before
- // sending associated request".
-
- cached_group_controller = group_controller_;
- cached_id = id_;
- id_ = kInvalidInterfaceId;
- }
- } else {
- pending_association_ = false;
- cached_peer_state = std::move(peer_state_);
- }
- }
-
- if (cached_group_controller) {
- cached_group_controller->CloseEndpointHandle(cached_id, reason);
- } else if (cached_peer_state) {
- cached_peer_state->OnPeerClosedBeforeAssociation(reason);
- }
- }
-
- void SetAssociationEventHandler(AssociationEventCallback handler) {
- internal::MayAutoLock locker(&lock_);
-
- if (!pending_association_ && !IsValidInterfaceId(id_))
- return;
-
- association_event_handler_ = std::move(handler);
- if (association_event_handler_.is_null()) {
- runner_ = nullptr;
- return;
- }
-
- runner_ = base::ThreadTaskRunnerHandle::Get();
- if (!pending_association_) {
- runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
- this, runner_, ASSOCIATED));
- } else if (!peer_state_) {
- runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
- this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
- }
- }
-
- bool NotifyAssociation(
- InterfaceId id,
- scoped_refptr<AssociatedGroupController> peer_group_controller) {
- scoped_refptr<State> cached_peer_state;
- {
- internal::MayAutoLock locker(&lock_);
-
- DCHECK(pending_association_);
- pending_association_ = false;
- cached_peer_state = std::move(peer_state_);
- }
-
- if (cached_peer_state) {
- cached_peer_state->OnAssociated(id, std::move(peer_group_controller));
- return true;
- }
- return false;
- }
-
- bool is_valid() const {
- internal::MayAutoLock locker(&lock_);
- return pending_association_ || IsValidInterfaceId(id_);
- }
-
- bool pending_association() const {
- internal::MayAutoLock locker(&lock_);
- return pending_association_;
- }
-
- InterfaceId id() const {
- internal::MayAutoLock locker(&lock_);
- return id_;
- }
-
- AssociatedGroupController* group_controller() const {
- internal::MayAutoLock locker(&lock_);
- return group_controller_.get();
- }
-
- const base::Optional<DisconnectReason>& disconnect_reason() const {
- internal::MayAutoLock locker(&lock_);
- return disconnect_reason_;
- }
-
- private:
- friend class base::RefCountedThreadSafe<State>;
-
- ~State() {
- DCHECK(!pending_association_);
- DCHECK(!IsValidInterfaceId(id_));
- }
-
- // Called by the peer, maybe from a different thread.
- void OnAssociated(InterfaceId id,
- scoped_refptr<AssociatedGroupController> group_controller) {
- AssociationEventCallback handler;
- {
- internal::MayAutoLock locker(&lock_);
-
- // There may be race between Close() of endpoint A and
- // NotifyPeerAssociation() of endpoint A_peer on different threads.
- // Therefore, it is possible that endpoint A has been closed but it
- // still gets OnAssociated() call from its peer.
- if (!pending_association_)
- return;
-
- pending_association_ = false;
- peer_state_ = nullptr;
- id_ = id;
- group_controller_ = std::move(group_controller);
-
- if (!association_event_handler_.is_null()) {
- if (runner_->BelongsToCurrentThread()) {
- handler = std::move(association_event_handler_);
- runner_ = nullptr;
- } else {
- runner_->PostTask(FROM_HERE,
- base::Bind(&ScopedInterfaceEndpointHandle::State::
- RunAssociationEventHandler,
- this, runner_, ASSOCIATED));
- }
- }
- }
-
- if (!handler.is_null())
- std::move(handler).Run(ASSOCIATED);
- }
-
- // Called by the peer, maybe from a different thread.
- void OnPeerClosedBeforeAssociation(
- const base::Optional<DisconnectReason>& reason) {
- AssociationEventCallback handler;
- {
- internal::MayAutoLock locker(&lock_);
-
- // There may be race between Close()/NotifyPeerAssociation() of endpoint
- // A and Close() of endpoint A_peer on different threads.
- // Therefore, it is possible that endpoint A is not in pending association
- // state but still gets OnPeerClosedBeforeAssociation() call from its
- // peer.
- if (!pending_association_)
- return;
-
- disconnect_reason_ = reason;
- // NOTE: This handle itself is still pending.
- peer_state_ = nullptr;
-
- if (!association_event_handler_.is_null()) {
- if (runner_->BelongsToCurrentThread()) {
- handler = std::move(association_event_handler_);
- runner_ = nullptr;
- } else {
- runner_->PostTask(
- FROM_HERE,
- base::Bind(&ScopedInterfaceEndpointHandle::State::
- RunAssociationEventHandler,
- this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
- }
- }
- }
-
- if (!handler.is_null())
- std::move(handler).Run(PEER_CLOSED_BEFORE_ASSOCIATION);
- }
-
- void RunAssociationEventHandler(
- scoped_refptr<base::SingleThreadTaskRunner> posted_to_runner,
- AssociationEvent event) {
- AssociationEventCallback handler;
-
- {
- internal::MayAutoLock locker(&lock_);
- if (posted_to_runner == runner_) {
- runner_ = nullptr;
- handler = std::move(association_event_handler_);
- }
- }
-
- if (!handler.is_null())
- std::move(handler).Run(event);
- }
-
- // Protects the following members if the handle is initially set to pending
- // association.
- mutable base::Optional<base::Lock> lock_;
-
- bool pending_association_ = false;
- base::Optional<DisconnectReason> disconnect_reason_;
-
- scoped_refptr<State> peer_state_;
-
- AssociationEventCallback association_event_handler_;
- scoped_refptr<base::SingleThreadTaskRunner> runner_;
-
- InterfaceId id_ = kInvalidInterfaceId;
- scoped_refptr<AssociatedGroupController> group_controller_;
-
- DISALLOW_COPY_AND_ASSIGN(State);
-};
-
-// ScopedInterfaceEndpointHandle -----------------------------------------------
-
-// static
-void ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(
- ScopedInterfaceEndpointHandle* handle0,
- ScopedInterfaceEndpointHandle* handle1) {
- ScopedInterfaceEndpointHandle result0;
- ScopedInterfaceEndpointHandle result1;
- result0.state_->InitPendingState(result1.state_);
- result1.state_->InitPendingState(result0.state_);
-
- *handle0 = std::move(result0);
- *handle1 = std::move(result1);
-}
-
ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle()
- : state_(new State) {}
+ : ScopedInterfaceEndpointHandle(kInvalidInterfaceId, true, nullptr) {}
ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
ScopedInterfaceEndpointHandle&& other)
- : state_(new State) {
- state_.swap(other.state_);
+ : id_(other.id_), is_local_(other.is_local_) {
+ group_controller_.swap(other.group_controller_);
+ other.id_ = kInvalidInterfaceId;
}
ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() {
- state_->Close(base::nullopt);
+ reset();
}
ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=(
ScopedInterfaceEndpointHandle&& other) {
reset();
- state_.swap(other.state_);
- return *this;
-}
+ swap(other);
-bool ScopedInterfaceEndpointHandle::is_valid() const {
- return state_->is_valid();
+ return *this;
}
-bool ScopedInterfaceEndpointHandle::pending_association() const {
- return state_->pending_association();
-}
+void ScopedInterfaceEndpointHandle::reset() {
+ if (!IsValidInterfaceId(id_))
+ return;
-InterfaceId ScopedInterfaceEndpointHandle::id() const {
- return state_->id();
-}
+ group_controller_->CloseEndpointHandle(id_, is_local_);
-AssociatedGroupController* ScopedInterfaceEndpointHandle::group_controller()
- const {
- return state_->group_controller();
+ id_ = kInvalidInterfaceId;
+ is_local_ = true;
+ group_controller_ = nullptr;
}
-const base::Optional<DisconnectReason>&
-ScopedInterfaceEndpointHandle::disconnect_reason() const {
- return state_->disconnect_reason();
+void ScopedInterfaceEndpointHandle::swap(ScopedInterfaceEndpointHandle& other) {
+ using std::swap;
+ swap(other.id_, id_);
+ swap(other.is_local_, is_local_);
+ swap(other.group_controller_, group_controller_);
}
-void ScopedInterfaceEndpointHandle::SetAssociationEventHandler(
- AssociationEventCallback handler) {
- state_->SetAssociationEventHandler(std::move(handler));
-}
+InterfaceId ScopedInterfaceEndpointHandle::release() {
+ InterfaceId result = id_;
-void ScopedInterfaceEndpointHandle::reset() {
- ResetInternal(base::nullopt);
-}
+ id_ = kInvalidInterfaceId;
+ is_local_ = true;
+ group_controller_ = nullptr;
-void ScopedInterfaceEndpointHandle::ResetWithReason(
- uint32_t custom_reason,
- const std::string& description) {
- ResetInternal(DisconnectReason(custom_reason, description));
+ return result;
}
ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
InterfaceId id,
+ bool is_local,
scoped_refptr<AssociatedGroupController> group_controller)
- : state_(new State(id, std::move(group_controller))) {
- DCHECK(!IsValidInterfaceId(state_->id()) || state_->group_controller());
-}
-
-bool ScopedInterfaceEndpointHandle::NotifyAssociation(
- InterfaceId id,
- scoped_refptr<AssociatedGroupController> peer_group_controller) {
- return state_->NotifyAssociation(id, peer_group_controller);
-}
-
-void ScopedInterfaceEndpointHandle::ResetInternal(
- const base::Optional<DisconnectReason>& reason) {
- scoped_refptr<State> new_state(new State);
- state_->Close(reason);
- state_.swap(new_state);
-}
-
-base::Callback<AssociatedGroupController*()>
-ScopedInterfaceEndpointHandle::CreateGroupControllerGetter() const {
- // We allow this callback to be run on any thread. If this handle is created
- // in non-pending state, we don't have a lock but it should still be safe
- // because the group controller never changes.
- return base::Bind(&State::group_controller, state_);
+ : id_(id),
+ is_local_(is_local),
+ group_controller_(std::move(group_controller)) {
+ DCHECK(!IsValidInterfaceId(id) || group_controller_);
}
} // namespace mojo