#pragma once #include "acl_cpp/acl_cpp_define.hpp" #include "acl_cpp/ipc/ipc_service.hpp" #if defined(_WIN32) || defined(_WIN64) struct acl_pthread_mutex_t; struct acl_pthread_cond_t; #else # include # ifndef acl_pthread_mutex_t # define acl_pthread_mutex_t pthread_mutex_t # endif # ifndef acl_pthread_cond_t # define acl_pthread_cond_t pthread_cond_t # endif #endif namespace acl { class ipc_client; class rpc_client; class rpc_service; class rpc_request; struct RPC_DAT { rpc_request* req; void* ctx; }; class ACL_CPP_API rpc_request : public ipc_request { public: rpc_request(void); virtual ~rpc_request(void); protected: friend class rpc_client; friend class rpc_service; /** * 在主线程中被调用,子类必须实现此接口, * 当子线程处理完请求任务后该接口将被调用,所以该类对象只能 * 是当本接口调用后才能被释放,禁止在调用本接口前释放本类对象 */ virtual void rpc_onover(void) = 0; /** * 虚接口:当子线程调用本对象的 rpc_signal 时,在主线程中会 * 调用本接口,通知在任务未完成前(即调用 rpc_onover 前)收到 * 子线程运行的中间状态信息;内部自动支持套接口或 _WIN32 窗口 * 消息;应用场景,例如,对于 HTTP 下载应用,在子线程中可以 * 一边下载,一边向主线程发送(调用 rpc_signal 方法)下载进程, * 则主线程会调用本类实例的此方法来处理此消息 */ virtual void rpc_wakeup(void* ctx) { (void) ctx; } protected: /** * 在子线程中被调用,子类必须实现此接口,用于处理具体任务 */ virtual void rpc_run(void) = 0; /** * 在子线程中被调用,内部自动支持套接口或 _WIN32 窗口消息 * 子类实例的 rpc_run 方法中可以多次调用此方法向主线程的 * 本类实例发送消息,主线程中调用本对象 rpc_wakeup 方法 * @param ctx {void*} 传递的参数指针,一般应该是动态地址 * 比较好,这样可以避免同一个参数被重复覆盖的问题 */ void rpc_signal(void* ctx); /** * 当子线程调用 rpc_signal 给主线程后,调用本方法可以等待 * 主线程发来下一步指令 * @param timeout {int} 等待超时时间(毫秒),当该值为 0 时 * 则采用非阻塞等待模式,当该值为 < 0 时,则采用完全阻塞 * 等待模式(即一直等到主线程发送 cond_signal 通知),当该 * 值 > 0 时,则等待的最大超时时间为 timeout 毫秒 * @return {bool} 返回 true 表示收到主线程发来的通知信号, * 否则,需要调用 cond_wait_timeout 判断是否是超时引起的 */ bool cond_wait(int timeout = -1); /** * 当 cond_wait 返回 false 时,应用应该调用本方法判断是否 * 是因为等待超时引起的 * @return {bool} 是否是等待超时 */ bool cond_wait_timeout() const { return wait_timedout_; } /** * 当子线程调用 cond_wait 时,在主线程中调用本方法通知子线程 * “醒来” * @return {bool} 当有子线程调用 cond_wait 时本函数通知子线程 * “醒来”并且返回 true,否则返回 false */ bool cond_signal(void); private: RPC_DAT dat_; ipc_client* ipc_; int cond_count_; acl_pthread_mutex_t* lock_; acl_pthread_cond_t* cond_; bool wait_timedout_; // 基类 ipc_request 虚函数,在子线程中被调用 virtual void run(ipc_client* ipc); #if defined(_WIN32) || defined(_WIN64) /** * 虚接口,子类实现此类用于处理具体的任务,该接口适用 * 于采用 _WIN32 消息的模式 * @param hWnd {HWND} WIN2 窗口句柄 */ virtual void run(HWND hWnd); #endif }; ////////////////////////////////////////////////////////////////////////// class aio_socket_stream; class ACL_CPP_API rpc_service : public ipc_service { public: /** * 构造函数 * @param nthread {int} 如果该值 > 1 则内部自动采用线程池,否则 * 则是一个请求一个线程 * @param ipc_keep {bool} 内部 IPC 消息流是否保持长连接,保持长 * 连接有助于提高消息传递的效率 */ rpc_service(int nthread, bool ipc_keep = true); ~rpc_service(void) {} /** * 主线程中运行:将请求任务放入子线程池的任务请求队列中,当线程池 * 中的一个子线程接收到该任务后便调用 rpc_request::rpc_run 方法调 * 用子类的方法,当任务处理完毕后给主线程发消息,在主线程中再调用 * rpc_request::rpc_callback * @param req {rpc_request*} rpc_request 子类实例,非空 */ void rpc_fork(rpc_request* req); private: // 基类虚函数:主线程对象接收到子线程消息的 // ipc 连接请求时的回调函数 virtual void on_accept(aio_socket_stream* client); #if defined(_WIN32) || defined(_WIN64) /** * 基类虚函数,当收到来自于子线程的 win32 消息时的回调函数 * @param hWnd {HWND} 窗口句柄 * @param msg {UINT} 用户自定义消息号 * @param wParam {WPARAM} 参数 * @param lParam {LPARAM} 参数 */ virtual void win32_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #endif }; } // namespace acl