Files
2026-10-23 16:13:31 +00:00

383 lines
16 KiB
C++

#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 <typename Observer>
void createObserver(
const std::string& serviceName,
int domain,
bool isService,
const std::string& moduleName,
typename Observer::TypeUser* callbacks,
const std::vector<std::string>& methods,
const std::string& clientName = "") {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<Observer>();
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<Observer>(*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<Observer>().insert(std::make_pair(callbacks, observer));
}
/// @brief called from generated code: Factory::create()
template <typename Sender>
typename Sender::TypeUser* createSender(
const std::string& serviceName,
int domain,
bool isService,
const std::string& moduleName,
const std::vector<std::string>& methods,
const std::string& clientName = "") {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<Sender>();
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<Sender>(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<Sender*>(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<std::recursive_mutex> lockMaps(m_mutex);
m_sender2actorConfig.insert(std::make_pair(isender, config));
typeUser2senderMap<Sender>().insert(std::make_pair(senderUser, isender));
return senderUser;
}
/// @brief called from generated code: ObsFactory::remove()
template <typename Observer>
void removeObserverByObserverUser(
typename Observer::TypeUser* observerUser, bool isService, const std::string& moduleName) {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<Observer>();
DDKIT_THROW_ON_FALSE_INFO(observerUser, interfaceNameApi(isService, moduleName) + "ObsFactory::remove()"
+ " called with Observer* == nullptr ?");
// global maps
DDKIT_THROW_ON_FALSE_INFO(typeUser2observerMap<Observer>().find(observerUser) != typeUser2observerMap<Observer>().end(),
interfaceNameApi(isService, moduleName) + "ObsFactory::remove()"
+ " called with Observer* not given to " + interfaceNameApi(isService, moduleName)
+ "ObsFactory::add*() before? (or removed twice?)");
IObserverPtr observer = typeUser2observerMap<Observer>().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<Observer>().erase(typeUser2observerMap<Observer>().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 <typename Sender>
void removeSenderBySenderUser(typename Sender::TypeUser* senderUser, bool isService, const std::string& moduleName) {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<Sender>();
DDKIT_THROW_ON_FALSE_INFO(senderUser, interfaceNameApi(isService, moduleName) + "Factory::remove()"
+ " called with Sender* == nullptr ?");
// global maps
DDKIT_THROW_ON_FALSE_INFO(typeUser2senderMap<Sender>().find(senderUser) != typeUser2senderMap<Sender>().end(),
interfaceNameApi(isService, moduleName) + "Factory::remove()"
+ " called with Sender* not received from " + interfaceNameApi(isService, moduleName)
+ "Factory::create() before? (or removed twice?)");
ISenderPtr sender = typeUser2senderMap<Sender>().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<Sender>().erase(typeUser2senderMap<Sender>().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 <typename Observer>
void shutdownObservers(bool isService, const std::string& moduleName) {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<Observer>();
typedef typename Observer::TypeUser TypeUser;
std::vector<TypeUser*> observerUsers;
for (auto& p : typeUser2observerMap<Observer>()) {
observerUsers.push_back(p.first);
}
for (TypeUser* typeUser : observerUsers) {
removeObserverByObserverUser<Observer>(typeUser, isService, moduleName);
}
}
/// @brief called from generated code: Factory::shutdown()
template <typename Sender>
void shutdownSenders(bool isService, const std::string& moduleName) {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<Sender>();
typedef typename Sender::TypeUser TypeUser;
std::vector<TypeUser*> senderUsers;
for (auto& p : typeUser2senderMap<Sender>()) {
senderUsers.push_back(p.first);
}
for (TypeUser* typeUser : senderUsers) {
removeSenderBySenderUser<Sender>(typeUser, isService, moduleName);
}
}
/// @brief called from generated code: ObsFactory::getSource()
template <typename Observer>
std::string getSource(bool isService, const std::string& moduleName) {
DDKIT_LOG_TRACE;
std::unique_lock<std::recursive_mutex> 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<int, SessionImplPtr> m_domain2session;
std::map<ActorConfiguration, SimpleActorPtr> m_config2actor; // holding Actors exactly as long, as add/create/remove tells
// Pointers to actors-configs
std::map<IObserverPtr, ActorConfiguration> m_observer2actorConfig;
std::map<ISenderPtr, ActorConfiguration> 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 <typename Sender> std::map<typename Sender::TypeUser*, ISenderPtr>& typeUser2senderMap() {
static std::map<typename Sender::TypeUser*, ISenderPtr> s_typeUser2sender;
return s_typeUser2sender;
}
template <typename Observer> std::map<typename Observer::TypeUser*, IObserverPtr>& typeUser2observerMap() {
static std::map<typename Observer::TypeUser*, IObserverPtr> s_typeUser2observer;
return s_typeUser2observer;
}
// no copying
BackwardManager(BackwardManager const&) = delete;
BackwardManager(BackwardManager&&) = delete;
void operator=(BackwardManager const&) = delete;
};
} // namespace actors
} // namespace ddkit