/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef META_EXT_EVENT_IMPL_H #define META_EXT_EVENT_IMPL_H #include #include #include #include #include #include #include #include #include #include #include META_BEGIN_NAMESPACE() template struct EventImplTraits { static bool IsCompatibleInterface(const ICallable::Ptr& c) { if (auto f = interface_pointer_cast(c)) { return IsFunctionCompatible(f); } return c->GetInterface(EventType::UID) != nullptr || c->GetInterface(IOnChanged::UID) != nullptr; } template static void Call(const ICallable::Ptr& p, Args&... args) { if (const auto f = interface_pointer_cast(p)) { if constexpr ((true && ... && HasUid_v>)) { CallMetaFunction(f, BASE_NS::forward(args)...); } else { CORE_LOG_E("Invalid function signature for meta function call"); } } else { if (const auto i = interface_cast(p)) { i->Invoke(args...); } else { // Allow to always call IOnChanged that takes no parameters, this allows to add handlers for unknown // event types if (auto ai = interface_cast(p)) { ai->Invoke(); } else { CORE_LOG_E("Invalid callable type for event callback"); } } } } }; template class EventImpl; template class EventImpl final : public IntroduceInterfaces { static_assert(BASE_NS::is_void_v, "EventHandler callable must return void"); using Token = typename IEvent::Token; using Traits = EventImplTraits; BASE_NS::shared_ptr GetClone() const override { BASE_NS::shared_ptr p(new EventImpl(*this)); return interface_pointer_cast(p); } EventImpl(const EventImpl& e) : handlers_(e.handlers_), name_(e.name_) {} public: explicit EventImpl(BASE_NS::string name = {}) : name_(BASE_NS::move(name)) {} ~EventImpl() override { // check that the invocation doesn't destroy its event impl CORE_ASSERT_MSG(threadId_ == CORE_NS::ThreadId {}, "EventImpl not allowed to destroy itself when invoked"); Reset(); } EventImpl& operator=(const EventImpl&) = delete; META_NO_MOVE(EventImpl) void Reset() override { CORE_NS::UniqueLock lock { mutex_ }; handlers_.clear(); } bool IsCompatibleWith(const ICallable::Ptr& p) const override { return Traits::IsCompatibleInterface(p); } using IEvent::AddHandler; Token AddHandler(const ICallable::Ptr& p, Token userToken) override { if (Traits::IsCompatibleInterface(p)) { Token newToken = userToken ? userToken : (uintptr_t)p.get(); CORE_NS::UniqueLock lock { mutex_ }; for (auto it = handlers_.begin(); it != handlers_.end(); it++) { if (newToken == it->token) { // Already connected. ++it->count; return newToken; } } handlers_.push_back(HandlerData { newToken, p }); return newToken; } CORE_LOG_F("%s: Tried to add a non-matching handler", name_.c_str()); return 0; } bool RemoveHandler(Token p) override { if (p == 0) { return true; } CORE_NS::UniqueLock lock { mutex_ }; for (auto it = handlers_.begin(); it != handlers_.end(); it++) { if (p == it->token) { if (--it->count == 0) { handlers_.erase(it); } return true; } } CORE_LOG_F("%s: Tried to remove a non-existent handler", name_.c_str()); return false; } BASE_NS::vector GetHandlers() const override { CORE_NS::UniqueLock lock { mutex_ }; BASE_NS::vector handlers; handlers.reserve(handlers_.size()); for (auto&& p : handlers_) { handlers.emplace_back(p.ptr); } return handlers; } bool HasHandlers() const override { CORE_NS::UniqueLock lock { mutex_ }; return !handlers_.empty(); } BASE_NS::Uid GetCallableUid() const override { return BaseClass::UID; } BASE_NS::string_view GetName() const override { CORE_NS::UniqueLock lock { mutex_ }; return name_.empty() ? GetEventTypeName() : name_; } BASE_NS::string_view GetEventTypeName() const override { return BaseClass::NAME; } void Invoke(ARG... args) override { CORE_NS::UniqueLock lock { mutex_ }; if (threadId_ != CORE_NS::ThreadId {} && threadId_ != CORE_NS::CurrentThreadId()) { return; } bool resetThreadId = threadId_ == CORE_NS::ThreadId {}; if (resetThreadId) { threadId_ = CORE_NS::CurrentThreadId(); } size_t currentCallCount = ++callCount_; // we collect handlers to separate list of weak_ptr if (!handlers_.empty()) { // because callable can remove other handlers or callables BASE_NS::vector handlers; handlers.reserve(handlers_.size()); for (auto&& p : handlers_) { handlers.emplace_back(p.ptr); } // remember the old count when starting to iterate, so that we can detect if there was recursive call for (auto it = handlers.begin(); it != handlers.end() && currentCallCount == callCount_; ++it) { if (auto callable = it->lock()) { lock.Unlock(); Traits::Call(callable, args...); lock.Lock(); } } } if (resetThreadId) { threadId_ = {}; } } private: struct HandlerData { Token token; ICallable::Ptr ptr; std::size_t count { 1 }; }; BASE_NS::vector handlers_; size_t callCount_ {}; BASE_NS::string name_; mutable CORE_NS::Mutex mutex_; mutable CORE_NS::ThreadId threadId_; }; META_END_NAMESPACE() #endif