/*************************************************************************
** Copyright(c) 2016-2025 faker
** All rights reserved.
** Name : Mailer.h
** Desc : 一个高级订阅器,接口简单但强大。
** ETOPIC:邮件主题,需要使用DEF_MAIL_XXXX来定义
** Mailer:订阅器主类
** INotifier:发布器基类
** IReceiver:接收器基类
** Author : faker@2020-12-19 09:19:30
*************************************************************************/
#ifndef _1599C098_414D_4A2A_B59B_EF39E0151B90
#define _1599C098_414D_4A2A_B59B_EF39E0151B90
#include "depends/incfile.h"
#include "AnyT.h"
#include "xCores.h"
namespace x2lib
{
namespace ETOPIC
{
// 0x0001 ~ 0x7FFF,最高位为1表示为同步消息,为0表示为异步消息。带参对应Mail:eTopic, iArg0, iArg1, vArg2
static const uint16_t NONE = 0x0000;
static const uint16_t TYPE_NONE = 0x0000;
static const uint16_t TYPE_POST = 0x0001;
static const uint16_t TYPE_SEND = 0x0002;
static const uint16_t TYPE_AUTO = 0x0003;
static const uint16_t BEGIN = 0x1000;
static const uint16_t POST_BASE = 0x2000;
static const uint16_t POST_OVER = 0x2FFF;
static const uint16_t SEND_BASE = 0x3000;
static const uint16_t SEND_OVER = 0x3FFF;
static const uint16_t AUTO_BASE = 0x4000;
static const uint16_t AUTO_OVER = 0x4FFF;
static const uint16_t END = 0x7FFF;
static uint16_t typeOfTopic(uint16_t eTopic)
{
if(eTopic > ETOPIC::POST_BASE && eTopic < ETOPIC::POST_OVER) return ETOPIC::TYPE_POST;
else if(eTopic > ETOPIC::SEND_BASE && eTopic < ETOPIC::SEND_OVER) return ETOPIC::TYPE_SEND;
else if(eTopic > ETOPIC::AUTO_BASE && eTopic < ETOPIC::AUTO_OVER) return ETOPIC::TYPE_AUTO;
return ETOPIC::TYPE_NONE;
};
};
#define DEF_MAIL_BEGIN namespace x2lib {
#define DEF_MAIL_POSTX(tpcValue, tpcName, struName, reqArgs, repArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::POST_BASE + tpcValue; } \
static_assert((ETOPIC::POST_BASE + tpcValue)<ETOPIC::POST_OVER, "out of POST range! "); \
struct Req_##struName \
{ \
reqArgs; \
}; \
struct Rep_##struName \
{ \
repArgs; \
}
#define DEF_MAIL_POSTQ(tpcValue, tpcName, struName, reqArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::POST_BASE + tpcValue; } \
static_assert((ETOPIC::POST_BASE + tpcValue)<ETOPIC::POST_OVER, "out of POST range! "); \
struct Req_##struName \
{ \
reqArgs; \
};
#define DEF_MAIL_POSTP(tpcValue, tpcName, struName, repArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::POST_BASE + tpcValue; } \
static_assert((ETOPIC::POST_BASE + tpcValue)<ETOPIC::POST_OVER, "out of POST range! "); \
struct Rep_##struName \
{ \
repArgs; \
};
#define DEF_MAIL_POSTN(tpcValue, tpcName) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::POST_BASE + tpcValue; } \
static_assert((ETOPIC::POST_BASE + tpcValue)<ETOPIC::POST_OVER, "out of POST range! ");
#define DEF_MAIL_SENDX(tpcValue, tpcName, struName, reqArgs, repArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::SEND_BASE + tpcValue; } \
static_assert((ETOPIC::SEND_BASE + tpcValue)<ETOPIC::SEND_OVER, "out of SEND range! "); \
struct Req_##struName \
{ \
reqArgs; \
}; \
struct Rep_##struName \
{ \
repArgs; \
}
#define DEF_MAIL_SENDQ(tpcValue, tpcName, struName, reqArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::SEND_BASE + tpcValue; } \
static_assert((ETOPIC::SEND_BASE + tpcValue)<ETOPIC::SEND_OVER, "out of SEND range! "); \
struct Req_##struName \
{ \
reqArgs; \
};
#define DEF_MAIL_SENDP(tpcValue, tpcName, struName, repArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::SEND_BASE + tpcValue; } \
static_assert((ETOPIC::SEND_BASE + tpcValue)<ETOPIC::SEND_OVER, "out of SEND range! "); \
struct Rep_##struName \
{ \
repArgs; \
};
#define DEF_MAIL_SENDN(tpcValue, tpcName) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::SEND_BASE + tpcValue; } \
static_assert((ETOPIC::SEND_BASE + tpcValue)<ETOPIC::SEND_OVER, "out of SEND range! ");
#define DEF_MAIL_AUTOX(tpcValue, tpcName, struName, reqArgs, repArgs) \
namespace ETOPIC { static const uint16_t tpcName = ETOPIC::AUTO_BASE + tpcValue; } \
static_assert((ETOPIC::AUTO_BASE + tpcValue)<ETOPIC::AUTO_OVER, "out of AUTO range! "); \
struct Req_##struName \
{ \
reqArgs; \
}; \
struct Rep_##struName \
{ \
repArgs; \
}
#define ALIAS_MAIL_ITX(tpcNameOld, tpcNameNew, struNameOld, struNameNew) \
namespace ETOPIC { static const uint16_t tpcNameNew = ETOPIC::tpcNameOld; } \
typedef Req_##struNameOld Req_##struNameNew; \
typedef Rep_##struNameOld Rep_##struNameNew;
#define ALIAS_MAIL_ITQ(tpcNameOld, tpcNameNew, struNameOld, struNameNew) \
namespace ETOPIC { static const uint16_t tpcNameNew = ETOPIC::tpcNameOld; } \
typedef Req_##struNameOld Req_##struNameNew;
#define ALIAS_MAIL_ITP(tpcNameOld, tpcNameNew, struNameOld, struNameNew) \
namespace ETOPIC { static const uint16_t tpcNameNew = ETOPIC::tpcNameOld; } \
typedef Rep_##struNameOld Rep_##struNameNew;
#define ALIAS_MAIL_ITN(tpcNameOld, tpcNameNew) \
namespace ETOPIC { static const uint16_t tpcNameNew = ETOPIC::tpcNameOld; }
#define DEF_MAIL_END }
/*************************************************************************
** Desc : 订阅器
** Author : faker@2020-12-19
*************************************************************************/
class Mailer: public xCores::Engine::ICylinder
{
public:
/*************************************************************************
** Desc : 邮件【数据包】
** Author : faker@2020-12-19
*************************************************************************/
struct Mail
{
Mail(uint16_t eTopic, const AnyT& reqArg = *static_cast<AnyT*>(nullptr), const AnyT& repArg = *static_cast<AnyT*>(nullptr))
{
if (eTopic > ETOPIC::BEGIN && eTopic < ETOPIC::END)
{
this->eTopic = eTopic;
}
else
{
this->eTopic = ETOPIC::NONE;
}
if (&reqArg) this->reqArg = reqArg;
if (&repArg) this->repArg = repArg;
this->isTell = false;
}
uint16_t eTopic; // 邮件主题
AnyT reqArg; // 请求参数
AnyT repArg; // 返回参数
bool isTell; // 是否需要回调通知,供外部使用,仅Post有效
};
/*************************************************************************
** Desc : 发布器基类
** Author : faker@2020-12-19
*************************************************************************/
class INotifier
{
public:
// 后挂钩先处理
virtual bool OnSendMail(const Mail* pMail) = 0;
virtual bool OnPostMail(const Mail* pMail) = 0;
};
/*************************************************************************
** Desc : 接收器基类
** Author : faker@2020-12-19
*************************************************************************/
class IReceiver
{
public:
// 先绑定先接收
virtual bool OnTellMail(const Mail* pMail) = 0;
};
Mailer() :m_SigMailing(0, 32), /*m_SigMailend(0, 32),*/ m_Signal(0, 1)
{
m_isRunning = false;
m_nExit = 0;
}
static Mailer* GetInstance()
{
return *_get_static_ptr_();
}
static Mailer* Initialize()
{
if (*_get_static_ptr_() == nullptr)
{
*_get_static_ptr_() = new Mailer();
}
return *_get_static_ptr_();
}
static void UnInitialize()
{
if (*_get_static_ptr_() != nullptr)
{
//delete *_get_static_ptr_();
//*_get_static_ptr_() = nullptr;
}
}
/*************************************************************************
** Desc : 获取勾住了eTopic的发布器
** Param : [in] eTopic
** Return : 发布器集合
** Author : faker@2021-4-18
*************************************************************************/
const std::unordered_set<Mailer::INotifier*>& GetNotifiers(uint16_t eTopic)
{
return m_mspNotifier[eTopic];
}
/*************************************************************************
** Desc : 获取订阅了eTopic的接收器
** Param : [in] eTopic
** Return : 接收器集合
** Author : faker@2021-4-18
*************************************************************************/
const std::unordered_set<Mailer::IReceiver*>& GetReceivers(uint16_t eTopic)
{
return m_mspReceiver[eTopic];
}
/*************************************************************************
** Desc : 异步请求,向发布器请求一个Mail
** Param : [in] eTopic
** Param : [in] req
** Param : [in] isTell 是否需要回调通知
** Return :
** Author : faker@2020-12-19
*************************************************************************/
void Post(uint16_t eTopic, const AnyT& req = *static_cast<AnyT*>(nullptr), bool isTell = true)
{
assert(ETOPIC::TYPE_POST == ETOPIC::typeOfTopic(eTopic));
m_mtxQuepMailing.Lock();
Mail* pMail = new Mail(eTopic, req);
pMail->isTell = isTell;
m_quepMailing.push(pMail);
m_mtxQuepMailing.Unlock();
m_Signal.Notify();
}
/*************************************************************************
** Desc : 同步请求,向发布器请求一个Mail
** Param : [in] eTopic
** Param : [in] req
** Param : [in] rep 返回参数
** Return :
** Author : faker@2020-12-19
*************************************************************************/
const AnyT Send(uint16_t eTopic, const AnyT& req = *static_cast<AnyT*>(nullptr), const AnyT& rep = *static_cast<AnyT*>(nullptr))
{
assert(ETOPIC::TYPE_SEND == ETOPIC::typeOfTopic(eTopic));
m_mtxQuepMailing.Lock();
Mail mail(eTopic, req, rep);
m_quepMailing.push(&mail);
m_mtxQuepMailing.Unlock();
m_Signal.Notify();
m_SigMailing.Wait();
return mail.repArg;
}
/*************************************************************************
** Desc : 回调通知,由Notifier在处理完Post请求的Mail时调用
** Param : [in] eTopic
** Param : [in] req 调用Post时传递的参数
** Param : [in] rep
** Return :
** Author : faker@2020-12-19
*************************************************************************/
void Tell(uint16_t eTopic, const AnyT& req = *static_cast<AnyT*>(nullptr), const AnyT& rep = *static_cast<AnyT*>(nullptr))
{
assert(ETOPIC::TYPE_POST == ETOPIC::typeOfTopic(eTopic) || ETOPIC::TYPE_AUTO == ETOPIC::typeOfTopic(eTopic));
m_mtxQuepMailend.Lock();
m_quepMailend.push(new Mail(eTopic, req, rep));
m_mtxQuepMailend.Unlock();
m_Signal.Notify();
}
void Tell(Mail& mail)
{
assert(ETOPIC::TYPE_POST == ETOPIC::typeOfTopic(mail.eTopic) || ETOPIC::TYPE_AUTO == ETOPIC::typeOfTopic(mail.eTopic));
m_mtxQuepMailend.Lock();
m_quepMailend.push(new Mail(mail));
m_mtxQuepMailend.Unlock();
m_Signal.Notify();
}
/*************************************************************************
** Desc : 绑定邮件,只有绑定后才可以接收相关Mail
** Param : [in] pReceiver 需要接受邮件的接收器
** Param : [in] ... 所有需要关注的eTopic,最后一个参数必须是ETOPIC::TYPE_NONE
** Return :
** Author : faker@2020-12-19
*************************************************************************/
void Bind(IReceiver* pReceiver, ...)
{
va_list body;
va_start(body, pReceiver);
m_mtxMappReceiver.Lock();
do
{
uint16_t eTopic = va_arg(body, uint16_t);
if (eTopic == ETOPIC::TYPE_NONE) { break; }
m_mspReceiver[eTopic].emplace(pReceiver);
} while (true);
m_mtxMappReceiver.Unlock();
va_end(body);
}
/*************************************************************************
** Desc : 解除绑定邮件
** Param : [in] pReceiver 需要解绑的接收器
** Param : [in] ... 需要解绑的eTopic,最后一个参数必须是ETOPIC::TYPE_NONE
** Return :
** Author : faker@2020-12-19
*************************************************************************/
void UnBind(IReceiver* pReceiver, ...)
{
va_list body;
va_start(body, pReceiver);
m_mtxMappReceiver.Lock();
do
{
uint16_t eTopic = va_arg(body, uint16_t);
if (eTopic == ETOPIC::TYPE_NONE) { break; }
m_mspReceiver[eTopic].erase(pReceiver);
} while (true);
m_mtxMappReceiver.Unlock();
va_end(body);
}
/*************************************************************************
** Desc : 勾住邮件,只有勾住后才可以处理Post/Send的请求
** Param : [in] pNotifier 处理邮件的发布器
** Param : [in] ... 所有需要关注的eTopic,最后一个参数必须是ETOPIC::TYPE_NONE
** Return :
** Author : faker@2020-12-19
*************************************************************************/
void Hook(INotifier* pNotifier, ...)
{
va_list body;
va_start(body, pNotifier);
m_mtxMappNotifier.Lock();
do
{
uint16_t eTopic = va_arg(body, uint16_t);
if (eTopic == ETOPIC::TYPE_NONE) { break; }
m_mspNotifier[eTopic].insert(m_mspNotifier[eTopic].begin(), pNotifier);
}while (true);
m_mtxMappNotifier.Unlock();
va_end(body);
}
/*************************************************************************
** Desc : 去勾邮件
** Param : [in] pNotifier 处理邮件的发布器
** Param : [in] ... 所有需要去勾的eTopic,最后一个参数必须是ETOPIC::TYPE_NONE
** Return :
** Author : faker@2020-12-19
*************************************************************************/
void UnHook(INotifier* pNotifier, uint16_t eTopic0 = ETOPIC::NONE, ...)
{
m_mtxMappNotifier.Lock();
if (eTopic0 == ETOPIC::NONE)
{
for (std::map<uint16_t, std::unordered_set<INotifier*>>::iterator it = m_mspNotifier.begin(); it!= m_mspNotifier.end();++it)
{
it->second.erase(pNotifier);
}
}
else
{
va_list body;
va_start(body, eTopic0);
m_mspNotifier[eTopic0].erase(pNotifier);
do
{
uint16_t eTopic = va_arg(body, uint16_t);
if (eTopic == ETOPIC::TYPE_NONE) { break; }
m_mspNotifier[eTopic].erase(pNotifier);
} while (true);
va_end(body);
}
m_mtxMappNotifier.Unlock();
}
/*************************************************************************
** Desc : 执行函数
** Param : [in] pSigReady 通过调用pSigReady->Notify()通知其他线程,当前线程已准备好
** Param : [in] vData 请传入int值,1只运行Eval队列,2只运行Bind队列,否则依次执行,通过设置iMode可以使得发送队列和接收队列位于不同的线程
** Return :
** Author : faker@2020-12-19
*************************************************************************/
virtual int Execute(xCores::Signal* pSigReady, void* vData)
{
int iMode = (int)vData;
m_isRunning = true;
pSigReady->Notify();
do
{
bool isIdle = true;
// 调用Hooker执行
Mail* pMailing = nullptr;
if (iMode != 2)
{
m_mtxQuepMailing.Lock();
if (m_quepMailing.size() > 0)
{
pMailing = m_quepMailing.front();
m_quepMailing.pop();
isIdle = false;
}
m_mtxQuepMailing.Unlock();
if (pMailing)
{
bool isSend = ETOPIC::typeOfTopic(pMailing->eTopic) == ETOPIC::TYPE_SEND;
m_mtxMappNotifier.Lock();
std::unordered_set<INotifier*>& setNotifier = m_mspNotifier[pMailing->eTopic];
for (auto& it : setNotifier)
{
bool isBreak = false;
if (isSend) { isBreak = it->OnSendMail(pMailing); }
else { isBreak = it->OnPostMail(pMailing); }
if (isBreak) { break; }
}
m_mtxMappNotifier.Unlock();
if (isSend) { m_SigMailing.Notify(1); }
else { delete pMailing; }
}
}
// 回调Binder执行
Mail* pMailend = nullptr;
if (iMode != 1)
{
m_mtxQuepMailend.Lock();
if (m_quepMailend.size() > 0)
{
pMailend = m_quepMailend.front();
m_quepMailend.pop();
isIdle = false;
}
m_mtxQuepMailend.Unlock();
if (pMailend)
{
m_mtxMappReceiver.Lock();
std::unordered_set<IReceiver*>& setReceiver = m_mspReceiver[pMailend->eTopic];
for (auto& it : setReceiver)
{
bool isBreak = isBreak = it->OnTellMail(pMailend);
if (isBreak) { break; }
}
m_mtxMappReceiver.Unlock();
delete pMailend;
}
}
if (isIdle) { m_Signal.Wait(); }
} while (m_isRunning);
return m_nExit;
}
/*************************************************************************
** Desc : 退出
** Param : [in] nExit 退出代码,将作为Execute的返回值
** Return :
** Author : faker@2020-12-19
*************************************************************************/
virtual void Exit(int nExit)
{
m_nExit = nExit;
m_isRunning = false;
m_Signal.Notify(1);
}
private:
static Mailer** _get_static_ptr_() { static Mailer* pInstance = nullptr; return &pInstance; }
std::map<uint16_t, std::unordered_set<IReceiver*>> m_mspReceiver; // 订阅者集合
std::map<uint16_t, std::unordered_set<INotifier*>> m_mspNotifier;
std::queue<Mail*> m_quepMailing; // 待处理队列
std::queue<Mail*> m_quepMailend; // 处理完成队列
xCores::Mutex m_mtxMappNotifier;
xCores::Mutex m_mtxMappReceiver;
xCores::Mutex m_mtxQuepMailing;
xCores::Mutex m_mtxQuepMailend;
xCores::Signal m_SigMailing;
//xCores::Signal m_SigMailend;
xCores::Signal m_Signal;
bool m_isRunning;
int m_nExit;
//std::map<Hooker*, std::queue<Mail*>> m_mqTxMail;
};
}
#endif
Mailer——一个优雅的订阅器(支持多线程),接口简单且强大
最新推荐文章于 2024-04-24 17:47:03 发布