1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00
SqMod/module/Library/ZMQ.hpp

1083 lines
36 KiB
C++
Raw Normal View History

2021-02-02 18:07:02 +01:00
#pragma once
// ------------------------------------------------------------------------------------------------
#include "Core/Utility.hpp"
2021-02-02 19:31:21 +01:00
#include "Library/IO/Buffer.hpp"
2021-02-02 18:07:02 +01:00
// ------------------------------------------------------------------------------------------------
#include <mutex>
#include <thread>
#include <vector>
#include <memory>
#include <chrono>
#include <utility>
#include <algorithm>
// ------------------------------------------------------------------------------------------------
#include <zmq.h>
#include <concurrentqueue.h>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
struct ZCtx;
2021-02-03 11:01:46 +01:00
struct ZSkt;
2021-02-02 18:07:02 +01:00
struct ZContext;
2021-02-03 11:01:46 +01:00
struct ZSocket;
2021-02-02 18:07:02 +01:00
/* ------------------------------------------------------------------------------------------------
* Core implementation and management for a ZMQ context.
*/
struct ZCtx
{
/* --------------------------------------------------------------------------------------------
* Smart pointers to this type. Helper typedefs.
*/
using Ptr = std::shared_ptr< ZCtx >;
/* --------------------------------------------------------------------------------------------
* Context pointer.
*/
void * mPtr;
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
ZCtx()
: mPtr(zmq_ctx_new())
{
if (!mPtr)
{
STHROWF("Unable to initialize context: {}", zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
explicit ZCtx(void * ptr)
: mPtr(ptr)
{
if (!mPtr)
{
STHROWF("Invalid context");
}
}
/* --------------------------------------------------------------------------------------------
* Copy constructor (disabled).
*/
ZCtx(const ZCtx &) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor (disabled).
*/
ZCtx(ZCtx &&) noexcept = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~ZCtx()
{
if (mPtr)
{
int r = zmq_ctx_term(mPtr);
// Just in case
if (r != 0)
{
2021-02-03 11:10:15 +01:00
LogFtl("Context failed to terminate properly: [%d], %s", r, zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
}
}
/* --------------------------------------------------------------------------------------------
* Assignment operator (disabled).
*/
ZCtx & operator = (const ZCtx &) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment (disabled).
*/
ZCtx & operator = (ZCtx &&) noexcept = delete;
/* --------------------------------------------------------------------------------------------
* Implicit conversion to boolean operator.
*/
operator bool () const noexcept { return static_cast< bool >(mPtr); } // NOLINT(google-explicit-constructor)
/* --------------------------------------------------------------------------------------------
* Implicit conversion to context pointer (void *) operator.
*/
operator void * () const noexcept { return mPtr; } // NOLINT(google-explicit-constructor)
};
/* ------------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Core implementation and management for a message.
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
struct ZMsg
2021-02-02 18:07:02 +01:00
{
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* List of messages.
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
using List = std::vector< Buffer >;
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Raw union data size.
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
static constexpr size_t SIZE = sizeof(Buffer) > sizeof(List) ? sizeof(Buffer) : sizeof(List);
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Tag used to indicate a multi-part message.
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
struct Multipart { };
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Message contents.
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
union
{
Buffer mBuff;
List mList;
uint8_t mData[SIZE];
};
/* --------------------------------------------------------------------------------------------
* Whether this is a multi-part-message.
*/
const bool mMulti;
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
ZMsg() // NOLINT(cppcoreguidelines-pro-type-member-init)
: mBuff(), mMulti(false)
{
}
/* --------------------------------------------------------------------------------------------
* Default multi-part constructor.
*/
explicit ZMsg(Multipart) // NOLINT(cppcoreguidelines-pro-type-member-init)
: mList(), mMulti(true)
{
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
explicit ZMsg(const Buffer & b) // NOLINT(cppcoreguidelines-pro-type-member-init,modernize-pass-by-value)
: mBuff(b), mMulti(false)
{
}
/* --------------------------------------------------------------------------------------------
* Copy multi-part constructor.
*/
explicit ZMsg(const List & l) // NOLINT(cppcoreguidelines-pro-type-member-init,modernize-pass-by-value)
: mList(l), mMulti(true)
{
}
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
explicit ZMsg(Buffer && b) // NOLINT(cppcoreguidelines-pro-type-member-init)
: mBuff(std::move(b)), mMulti(false)
{
}
/* --------------------------------------------------------------------------------------------
* Move multi-part constructor.
*/
explicit ZMsg(List && l) // NOLINT(cppcoreguidelines-pro-type-member-init)
: mList(std::move(l)), mMulti(true)
{
}
/* --------------------------------------------------------------------------------------------
* Copy constructor (disabled).
*/
ZMsg(const ZMsg &) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor (disabled).
*/
ZMsg(ZMsg &&) noexcept = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~ZMsg()
{
// Is this multi-part?
if (mMulti)
{
mList.~List(); // Invoke list destructor
}
else
{
mBuff.~Buffer(); // Invoke buffer destructor
}
}
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Assignment operator (disabled).
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
ZMsg & operator = (const ZMsg &) = delete;
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Move assignment (disabled).
2021-02-02 18:07:02 +01:00
*/
2021-02-04 06:55:10 +01:00
ZMsg & operator = (ZMsg &&) noexcept = delete;
/* --------------------------------------------------------------------------------------------
* Push the specified message to the multi-part list.
*/
void Push(zmq_msg_t & msg)
{
mList.emplace_back(static_cast< Buffer::ConstPtr >(zmq_msg_data(&msg)),
static_cast< Buffer::SzType >(zmq_msg_size(&msg)));
}
};
/* ------------------------------------------------------------------------------------------------
* Core implementation and management for a ZMQ socket.
*/
struct ZSkt : SqChainedInstances< ZSkt >
{
/* --------------------------------------------------------------------------------------------
* Smart pointers to this type. Helper typedefs.
*/
using Ptr = std::shared_ptr< ZSkt >;
/* --------------------------------------------------------------------------------------------
* Message queue type.
*/
using Item = std::unique_ptr< ZMsg >;
/* --------------------------------------------------------------------------------------------
* Message queue type.
*/
using Queue = moodycamel::ConcurrentQueue< Item >;
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Context pointer.
*/
void * mPtr;
/* --------------------------------------------------------------------------------------------
* Socket status.
*/
bool mRun;
/* --------------------------------------------------------------------------------------------
* Messages should be delivered as string instead of binary data.
*/
bool mStringMessages;
/* --------------------------------------------------------------------------------------------
* Socket type.
*/
int mType;
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Synchronization mutex.
*/
std::mutex mMtx;
/* --------------------------------------------------------------------------------------------
* Messages received from the socket.
*/
Queue mOutputQueue;
/* --------------------------------------------------------------------------------------------
* Messages to be sent through the socket.
*/
Queue mInputQueue;
/* --------------------------------------------------------------------------------------------
* Message received callback.
*/
Function mOnData;
/* --------------------------------------------------------------------------------------------
* Processing thread.
*/
std::thread mThread;
/* --------------------------------------------------------------------------------------------
* Socket context.
*/
ZCtx::Ptr mContext;
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
ZSkt(const ZCtx::Ptr & ctx, int type)
2021-02-02 18:07:02 +01:00
: SqChainedInstances< ZSkt >()
, mPtr(nullptr), mRun(true), mStringMessages(true), mType(type), mMtx()
2021-02-04 06:55:10 +01:00
, mOutputQueue(4096), mInputQueue(4096)
, mOnData(), mThread(), mContext(ctx)
2021-02-02 18:07:02 +01:00
{
// Validate the context
if (!ctx)
2021-02-02 18:07:02 +01:00
{
STHROWF("Invalid context");
2021-02-02 18:07:02 +01:00
}
// Create the processing thread
mThread = std::thread(&ZSkt::Proc, this);
2021-02-02 18:07:02 +01:00
// Remember this instance
ChainInstance();
2021-02-03 11:01:46 +01:00
// Wait for the socket to be created before we attempt to use it
for (int n = 0; n < 100; ++n)
{
2021-02-03 11:01:46 +01:00
using namespace std::chrono_literals;
// Acquire exclusive access to the socket
mMtx.lock();
// Was the socket created?
if (mPtr)
{
// Release exclusive access to the socket
mMtx.unlock();
// Socket created
break;
}
else
{
// Release exclusive access to the socket
mMtx.unlock();
// Wait for the socket to be created
std::this_thread::sleep_for(10ms);
}
}
// If it wasn't created by this point then something isn't right
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Copy constructor (disabled).
*/
ZSkt(const ZSkt &) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor (disabled).
*/
ZSkt(ZSkt &&) noexcept = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~ZSkt()
{
// Anything to close?
2021-02-02 18:07:02 +01:00
if (mPtr)
{
Close();
2021-02-02 18:07:02 +01:00
}
// Forget about this instance
UnchainInstance();
}
/* --------------------------------------------------------------------------------------------
* Assignment operator (disabled).
*/
ZSkt & operator = (const ZSkt &) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment (disabled).
*/
ZSkt & operator = (ZSkt &&) noexcept = delete;
/* --------------------------------------------------------------------------------------------
* Implicit conversion to boolean operator.
*/
operator bool () const noexcept { return static_cast< bool >(mPtr); } // NOLINT(google-explicit-constructor)
/* --------------------------------------------------------------------------------------------
* Implicit conversion to socket pointer (void *) operator.
*/
operator void * () const noexcept { return mPtr; } // NOLINT(google-explicit-constructor)
/* --------------------------------------------------------------------------------------------
* Internal processing thread.
* NOTE: Messages are being sent in whatever order we can.
* Don't expect them be in the order you sent or receive them.
* That's the cost of simplicity. And something I can live with under the circumstances.
*/
void Proc()
{
// Acquire exclusive access to the socket
mMtx.lock();
// Create the socket in this thread
mPtr = zmq_socket(*mContext, mType);
// Validate the socket
if (!mPtr)
{
LogErr("Unable to initialize socket: %s", zmq_strerror(errno));
// Stop the thread
return;
}
2021-02-03 11:01:46 +01:00
// Release exclusive access to the socket
mMtx.unlock();
// Enter processing loop
while (mRun)
2021-02-02 18:07:02 +01:00
{
using namespace std::chrono_literals;
// Acquire exclusive access to the socket
mMtx.lock();
2021-02-02 20:56:40 +01:00
// Perform tasks until there's none left
2021-02-04 06:55:10 +01:00
if (!Recv() && !Send())
2021-02-02 20:56:40 +01:00
{
// Release exclusive access to the socket
mMtx.unlock();
2021-02-02 20:56:40 +01:00
// Don't exhaust resources pointlessly
std::this_thread::sleep_for(50ms);
}
else
{
// Release exclusive access to the socket
mMtx.unlock();
}
}
// Acquire exclusive access to the socket
mMtx.lock();
// Close the socket
int r = zmq_close(mPtr);
// Forget about it
mPtr = nullptr;
// Release exclusive access to the socket
mMtx.unlock();
// Validate result
if (r != 0)
{
2021-02-03 11:10:15 +01:00
LogErr("Unable to close socket: [%d] {%s}", r, zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
}
/* --------------------------------------------------------------------------------------------
* Flush messages from the queue to the script.
*/
void Flush(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Stop sockets and prepare for a shutdown.
*/
void Close()
{
// Is the processing thread running?
if (mThread.joinable())
{
// Acquire exclusive access
mMtx.lock();
// Stop the loop
mRun = false;
2021-02-02 18:07:02 +01:00
// Yield exclusive access
mMtx.unlock();
// Wait for the thread
mThread.join();
}
else
2021-02-02 18:07:02 +01:00
{
mRun = false; // Just in case
2021-02-02 18:07:02 +01:00
}
2021-02-03 11:01:46 +01:00
// Forget about the context
mContext.reset();
2021-02-02 18:07:02 +01:00
}
2021-02-02 19:31:21 +01:00
/* --------------------------------------------------------------------------------------------
* Queue a message to be sent through the socket.
*/
void Send(const Buffer & data)
2021-02-02 19:31:21 +01:00
{
2021-02-04 06:55:10 +01:00
mInputQueue.enqueue(std::make_unique< ZMsg >(data));
2021-02-02 19:31:21 +01:00
}
/* --------------------------------------------------------------------------------------------
* Queue a message to be sent through the socket.
*/
void Send(Buffer && data)
2021-02-02 19:31:21 +01:00
{
2021-02-04 06:55:10 +01:00
mInputQueue.enqueue(std::make_unique< ZMsg >(std::move(data)));
}
/* --------------------------------------------------------------------------------------------
* Queue a multi-part message to be sent through the socket.
*/
void Send(const ZMsg::List & list)
{
mInputQueue.enqueue(std::make_unique< ZMsg >(list));
2021-02-02 19:31:21 +01:00
}
/* --------------------------------------------------------------------------------------------
* Queue a multi-part message to be sent through the socket.
*/
2021-02-04 06:55:10 +01:00
void Send(ZMsg::List && list)
2021-02-02 19:31:21 +01:00
{
2021-02-04 06:55:10 +01:00
mInputQueue.enqueue(std::make_unique< ZMsg >(std::move(list)));
2021-02-02 19:31:21 +01:00
}
2021-02-02 18:07:02 +01:00
protected:
/* --------------------------------------------------------------------------------------------
* Receive one message from the socket.
*/
2021-02-02 20:56:40 +01:00
bool Recv()
2021-02-02 18:07:02 +01:00
{
// Need someone to receive the message
zmq_msg_t msg;
// Initialize to an empty message
int r = zmq_msg_init(&msg);
// Make sure we have a message
if (r != 0)
{
LogErr("Unable to initialize ZMQ message");
// We couldn't receive anything
return false;
}
2021-02-02 18:07:02 +01:00
// Ask for a message, if any
r = zmq_msg_recv(&msg, mPtr, ZMQ_DONTWAIT);
2021-02-04 06:55:10 +01:00
// Is this a multi-part message?
if (zmq_msg_more(&msg) == 1)
{
return RecvMore(msg, r);
}
// Extract the message data
2021-02-04 06:55:10 +01:00
Item item = std::make_unique< ZMsg >(Buffer(static_cast< Buffer::ConstPtr >(zmq_msg_data(&msg)),
static_cast< Buffer::SzType >(zmq_msg_size(&msg))));
// Release this message
zmq_msg_close(&msg);
2021-02-02 18:07:02 +01:00
// Did we have a message?
if (r >= 0)
{
2021-02-02 20:56:40 +01:00
// Put it in the queue
2021-02-04 06:55:10 +01:00
mOutputQueue.enqueue(std::move(item));
2021-02-02 20:56:40 +01:00
// We received a message
return true;
2021-02-02 18:07:02 +01:00
}
2021-02-02 20:56:40 +01:00
// No message was retrieved
return false;
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
2021-02-04 06:55:10 +01:00
* Receive multiple messages from the socket.
*/
bool RecvMore(zmq_msg_t & msg, int r)
{
size_t more_sz = sizeof(int);
// Used to see if more parts follow
int more = 1;
// Create an empty multi-part message container
Item item = std::make_unique< ZMsg >(ZMsg::Multipart{});
// Add the initial message to the list
if (r >= 0)
{
// Save it to the list
item->Push(msg);
// Close the message
zmq_msg_close(&msg);
}
// Keep receiving messages while there's more
do
{
// Initialize an empty message
r = zmq_msg_init(&msg);
// Make sure we have a message
if (r != 0)
{
LogErr("Unable to initialize ZMQ message part");
// Abort everything
return false;
}
// Ask for another message, if any (blocking operation!)
r = zmq_msg_recv(&msg, mPtr, 0);
// Do we actually have a message?
if (r >= 0)
{
// Save it to the list
item->Push(msg);
// Close the message
zmq_msg_close(&msg);
}
// See if the message part last received from the socket was a data part with more parts to follow.
zmq_getsockopt(mPtr, ZMQ_RCVMORE, &more, &more_sz);
} while (more);
// Did we actually have any valid messages?
if (!(item->mList.empty()))
{
mOutputQueue.enqueue(std::move(item));
// Messages were present in the list
return true;
}
// No messages were added
return false;
}
/* --------------------------------------------------------------------------------------------
* Send one queued message to the socket.
2021-02-02 18:07:02 +01:00
*/
bool Send()
{
// Need someone to receive the message
Item data;
2021-02-02 18:07:02 +01:00
// Try to get a message from the queue
if (mInputQueue.try_dequeue(data))
2021-02-02 18:07:02 +01:00
{
2021-02-04 06:55:10 +01:00
if (data->mMulti)
2021-02-02 18:07:02 +01:00
{
2021-02-04 06:55:10 +01:00
SendMore(data->mList);
}
else
{
SendOne(data->mBuff);
2021-02-02 18:07:02 +01:00
}
// One item was found in the queue
return true;
}
else
{
return false; // No item in the queue
}
}
2021-02-04 06:55:10 +01:00
/* --------------------------------------------------------------------------------------------
* Send a single message to the socket.
*/
void SendOne(Buffer & buff) const
{
// Attempt to send the message
int r = zmq_send(mPtr, buff.Data(), buff.Position(), ZMQ_DONTWAIT);
// Could we send what the message had?
if (r >= 0 && static_cast< Buffer::SzType >(r) != buff.Position())
{
LogErr("Unable to send data to socket: [%d], {%s}", r, zmq_strerror(errno));
// NOTE: Should we put the buffer back into the queue?
}
}
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Send a multi-part message to the socket.
*/
2021-02-04 06:55:10 +01:00
void SendMore(ZMsg::List & list) const
2021-02-02 18:07:02 +01:00
{
2021-02-04 06:55:10 +01:00
// Send all message parts
for (size_t i = 0, n = list.size(); i < n; ++i)
2021-02-02 18:07:02 +01:00
{
2021-02-04 06:55:10 +01:00
// Attempt to send the message
int r = zmq_send(mPtr, list[i].Data(), list[i].Position(), (i + 1) == n ? ZMQ_DONTWAIT : ZMQ_SNDMORE);
// Could we send what the message had?
if (r >= 0 && static_cast< Buffer::SzType >(r) != list[i].Position())
2021-02-02 18:07:02 +01:00
{
2021-02-04 06:55:10 +01:00
LogErr("Unable to send multi-part data to socket: [%d], %s", r, zmq_strerror(errno));
// NOTE: Should we abort the whole thing? But we probably already sent some.
2021-02-02 18:07:02 +01:00
}
}
}
};
/* ------------------------------------------------------------------------------------------------
* Interface for ZMQ contexts.
*/
struct ZContext
{
2021-02-02 19:31:21 +01:00
/* --------------------------------------------------------------------------------------------
* Pointer to the interfaced context.
*/
ZCtx::Ptr mHnd;
2021-02-02 19:31:21 +01:00
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
ZContext()
: mHnd(std::make_shared< ZCtx >())
2021-02-02 18:07:02 +01:00
{
}
/* --------------------------------------------------------------------------------------------
* Pointer constructor.
*/
explicit ZContext(ZCtx::Ptr ptr)
: mHnd(std::move(ptr))
2021-02-02 18:07:02 +01:00
{
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
ZContext(const ZContext &) = default;
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
ZContext(ZContext &&) noexcept = default;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~ZContext() = default;
/* --------------------------------------------------------------------------------------------
* Assignment operator.
*/
ZContext & operator = (const ZContext &) = default;
/* --------------------------------------------------------------------------------------------
* Move assignment.
*/
ZContext & operator = (ZContext &&) noexcept = default;
/* --------------------------------------------------------------------------------------------
* Make sure a context instance is referenced.
*/
void Validate() const
{
if (!mHnd)
2021-02-02 18:07:02 +01:00
{
STHROWF("Invalid context instance");
}
}
/* --------------------------------------------------------------------------------------------
* Make sure a context instance is referenced and return the context.
*/
SQMOD_NODISCARD ZCtx & Valid() { Validate(); return *mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Make sure a context instance is referenced and return the context.
*/
SQMOD_NODISCARD const ZCtx & Valid() const { Validate(); return *mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Make sure a context instance is referenced and return the reference.
*/
SQMOD_NODISCARD ZCtx::Ptr & ValidRef() { Validate(); return mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Make sure a context instance is referenced and return the reference.
*/
SQMOD_NODISCARD const ZCtx::Ptr & ValidRef() const { Validate(); return mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Check if a context instance is referenced.
*/
SQMOD_NODISCARD bool IsNull() const
{
return static_cast< bool >(mHnd);
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value of an option.
*/
SQMOD_NODISCARD int Get(int opt) const
{
return zmq_ctx_get(Valid(), opt);
}
/* --------------------------------------------------------------------------------------------
* Modify the value of an option.
*/
void Set(int opt, int value)
{
int r = zmq_ctx_set(Valid(), opt, value);
// Validate result
if (r != 0)
{
STHROWF("Unable to set context option: [{}] {}", r, zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
}
/* --------------------------------------------------------------------------------------------
* Modify the value of an option.
*/
void Shutdown() const
{
int r = zmq_ctx_shutdown(Valid());
// Validate result
if (r != 0)
{
STHROWF("Unable to shutdown context: {}", zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
}
/* --------------------------------------------------------------------------------------------
* Helper function to create sockets.
*/
SQMOD_NODISCARD LightObj Socket(int type) const;
};
/* ------------------------------------------------------------------------------------------------
* Interface for ZMQ sockets.
*/
struct ZSocket
{
2021-02-02 19:31:21 +01:00
/* --------------------------------------------------------------------------------------------
* Pointer to the interfaced socket.
*/
2021-02-03 11:01:46 +01:00
ZSkt::Ptr mHnd;
2021-02-02 19:31:21 +01:00
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
ZSocket(const ZContext & ctx, int type)
: mHnd(std::make_shared< ZSkt >(ctx.mHnd, type))
2021-02-02 18:07:02 +01:00
{
2021-02-03 11:01:46 +01:00
if (!(mHnd->mPtr))
{
STHROWF("Failed to create socket");
}
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Pointer constructor.
*/
explicit ZSocket(ZSkt::Ptr ptr)
2021-02-02 19:31:21 +01:00
: mHnd(std::move(ptr))
2021-02-02 18:07:02 +01:00
{
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
ZSocket(const ZSocket &) = default;
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
ZSocket(ZSocket &&) noexcept = default;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~ZSocket()
{
Close();
}
/* --------------------------------------------------------------------------------------------
* Assignment operator.
*/
ZSocket & operator = (const ZSocket &) = default;
/* --------------------------------------------------------------------------------------------
* Move assignment.
*/
ZSocket & operator = (ZSocket &&) noexcept = default;
/* --------------------------------------------------------------------------------------------
* Make sure a socket instance is referenced.
*/
void Validate() const
{
2021-02-02 19:31:21 +01:00
if (!mHnd)
2021-02-02 18:07:02 +01:00
{
STHROWF("Invalid socket instance");
}
}
/* --------------------------------------------------------------------------------------------
* Make sure a socket instance is referenced and return the socket.
*/
2021-02-02 19:31:21 +01:00
SQMOD_NODISCARD ZSkt & Valid() { Validate(); return *mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Make sure a socket instance is referenced and return the socket.
*/
2021-02-02 19:31:21 +01:00
SQMOD_NODISCARD const ZSkt & Valid() const { Validate(); return *mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Make sure a socket instance is referenced and return the reference.
*/
2021-02-02 19:31:21 +01:00
SQMOD_NODISCARD ZSkt::Ptr & ValidRef() { Validate(); return mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
* Make sure a socket instance is referenced and return the reference.
*/
2021-02-02 19:31:21 +01:00
SQMOD_NODISCARD const ZSkt::Ptr & ValidRef() const { Validate(); return mHnd; }
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-02 19:31:21 +01:00
* Check if a socket instance is referenced.
2021-02-02 18:07:02 +01:00
*/
SQMOD_NODISCARD bool IsNull() const
{
2021-02-02 19:31:21 +01:00
return static_cast< bool >(mHnd);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value of a socket option.
*/
SQMOD_NODISCARD LightObj GetOpt(int opt);
/* --------------------------------------------------------------------------------------------
* Modify the value of a socket option.
*/
void SetOpt(int opt, LightObj & value);
/* --------------------------------------------------------------------------------------------
* Instruct the socket to always deliver messages as strings instead of binary data.
*/
SQMOD_NODISCARD bool GetStringMessages() const
{
return Valid().mStringMessages;
}
/* --------------------------------------------------------------------------------------------
* Instruct the socket to always deliver messages as strings instead of binary data.
*/
void SetStringMessages(bool value)
{
Valid().mStringMessages = value;
}
2021-02-02 19:31:21 +01:00
/* --------------------------------------------------------------------------------------------
* Callback to receive incoming messages.
*/
ZSocket & OnData(Function & cb)
{
Valid().mOnData = std::move(cb);
// Allow chaining
return *this;
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Accept incoming connections on the socket.
*/
2021-02-02 19:31:21 +01:00
ZSocket & Bind(StackStrF & ep)
2021-02-02 18:07:02 +01:00
{
// Acquire exclusive access to the socket
std::lock_guard< std::mutex > guard(Valid().mMtx);
// Attempt to bind the socket
int r = zmq_bind(Valid(), ep.mPtr);
// Validate result
if (r != 0)
{
STHROWF("Unable to bind socket: [{}] {}", r, zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Create outgoing connection from the socket
*/
2021-02-02 19:31:21 +01:00
ZSocket & Connect(StackStrF & ep)
2021-02-02 18:07:02 +01:00
{
// Acquire exclusive access to the socket
std::lock_guard< std::mutex > guard(Valid().mMtx);
// Attempt to connect the socket
int r = zmq_connect(Valid(), ep.mPtr);
// Validate result
if (r != 0)
{
STHROWF("Unable to connect socket: [{}] {}", r, zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Create outgoing connection from the socket
*/
2021-02-02 19:31:21 +01:00
ZSocket & Disconnect(StackStrF & ep)
2021-02-02 18:07:02 +01:00
{
// Acquire exclusive access to the socket
std::lock_guard< std::mutex > guard(Valid().mMtx);
// Attempt to connect the socket
int r = zmq_disconnect(Valid(), ep.mPtr);
// Validate result
if (r != 0)
{
STHROWF("Unable to disconnect socket: [{}] {}", r, zmq_strerror(errno));
2021-02-02 18:07:02 +01:00
}
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Close the managed socket.
*/
2021-02-02 19:31:21 +01:00
ZSocket & Close()
2021-02-02 18:07:02 +01:00
{
Valid().Close();
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
2021-02-02 18:07:02 +01:00
}
/* --------------------------------------------------------------------------------------------
* Send a binary message to the socket.
2021-02-02 18:07:02 +01:00
*/
ZSocket & SendBuffer(SqBuffer & data)
2021-02-02 18:07:02 +01:00
{
// Validate buffer
data.ValidateDeeper();
// Create a copy and send it
Valid().Send(Buffer(data.GetInst().Data(), data.GetInst().Position()));
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
2021-02-02 18:07:02 +01:00
}
2021-02-02 19:31:21 +01:00
/* --------------------------------------------------------------------------------------------
* Send a string message to the socket.
*/
ZSocket & SendString(StackStrF & str)
2021-02-02 19:31:21 +01:00
{
Valid().Send(Buffer(static_cast< Buffer::ConstPtr >(str.mPtr), ClampL< SQInteger, Buffer::SzType >(str.mLen)));
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
}
2021-02-02 18:07:02 +01:00
/* --------------------------------------------------------------------------------------------
2021-02-02 19:31:21 +01:00
* Send a multi-part ZMQ message to the socket.
*/
ZSocket & SendBuffers(Array & arr)
2021-02-02 19:31:21 +01:00
{
Validate();
2021-02-04 06:55:10 +01:00
ZMsg::List list;
2021-02-02 19:31:21 +01:00
// Extract the messages from the array
arr.Foreach([&list](HSQUIRRELVM vm, SQInteger) {
// Extract the buffer from the stack
SqBuffer * data = ClassType< SqBuffer >::GetInstance(vm, -1);
2021-02-02 19:31:21 +01:00
// In case we didn't fail at the null part (it should)
if (data && data->GetRef())
2021-02-02 19:31:21 +01:00
{
// Add the message to the list
list.emplace_back(data->GetInst().Data(), data->GetInst().Position());
2021-02-02 19:31:21 +01:00
}
// Continue
return SQ_OK;
});
// Send the message list
mHnd->Send(std::move(list));
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
}
/* --------------------------------------------------------------------------------------------
* Send a multi-part string message to the socket.
2021-02-02 18:07:02 +01:00
*/
2021-02-02 19:31:21 +01:00
ZSocket & SendStrings(Array & arr)
{
Validate();
2021-02-04 06:55:10 +01:00
ZMsg::List list;
2021-02-02 19:31:21 +01:00
// Extract the messages from the array
arr.Foreach([&list](HSQUIRRELVM vm, SQInteger) {
StackStrF str(vm, -1);
// Extract the string from the stack
if (SQ_FAILED(str.Proc(false)))
{
return str.mRes; // Abort
}
// Create a new message
list.emplace_back(static_cast< Buffer::ConstPtr >(str.mPtr), ClampL< SQInteger, Buffer::SzType >(str.mLen));
2021-02-02 19:31:21 +01:00
// Continue
return SQRESULT(SQ_OK);
});
// Send the message list
mHnd->Send(std::move(list));
2021-02-02 19:31:21 +01:00
// Allow chaining
return *this;
}
2021-02-02 18:07:02 +01:00
};
} // Namespace:: SqMod