Mailer——一个优雅的订阅器(支持多线程),接口简单且强大

/*************************************************************************
** 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

猜你喜欢

转载自blog.csdn.net/xk641018299/article/details/111461454
今日推荐