// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_SET_H_ #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_SET_H_ #include #include #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/stl_util.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" #include "mojo/public/cpp/bindings/interface_ptr.h" namespace mojo { using InterfacePtrSetElementId = size_t; namespace internal { // TODO(blundell): This class should be rewritten to be structured // similarly to BindingSet if possible, with PtrSet owning its // Elements and those Elements calling back into PtrSet on connection // error. template class Ptr> class PtrSet { public: PtrSet() {} ~PtrSet() { CloseAll(); } InterfacePtrSetElementId AddPtr(Ptr ptr) { InterfacePtrSetElementId id = next_ptr_id_++; auto weak_interface_ptr = new Element(std::move(ptr)); ptrs_.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(weak_interface_ptr->GetWeakPtr())); ClearNullPtrs(); return id; } template void ForAllPtrs(FunctionType function) { for (const auto& it : ptrs_) { if (it.second) function(it.second->get()); } ClearNullPtrs(); } void CloseAll() { for (const auto& it : ptrs_) { if (it.second) it.second->Close(); } ptrs_.clear(); } bool empty() const { return ptrs_.empty(); } // Calls FlushForTesting on all Ptrs sequentially. Since each call is a // blocking operation, may be very slow as the number of pointers increases. void FlushForTesting() { for (const auto& it : ptrs_) { if (it.second) it.second->FlushForTesting(); } ClearNullPtrs(); } bool HasPtr(InterfacePtrSetElementId id) { return ptrs_.find(id) != ptrs_.end(); } Ptr RemovePtr(InterfacePtrSetElementId id) { auto it = ptrs_.find(id); if (it == ptrs_.end()) return Ptr(); Ptr ptr; if (it->second) ptr = it->second->Take(); ptrs_.erase(it); return ptr; } private: class Element { public: explicit Element(Ptr ptr) : ptr_(std::move(ptr)), weak_ptr_factory_(this) { ptr_.set_connection_error_handler(base::Bind(&DeleteElement, this)); } ~Element() {} void Close() { ptr_.reset(); // Resetting the interface ptr means that it won't call this object back // on connection error anymore, so this object must delete itself now. DeleteElement(this); } Interface* get() { return ptr_.get(); } Ptr Take() { return std::move(ptr_); } base::WeakPtr GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } void FlushForTesting() { ptr_.FlushForTesting(); } private: static void DeleteElement(Element* element) { delete element; } Ptr ptr_; base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(Element); }; void ClearNullPtrs() { base::EraseIf(ptrs_, [](const auto& pair) { return !(pair.second); }); } InterfacePtrSetElementId next_ptr_id_ = 0; std::map> ptrs_; }; } // namespace internal template using InterfacePtrSet = internal::PtrSet; template using AssociatedInterfacePtrSet = internal::PtrSet; } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_SET_H_