#pragma once #include "ddkit/utils/assert.hpp" #include "ddkit/actors/Key.hpp" #include "ddkit/actors/SessionManager.hpp" #include "ddkit/actors/SessionImpl.hpp" #include "ddkit/actors/SimpleActor.hpp" namespace ddkit { namespace actors { /// @brief The calls (add/create/remove/remove/shutdown/shutdown) from the generated code are delegated here. /// We try to create/remove Actors as much as "normal" as it would be done via Session/Service/Client /// but of course there are some differences, as two calls may create only one Actor etc. /// Keep one SimpleActorPtr as replacement for the not-existing Service/Client-proxys in own list. class BackwardManager { public: static BackwardManager& instance(); /// @brief called from generated code: ObsFactory::add() template void createObserver( const std::string& serviceName, int domain, bool isService, const std::string& moduleName, typename Observer::TypeUser* callbacks, const std::vector& methods, const std::string& clientName = "") { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "add(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << " Please note that it may be removed in future versions."; ddkit::log::debug() << "create observer by key api: " << serviceName << ", " << std::to_string(domain); checkApiServiceName(serviceName); checkObserverType(); ActorConfiguration config(moduleName, isService, domain, serviceName); SimpleActorPtr actor = createOrGetActor(serviceName, domain, isService, moduleName); DDKIT_THROW_ON_FALSE_INFO(actor, "Failed to create actor"); IObserverPtr observer = actor->createObserver(*callbacks, methods); // if empty there is no need to send data over the wire if (!clientName.empty() && !isService) { actor->setClientName(clientName); } // global maps m_observer2actorConfig.insert(std::make_pair(observer, config)); typeUser2observerMap().insert(std::make_pair(callbacks, observer)); } /// @brief called from generated code: Factory::create() template typename Sender::TypeUser* createSender( const std::string& serviceName, int domain, bool isService, const std::string& moduleName, const std::vector& methods, const std::string& clientName = "") { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "create(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << "Please note that it may be removed in future versions."; ddkit::log::debug() << "create sender by key api: " << moduleName; checkApiServiceName(serviceName); checkSenderType(); ActorConfiguration config(moduleName, isService, domain, serviceName); SimpleActorPtr actor = createOrGetActor(serviceName, domain, isService, moduleName); DDKIT_THROW_ON_FALSE_INFO(actor, "Failed to create actor"); ISenderPtr isender = actor->createSender(methods); DDKIT_THROW_ON_FALSE_INFO(isender, "BackwardManager::createSender: could not create sender"); // if empty there is no need to send data over the wire if (!clientName.empty()) { actor->setClientName(clientName); } // return raw-ptr for old C-API Sender* sender = dynamic_cast(isender.get()); // downcast - Sender inherits ISender DDKIT_THROW_ON_FALSE(sender); typename Sender::TypeUser* senderUser = sender; // upcast - Sender inherits IService/IClient // global maps std::unique_lock lockMaps(m_mutex); m_sender2actorConfig.insert(std::make_pair(isender, config)); typeUser2senderMap().insert(std::make_pair(senderUser, isender)); return senderUser; } /// @brief called from generated code: ObsFactory::remove() template void removeObserverByObserverUser( typename Observer::TypeUser* observerUser, bool isService, const std::string& moduleName) { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "remove(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << "Please note that it may be removed in future versions."; ddkit::log::debug() << "Remove Observer By Observer User: " << moduleName; { checkObserverType(); DDKIT_THROW_ON_FALSE_INFO(observerUser, interfaceNameApi(isService, moduleName) + "ObsFactory::remove()" + " called with Observer* == nullptr ?"); // global maps DDKIT_THROW_ON_FALSE_INFO(typeUser2observerMap().find(observerUser) != typeUser2observerMap().end(), interfaceNameApi(isService, moduleName) + "ObsFactory::remove()" + " called with Observer* not given to " + interfaceNameApi(isService, moduleName) + "ObsFactory::add*() before? (or removed twice?)"); IObserverPtr observer = typeUser2observerMap().at(observerUser); DDKIT_THROW_ON_FALSE_INFO(observer, "removeObserverByObserverUser: observer not found"); ActorConfiguration config = m_observer2actorConfig.at(observer); ddkit::log::debug() << "called Obs...remove() on " << config; DDKIT_THROW_ON_FALSE(config.isService == isService); DDKIT_THROW_ON_FALSE(config.module == moduleName); // global maps m_observer2actorConfig.erase(observer); typeUser2observerMap().erase(typeUser2observerMap().find(observerUser)); SessionImplPtr sessionImpl = findSession(config.domain); if (sessionImpl) { SimpleActorPtr actor = sessionImpl->findActor(config.serviceName, config.isService, config.module); if (actor) { if (actor->hasSender()) { ddkit::log::warn() << "The actor still has a sender, the observer will be deleted after sender is also destoryed!" << " Consider using the OO-Api."; } else { auto it = m_config2actor.find(config); if (it != m_config2actor.end()) { if (m_config2actor.at(config) == actor) { m_config2actor.erase(it); actor->shutdown(); // wait until dispatcher etc are done. while (actor.use_count() > 1) { ddkit::utils::system::msSleep(10); } } } } } sessionImpl.reset(); // local hold ptr removeSessionIfEmpty(config.domain); } } checkIfEmpty(); } /// @brief called from generated code: Factory::remove() template void removeSenderBySenderUser(typename Sender::TypeUser* senderUser, bool isService, const std::string& moduleName) { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "remove(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << "Please note that it may be removed in future versions."; ddkit::log::debug() << "RemoveSender By Sender User: " << moduleName; { checkSenderType(); DDKIT_THROW_ON_FALSE_INFO(senderUser, interfaceNameApi(isService, moduleName) + "Factory::remove()" + " called with Sender* == nullptr ?"); // global maps DDKIT_THROW_ON_FALSE_INFO(typeUser2senderMap().find(senderUser) != typeUser2senderMap().end(), interfaceNameApi(isService, moduleName) + "Factory::remove()" + " called with Sender* not received from " + interfaceNameApi(isService, moduleName) + "Factory::create() before? (or removed twice?)"); ISenderPtr sender = typeUser2senderMap().at(senderUser); DDKIT_THROW_ON_FALSE_INFO(sender, "Sender not found"); ActorConfiguration config = m_sender2actorConfig.at(sender); ddkit::log::debug() << "called ...remove() on " << config; DDKIT_THROW_ON_FALSE(config.isService == isService); DDKIT_THROW_ON_FALSE(config.module == moduleName); // remove sender from global maps m_sender2actorConfig.erase(sender); typeUser2senderMap().erase(typeUser2senderMap().find(senderUser)); SessionImplPtr sessionImpl = findSession(config.domain); if (sessionImpl) { SimpleActorPtr actor = sessionImpl->findActor(config.serviceName, config.isService, config.module); if (actor) { actor->discardSender(); if (actor->hasObserver()) { // still has a observer dont shutdown actor! ddkit::log::warn() << "There is still a observer, make sure to delete it too." << " Consider using the new OO-Api."; } else { ddkit::log::debug() << "Does not have a observer, we can shutdown the actor."; auto configIt = m_config2actor.find(config); if (configIt != m_config2actor.end()) { if (m_config2actor.at(config) == actor) { m_config2actor.erase(configIt); actor->shutdown(); // wait until dispatcher etc are done. while (actor.use_count() > 1) { ddkit::utils::system::msSleep(10); } } } } } sessionImpl.reset(); // local hold session-ptr removeSessionIfEmpty(config.domain); } } checkIfEmpty(); } /// @brief called from generated code: ObsFactory::shutdown() template void shutdownObservers(bool isService, const std::string& moduleName) { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "shutdown(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << "Please note that it may be removed in future versions."; ddkit::log::debug() << "shutdown observers: " << moduleName; checkObserverType(); typedef typename Observer::TypeUser TypeUser; std::vector observerUsers; for (auto& p : typeUser2observerMap()) { observerUsers.push_back(p.first); } for (TypeUser* typeUser : observerUsers) { removeObserverByObserverUser(typeUser, isService, moduleName); } } /// @brief called from generated code: Factory::shutdown() template void shutdownSenders(bool isService, const std::string& moduleName) { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "shutdown(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << "Please note that it may be removed in future versions."; ddkit::log::debug() << "Shutdown senders: " << moduleName; checkSenderType(); typedef typename Sender::TypeUser TypeUser; std::vector senderUsers; for (auto& p : typeUser2senderMap()) { senderUsers.push_back(p.first); } for (TypeUser* typeUser : senderUsers) { removeSenderBySenderUser(typeUser, isService, moduleName); } } /// @brief called from generated code: ObsFactory::getSource() template std::string getSource(bool isService, const std::string& moduleName) { DDKIT_LOG_TRACE; std::unique_lock lock(m_mutex); ddkit::log::warn() << "getSource(): You are using the deprecated C-Style API. " << "It is recommended to upgrade to the new object-oriented API. " << "Please note that it may be removed in future versions. " << "Use ddkit::actors::CallbackInfo::current(); instead."; CallbackInfo callbackInfo = SessionManager::instance().callbackInfo(); bool isToServiceReal = callbackInfo.isFromClient; if (isService != isToServiceReal) { ddkit::log::warn() << "getSource() was called on ObsFactory of " << interfaceNameApi(isService, moduleName) << " but callback right now is sending data to " << serviceClientNameApi(isToServiceReal, moduleName) << ". Probably one should use \"" << interfaceNameApi(isToServiceReal, moduleName) << "ObsFactory\" instead of \"" << interfaceNameApi(isService, moduleName) << "ObsFactory\", to remove this warning."; } // as "source" we gave back the serviceName or the clientId, depending on the side if (callbackInfo.isFromClient) { // client called service return callbackInfo.clientName; // this is sendingActor (=clientActorId) or user-given clientName } else { // service called client return callbackInfo.serviceName; } } SessionImplPtr createOrGetSession_(int domain); bool isClean() const; private: std::recursive_mutex m_mutex; std::map m_domain2session; std::map m_config2actor; // holding Actors exactly as long, as add/create/remove tells // Pointers to actors-configs std::map m_observer2actorConfig; std::map m_sender2actorConfig; BackwardManager(); ~BackwardManager(); SimpleActorPtr createOrGetActor(const std::string& serviceName, int domain, bool isService, const std::string& moduleName); SessionImplPtr findSession(int domain); SessionImplPtr createOrGetSession(int domain); void removeSessionIfEmpty(int domain); void checkIfEmpty(); // these functions are used to circumvent the fact das data members cant be templated // which is only somewhat fine since BackwardManger is a singleton class template std::map& typeUser2senderMap() { static std::map s_typeUser2sender; return s_typeUser2sender; } template std::map& typeUser2observerMap() { static std::map s_typeUser2observer; return s_typeUser2observer; } // no copying BackwardManager(BackwardManager const&) = delete; BackwardManager(BackwardManager&&) = delete; void operator=(BackwardManager const&) = delete; }; } // namespace actors } // namespace ddkit