| | |
| | | #ifndef SOCKET_GWTJHBPO |
| | | #define SOCKET_GWTJHBPO |
| | | |
| | | #include "shm_queue.h" |
| | | #include "bh_util.h" |
| | | #include "defs.h" |
| | | #include "sendq.h" |
| | | #include "shm_msg_queue.h" |
| | | #include <atomic> |
| | | #include <boost/noncopyable.hpp> |
| | | #include <condition_variable> |
| | | #include <functional> |
| | | #include <memory> |
| | | #include <mutex> |
| | | #include <thread> |
| | | #include <vector> |
| | | |
| | | using namespace bhome_msg; |
| | | class ShmSocket : private boost::noncopyable |
| | | { |
| | | typedef bhome_shm::ShmMsgQueue Queue; |
| | | |
| | | protected: |
| | | typedef ShmMsgQueue Queue; |
| | | |
| | | public: |
| | | enum Type { |
| | | eSockRequest, |
| | | eSockReply, |
| | | eSockSubscribe, |
| | | eSockPublish, |
| | | eSockBus, |
| | | }; |
| | | typedef std::function<void(bhome_msg::BHMsg &msg)> RecvCB; |
| | | typedef std::function<void(bhome_msg::MsgI &imsg)> RecvRawCB; |
| | | typedef ShmMsgQueue::MQId MQId; |
| | | typedef bhome_shm::SharedMemory Shm; |
| | | typedef std::function<void(ShmSocket &sock, Queue::RawData &val)> RawRecvCB; |
| | | typedef std::function<void(ShmSocket &sock, MsgI &imsg, BHMsgHead &head)> RecvCB; |
| | | typedef std::function<bool(ShmSocket &sock, MsgI &imsg, BHMsgHead &head)> PartialRecvCB; |
| | | typedef std::function<void(ShmSocket &sock)> IdleCB; |
| | | |
| | | ShmSocket(Type type, bhome_shm::SharedMemory &shm); |
| | | ShmSocket(Type type); |
| | | ShmSocket(Shm &shm, const MQId id, const int len); |
| | | ShmSocket(Shm &shm, const bool create_or_else_find, const MQId id, const int len); |
| | | ShmSocket(int64_t offset, Shm &shm, const MQId id); |
| | | ~ShmSocket(); |
| | | // bool Request(const std::string &topic, const void *data, const size_t size, onReply); |
| | | bool RequestAndWait() { return false; } // call Request, and wait onReply notify cv |
| | | |
| | | // bool HandleRequest(onData); |
| | | bool ReadRequest(); // exclude with HandleRequest |
| | | bool SendReply(); // exclude with HandleRequest |
| | | |
| | | bool Publish(const std::string &topic, const void *data, const size_t size, const int timeout_ms); |
| | | bool Subscribe(const std::vector<std::string> &topics, const int timeout_ms); |
| | | bool RecvSub(std::string &topic, std::string &data, const int timeout_ms); |
| | | |
| | | static bool Remove(SharedMemory &shm, const MQId id) { return Queue::Remove(shm, id); } |
| | | bool Remove() { return Remove(shm(), id()); } |
| | | MQId id() const { return mq().Id(); } |
| | | int64_t AbsAddr() const { return mq().AbsAddr(); } |
| | | void SetNodeProc(const int proc_index, const int socket_index) |
| | | { |
| | | node_proc_index_ = proc_index; |
| | | socket_index_ = socket_index; |
| | | } |
| | | // start recv. |
| | | bool Start(const RecvCB &onData, int nworker = 1); |
| | | bool StartRaw(const RecvRawCB &onData, int nworker = 1); |
| | | bool Start(int nworker = 1, const RecvCB &onMsg = RecvCB(), const RawRecvCB &onRaw = RawRecvCB(), const IdleCB &onIdle = IdleCB()); |
| | | bool Start(const RecvCB &onData, const IdleCB &onIdle, int nworker = 1) { return Start(nworker, onData, RawRecvCB(), onIdle); } |
| | | bool Start(const RecvCB &onData, int nworker = 1) { return Start(nworker, onData); } |
| | | bool Stop(); |
| | | |
| | | template <class Body> |
| | | bool CenterSend(const MQInfo &remote, BHMsgHead &head, Body &body) |
| | | { |
| | | try { |
| | | //TODO alloc outsiez and use send. |
| | | MsgI msg; |
| | | if (!msg.Make(head, body)) { return false; } |
| | | DEFER1(msg.Release()); |
| | | |
| | | return Send(remote, msg); |
| | | } catch (...) { |
| | | SetLastError(eError, "Send internal error."); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | bool RequestAlloc(const int64_t size, std::function<void(MsgI &msg)> const &onResult); |
| | | |
| | | template <class Body> |
| | | bool Send(const MQInfo &remote, BHMsgHead &head, Body &body, RecvCB &&cb = RecvCB()) |
| | | { |
| | | return Send(remote, MsgI::Serialize(head, body), head.msg_id(), std::move(cb)); |
| | | } |
| | | template <class... T> |
| | | bool Send(const MQInfo &remote, const MsgI &imsg, T &&...t) |
| | | { |
| | | return SendImpl(remote, imsg, std::forward<decltype(t)>(t)...); |
| | | } |
| | | template <class... T> |
| | | bool Send(const MQInfo &remote, const int64_t cmd, T &&...t) |
| | | { |
| | | return SendImpl(remote, cmd, std::forward<decltype(t)>(t)...); |
| | | } |
| | | |
| | | template <class Body> |
| | | bool SendAndRecv(const MQInfo &remote, BHMsgHead &head, Body &body, MsgI &reply, BHMsgHead &reply_head, const int timeout_ms) |
| | | { |
| | | struct State { |
| | | std::mutex mutex; |
| | | std::condition_variable cv; |
| | | bool canceled = false; |
| | | }; |
| | | |
| | | try { |
| | | std::shared_ptr<State> st(new State); |
| | | |
| | | auto endtime = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms); |
| | | |
| | | auto OnRecv = [st, &reply, &reply_head](ShmSocket &sock, MsgI &msg, BHMsgHead &head) { |
| | | std::unique_lock<std::mutex> lk(st->mutex); |
| | | if (!st->canceled) { |
| | | reply.swap(msg); |
| | | reply_head.Swap(&head); |
| | | st->cv.notify_one(); |
| | | } else { // ignore |
| | | } |
| | | }; |
| | | |
| | | std::unique_lock<std::mutex> lk(st->mutex); |
| | | bool sendok = Send(remote, head, body, std::move(OnRecv)); |
| | | if (!sendok) { |
| | | LOG_DEBUG() << "send timeout"; |
| | | } |
| | | if (sendok && st->cv.wait_until(lk, endtime) == std::cv_status::no_timeout) { |
| | | return true; |
| | | } else { |
| | | st->canceled = true; |
| | | SetLastError(ETIMEDOUT, "timeout"); |
| | | return false; |
| | | } |
| | | } catch (...) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | Shm &shm() const { return mq().shm(); } |
| | | |
| | | protected: |
| | | Queue &mq() { return mq_; } // programmer should make sure that mq_ is valid. |
| | | const Queue &mq() const { return mq_; } |
| | | std::mutex &mutex() { return mutex_; } |
| | | |
| | | private: |
| | | bool StopNoLock(); |
| | | bhome_shm::SharedMemory &shm_; |
| | | const Type type_; |
| | | bool RunningNoLock() { return !workers_.empty(); } |
| | | |
| | | bool Send(const MQInfo &remote, std::string &&content, const std::string &msg_id, RecvCB &&cb = RecvCB()); |
| | | |
| | | template <class... Rest> |
| | | bool SendImpl(const MQInfo &remote, Rest &&...rest) |
| | | { |
| | | return send_buffer_.Append(remote, std::forward<decltype(rest)>(rest)...); |
| | | } |
| | | |
| | | std::vector<std::thread> workers_; |
| | | std::mutex mutex_; |
| | | std::atomic<bool> run_; |
| | | |
| | | std::unique_ptr<Queue> mq_; |
| | | Queue mq_; |
| | | template <class Key, class CB> |
| | | class CallbackRecords |
| | | { |
| | | std::unordered_map<Key, CB> store_; |
| | | |
| | | public: |
| | | bool empty() const { return store_.empty(); } |
| | | bool Store(const Key &id, CB &&cb) { return store_.emplace(id, std::move(cb)).second; } |
| | | bool Pick(const Key &id, CB &cb) |
| | | { |
| | | auto pos = store_.find(id); |
| | | if (pos != store_.end()) { |
| | | cb.swap(pos->second); |
| | | store_.erase(pos); |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | Synced<CallbackRecords<std::string, RecvCB>> per_msg_cbs_; |
| | | Synced<CallbackRecords<int, RawRecvCB>> alloc_cbs_; |
| | | SendQ send_buffer_; |
| | | |
| | | // node request center alloc memory. |
| | | int node_proc_index_ = -1; |
| | | int socket_index_ = -1; |
| | | std::atomic<int> alloc_id_; |
| | | }; |
| | | |
| | | #endif // end of include guard: SOCKET_GWTJHBPO |