From e94ecab6544dd5875ed70b9241b2a39107a41ac5 Mon Sep 17 00:00:00 2001 From: Jonathan Bendes Date: Wed, 27 Mar 2024 13:36:01 -0400 Subject: [PATCH 1/4] Trying a less threaded version of core zcm --- docs/lcm_to_zcm.md | 1 - docs/transports.md | 8 +- examples/c/nblock_inproc.c | 2 +- examples/c/pub.c | 3 +- examples/cpp/Unsub.cpp | 2 +- .../cpp/transport/SerialTransportTest.cpp | 2 +- examples/julia/nonblock_test.jl | 6 +- examples/node-client/package.json | 4 +- examples/python/handle_test.py | 4 +- gen/version.h | 4 +- test/node/package.json | 2 +- test/zcm/ApiRetcodesTest.hpp | 23 - test/zcm/DispatchLoopTest.hpp | 2 +- test/zcm/Forking2Test.hpp | 6 - test/zcm/ForkingTest.hpp | 9 - zcm/blocking.cpp | 687 +++--------------- zcm/blocking.h | 10 +- zcm/java/jni/zcm_zcm_ZCMJNI.c | 19 +- zcm/java/jni/zcm_zcm_ZCMJNI.h | 16 +- zcm/js/node/package.json | 2 +- zcm/julia/ZCM.jl | 27 +- zcm/nonblocking.c | 56 +- zcm/nonblocking.h | 10 +- zcm/python/zerocm.pyx | 38 +- zcm/transport.h | 53 +- zcm/transport/generic_serial_transport.c | 6 +- zcm/transport/transport.cpp.template | 38 +- zcm/transport/transport_can.cpp | 27 +- zcm/transport/transport_file.cpp | 19 +- zcm/transport/transport_inproc.cpp | 17 +- zcm/transport/transport_ipcshm.cpp | 29 +- zcm/transport/transport_serial.cpp | 169 +++-- zcm/transport/transport_zmq_local.cpp | 22 +- zcm/transport/udp/udp.cpp | 3 +- zcm/zcm-cpp-impl.hpp | 28 +- zcm/zcm-cpp.hpp | 8 +- zcm/zcm.c | 138 ++-- zcm/zcm.h | 85 +-- 38 files changed, 590 insertions(+), 995 deletions(-) diff --git a/docs/lcm_to_zcm.md b/docs/lcm_to_zcm.md index cbcb983cc..5c3193c79 100644 --- a/docs/lcm_to_zcm.md +++ b/docs/lcm_to_zcm.md @@ -71,7 +71,6 @@ allows LCM users to gradually migrate to ZCM. ### Known incompatibilities: - `zcm_get_fileno()` is not supported - - `zcm_handle_timeout()` is not supported - `zcm_subscription_set_queue_capacity` is not supported - LCMType-style enums are not supported - Language bindings not currently supported: diff --git a/docs/transports.md b/docs/transports.md index 4333c3cb7..661e3119f 100644 --- a/docs/transports.md +++ b/docs/transports.md @@ -279,14 +279,14 @@ By default, this number is 512. - `int update(zcm_trans_t *zt)` - This method is called from the `zcm_handle_nonblock()` function. + This method is called from the `zcm_handle()` function. This method provides a periodically-running routine that can perform updates to the underlying hardware or other general maintenance to this transport. A transport implementing this function will typically use this time to flush out any bytes left in its internal buffer. This method should **never block**. Again, this method is called from - `zcm_handle_nonblock()` and thus runs at the same frequency as - `zcm_handle_nonblock()`. Failure to call `zcm_handle_nonblock()` + `zcm_handle()` and thus runs at the same frequency as + `zcm_handle()`. Failure to call `zcm_handle()` while using an nonblock transport may cause the transport to work incorrectly on both message send and recv. @@ -354,7 +354,7 @@ For the blocking case, there are the following approaches: For the non-blocking case, there is a single approach: - - `zcm_handle_nonblock() /* returns non-zero if a message was available and dispatched */` + - `zcm_handle() /* returns non-zero if a message was available and dispatched */` To prevent errors, the internal library checks that the API method matches the transport type. diff --git a/examples/c/nblock_inproc.c b/examples/c/nblock_inproc.c index 21f8398e1..0b70d1563 100644 --- a/examples/c/nblock_inproc.c +++ b/examples/c/nblock_inproc.c @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) my_data.timestamp = i; example_t_publish(&zcm, "EXAMPLE", &my_data); usleep(100000); - zcm_handle_nonblock(&zcm); + zcm_handle(&zcm, 0); } free(my_data.ranges); diff --git a/examples/c/pub.c b/examples/c/pub.c index 8dd748bc3..09419834b 100644 --- a/examples/c/pub.c +++ b/examples/c/pub.c @@ -40,10 +40,11 @@ int main(int argc, char *argv[]) my_data.name = EXAMPLE_T_test_const_string; my_data.enabled = 1; + uint64_t sleepUs = 1000000/HZ; while (1) { if (example_t_publish(zcm, "EXAMPLE", &my_data) == 0) ++my_data.timestamp; - usleep(1000000/HZ); + if (sleepUs > 0) usleep(sleepUs); } zcm_destroy(zcm); diff --git a/examples/cpp/Unsub.cpp b/examples/cpp/Unsub.cpp index b92b26016..8e8bec68b 100644 --- a/examples/cpp/Unsub.cpp +++ b/examples/cpp/Unsub.cpp @@ -39,7 +39,7 @@ static void dispatchOneMessage(zcm::ZCM& zcm, uint64_t sleepTimeUs) } else { uint64_t end = utime() + sleepTimeUs; while (utime() < end) { - if (zcm.handleNonblock() == ZCM_EOK) break; + if (zcm.handle(0) == ZCM_EOK) break; } } } diff --git a/examples/cpp/transport/SerialTransportTest.cpp b/examples/cpp/transport/SerialTransportTest.cpp index 0de6cd790..72122c7fc 100644 --- a/examples/cpp/transport/SerialTransportTest.cpp +++ b/examples/cpp/transport/SerialTransportTest.cpp @@ -92,7 +92,7 @@ int main(int argc, const char *argv[]) nextPublish = now + PUBLISH_DT; } - zcmLocal.handleNonblock(); + zcmLocal.handle(0); } zcmLocal.unsubscribe(sub); diff --git a/examples/julia/nonblock_test.jl b/examples/julia/nonblock_test.jl index 4aca70ed1..678ba77dc 100644 --- a/examples/julia/nonblock_test.jl +++ b/examples/julia/nonblock_test.jl @@ -21,13 +21,13 @@ msg = example_t() msg.timestamp = 0; publish(zcm, "EXAMPLE", msg) -handle_nonblock(zcm) +handle(zcm, 0) msg.timestamp = 1; publish(zcm, "EXAMPLE", msg) -handle_nonblock(zcm) +handle(zcm, 0) msg.timestamp = 2; publish(zcm, "EXAMPLE", msg) -handle_nonblock(zcm) +handle(zcm, 0) unsubscribe(zcm, sub) diff --git a/examples/node-client/package.json b/examples/node-client/package.json index 8048731cc..e3d98cc10 100644 --- a/examples/node-client/package.json +++ b/examples/node-client/package.json @@ -1,6 +1,6 @@ { "name": "zcm-example", - "version": "1.2.0", + "version": "2.0.0", "description": "", "scripts": { "//": [ @@ -14,6 +14,6 @@ "big-integer": "^1.6.27", "express": "^4.14.0", "ref-napi": "^3.0.0", - "zerocm": "file:../../build/zcm/js/zerocm-1.2.0.tgz" + "zerocm": "file:../../build/zcm/js/zerocm-2.0.0.tgz" } } diff --git a/examples/python/handle_test.py b/examples/python/handle_test.py index 5d9d8c23e..e4efaf5ad 100755 --- a/examples/python/handle_test.py +++ b/examples/python/handle_test.py @@ -23,7 +23,7 @@ def handler(channel, msg): msg = example_t() msg.timestamp = 10 -ret = zcm.handleNonblock() +ret = zcm.handle(0) if ret != zerocm.ZCM_EAGAIN: print("Failed to return successfully when no message is ready") sys.exit(1) @@ -44,7 +44,7 @@ def handler(channel, msg): time.sleep(0.5) # handle incoming message -ret = zcm.handleNonblock() +ret = zcm.handle(0) if ret != zerocm.ZCM_EOK: print("Failed to return successfully when a message is ready") sys.exit(1) diff --git a/gen/version.h b/gen/version.h index 772dc33e9..9587be561 100644 --- a/gen/version.h +++ b/gen/version.h @@ -1,8 +1,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define ZCM_MAJOR_VERSION 1 -#define ZCM_MINOR_VERSION 2 +#define ZCM_MAJOR_VERSION 2 +#define ZCM_MINOR_VERSION 0 #define ZCM_MICRO_VERSION 0 #endif diff --git a/test/node/package.json b/test/node/package.json index f1d3473c2..206e74e2f 100644 --- a/test/node/package.json +++ b/test/node/package.json @@ -13,7 +13,7 @@ "author": "The Zcm Team", "license": "LGPL", "dependencies": { - "zerocm": "file:../../build/zcm/js/zerocm-1.2.0.tgz", + "zerocm": "file:../../build/zcm/js/zerocm-2.0.0.tgz", "ref-napi": "^3.0.0" } } diff --git a/test/zcm/ApiRetcodesTest.hpp b/test/zcm/ApiRetcodesTest.hpp index cffb69c3f..f5e910a34 100644 --- a/test/zcm/ApiRetcodesTest.hpp +++ b/test/zcm/ApiRetcodesTest.hpp @@ -172,29 +172,6 @@ class ApiRetcodesTest : public CxxTest::TestSuite zcm_cleanup(&zcm); } - void testPublishMsgdrop(void) - { - zcm_t zcm; - zcm_init(&zcm, "test-pub-blockforever", NULL); - zcm_start(&zcm); - - // NOTE: We assume that 100000 publish calls are enought to overflow the send buffer - const int MAX_PUBS = 100000; - for (int i = 0; i < MAX_PUBS; i++) { - uint8_t data = 'a'; - int ret = zcm_publish(&zcm, "CHANNEL", &data, 1); - if (ret == ZCM_EAGAIN) { - goto done; - } - } - - TS_FAIL("Failed to get an error return code from zcm_publish()"); - - done: - zcm_stop(&zcm); - zcm_cleanup(&zcm); - } - void testSub(void) { zcm_t zcm; diff --git a/test/zcm/DispatchLoopTest.hpp b/test/zcm/DispatchLoopTest.hpp index 3438f758f..45e1022c5 100644 --- a/test/zcm/DispatchLoopTest.hpp +++ b/test/zcm/DispatchLoopTest.hpp @@ -99,7 +99,7 @@ class DispatchLoopTest : public CxxTest::TestSuite std::thread kill {killThread}; std::thread ctrl {controlThread, zcm}; - zcm_handle(zcm); + zcm_handle(zcm, 0); running = false; ctrl.join(); diff --git a/test/zcm/Forking2Test.hpp b/test/zcm/Forking2Test.hpp index e1c53e302..83d9bbdb5 100644 --- a/test/zcm/Forking2Test.hpp +++ b/test/zcm/Forking2Test.hpp @@ -16,12 +16,6 @@ static void handler(const zcm_recv_buf_t *rbuf, const char *channel, void *usr) numrecv++; } -static void handle(zcm_t *zcm) -{ - int rc = zcm_handle(zcm); - TS_ASSERT_EQUALS(rc, -1) -} - class Forking2Test : public CxxTest::TestSuite { public: diff --git a/test/zcm/ForkingTest.hpp b/test/zcm/ForkingTest.hpp index e5fb9a9db..f81c7c7a9 100644 --- a/test/zcm/ForkingTest.hpp +++ b/test/zcm/ForkingTest.hpp @@ -15,15 +15,6 @@ static void handler(const zcm_recv_buf_t *rbuf, const char *channel, void *usr) numrecv++; } -static void handle(zcm_t *zcm) -{ - int rc = zcm_handle(zcm); - if (rc == -1) { - printf("handle() failed!\n"); - exit(1); - } -} - bool sub(zcm_t *zcm) { zcm_subscribe(zcm, CHANNEL, handler, NULL); diff --git a/zcm/blocking.cpp b/zcm/blocking.cpp index e1a4197f7..3b6aeb069 100644 --- a/zcm/blocking.cpp +++ b/zcm/blocking.cpp @@ -3,7 +3,6 @@ #include "zcm/blocking.h" #include "zcm/transport.h" #include "zcm/zcm_coretypes.h" -#include "zcm/util/threadsafe_queue.hpp" #include "zcm/util/topology.hpp" #include "util/TimeUtil.hpp" @@ -19,8 +18,9 @@ #include #include #include -#include #include +#include +#include using namespace std; #define RECV_TIMEOUT 100 @@ -39,45 +39,6 @@ using namespace std; #define SET_THREAD_NAME(name) #endif -// A C++ class that manages a zcm_msg_t* -struct Msg -{ - zcm_msg_t msg; - - // NOTE: copy the provided data into this object - Msg(uint64_t utime, const char* channel, size_t len, const uint8_t* buf) - { - msg.utime = utime; - msg.channel = strdup(channel); - msg.len = len; - msg.buf = (uint8_t*)malloc(len); - memcpy(msg.buf, buf, len); - } - - Msg(zcm_msg_t* msg) : Msg(msg->utime, msg->channel, msg->len, msg->buf) {} - - ~Msg() - { - if (msg.channel) - free((void*)msg.channel); - if (msg.buf) - free((void*)msg.buf); - memset(&msg, 0, sizeof(msg)); - } - - zcm_msg_t* get() - { - return &msg; - } - - private: - // Disable all copying and moving - Msg(const Msg& other) = delete; - Msg(Msg&& other) = delete; - Msg& operator=(const Msg& other) = delete; - Msg& operator=(Msg&& other) = delete; -}; - static bool isRegexChannel(const string& channel) { // These chars are considered regex @@ -106,37 +67,26 @@ struct zcm_blocking void run(); void start(); - int stop(bool block); - int handle(); - int handle_nonblock(); - - + void stop(); void pause(); void resume(); + int handle(unsigned timeout); + int flush(); + int setQueueSize(unsigned numMsgs); - int publish(const string& channel, const uint8_t* data, uint32_t len); + int publish(const char* channel, const uint8_t* data, uint32_t len); zcm_sub_t* subscribe(const string& channel, zcm_msg_handler_t cb, void* usr, bool block); int unsubscribe(zcm_sub_t* sub, bool block); - int flush(bool block); - int setQueueSize(uint32_t numMsgs, bool block); - int queryDrops(uint64_t *out_drops); + int queryDrops(uint64_t *outDrops); int writeTopology(string name); private: - void sendThreadFunc(); void recvThreadFunc(); - void hndlThreadFunc(); - bool startRecvThread(); - void dispatchMsg(zcm_msg_t* msg); - bool dispatchOneMessage(bool returnIfPaused); - bool sendOneMessage(bool returnIfPaused); - - // Mutexes protecting the ...OneMessage() functions - mutex dispOneMutex; - mutex sendOneMutex; + void dispatchMsg(const zcm_msg_t& msg); + int recvOneMessage(unsigned timeout); bool deleteSubEntry(zcm_sub_t* sub, size_t nentriesleft); bool deleteFromSubList(SubList& slist, zcm_sub_t* sub); @@ -147,59 +97,20 @@ struct zcm_blocking unordered_map subsRegex; size_t mtu; - mutex receivedTopologyMutex; zcm::TopologyMap receivedTopologyMap; - mutex sentTopologyMutex; zcm::TopologyMap sentTopologyMap; // These 2 mutexes used to implement a read-write style infrastructure on the subscription // lists. Both the recvThread and the message dispatch may read the subscriptions // concurrently, but subscribe() and unsubscribe() need both locks in order to write to it. - mutex subDispMutex; - mutex subRecvMutex; - - static constexpr size_t QUEUE_SIZE = 16; - ThreadsafeQueue sendQueue {QUEUE_SIZE}; - ThreadsafeQueue recvQueue {QUEUE_SIZE}; - - typedef enum { - RECV_MODE_NONE = 0, - RECV_MODE_RUN, - RECV_MODE_SPAWN, - RECV_MODE_HANDLE - } RecvMode_t; - RecvMode_t recvMode {RECV_MODE_NONE}; - - // This mutex protects read and write access to the recv mode flag - mutex recvModeMutex; + mutex subMutex; - thread sendThread; thread recvThread; - thread hndlThread; - - typedef enum { - THREAD_STATE_STOPPED = 0, - THREAD_STATE_RUNNING, - THREAD_STATE_HALTING, - THREAD_STATE_HALTED, - } ThreadState_t; - - ThreadState_t sendThreadState {THREAD_STATE_STOPPED}; - ThreadState_t recvThreadState {THREAD_STATE_STOPPED}; - ThreadState_t hndlThreadState {THREAD_STATE_STOPPED}; - - // These mutexes protect read and write access to the ThreadState flags - // (if you also want to hold the dispOneMutex or sendOneMutex, those mutexes must be locked - // before these ones) - mutex sendStateMutex; - mutex recvStateMutex; - mutex hndlStateMutex; - - // Flag and condition variables used to pause the sendThread (use sendStateMutex) - // and hndlThread (use hndlStateMutex) - bool paused {false}; - condition_variable sendPauseCond; - condition_variable hndlPauseCond; + + enum class RunState {NONE, RUNNING, PAUSE, PAUSED, STOP}; + atomic runState {RunState::NONE}; + condition_variable pauseCond; + mutex pauseCondLk; }; zcm_blocking_t::zcm_blocking(zcm_t* z_, zcm_trans_t* zt_) @@ -213,7 +124,7 @@ zcm_blocking_t::zcm_blocking(zcm_t* z_, zcm_trans_t* zt_) zcm_blocking_t::~zcm_blocking() { // Shutdown all threads - stop(true); + if (runState != RunState::NONE) stop(); // Destroy the transport zcm_trans_destroy(zt); @@ -234,226 +145,87 @@ zcm_blocking_t::~zcm_blocking() void zcm_blocking_t::run() { - unique_lock lk1(recvModeMutex); - if (recvMode != RECV_MODE_NONE) { - ZCM_DEBUG("Err: call to run() when 'recvMode != RECV_MODE_NONE'"); + if (runState != RunState::NONE) { + ZCM_DEBUG("Err: call to run() when 'runState != RunState::NONE'"); return; } - recvMode = RECV_MODE_RUN; + runState = RunState::RUNNING; - // Run it! - { - unique_lock lk2(hndlStateMutex); - lk1.unlock(); - hndlThreadState = THREAD_STATE_RUNNING; - recvQueue.enable(); - } - hndlThreadFunc(); - - // Restore the "non-running" state - lk1.lock(); - recvMode = RECV_MODE_NONE; + recvThreadFunc(); } -// TODO: should this call be thread safe? void zcm_blocking_t::start() { - unique_lock lk1(recvModeMutex); - if (recvMode != RECV_MODE_NONE) { - ZCM_DEBUG("Err: call to start() when 'recvMode != RECV_MODE_NONE'"); + if (runState != RunState::NONE) { + ZCM_DEBUG("Err: call to start() when 'runState != RunState::NONE'"); return; } - recvMode = RECV_MODE_SPAWN; - - unique_lock lk2(hndlStateMutex); - lk1.unlock(); - // Start the hndl thread - hndlThreadState = THREAD_STATE_RUNNING; - recvQueue.enable(); - hndlThread = thread{&zcm_blocking::hndlThreadFunc, this}; -} - -int zcm_blocking_t::stop(bool block) -{ - unique_lock lk1(recvModeMutex); - - // Shutdown recv and hndl threads - if (recvMode == RECV_MODE_RUN || recvMode == RECV_MODE_SPAWN) { - unique_lock lk2(hndlStateMutex); - if (hndlThreadState == THREAD_STATE_RUNNING) { - hndlThreadState = THREAD_STATE_HALTING; - recvQueue.disable(); - lk2.unlock(); - hndlPauseCond.notify_all(); - if (block && recvMode == RECV_MODE_SPAWN) { - hndlThread.join(); - lk2.lock(); - hndlThreadState = THREAD_STATE_STOPPED; - } - } - - // Shutdown recv thread - } else if (recvMode == RECV_MODE_HANDLE) { - unique_lock lk2(recvStateMutex); - if (recvThreadState == THREAD_STATE_RUNNING) { - recvThreadState = THREAD_STATE_HALTING; - recvQueue.disable(); - lk2.unlock(); - if (block) { - recvThread.join(); - lk2.lock(); - recvThreadState = THREAD_STATE_STOPPED; - } - } - } - - // Shutdown send thread - { - unique_lock lk2(sendStateMutex); - if (sendThreadState == THREAD_STATE_RUNNING) { - sendThreadState = THREAD_STATE_HALTING; - sendQueue.disable(); - lk2.unlock(); - sendPauseCond.notify_all(); - if (block) { - sendThread.join(); - lk2.lock(); - sendThreadState = THREAD_STATE_STOPPED; - } - } - } + runState = RunState::RUNNING; - if (!block) { - switch (recvMode) { - case RECV_MODE_SPAWN: - { - unique_lock lk2(hndlStateMutex); - if (hndlThreadState == THREAD_STATE_HALTING) return ZCM_EAGAIN; - if (hndlThreadState == THREAD_STATE_HALTED) { - hndlThread.join(); - hndlThreadState = THREAD_STATE_STOPPED; - } - } - break; - case RECV_MODE_HANDLE: - { - unique_lock lk2(recvStateMutex); - if (recvThreadState == THREAD_STATE_HALTING) return ZCM_EAGAIN; - if (recvThreadState == THREAD_STATE_HALTED) { - recvThread.join(); - recvThreadState = THREAD_STATE_STOPPED; - } - } - break; - default: break; - } - - unique_lock lk2(sendStateMutex); - if (sendThreadState == THREAD_STATE_HALTING) return ZCM_EAGAIN; - if (sendThreadState == THREAD_STATE_HALTED) { - sendThread.join(); - sendThreadState = THREAD_STATE_STOPPED; - } - } - - // Restore the "non-running" state - recvMode = RECV_MODE_NONE; - return ZCM_EOK; + recvThread = thread{&zcm_blocking::recvThreadFunc, this}; } -bool zcm_blocking_t::startRecvThread() +void zcm_blocking_t::stop() { - unique_lock lk1(recvModeMutex); - if (recvMode != RECV_MODE_NONE && recvMode != RECV_MODE_HANDLE) { - ZCM_DEBUG("Err: call to handle() or handle_nonblock() " - "when 'recvMode != RECV_MODE_NONE && recvMode != RECV_MODE_HANDLE'"); - return false; - } - - // If this is the first time handle() is called, we need to start the recv thread - if (recvMode == RECV_MODE_NONE) { - recvMode = RECV_MODE_HANDLE; - - unique_lock lk2(recvStateMutex); - lk1.unlock(); - // Spawn the recv thread - recvThreadState = THREAD_STATE_RUNNING; - recvQueue.enable(); - recvThread = thread{&zcm_blocking::recvThreadFunc, this}; - } - - return true; + if (runState != RunState::RUNNING && runState != RunState::PAUSED) return; + runState = RunState::STOP; + pauseCond.notify_all(); + recvThread.join(); } -int zcm_blocking_t::handle() +void zcm_blocking_t::pause() { - if (!startRecvThread()) return ZCM_EINVALID; - - unique_lock lk(dispOneMutex); - return dispatchOneMessage(true) ? ZCM_EOK : ZCM_EAGAIN; + if (runState != RunState::RUNNING) return; + runState = RunState::PAUSE; + pauseCond.notify_all(); + unique_lock lk(pauseCondLk); + pauseCond.wait(lk, [this](){ return runState == RunState::PAUSED; }); } -int zcm_blocking_t::handle_nonblock() +void zcm_blocking_t::resume() { - if (!startRecvThread()) return ZCM_EINVALID; - - unique_lock lk(dispOneMutex); - if (!recvQueue.hasMessage()) return ZCM_EAGAIN; - return dispatchOneMessage(true) ? ZCM_EOK : ZCM_EAGAIN; + if (runState != RunState::PAUSED) return; + runState = RunState::RUNNING; + pauseCond.notify_all(); } -void zcm_blocking_t::pause() +int zcm_blocking_t::handle(unsigned timeoutMs) { - unique_lock lk1(sendStateMutex); - unique_lock lk2(hndlStateMutex); - paused = true; + if (runState != RunState::NONE && runState != RunState::PAUSED) return ZCM_EINVALID; + return recvOneMessage(timeoutMs); } -void zcm_blocking_t::resume() +int zcm_blocking_t::setQueueSize(unsigned numMsgs) { - unique_lock lk1(sendStateMutex); - unique_lock lk2(hndlStateMutex); - paused = false; - // Intentionally unlocking in this order - lk2.unlock(); - lk1.unlock(); - sendPauseCond.notify_all(); - hndlPauseCond.notify_all(); + if (runState != RunState::NONE && runState != RunState::PAUSED) return ZCM_EINVALID; + return zcm_trans_set_queue_size(zt, numMsgs); } -int zcm_blocking_t::publish(const string& channel, const uint8_t* data, uint32_t len) +int zcm_blocking_t::publish(const char* channel, const uint8_t* data, uint32_t len) { // Check the validity of the request if (len > mtu) return ZCM_EINVALID; - if (channel.size() > ZCM_CHANNEL_MAXLEN) return ZCM_EINVALID; - - // If needed: spawn the send thread - { - unique_lock lk(sendStateMutex); - if (sendThreadState == THREAD_STATE_STOPPED) { - sendThreadState = THREAD_STATE_RUNNING; - sendThread = thread{&zcm_blocking::sendThreadFunc, this}; - } - } - - bool success = sendQueue.pushIfRoom(TimeUtil::utime(), channel.c_str(), len, data); - if (!success) { - ZCM_DEBUG("sendQueue has no free space"); - return ZCM_EAGAIN; - } + if (strlen(channel) > ZCM_CHANNEL_MAXLEN) return ZCM_EINVALID; + + zcm_msg_t msg = { + .utime = TimeUtil::utime(), + .channel = channel, + .len = len, + // cast await const ok as sendmsg guaranteed to not modify data + .buf = (uint8_t*)data, + }; + int ret = zcm_trans_sendmsg(zt, msg); + if (ret != ZCM_EOK) ZCM_DEBUG("zcm_trans_sendmsg() returned error, dropping the msg!"); #ifdef TRACK_TRAFFIC_TOPOLOGY int64_t hashBE = 0, hashLE = 0; if (__int64_t_decode_array(data, 0, len, &hashBE, 1) == 8 && __int64_t_decode_little_endian_array(data, 0, len, &hashLE, 1) == 8) { - unique_lock lk(sentTopologyMutex, defer_lock); - if (lk.try_lock()) { - sentTopologyMap[channel].emplace(hashBE, hashLE); - } + sentTopologyMap[channel].emplace(hashBE, hashLE); } #endif - return ZCM_EOK; + return ret; } // Note: We use a lock on subscribe() to make sure it can be @@ -463,13 +235,11 @@ zcm_sub_t* zcm_blocking_t::subscribe(const string& channel, zcm_msg_handler_t cb, void* usr, bool block) { - unique_lock lk1(subDispMutex, std::defer_lock); - unique_lock lk2(subRecvMutex, std::defer_lock); + unique_lock lk(subMutex, std::defer_lock); if (block) { // Intentionally locking in this order - lk1.lock(); - lk2.lock(); - } else if (!lk1.try_lock() || !lk2.try_lock()) { + lk.lock(); + } else if (!lk.try_lock()) { return nullptr; } int rc; @@ -505,13 +275,10 @@ zcm_sub_t* zcm_blocking_t::subscribe(const string& channel, // on modifying and reading the 'subs' and 'subsRegex' containers int zcm_blocking_t::unsubscribe(zcm_sub_t* sub, bool block) { - unique_lock lk1(subDispMutex, std::defer_lock); - unique_lock lk2(subRecvMutex, std::defer_lock); + unique_lock lk(subMutex, std::defer_lock); if (block) { - // Intentionally locking in this order - lk1.lock(); - lk2.lock(); - } else if (!lk1.try_lock() || !lk2.try_lock()) { + lk.lock(); + } else if (!lk.try_lock()) { return ZCM_EAGAIN; } @@ -532,217 +299,67 @@ int zcm_blocking_t::unsubscribe(zcm_sub_t* sub, bool block) return ZCM_EOK; } -int zcm_blocking_t::flush(bool block) +int zcm_blocking_t::flush() { - size_t n; - - { - sendQueue.disable(); - - unique_lock lk(sendOneMutex, defer_lock); - - if (block) lk.lock(); - else if (!lk.try_lock()) { - sendQueue.enable(); - sendPauseCond.notify_all(); - return ZCM_EAGAIN; - } - - sendQueue.enable(); - n = sendQueue.numMessages(); - for (size_t i = 0; i < n; ++i) sendOneMessage(false); - } - sendPauseCond.notify_all(); - - { - recvQueue.disable(); - - unique_lock lk(dispOneMutex, defer_lock); - - if (block) lk.lock(); - else if (!lk.try_lock()) { - recvQueue.enable(); - hndlPauseCond.notify_all(); - return ZCM_EAGAIN; - } - - recvQueue.enable(); - n = recvQueue.numMessages(); - for (size_t i = 0; i < n; ++i) dispatchOneMessage(false); - } - hndlPauseCond.notify_all(); - - return ZCM_EOK; -} - -int zcm_blocking_t::setQueueSize(uint32_t numMsgs, bool block) -{ - if (sendQueue.getCapacity() != numMsgs) { - - sendQueue.disable(); - - unique_lock lk(sendOneMutex, defer_lock); - - if (block) lk.lock(); - else if (!lk.try_lock()) { - sendQueue.enable(); - lk.unlock(); - unique_lock lk2(sendStateMutex); - sendPauseCond.notify_all(); - return ZCM_EAGAIN; - } - - sendQueue.setCapacity(numMsgs); - sendQueue.enable(); - lk.unlock(); - unique_lock lk2(sendStateMutex); - sendPauseCond.notify_all(); - } - - if (recvQueue.getCapacity() != numMsgs) { - - recvQueue.disable(); - - unique_lock lk(dispOneMutex, defer_lock); - - if (block) lk.lock(); - else if (!lk.try_lock()) { - recvQueue.enable(); - lk.unlock(); - unique_lock lk2(hndlStateMutex); - hndlPauseCond.notify_all(); - return ZCM_EAGAIN; - } - - recvQueue.setCapacity(numMsgs); - recvQueue.enable(); - lk.unlock(); - unique_lock lk2(hndlStateMutex); - hndlPauseCond.notify_all(); + if (runState != RunState::NONE && runState != RunState::PAUSED) { + ZCM_DEBUG("Err: must be paused or stopped in order to flush"); + return ZCM_EINVALID; } - return ZCM_EOK; -} + int ret; + do { + ret = recvOneMessage(0); + if (ret == ZCM_EAGAIN) ret = ZCM_EOK; + else if (ret == ZCM_EOK) ret = ZCM_EAGAIN; + } while (ret == ZCM_EAGAIN); -int zcm_blocking_t::queryDrops(uint64_t *out_drops) -{ - return zcm_trans_query_drops(zt, out_drops); + return ret; } -void zcm_blocking_t::sendThreadFunc() +int zcm_blocking_t::queryDrops(uint64_t *outDrops) { - // Name the send thread - SET_THREAD_NAME("ZeroCM_sender"); - - while (true) { - { - unique_lock lk(sendStateMutex); - sendPauseCond.wait(lk, [&]{ - return (!paused && sendQueue.isEnabled()) || - sendThreadState == THREAD_STATE_HALTING; - }); - if (sendThreadState == THREAD_STATE_HALTING) break; - } - unique_lock lk(sendOneMutex); - sendOneMessage(true); - } - - unique_lock lk(sendStateMutex); - sendThreadState = THREAD_STATE_HALTED; + return zcm_trans_query_drops(zt, outDrops); } void zcm_blocking_t::recvThreadFunc() { - // Name the recv thread + // Intentionally not setting runState = RUNNING because it could conflict + // with a start -> pause call order SET_THREAD_NAME("ZeroCM_receiver"); while (true) { - { - unique_lock lk(recvStateMutex); - if (recvThreadState == THREAD_STATE_HALTING) break; - } - zcm_msg_t msg; - int rc = zcm_trans_recvmsg(zt, &msg, RECV_TIMEOUT); - if (rc == ZCM_EOK) { - { - unique_lock lk(subRecvMutex); - - // Check if message matches a non regex channel - auto it = subs.find(msg.channel); - if (it == subs.end()) { - // Check if message matches a regex channel - bool foundRegex = false; - for (auto& it : subsRegex) { - for (auto& sub : it.second) { - regex* r = (regex*)sub->regexobj; - if (regex_match(msg.channel, *r)) { - foundRegex = true; - break; - } - } - if (foundRegex) break; - } - // No subscription actually wants the message - if (!foundRegex) continue; - } - } - // Note: After this returns, you have either successfully pushed a message - // into the queue, or the queue was disabled and you will quit out of - // this loop when you re-check the running condition - recvQueue.push(&msg); + if (runState == RunState::PAUSE) { + runState = RunState::PAUSED; + pauseCond.notify_all(); + unique_lock lk(pauseCondLk); + pauseCond.wait(lk, [this](){ return runState != RunState::PAUSED; }); } - } - unique_lock lk(recvStateMutex); - recvThreadState = THREAD_STATE_HALTED; -} -void zcm_blocking_t::hndlThreadFunc() -{ - // Name the handle thread - SET_THREAD_NAME("ZeroCM_handler"); + if (runState == RunState::STOP) break; - { - // Spawn the recv thread - unique_lock lk(recvStateMutex); - recvThreadState = THREAD_STATE_RUNNING; - recvThread = thread{&zcm_blocking::recvThreadFunc, this}; + recvOneMessage(RECV_TIMEOUT); } - // Become the handle thread - while (true) { - { - unique_lock lk(hndlStateMutex); - hndlPauseCond.wait(lk, [&]{ - return (!paused && recvQueue.isEnabled()) || - hndlThreadState == THREAD_STATE_HALTING; - }); - if (hndlThreadState == THREAD_STATE_HALTING) break; - } - unique_lock lk(dispOneMutex); - dispatchOneMessage(true); - } - - { - // Shutdown recv thread - unique_lock lk(recvStateMutex); - recvThreadState = THREAD_STATE_HALTING; - recvQueue.disable(); - lk.unlock(); - recvThread.join(); - } + runState = RunState::NONE; +} - unique_lock lk(hndlStateMutex); - hndlThreadState = THREAD_STATE_HALTED; +int zcm_blocking_t::recvOneMessage(unsigned timeout) +{ + zcm_msg_t msg; + int rc = zcm_trans_recvmsg(zt, &msg, timeout); + if (rc != ZCM_EOK) return rc; + dispatchMsg(msg); + return ZCM_EOK; } -void zcm_blocking_t::dispatchMsg(zcm_msg_t* msg) +void zcm_blocking_t::dispatchMsg(const zcm_msg_t& msg) { zcm_recv_buf_t rbuf; - rbuf.recv_utime = msg->utime; + rbuf.recv_utime = msg.utime; rbuf.zcm = z; - rbuf.data = msg->buf; - rbuf.data_size = msg->len; + rbuf.data = msg.buf; + rbuf.data_size = msg.len; // Note: We use a lock on dispatch to ensure there is not // a race on modifying and reading the 'subs' container. @@ -750,13 +367,13 @@ void zcm_blocking_t::dispatchMsg(zcm_msg_t* msg) // zcm_unsubscribe from a callback without deadlocking. bool wasDispatched = false; { - unique_lock lk(subDispMutex); + unique_lock lk(subMutex); // dispatch to a non regex channel - auto it = subs.find(msg->channel); + auto it = subs.find(msg.channel); if (it != subs.end()) { for (zcm_sub_t* sub : it->second) { - sub->callback(&rbuf, msg->channel, sub->usr); + sub->callback(&rbuf, msg.channel, sub->usr); wasDispatched = true; } } @@ -765,8 +382,8 @@ void zcm_blocking_t::dispatchMsg(zcm_msg_t* msg) for (auto& it : subsRegex) { for (auto& sub : it.second) { regex* r = (regex*)sub->regexobj; - if (regex_match(msg->channel, *r)) { - sub->callback(&rbuf, msg->channel, sub->usr); + if (regex_match(msg.channel, *r)) { + sub->callback(&rbuf, msg.channel, sub->usr); wasDispatched = true; } } @@ -776,12 +393,9 @@ void zcm_blocking_t::dispatchMsg(zcm_msg_t* msg) #ifdef TRACK_TRAFFIC_TOPOLOGY if (wasDispatched) { int64_t hashBE = 0, hashLE = 0; - if (__int64_t_decode_array(msg->buf, 0, msg->len, &hashBE, 1) == 8 && - __int64_t_decode_little_endian_array(msg->buf, 0, msg->len, &hashLE, 1) == 8) { - unique_lock lk(receivedTopologyMutex, defer_lock); - if (lk.try_lock()) { - receivedTopologyMap[msg->channel].emplace(hashBE, hashLE); - } + if (__int64_t_decode_array(msg.buf, 0, msg.len, &hashBE, 1) == 8 && + __int64_t_decode_little_endian_array(msg.buf, 0, msg.len, &hashLE, 1) == 8) { + receivedTopologyMap[msg.channel].emplace(hashBE, hashLE); } } #else @@ -789,42 +403,6 @@ void zcm_blocking_t::dispatchMsg(zcm_msg_t* msg) #endif } -bool zcm_blocking_t::dispatchOneMessage(bool returnIfPaused) -{ - Msg* m = recvQueue.top(); - // If the Queue was forcibly woken-up, recheck the - // running condition, and then retry. - if (m == nullptr) return false; - - if (returnIfPaused) { - unique_lock lk(hndlStateMutex); - if (paused || hndlThreadState == THREAD_STATE_HALTING) return false; - } - - dispatchMsg(m->get()); - recvQueue.pop(); - return true; -} - -bool zcm_blocking_t::sendOneMessage(bool returnIfPaused) -{ - Msg* m = sendQueue.top(); - // If the Queue was forcibly woken-up, recheck the - // running condition, and then retry. - if (m == nullptr) return false; - - if (returnIfPaused) { - unique_lock lk(sendStateMutex); - if (paused || sendThreadState == THREAD_STATE_HALTING) return false; - } - - zcm_msg_t* msg = m->get(); - int ret = zcm_trans_sendmsg(zt, *msg); - if (ret != ZCM_EOK) ZCM_DEBUG("zcm_trans_sendmsg() returned error, dropping the msg!"); - sendQueue.pop(); - return true; -} - bool zcm_blocking_t::deleteSubEntry(zcm_sub_t* sub, size_t nentriesleft) { int rc = ZCM_EOK; @@ -855,17 +433,8 @@ bool zcm_blocking_t::deleteFromSubList(SubList& slist, zcm_sub_t* sub) int zcm_blocking_t::writeTopology(string name) { - decltype(receivedTopologyMap) _receivedTopologyMap; - { - unique_lock lk(receivedTopologyMutex); - _receivedTopologyMap = receivedTopologyMap; - } - decltype(sentTopologyMap) _sentTopologyMap; - { - unique_lock lk(sentTopologyMutex); - _sentTopologyMap = sentTopologyMap; - } - return zcm::writeTopology(name, _receivedTopologyMap, _sentTopologyMap); + if (runState != RunState::NONE && runState != RunState::PAUSED) return ZCM_EUNKNOWN; + return zcm::writeTopology(name, receivedTopologyMap, sentTopologyMap); } /////////////// C Interface Functions //////////////// @@ -902,9 +471,9 @@ int zcm_blocking_unsubscribe(zcm_blocking_t* zcm, zcm_sub_t* sub) return zcm->unsubscribe(sub, true); } -void zcm_blocking_flush(zcm_blocking_t* zcm) +int zcm_blocking_flush(zcm_blocking_t* zcm) { - zcm->flush(true); + return zcm->flush(); } void zcm_blocking_run(zcm_blocking_t* zcm) @@ -929,22 +498,17 @@ void zcm_blocking_resume(zcm_blocking_t* zcm) void zcm_blocking_stop(zcm_blocking_t* zcm) { - zcm->stop(true); -} - -int zcm_blocking_handle(zcm_blocking_t* zcm) -{ - return zcm->handle(); + return zcm->stop(); } -int zcm_blocking_handle_nonblock(zcm_blocking_t* zcm) +int zcm_blocking_handle(zcm_blocking_t* zcm, unsigned timeout) { - return zcm->handle_nonblock(); + return zcm->handle(timeout); } -void zcm_blocking_set_queue_size(zcm_blocking_t* zcm, uint32_t sz) +int zcm_blocking_set_queue_size(zcm_blocking_t* zcm, unsigned num_messages) { - zcm->setQueueSize(sz, true); + return zcm->setQueueSize(num_messages); } int zcm_blocking_query_drops(zcm_blocking_t *zcm, uint64_t *out_drops) @@ -976,21 +540,6 @@ int zcm_blocking_try_unsubscribe(zcm_blocking_t* zcm, zcm_sub_t* sub) { return zcm->unsubscribe(sub, false); } - -int zcm_blocking_try_flush(zcm_blocking_t* zcm) -{ - return zcm->flush(false); -} - -int zcm_blocking_try_stop(zcm_blocking_t* zcm) -{ - return zcm->stop(false); -} - -int zcm_blocking_try_set_queue_size(zcm_blocking_t* zcm, uint32_t sz) -{ - return zcm->setQueueSize(sz, false); -} /****************************************************************************/ } diff --git a/zcm/blocking.h b/zcm/blocking.h index b5f75a886..5067eb100 100644 --- a/zcm/blocking.h +++ b/zcm/blocking.h @@ -21,16 +21,15 @@ zcm_sub_t* zcm_blocking_subscribe(zcm_blocking_t* zcm, const char* channel, int zcm_blocking_unsubscribe(zcm_blocking_t* zcm, zcm_sub_t* sub); -void zcm_blocking_flush(zcm_blocking_t* zcm); +int zcm_blocking_flush(zcm_blocking_t* zcm); void zcm_blocking_run(zcm_blocking_t* zcm); void zcm_blocking_start(zcm_blocking_t* zcm); void zcm_blocking_stop(zcm_blocking_t* zcm); void zcm_blocking_pause(zcm_blocking_t* zcm); void zcm_blocking_resume(zcm_blocking_t* zcm); -int zcm_blocking_handle(zcm_blocking_t* zcm); -int zcm_blocking_handle_nonblock(zcm_blocking_t* zcm); -void zcm_blocking_set_queue_size(zcm_blocking_t* zcm, uint32_t numMsgs); +int zcm_blocking_handle(zcm_blocking_t* zcm, unsigned timeout); +int zcm_blocking_set_queue_size(zcm_blocking_t* zcm, unsigned num_messages); int zcm_blocking_query_drops(zcm_blocking_t *zcm, uint64_t *out_drops); int zcm_blocking_write_topology(zcm_blocking_t* zcm, const char* name); @@ -45,9 +44,6 @@ int zcm_blocking_write_topology(zcm_blocking_t* zcm, const char* name); zcm_sub_t* zcm_blocking_try_subscribe(zcm_blocking_t* zcm, const char* channel, zcm_msg_handler_t cb, void* usr); int zcm_blocking_try_unsubscribe(zcm_blocking_t* zcm, zcm_sub_t* sub); -int zcm_blocking_try_flush(zcm_blocking_t* zcm); -int zcm_blocking_try_stop(zcm_blocking_t* zcm); -int zcm_blocking_try_set_queue_size(zcm_blocking_t* zcm, uint32_t numMsgs); /****************************************************************************/ #ifdef __cplusplus diff --git a/zcm/java/jni/zcm_zcm_ZCMJNI.c b/zcm/java/jni/zcm_zcm_ZCMJNI.c index 7cc01a01d..006624c6b 100644 --- a/zcm/java/jni/zcm_zcm_ZCMJNI.c +++ b/zcm/java/jni/zcm_zcm_ZCMJNI.c @@ -202,8 +202,21 @@ JNIEXPORT jint JNICALL Java_zcm_zcm_ZCMJNI_unsubscribe return ret; } -PASS_THROUGH_FUNC(flush, flush, void, ()V) +/* + * Class: zcm_zcm_ZCMJNI + * Method: handle + * Signature: (Lzcm/zcm/ZCM;Lzcm/zcm/ZCM/Subscription;)I + */ +JNIEXPORT jint JNICALL Java_zcm_zcm_ZCMJNI_handle +(JNIEnv *env, jobject self, jint timeoutJ) +{ + Internal *I = getNativePtr(env, self); + assert(I); + + if (timeoutJ < 0) return ZCM_EINVALID; + return zcm_handle(I->zcm, timeoutJ); +} + +PASS_THROUGH_FUNC(flush, flush, jint, ()I) PASS_THROUGH_FUNC(pause, pause, void, ()V) PASS_THROUGH_FUNC(resume, resume, void, ()V) -PASS_THROUGH_FUNC(handle, handle, jint, ()I) -PASS_THROUGH_FUNC(handleNonblock, handle_nonblock, jint, ()I) diff --git a/zcm/java/jni/zcm_zcm_ZCMJNI.h b/zcm/java/jni/zcm_zcm_ZCMJNI.h index fee90943e..9723e1abd 100644 --- a/zcm/java/jni/zcm_zcm_ZCMJNI.h +++ b/zcm/java/jni/zcm_zcm_ZCMJNI.h @@ -66,9 +66,9 @@ JNIEXPORT jint JNICALL Java_zcm_zcm_ZCMJNI_unsubscribe /* * Class: zcm_zcm_ZCMJNI * Method: flush - * Signature: ()V + * Signature: ()I */ -JNIEXPORT void JNICALL Java_zcm_zcm_ZCMJNI_flush +JNIEXPORT jint JNICALL Java_zcm_zcm_ZCMJNI_flush (JNIEnv *, jobject); /* @@ -90,18 +90,10 @@ JNIEXPORT void JNICALL Java_zcm_zcm_ZCMJNI_resume /* * Class: zcm_zcm_ZCMJNI * Method: handle - * Signature: ()I + * Signature: (I)I */ JNIEXPORT jint JNICALL Java_zcm_zcm_ZCMJNI_handle - (JNIEnv *, jobject); - -/* - * Class: zcm_zcm_ZCMJNI - * Method: handleNonblock - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_zcm_zcm_ZCMJNI_handleNonblock - (JNIEnv *, jobject); + (JNIEnv *, jobject, jint); #ifdef __cplusplus } diff --git a/zcm/js/node/package.json b/zcm/js/node/package.json index 26ba46d6c..8821485ac 100644 --- a/zcm/js/node/package.json +++ b/zcm/js/node/package.json @@ -1,6 +1,6 @@ { "name": "zerocm", - "version": "1.2.0", + "version": "2.0.0", "description": "Native Node.js bindings for Zero Communications and Marshalling (ZCM) library", "main": "index.js", "author": "The ZCM Team", diff --git a/zcm/julia/ZCM.jl b/zcm/julia/ZCM.jl index f9f790427..1c2a9d03a 100644 --- a/zcm/julia/ZCM.jl +++ b/zcm/julia/ZCM.jl @@ -19,7 +19,6 @@ export Zcm, start, stop, handle, - handle_nonblock, set_queue_size, write_topology, read_bits, @@ -261,14 +260,7 @@ function resume(zcm::Zcm) end function flush(zcm::Zcm) - while (true) - ret = ccall(("zcm_try_flush", "libzcm"), Cint, (Ptr{Native.Zcm},), zcm) - if (ret == Cint(0)) - break - else - yield() - end - end + ccall(("zcm_flush", "libzcm"), Cint, (Ptr{Native.Zcm},), zcm) end function start(zcm::Zcm) @@ -277,22 +269,11 @@ function start(zcm::Zcm) end function stop(zcm::Zcm) - while (true) - ret = ccall(("zcm_try_stop", "libzcm"), Cint, (Ptr{Native.Zcm},), zcm) - if (ret == Cint(0)) - break - else - yield() - end - end -end - -function handle(zcm::Zcm) - ccall(("zcm_handle", "libzcm"), Cint, (Ptr{Native.Zcm},), zcm) + ccall(("zcm_stop", "libzcm"), Nothing, (Ptr{Native.Zcm},), zcm) end -function handle_nonblock(zcm::Zcm) - ccall(("zcm_handle_nonblock", "libzcm"), Cint, (Ptr{Native.Zcm},), zcm) +function handle(zcm::Zcm, timeout:Unsigned) + ccall(("zcm_handle", "libzcm"), Cint, (Ptr{Native.Zcm},Cuint,), zcm, timeout) end function set_queue_size(zcm::Zcm, num::Integer) diff --git a/zcm/nonblocking.c b/zcm/nonblocking.c index e6c5ef427..5bb9478ab 100644 --- a/zcm/nonblocking.c +++ b/zcm/nonblocking.c @@ -19,6 +19,7 @@ struct zcm_nonblocking bool subInUse[ZCM_NONBLOCK_SUBS_MAX]; bool subIsRegex[ZCM_NONBLOCK_SUBS_MAX]; size_t subInUseEnd; + size_t mtu; }; static bool isRegexChannel(const char* c, size_t clen) @@ -67,6 +68,9 @@ int zcm_nonblocking_try_create(zcm_nonblocking_t** zcm, zcm_t* z, zcm_trans_t* z (*zcm)->subInUse[i] = false; (*zcm)->subInUseEnd = 0; + + (*zcm)->mtu = zcm_trans_get_mtu(zt); + return ZCM_EOK; } @@ -82,12 +86,16 @@ void zcm_nonblocking_destroy(zcm_nonblocking_t* zcm) int zcm_nonblocking_publish(zcm_nonblocking_t* z, const char* channel, const uint8_t* data, uint32_t len) { - zcm_msg_t msg; - - msg.channel = channel; - msg.len = len; - /* Casting away constness okay because msg isn't used past end of function */ - msg.buf = (uint8_t*) data; + /* Check the validity of the request */ + if (len > z->mtu) return ZCM_EINVALID; + + zcm_msg_t msg = { + .utime = 0, + .channel = channel, + .len = len, + /* Casting away constness okay because msg isn't used past end of function */ + .buf = (uint8_t*) data, + }; return zcm_trans_sendmsg(z->zt, msg); } @@ -198,12 +206,12 @@ static void dispatch_message(zcm_nonblocking_t* zcm, zcm_msg_t* msg) } } -int zcm_nonblocking_handle_nonblock(zcm_nonblocking_t* zcm) +int zcm_nonblocking_handle(zcm_nonblocking_t* zcm) { int ret; zcm_msg_t msg; - /* Perform any required traansport-level updates */ + /* Perform any required transport-level updates */ zcm_trans_update(zcm->zt); /* Try to receive a messages from the transport and dispatch them */ @@ -214,15 +222,33 @@ int zcm_nonblocking_handle_nonblock(zcm_nonblocking_t* zcm) return ZCM_EOK; } -void zcm_nonblocking_flush(zcm_nonblocking_t* zcm) +int zcm_nonblocking_flush(zcm_nonblocking_t* zcm) { - /* Call twice because we need to make sure publish and subscribe are both handled */ - zcm_trans_update(zcm->zt); - zcm_trans_update(zcm->zt); + bool dispatchedMessage; + bool additionalUpdateRequired; + do { + int ret; - zcm_msg_t msg; - while (zcm_trans_recvmsg(zcm->zt, &msg, 0) == ZCM_EOK) - dispatch_message(zcm, &msg); + ret = zcm_trans_update(zcm->zt); + if (ret != ZCM_EOK && ret != ZCM_EAGAIN) return ret; + additionalUpdateRequired = ret == ZCM_EAGAIN; + + zcm_msg_t msg; + do { + ret = zcm_trans_recvmsg(zcm->zt, &msg, 0); + if (ret != ZCM_EOK && ret != ZCM_EAGAIN) return ret; + dispatchedMessage = ret == ZCM_EOK; + if (dispatchedMessage) dispatch_message(zcm, &msg); + } while (dispatchedMessage); + + } while (dispatchedMessage || additionalUpdateRequired); + + return ZCM_EOK; +} + +int zcm_nonblocking_set_queue_size(zcm_nonblocking_t* zcm, unsigned num_messages) +{ + return zcm_trans_set_queue_size(zcm->zt, num_messages); } #ifndef ZCM_EMBEDDED diff --git a/zcm/nonblocking.h b/zcm/nonblocking.h index b1913bb8e..e593763d8 100644 --- a/zcm/nonblocking.h +++ b/zcm/nonblocking.h @@ -22,12 +22,14 @@ zcm_sub_t* zcm_nonblocking_subscribe(zcm_nonblocking_t* zcm, const char* channel int zcm_nonblocking_unsubscribe(zcm_nonblocking_t* zcm, zcm_sub_t* sub); -int zcm_nonblocking_query_drops(zcm_nonblocking_t *zcm, uint64_t *out_drops); - /* Returns 1 if a message was dispatched, and 0 otherwise */ -int zcm_nonblocking_handle_nonblock(zcm_nonblocking_t* zcm); +int zcm_nonblocking_handle(zcm_nonblocking_t* zcm); + +int zcm_nonblocking_flush(zcm_nonblocking_t* zcm); + +int zcm_nonblocking_query_drops(zcm_nonblocking_t *zcm, uint64_t *out_drops); -void zcm_nonblocking_flush(zcm_nonblocking_t* zcm); +int zcm_nonblocking_set_queue_size(zcm_nonblocking_t* zcm, unsigned num_messages); #ifndef ZCM_EMBEDDED int zcm_nonblocking_write_topology(zcm_nonblocking_t* zcm, const char* name); diff --git a/zcm/python/zerocm.pyx b/zcm/python/zerocm.pyx index 3292cdae2..4c252f996 100644 --- a/zcm/python/zerocm.pyx +++ b/zcm/python/zerocm.pyx @@ -17,7 +17,7 @@ cdef extern from "zcm/zcm.h": ZCM_EINTR, ZCM_EUNKNOWN, ZCM_EMEMORY, - ZCM_EUNIMPL, + ZCM_EUNSUPPORTED, ZCM_NUM_RETURN_CODES ctypedef struct zcm_t: pass @@ -40,18 +40,15 @@ cdef extern from "zcm/zcm.h": int zcm_publish(zcm_t* zcm, const char* channel, const uint8_t* data, uint32_t dlen) - int zcm_try_flush (zcm_t* zcm) - - void zcm_run (zcm_t* zcm) - void zcm_start (zcm_t* zcm) - int zcm_try_stop (zcm_t* zcm) + void zcm_run (zcm_t* zcm) + void zcm_start (zcm_t* zcm) + void zcm_stop (zcm_t* zcm) void zcm_pause (zcm_t* zcm) void zcm_resume (zcm_t* zcm) - int zcm_handle (zcm_t* zcm) - int zcm_try_set_queue_size(zcm_t* zcm, uint32_t numMsgs) - int zcm_write_topology (zcm_t* zcm, const char* name) - - int zcm_handle_nonblock(zcm_t* zcm) + int zcm_handle (zcm_t* zcm, unsigned timeout) + int zcm_flush (zcm_t* zcm) + int zcm_set_queue_size(zcm_t* zcm, unsigned num_messages) + int zcm_write_topology(zcm_t* zcm, const char* name) ctypedef struct zcm_eventlog_t: pass @@ -152,29 +149,26 @@ cdef class ZCM: def publish_raw(self, str channel, bytes data): cdef const uint8_t* _data = data return zcm_publish(self.zcm, channel.encode('utf-8'), _data, len(data) * sizeof(uint8_t)) - def flush(self): - while zcm_try_flush(self.zcm) != ZCM_EOK: - time.sleep(0) # yield the gil def run(self): zcm_run(self.zcm) def start(self): zcm_start(self.zcm) def stop(self): - while zcm_try_stop(self.zcm) != ZCM_EOK: - time.sleep(0) # yield the gil + zcm_stop(self.zcm) def pause(self): zcm_pause(self.zcm) def resume(self): zcm_resume(self.zcm) - def handle(self): - return zcm_handle(self.zcm) + def handle(self, timeout): + if (timeout < 0): + return ZCM_EINVALID + return zcm_handle(self.zcm, timeout) + def flush(self): + return zcm_flush(self.zcm) def setQueueSize(self, numMsgs): - while zcm_try_set_queue_size(self.zcm, numMsgs) != ZCM_EOK: - time.sleep(0) # yield the gil + return zcm_set_queue_size(self.zcm, numMsgs) def writeTopology(self, str name): return zcm_write_topology(self.zcm, name.encode('utf-8')) - def handleNonblock(self): - return zcm_handle_nonblock(self.zcm) cdef class LogEvent: cdef int64_t eventnum diff --git a/zcm/transport.h b/zcm/transport.h index 443518906..d4b35824c 100644 --- a/zcm/transport.h +++ b/zcm/transport.h @@ -63,6 +63,8 @@ * this method can return ZCM_EINVALID. On receipt of valid params, * this method should block until the message has been successfully * sent and should return ZCM_EOK. + * NOTE: This method should work concurrently and correctly with + * recvmsg() and recvmsg_enable() * * int recvmsg_enable(zcm_trans_t* zt, const char* channel, bool enable) * -------------------------------------------------------------------- @@ -91,10 +93,21 @@ * int query_drops(zcm_trans_t* zt, uint64_t *out_drops); * -------------------------------------------------------------------- * This method provides the caller access to an internal transport drop counter - * Implementing this is not required. If unimplemented, it should return ZCM_EUNIMPL. + * Implementing this is not required. If unimplemented, it should + * return ZCM_EUNSUPPORTED. * If implemented, the out-parameter *out_drops should be populated and the * call should return ZCM_EOK. * + * int set_queue_size(zcm_trans_t* zt, unsigned num_messages) + * -------------------------------------------------------------------- + * This method instructs the transport that the user would like the + * transport to buffer at least this many messages in between + * successive calls to recvmsg(). This should be treated as a hint and + * is not guaranteed by the transport. If the transport is able to set + * its buffers to that size, it should return EOK, otherwise if it + * wasn't able to set its buffers to that size, but may be able to in + * the future, return EAGAIN, otherwise return ZCM_EINVALID. + * * int update(zcm_trans_t* zt); * -------------------------------------------------------------------- * This method is unused (in this mode) and should not be called by the user. @@ -136,6 +149,8 @@ * this method should *never block*. If the transport cannot accept the * message due to unavailability, ZCM_EAGAIN should be returned. * On success ZCM_EOK should be returned. + * NOTE: This method should work concurrently and correctly with + * recvmsg() and recvmsg_enable() * * int recvmsg_enable(zcm_trans_t* zt, const char* channel, bool enable) * -------------------------------------------------------------------- @@ -161,20 +176,34 @@ * int query_drops(zcm_trans_t* zt, uint64_t *out_drops); * -------------------------------------------------------------------- * This method provides the caller access to an internal transport drop counter - * Implementing this is not required. If unimplemented, it should return ZCM_EUNIMPL. + * Implementing this is not required. If unimplemented, it should + * return ZCM_EUNSUPPORTED. * If implemented, the out-parameter *out_drops should be populated and the * call should return ZCM_EOK. * + * int set_queue_size(zcm_trans_t* zt, unsigned num_messages) + * -------------------------------------------------------------------- + * This method instructs the transport that the user would like the + * transport to buffer at least this many messages in between + * successive calls to recvmsg(). This should be treated as a hint and + * is not guaranteed by the transport. If the transport is able to set + * its buffers to that size, it should return EOK, otherwise if it + * wasn't able to set its buffers to that size, but may be able to in + * the future, return EAGAIN, otherwise it should return an error. + * * int update(zcm_trans_t* zt) * -------------------------------------------------------------------- - * This method is called from the zcm_handle_nonblock() function. - * This method provides a periodicly-running routine that can perform + * This method is called from the zcm_handle() function. + * This method provides a periodically-running routine that can perform * updates to the underlying hardware or other general mantainence to * this transport. This method should *never block*. Again, this - * method is called from zcm_handle_nonblock() and thus runs at the same - * frequency as zcm_handle_nonblock(). Failure to call zcm_handle_nonblock() + * method is called from zcm_handle() and thus runs at the same + * frequency as zcm_handle(). Failure to call zcm_handle() * while using an nonblock transport may cause the transport to work * incorrectly on both message send and recv. + * Returns ZCM_EOK if transport has no data awaiting transfer, + * ZCM_EAGAIN if data is awaiting transfer, and any other error where + * appropriate * * void destroy(zcm_trans_t* zt) * -------------------------------------------------------------------- @@ -226,6 +255,7 @@ struct zcm_trans_methods_t int (*recvmsg_enable)(zcm_trans_t* zt, const char* channel, bool enable); int (*recvmsg)(zcm_trans_t* zt, zcm_msg_t* msg, unsigned timeout); int (*query_drops)(zcm_trans_t *zt, uint64_t *out_drops); + int (*set_queue_size)(zcm_trans_t* zt, unsigned num_messages); int (*update)(zcm_trans_t* zt); void (*destroy)(zcm_trans_t* zt); }; @@ -245,11 +275,18 @@ static ZCM_TRANSPORT_INLINE int zcm_trans_recvmsg(zcm_trans_t* zt, zcm_msg_t* ms static ZCM_TRANSPORT_INLINE int zcm_trans_query_drops(zcm_trans_t* zt, uint64_t *out_drops) { - /* Possibly unimplemented, return ZCM_EUNIMPL */ - if (!zt->vtbl->query_drops) return ZCM_EUNIMPL; + /* Possibly unimplemented, return ZCM_EUNSUPPORTED */ + if (!zt->vtbl->query_drops) return ZCM_EUNSUPPORTED; return zt->vtbl->query_drops(zt, out_drops); } +static ZCM_TRANSPORT_INLINE int zcm_trans_set_queue_size(zcm_trans_t* zt, unsigned num_messages) +{ + /* Possibly unimplemented, return ZCM_EUNSUPPORTED */ + if (!zt->vtbl->set_queue_size) return ZCM_EUNSUPPORTED; + return zt->vtbl->set_queue_size(zt, num_messages); +} + static ZCM_TRANSPORT_INLINE int zcm_trans_update(zcm_trans_t* zt) { return zt->vtbl->update(zt); } diff --git a/zcm/transport/generic_serial_transport.c b/zcm/transport/generic_serial_transport.c index d1ebb5d80..d36dff12e 100644 --- a/zcm/transport/generic_serial_transport.c +++ b/zcm/transport/generic_serial_transport.c @@ -235,15 +235,16 @@ int serial_recvmsg(zcm_trans_generic_serial_t *zt, zcm_msg_t *msg, unsigned time int serial_update_rx(zcm_trans_t *_zt) { zcm_trans_generic_serial_t* zt = cast(_zt); + if (cb_room(&zt->recvBuffer) == 0) return ZCM_EMEMORY; cb_flush_in(&zt->recvBuffer, zt->get, zt->put_get_usr); - return ZCM_EOK; + return cb_room(&zt->recvBuffer) == 0 ? ZCM_EAGAIN : ZCM_EOK; } int serial_update_tx(zcm_trans_t *_zt) { zcm_trans_generic_serial_t* zt = cast(_zt); cb_flush_out(&zt->sendBuffer, zt->put, zt->put_get_usr); - return ZCM_EOK; + return cb_size(&zt->sendBuffer) == 0 ? ZCM_EOK : ZCM_EAGAIN; } /********************** STATICS **********************/ @@ -272,6 +273,7 @@ static zcm_trans_methods_t methods = { &_serial_recvmsg_enable, &_serial_recvmsg, NULL, // drops + NULL, // set_queue_size &_serial_update, &zcm_trans_generic_serial_destroy, }; diff --git a/zcm/transport/transport.cpp.template b/zcm/transport/transport.cpp.template index fa64c0673..f25a7f369 100644 --- a/zcm/transport/transport.cpp.template +++ b/zcm/transport/transport.cpp.template @@ -23,7 +23,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t } /********************** METHODS **********************/ - size_t get_mtu() + size_t getMtu() { return MTU; } @@ -34,19 +34,25 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t assert(0); } - int recvmsg_enable(const char *channel, bool enable) + int recvmsgEnable(const char *channel, bool enable) { // WRITE ME assert(0); } - int recvmsg(zcm_msg_t *msg, int timeout) + int recvmsg(zcm_msg_t *msg, unsigned timeout) { // WRITE ME assert(0); } - int query_drops(uint64_t *out_drops) + int setQueueSize(unsigned numMsgs) + { + // WRITE ME + assert(0); + } + + int queryDrops(uint64_t *outDrops) { // WRITE ME assert(0); @@ -72,20 +78,23 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return (ZCM_TRANS_CLASSNAME*)zt; } - static size_t _get_mtu(zcm_trans_t *zt) - { return cast(zt)->get_mtu(); } + static size_t _getMtu(zcm_trans_t *zt) + { return cast(zt)->getMtu(); } static int _sendmsg(zcm_trans_t *zt, zcm_msg_t msg) { return cast(zt)->sendmsg(msg); } - static int _recvmsg_enable(zcm_trans_t *zt, const char *channel, bool enable) - { return cast(zt)->recvmsg_enable(channel, enable); } + static int _recvmsgEnable(zcm_trans_t *zt, const char *channel, bool enable) + { return cast(zt)->recvmsgEnable(channel, enable); } - static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, int timeout) + static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } - static int _query_drops(zcm_trans_t *zt, uint64_t *out_drops) - { return cast(zt)->query_drops(out_drops); } + static int _queryDrops(zcm_trans_t *zt, uint64_t *outDrops) + { return cast(zt)->queryDrops(outDrops); } + + static int _setQueueSize(zcm_trans_t *zt, unsigned numMessages) + { return cast(zt)->setQueueSize(numMessages); } static int _update(zcm_trans_t *zt) { return cast(zt)->update(); } @@ -98,11 +107,12 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t }; zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { - &ZCM_TRANS_CLASSNAME::_get_mtu, + &ZCM_TRANS_CLASSNAME::_getMtu, &ZCM_TRANS_CLASSNAME::_sendmsg, - &ZCM_TRANS_CLASSNAME::_recvmsg_enable, + &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, - &ZCM_TRANS_CLASSNAME::_query_drops, + &ZCM_TRANS_CLASSNAME::_queryDrops, + &ZCM_TRANS_CLASSNAME::_setQueueSize, &ZCM_TRANS_CLASSNAME::_update, &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/transport/transport_can.cpp b/zcm/transport/transport_can.cpp index bcc1e520b..671a6f3de 100644 --- a/zcm/transport/transport_can.cpp +++ b/zcm/transport/transport_can.cpp @@ -268,7 +268,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t } /********************** METHODS **********************/ - size_t get_mtu() + size_t getMtu() { return zcm_trans_get_mtu(this->gst); } @@ -314,6 +314,17 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return ZCM_EAGAIN; } + int setQueueSize(unsigned numMsgs) + { + // kernel buffers have 2x overhead on buffers for internal bookkeeping + int recvQueueSize = getMtu() * numMsgs * 2; + if (setsockopt(soc, SOL_SOCKET, SO_RCVBUF, + (void *)&recvQueueSize, sizeof(recvQueueSize)) < 0) { + return ZCM_EUNKNOWN; + } + return ZCM_EOK; + } + /********************** STATICS **********************/ static zcm_trans_methods_t methods; static ZCM_TRANS_CLASSNAME *cast(zcm_trans_t *zt) @@ -322,18 +333,21 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return (ZCM_TRANS_CLASSNAME*)zt; } - static size_t _get_mtu(zcm_trans_t *zt) - { return cast(zt)->get_mtu(); } + static size_t _getMtu(zcm_trans_t *zt) + { return cast(zt)->getMtu(); } static int _sendmsg(zcm_trans_t *zt, zcm_msg_t msg) { return cast(zt)->sendmsg(msg); } - static int _recvmsg_enable(zcm_trans_t *zt, const char *channel, bool enable) + static int _recvmsgEnable(zcm_trans_t *zt, const char *channel, bool enable) { return cast(zt)->recvmsgEnable(channel, enable); } static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } + static int _setQueueSize(zcm_trans_t *zt, unsigned numMsgs) + { return cast(zt)->setQueueSize(numMsgs); } + static void _destroy(zcm_trans_t *zt) { delete cast(zt); } @@ -342,11 +356,12 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t }; zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { - &ZCM_TRANS_CLASSNAME::_get_mtu, + &ZCM_TRANS_CLASSNAME::_getMtu, &ZCM_TRANS_CLASSNAME::_sendmsg, - &ZCM_TRANS_CLASSNAME::_recvmsg_enable, + &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, NULL, // drops + &ZCM_TRANS_CLASSNAME::_setQueueSize, NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/transport/transport_file.cpp b/zcm/transport/transport_file.cpp index 8cadd40ee..5b46db830 100644 --- a/zcm/transport/transport_file.cpp +++ b/zcm/transport/transport_file.cpp @@ -86,7 +86,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t } /********************** METHODS **********************/ - size_t get_mtu() + size_t getMtu() { return MTU; } @@ -96,7 +96,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t assert(good()); assert(mode == "w" || mode == "a"); - if (msg.len > get_mtu()) + if (msg.len > getMtu()) return ZCM_EINVALID; zcm::LogEvent le; @@ -110,7 +110,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return ZCM_EOK; } - int recvmsg_enable(const char *channel, bool enable) + int recvmsgEnable(const char *channel, bool enable) { return ZCM_EOK; } @@ -170,14 +170,14 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return (ZCM_TRANS_CLASSNAME*)zt; } - static size_t _get_mtu(zcm_trans_t *zt) - { return cast(zt)->get_mtu(); } + static size_t _getMtu(zcm_trans_t *zt) + { return cast(zt)->getMtu(); } static int _sendmsg(zcm_trans_t *zt, zcm_msg_t msg) { return cast(zt)->sendmsg(msg); } - static int _recvmsg_enable(zcm_trans_t *zt, const char *channel, bool enable) - { return cast(zt)->recvmsg_enable(channel, enable); } + static int _recvmsgEnable(zcm_trans_t *zt, const char *channel, bool enable) + { return cast(zt)->recvmsgEnable(channel, enable); } static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } @@ -189,11 +189,12 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t }; zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { - &ZCM_TRANS_CLASSNAME::_get_mtu, + &ZCM_TRANS_CLASSNAME::_getMtu, &ZCM_TRANS_CLASSNAME::_sendmsg, - &ZCM_TRANS_CLASSNAME::_recvmsg_enable, + &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, NULL, // drops + NULL, // set_queue_size NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/transport/transport_inproc.cpp b/zcm/transport/transport_inproc.cpp index bda94e51e..01bba7a95 100644 --- a/zcm/transport/transport_inproc.cpp +++ b/zcm/transport/transport_inproc.cpp @@ -50,7 +50,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t bool good() { return true; } /********************** METHODS **********************/ - size_t get_mtu() { return MTU; } + size_t getMtu() { return MTU; } int sendmsg(zcm_msg_t msg) { @@ -87,13 +87,13 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t int recvmsg_enable(const char *channel, bool enable) { return ZCM_EOK; } - int recvmsg(zcm_msg_t *msg, unsigned timeout) + int recvmsg(zcm_msg_t *msg, unsigned timeoutMs) { std::unique_lock lk(msgLock, defer_lock); if (trans_type == ZCM_BLOCKING) { lk.lock(); - bool available = msgCond.wait_for(lk, chrono::milliseconds(timeout), + bool available = msgCond.wait_for(lk, chrono::milliseconds(timeoutMs), [&](){ return !msgs.empty(); }); if (!available) return ZCM_EAGAIN; } else { @@ -127,13 +127,13 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return (ZCM_TRANS_CLASSNAME*)zt; } - static size_t _get_mtu(zcm_trans_t *zt) - { return cast(zt)->get_mtu(); } + static size_t _getMtu(zcm_trans_t *zt) + { return cast(zt)->getMtu(); } static int _sendmsg(zcm_trans_t *zt, zcm_msg_t msg) { return cast(zt)->sendmsg(msg); } - static int _recvmsg_enable(zcm_trans_t *zt, const char *channel, bool enable) + static int _recvmsgEnable(zcm_trans_t *zt, const char *channel, bool enable) { return cast(zt)->recvmsg_enable(channel, enable); } static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) @@ -150,11 +150,12 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t }; zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { - &ZCM_TRANS_CLASSNAME::_get_mtu, + &ZCM_TRANS_CLASSNAME::_getMtu, &ZCM_TRANS_CLASSNAME::_sendmsg, - &ZCM_TRANS_CLASSNAME::_recvmsg_enable, + &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, NULL, // drops + NULL, // set_queue_size &ZCM_TRANS_CLASSNAME::_update, &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/transport/transport_ipcshm.cpp b/zcm/transport/transport_ipcshm.cpp index 26f678951..7897355ed 100644 --- a/zcm/transport/transport_ipcshm.cpp +++ b/zcm/transport/transport_ipcshm.cpp @@ -213,7 +213,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t } /********************** METHODS **********************/ - size_t get_mtu() + size_t getMtu() { return msg_payload_sz; } @@ -246,7 +246,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return ZCM_EOK; } - int recvmsg_enable(const char *channel, bool enable) + int recvmsgEnable(const char *channel, bool enable) { return ZCM_EOK; } @@ -302,11 +302,11 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return ZCM_EOK; } - int query_drops(uint64_t *out_drops) + int queryDrops(uint64_t *outDrops) { - if (!out_drops) return ZCM_EINVALID; + if (!outDrops) return ZCM_EINVALID; uint64_t drops = lf_bcast_sub_drops(sub); - *out_drops = drops; + *outDrops = drops; return ZCM_EOK; } @@ -318,20 +318,20 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return (ZCM_TRANS_CLASSNAME*)zt; } - static size_t _get_mtu(zcm_trans_t *zt) - { return cast(zt)->get_mtu(); } + static size_t _getMtu(zcm_trans_t *zt) + { return cast(zt)->getMtu(); } static int _sendmsg(zcm_trans_t *zt, zcm_msg_t msg) { return cast(zt)->sendmsg(msg); } - static int _recvmsg_enable(zcm_trans_t *zt, const char *channel, bool enable) - { return cast(zt)->recvmsg_enable(channel, enable); } + static int _recvmsgEnable(zcm_trans_t *zt, const char *channel, bool enable) + { return cast(zt)->recvmsgEnable(channel, enable); } static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } - static int _query_drops(zcm_trans_t *zt, uint64_t *out_drops) - { return cast(zt)->query_drops(out_drops); } + static int _queryDrops(zcm_trans_t *zt, uint64_t *outDrops) + { return cast(zt)->queryDrops(outDrops); } static void _destroy(zcm_trans_t *zt) { delete cast(zt); } @@ -341,11 +341,12 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t }; zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { - &ZCM_TRANS_CLASSNAME::_get_mtu, + &ZCM_TRANS_CLASSNAME::_getMtu, &ZCM_TRANS_CLASSNAME::_sendmsg, - &ZCM_TRANS_CLASSNAME::_recvmsg_enable, + &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, - &ZCM_TRANS_CLASSNAME::_query_drops, + &ZCM_TRANS_CLASSNAME::_queryDrops, + NULL, // set_queue_size NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/transport/transport_serial.cpp b/zcm/transport/transport_serial.cpp index b0cb8e060..50ce151a9 100644 --- a/zcm/transport/transport_serial.cpp +++ b/zcm/transport/transport_serial.cpp @@ -8,20 +8,19 @@ #include "util/TimeUtil.hpp" -#include -#include -#include -#include -#include -#include - #include #include - -#include +#include +#include +#include #include #include +#include +#include +#include #include +#include + using namespace std; // TODO: This transport layer needs to be "hardened" to handle @@ -243,10 +242,8 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t int baud; bool hwFlowControl; - bool raw; string rawChan; - int rawSize; - std::unique_ptr rawBuf; + std::vector rawBuf; string address; @@ -266,7 +263,6 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t ZCM_TRANS_CLASSNAME(zcm_url_t* url) { trans_type = ZCM_BLOCKING; - vtbl = &methods; // build 'options' auto* opts = zcm_url_opts(url); @@ -298,7 +294,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t } } - raw = false; + bool raw = false; auto* rawStr = findOption("raw"); if (rawStr) { if (*rawStr == "true") { @@ -317,7 +313,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t rawChan = *rawChanStr; } - rawSize = 1024; + size_t rawSize = 1024; auto* rawSizeStr = findOption("raw_size"); if (rawSizeStr) { rawSize = atoi(rawSizeStr->c_str()); @@ -331,9 +327,11 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t ser.open(address, baud, hwFlowControl); if (raw) { - rawBuf.reset(new uint8_t[rawSize]); + vtbl = &rawMethods; + rawBuf.resize(rawSize); gst = nullptr; } else { + vtbl = &methods; gst = zcm_trans_generic_serial_create(&ZCM_TRANS_CLASSNAME::get, &ZCM_TRANS_CLASSNAME::put, this, @@ -375,86 +373,117 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t { return TimeUtil::utime(); } /********************** METHODS **********************/ + size_t getMtuRaw() + { return MTU; } + size_t getMtu() - { return raw ? MTU : zcm_trans_get_mtu(this->gst); } + { return zcm_trans_get_mtu(this->gst); } + + int sendmsgRaw(zcm_msg_t msg) + { + if (!msg.len) return ZCM_EOK; + do { + int ret = ser.write(msg.buf, msg.len); + + if (ret < 0) break; + if ((size_t)ret == msg.len) return ZCM_EOK; + + msg.buf += ret; + msg.len -= ret; + } while (true); + + return ZCM_EUNKNOWN; + } int sendmsg(zcm_msg_t msg) { - if (raw) { - if (put(msg.buf, msg.len, this) != 0) return ZCM_EOK; - return ZCM_EAGAIN; - } else { - // Note: No need to lock here ONLY because the internals of - // generic serial transport sendmsg only use the sendBuffer - // and touch no variables related to receiving - int ret = zcm_trans_sendmsg(this->gst, msg); - if (ret != ZCM_EOK) return ret; - return serial_update_tx(this->gst); + // Note: No need to lock here ONLY because the internals of + // generic serial transport sendmsg only use the sendBuffer + // and touch no variables related to receiving + int ret = zcm_trans_sendmsg(this->gst, msg); + if (ret != ZCM_EOK) return ret; + while (true) { + ret = serial_update_tx(this->gst); + if (ret != ZCM_EAGAIN) break; } + return ret; } + int recvmsgEnableRaw(const char* channel, bool enable) + { return ZCM_EOK; } + int recvmsgEnable(const char* channel, bool enable) - { return raw ? ZCM_EOK : zcm_trans_recvmsg_enable(this->gst, channel, enable); } + { return zcm_trans_recvmsg_enable(this->gst, channel, enable); } - int recvmsg(zcm_msg_t* msg, unsigned timeoutMs) + int recvmsgRaw(zcm_msg_t* msg, unsigned timeoutMs) { + // used by get() timeoutLeftUs = timeoutMs * 1e3; - if (raw) { - size_t sz = get(rawBuf.get(), rawSize, this); - if (sz == 0 || rawChan.empty()) return ZCM_EAGAIN; + size_t sz = get(rawBuf.data(), rawBuf.size(), this); + if (sz == 0 || rawChan.empty()) return ZCM_EAGAIN; - msg->utime = timestamp_now(this); - msg->channel = rawChan.c_str(); - msg->len = sz; - msg->buf = rawBuf.get(); + msg->utime = timestamp_now(this); + msg->channel = rawChan.c_str(); + msg->len = sz; + msg->buf = rawBuf.data(); - return ZCM_EOK; - } else { - do { - uint64_t startUtime = TimeUtil::utime(); + return ZCM_EOK; + } - // Note: No need to lock here ONLY because the internals of - // generic serial transport recvmsg only use the recv related - // data members and touch no variables related to sending - int ret = zcm_trans_recvmsg(this->gst, msg, 0); - if (ret == ZCM_EOK) return ret; + int recvmsg(zcm_msg_t* msg, unsigned timeoutMs) + { + uint64_t endUtime = TimeUtil::utime() + timeoutMs * 1e3; - uint64_t diff = TimeUtil::utime() - startUtime; - startUtime = TimeUtil::utime(); - // Note: timeoutLeftUs is calculated here because serial_update_rx - // needs it to be set properly so that the blocking read in - // `get` knows how long it has to exit - timeoutLeftUs = timeoutLeftUs > diff ? timeoutLeftUs - diff : 0; + do { - serial_update_rx(this->gst); + // Note: No need to lock here ONLY because the internals of + // generic serial transport recvmsg only use the recv related + // data members and touch no variables related to sending + int ret = zcm_trans_recvmsg(this->gst, msg, 0); + if (ret == ZCM_EOK) return ret; - diff = TimeUtil::utime() - startUtime; - timeoutLeftUs = timeoutLeftUs > diff ? timeoutLeftUs - diff : 0; + // Note: timeoutLeftUs is calculated here because serial_update_rx + // needs it to be set properly so that the blocking read in + // `get` knows how long it has to exit + uint64_t now = TimeUtil::utime(); + timeoutLeftUs = endUtime < now ? 0 : endUtime - now; + serial_update_rx(this->gst); - } while (timeoutLeftUs > 0); + } while (TimeUtil::utime() < endUtime); - return ZCM_EAGAIN; - } + return ZCM_EAGAIN; } /********************** STATICS **********************/ - static zcm_trans_methods_t methods; + static zcm_trans_methods_t methods, rawMethods; static ZCM_TRANS_CLASSNAME* cast(zcm_trans_t* zt) { - assert(zt->vtbl == &methods); + assert(zt->vtbl == &rawMethods || zt->vtbl == &methods); return (ZCM_TRANS_CLASSNAME*)zt; } + static size_t _getMtuRaw(zcm_trans_t* zt) + { return cast(zt)->getMtuRaw(); } + static size_t _getMtu(zcm_trans_t* zt) { return cast(zt)->getMtu(); } + static int _sendmsgRaw(zcm_trans_t* zt, zcm_msg_t msg) + { return cast(zt)->sendmsgRaw(msg); } + static int _sendmsg(zcm_trans_t* zt, zcm_msg_t msg) { return cast(zt)->sendmsg(msg); } + static int _recvmsgEnableRaw(zcm_trans_t* zt, const char* channel, bool enable) + { return cast(zt)->recvmsgEnableRaw(channel, enable); } + static int _recvmsgEnable(zcm_trans_t* zt, const char* channel, bool enable) { return cast(zt)->recvmsgEnable(channel, enable); } + static int _recvmsgRaw(zcm_trans_t* zt, zcm_msg_t* msg, unsigned timeout) + { return cast(zt)->recvmsgRaw(msg, timeout); } + static int _recvmsg(zcm_trans_t* zt, zcm_msg_t* msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } @@ -470,6 +499,18 @@ zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, NULL, // drops + NULL, // set_queue_size + NULL, // update + &ZCM_TRANS_CLASSNAME::_destroy, +}; + +zcm_trans_methods_t ZCM_TRANS_CLASSNAME::rawMethods = { + &ZCM_TRANS_CLASSNAME::_getMtuRaw, + &ZCM_TRANS_CLASSNAME::_sendmsgRaw, + &ZCM_TRANS_CLASSNAME::_recvmsgEnableRaw, + &ZCM_TRANS_CLASSNAME::_recvmsgRaw, + NULL, // drops + NULL, // set_queue_size NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, }; @@ -478,11 +519,11 @@ static zcm_trans_t* create(zcm_url_t* url, char **opt_errmsg) { if (opt_errmsg) *opt_errmsg = NULL; // Feature unused in this transport auto* trans = new ZCM_TRANS_CLASSNAME(url); - if (trans->good()) - return trans; - - delete trans; - return nullptr; + if (!trans->good()) { + delete trans; + return nullptr; + } + return trans; } #ifdef USING_TRANS_SERIAL diff --git a/zcm/transport/transport_zmq_local.cpp b/zcm/transport/transport_zmq_local.cpp index 71e812ce9..2239fe20d 100644 --- a/zcm/transport/transport_zmq_local.cpp +++ b/zcm/transport/transport_zmq_local.cpp @@ -58,7 +58,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t Type type; string subnet; - int pubhwm = 1000, subhwm = 1000; + int pubhwm = 100, subhwm = 100; uint64_t rescanPeriodUs = 250e3; unordered_map> pubsocks; @@ -488,6 +488,22 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return handlePitems(msg); } + int setQueueSize(unsigned numMsgs) + { + subhwm = numMsgs; + + bool succ = true; + + for (auto& elt : subsocks) { + auto& sock = elt.second.first; + int rc; + rc = zmq_setsockopt(sock, ZMQ_RCVHWM, &subhwm, sizeof(subhwm)); + if (rc == -1) succ = false; + } + + return succ ? ZCM_EOK : ZCM_EUNKNOWN; + } + /********************** STATICS **********************/ static zcm_trans_methods_t methods; static ZCM_TRANS_CLASSNAME *cast(zcm_trans_t *zt) @@ -508,6 +524,9 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } + static int _setQueueSize(zcm_trans_t *zt, unsigned numMsgs) + { return cast(zt)->setQueueSize(numMsgs); } + static void _destroy(zcm_trans_t *zt) { delete cast(zt); } @@ -521,6 +540,7 @@ zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, NULL, // drops + &ZCM_TRANS_CLASSNAME::_setQueueSize, NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/transport/udp/udp.cpp b/zcm/transport/udp/udp.cpp index 3598c0e9b..4f0988674 100644 --- a/zcm/transport/udp/udp.cpp +++ b/zcm/transport/udp/udp.cpp @@ -80,8 +80,6 @@ struct UDP bool init(); ~UDP(); - int handle(); - int sendmsg(zcm_msg_t msg); int recvmsg(zcm_msg_t *msg, unsigned timeoutMs); @@ -486,6 +484,7 @@ zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, NULL, // drops + NULL, // set_queue_size NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, }; diff --git a/zcm/zcm-cpp-impl.hpp b/zcm/zcm-cpp-impl.hpp index 7107dd166..54bc3e471 100644 --- a/zcm/zcm-cpp-impl.hpp +++ b/zcm/zcm-cpp-impl.hpp @@ -130,36 +130,32 @@ inline void ZCM::resume() } #endif -#ifndef ZCM_EMBEDDED -inline int ZCM::handle() +inline int ZCM::handle(unsigned timeout) { - return zcm_handle(zcm); + return zcm_handle(zcm, timeout); } -#endif -#ifndef ZCM_EMBEDDED -inline void ZCM::setQueueSize(uint32_t sz) +inline int ZCM::flush() { - zcm_set_queue_size(zcm, sz); + return zcm_flush(zcm); } -#endif -#ifndef ZCM_EMBEDDED -inline int ZCM::writeTopology(const std::string& name) +inline int ZCM::setQueueSize(uint32_t sz) { - return zcm_write_topology(zcm, name.c_str()); + return zcm_set_queue_size(zcm, sz); } -#endif -inline int ZCM::handleNonblock() +inline int ZCM::queryDrops(uint64_t& outDrops) { - return zcm_handle_nonblock(zcm); + return zcm_query_drops(zcm, &outDrops); } -inline void ZCM::flush() +#ifndef ZCM_EMBEDDED +inline int ZCM::writeTopology(const std::string& name) { - zcm_flush(zcm); + return zcm_write_topology(zcm, name.c_str()); } +#endif inline int ZCM::publish(const std::string& channel, const uint8_t* data, uint32_t len) { diff --git a/zcm/zcm-cpp.hpp b/zcm/zcm-cpp.hpp index 1e5398e24..173883cae 100644 --- a/zcm/zcm-cpp.hpp +++ b/zcm/zcm-cpp.hpp @@ -41,12 +41,12 @@ class ZCM virtual inline void stop(); virtual inline void pause(); virtual inline void resume(); - virtual inline int handle(); - virtual inline void setQueueSize(uint32_t sz); + virtual inline int setQueueSize(unsigned sz); + virtual inline int queryDrops(uint64_t& outDrops); virtual inline int writeTopology(const std::string& name); #endif - virtual inline int handleNonblock(); - virtual inline void flush(); + virtual inline int handle(unsigned timeout); + virtual inline int flush(); public: inline int publish(const std::string& channel, const uint8_t* data, uint32_t len); diff --git a/zcm/zcm.c b/zcm/zcm.c index 4b238e325..62b5ac866 100644 --- a/zcm/zcm.c +++ b/zcm/zcm.c @@ -200,19 +200,6 @@ int zcm_publish(zcm_t* zcm, const char* channel, const uint8_t* data, uint32_t l return ret; } -void zcm_flush(zcm_t* zcm) -{ -#ifndef ZCM_EMBEDDED - switch (zcm->type) { - case ZCM_BLOCKING: zcm_blocking_flush(zcm->impl); break; - case ZCM_NONBLOCKING: zcm_nonblocking_flush(zcm->impl); break; - } -#else - ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); - zcm_nonblocking_flush(zcm->impl); -#endif -} - zcm_sub_t* zcm_subscribe(zcm_t* zcm, const char* channel, zcm_msg_handler_t cb, void* usr) { zcm_sub_t* ret = NULL; @@ -251,6 +238,59 @@ int zcm_unsubscribe(zcm_t* zcm, zcm_sub_t* sub) return ret; } +int zcm_handle(zcm_t* zcm, unsigned timeout) +{ + int ret = ZCM_EUNKNOWN; +#ifndef ZCM_EMBEDDED + switch (zcm->type) { + case ZCM_BLOCKING: + ret = zcm_blocking_handle(zcm->impl, timeout); + break; + case ZCM_NONBLOCKING: + ret = zcm_nonblocking_handle(zcm->impl); + break; + } +#else + ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); + ret = zcm_nonblocking_handle(zcm->impl); +#endif + return ret; +} + +int zcm_flush(zcm_t* zcm) +{ + int ret = ZCM_EUNKNOWN; +#ifndef ZCM_EMBEDDED + switch (zcm->type) { + case ZCM_BLOCKING: ret = zcm_blocking_flush(zcm->impl); break; + case ZCM_NONBLOCKING: ret = zcm_nonblocking_flush(zcm->impl); break; + } +#else + ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); + ret = zcm_nonblocking_flush(zcm->impl); +#endif + return ret; +} + +int zcm_set_queue_size(zcm_t* zcm, unsigned num_messages) +{ + int ret = ZCM_EUNKNOWN; +#ifndef ZCM_EMBEDDED + switch (zcm->type) { + case ZCM_BLOCKING: + ret = zcm_blocking_set_queue_size(zcm->impl, num_messages); + break; + case ZCM_NONBLOCKING: + ret = zcm_nonblocking_set_queue_size(zcm->impl, num_messages); + break; + } +#else + ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); + ret = zcm_nonblocking_set_queue_size(zcm->impl, num_messages); +#endif + return ret; +} + #ifndef ZCM_EMBEDDED void zcm_start(zcm_t* zcm) { @@ -291,22 +331,6 @@ void zcm_resume(zcm_t* zcm) } #endif -#ifndef ZCM_EMBEDDED -int zcm_handle(zcm_t* zcm) -{ - ZCM_ASSERT(zcm->type == ZCM_BLOCKING); - return zcm_blocking_handle(zcm->impl); -} -#endif - -#ifndef ZCM_EMBEDDED -void zcm_set_queue_size(zcm_t* zcm, uint32_t numMsgs) -{ - ZCM_ASSERT(zcm->type == ZCM_BLOCKING); - return zcm_blocking_set_queue_size(zcm->impl, numMsgs); -} -#endif - #ifndef ZCM_EMBEDDED int zcm_write_topology(zcm_t* zcm, const char* name) { @@ -319,25 +343,6 @@ int zcm_write_topology(zcm_t* zcm, const char* name) } #endif -int zcm_handle_nonblock(zcm_t* zcm) -{ - int ret = ZCM_EUNKNOWN; -#ifndef ZCM_EMBEDDED - switch (zcm->type) { - case ZCM_BLOCKING: - ret = zcm_blocking_handle_nonblock(zcm->impl); - break; - case ZCM_NONBLOCKING: - ret = zcm_nonblocking_handle_nonblock(zcm->impl); - break; - } -#else - ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); - ret = zcm_nonblocking_handle_nonblock(zcm->impl); -#endif - return ret; -} - int zcm_query_drops(zcm_t *zcm, uint64_t *out_drops) { int ret = ZCM_EUNKNOWN; @@ -398,41 +403,4 @@ int zcm_try_unsubscribe(zcm_t* zcm, zcm_sub_t* sub) #endif return ret; } - -int zcm_try_flush(zcm_t* zcm) -{ - int ret = ZCM_EUNKNOWN; -#ifndef ZCM_EMBEDDED - switch (zcm->type) { - case ZCM_BLOCKING: - ret = zcm_blocking_try_flush(zcm->impl); - break; - case ZCM_NONBLOCKING: - zcm_nonblocking_flush(zcm->impl); - ret = ZCM_EOK; - break; - } -#else - ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); - zcm_nonblocking_flush(zcm->impl); - ret = ZCM_EOK; -#endif - return ret; -} - -#ifndef ZCM_EMBEDDED -int zcm_try_stop(zcm_t* zcm) -{ - ZCM_ASSERT(zcm->type == ZCM_BLOCKING); - return zcm_blocking_try_stop(zcm->impl); -} -#endif - -#ifndef ZCM_EMBEDDED -int zcm_try_set_queue_size(zcm_t* zcm, uint32_t numMsgs) -{ - ZCM_ASSERT(zcm->type == ZCM_BLOCKING); - return zcm_blocking_try_set_queue_size(zcm->impl, numMsgs); -} -#endif /****************************************************************************/ diff --git a/zcm/zcm.h b/zcm/zcm.h index 1e8b01a9f..5595123af 100644 --- a/zcm/zcm.h +++ b/zcm/zcm.h @@ -11,8 +11,8 @@ extern "C" { * m: Minor * u: Micro */ -#define ZCM_MAJOR_VERSION 1 -#define ZCM_MINOR_VERSION 2 +#define ZCM_MAJOR_VERSION 2 +#define ZCM_MINOR_VERSION 0 #define ZCM_MICRO_VERSION 0 #include @@ -32,16 +32,16 @@ enum zcm_type ZCM_NONBLOCKING }; -#define ZCM_RETURN_CODES \ - X(ZCM_EOK, 0, "Okay, no errors") \ - X(ZCM_EINVALID, -1, "Invalid arguments") \ - X(ZCM_EAGAIN, -2, "Resource unavailable, try again") \ - X(ZCM_ECONNECT, -3, "Transport connection failed") \ - X(ZCM_EINTR, -4, "Operation was unexpectedly interrupted") \ - X(ZCM_EUNKNOWN, -5, "Unknown error") \ - X(ZCM_EMEMORY, -6, "Out of memory") \ - X(ZCM_EUNIMPL, -7, "Function is not implemented") \ - X(ZCM_NUM_RETURN_CODES, 8, "Invalid return code") +#define ZCM_RETURN_CODES \ + X(ZCM_EOK, 0, "Okay, no errors" ) \ + X(ZCM_EINVALID, -1, "Invalid arguments" ) \ + X(ZCM_EAGAIN , -2, "Resource unavailable, try again" ) \ + X(ZCM_ECONNECT, -3, "Transport connection failed" ) \ + X(ZCM_EINTR , -4, "Operation was unexpectedly interrupted") \ + X(ZCM_EUNKNOWN, -5, "Unknown error" ) \ + X(ZCM_EMEMORY, -6, "Out of memory" ) \ + X(ZCM_EUNSUPPORTED, -7, "Unsupported functionality" ) \ + X(ZCM_NUM_RETURN_CODES, 8, "Invalid return code" ) /* Return codes */ enum zcm_return_codes @@ -124,48 +124,46 @@ zcm_sub_t* zcm_subscribe(zcm_t* zcm, const char* channel, zcm_msg_handler_t cb, Returns ZCM_EOK on success, error code on failure */ int zcm_unsubscribe(zcm_t* zcm, zcm_sub_t* sub); -/* Publish a zcm message buffer. Note: the message may not be completely - sent after this call has returned. To block until the messages are transmitted, - call the zcm_flush() method. - Returns ZCM_EOK on success, error code on failure */ +/* Publish a zcm message buffer. + * Returns ZCM_EOK on success, error code on failure */ int zcm_publish(zcm_t* zcm, const char* channel, const uint8_t* data, uint32_t len); -/* Block until all published messages have been sent even if the underlying - transport is nonblocking. Additionally, dispatches all messages that have - already been received sequentially in this thread. */ -void zcm_flush(zcm_t* zcm); +/* Dispatches a single incoming message if available. + * Returns ZCM_EOK if a message was dispatched, error code on failure. + * timeout is in units of milliseconds */ +int zcm_handle(zcm_t* zcm, unsigned timeout); + +/* Dispatches messages continuously until it has no more to dispatch. + * This may run forever, if your handlers are too slow and you receive + * more messages in the time it takes all subscription handlers to run. + * If you want to guarantee a return, do something like: + * while(handle() == ZCM_EOK && ++i < n) */ +int zcm_flush(zcm_t* zcm); + +/* Request that the underlying transport queue at least this many messages. + * Messages will accumulate unless: + * - zcm_handle() is called repeatedly + * - zcm_run() was called + * - zcm_start() was called once or since the most recent zcm_stop() */ +int zcm_set_queue_size(zcm_t* zcm, unsigned num_messages); #ifndef ZCM_EMBEDDED /* Blocking Mode Only: Functions for controlling the message dispatch loop */ void zcm_run(zcm_t* zcm); void zcm_start(zcm_t* zcm); +// This function may be called if zcm_run() is currently running in another thread void zcm_stop(zcm_t* zcm); -void zcm_pause(zcm_t* zcm); /* pauses message dispatch and publishing, not transport */ +void zcm_pause(zcm_t* zcm); /* pauses all interaction with transport */ void zcm_resume(zcm_t* zcm); -int zcm_handle(zcm_t* zcm); /* returns ZCM_EOK normally, error code on failure. */ -/* Determines how many messages can be stored from the transport without being dispatched - As well as the number of messages that may be stored from the user without being - transmitted by the transport. Normal operation does not require the user to modify - this, but if the user is using zcm_pause() and forcing dispatches/transmission through - calls to zcm_flush(), it will be important to set an appropriate queue size based on - traffic and flush frequency. Note that if either queue reaches maximum capacity, - messages will not be read from / sent to the transport, which could cause significant - issues depending on the transport. */ -void zcm_set_queue_size(zcm_t* zcm, uint32_t numMsgs); /* Write topology file to filename. Returns ZCM_EOK normally, error code on failure */ int zcm_write_topology(zcm_t* zcm, const char* name); #endif -/* Non-Blocking Mode Only: Functions checking and dispatching messages - Returns ZCM_EOK if a message was dispatched, ZCM_EAGAIN if no messages, - error code otherwise */ -int zcm_handle_nonblock(zcm_t* zcm); - /* Query the drop counter on the underlying transport - NOTE: This may be unimplemented, in which case it will return ZCM_EIMPL and - the out-param will be disregarded. */ -int zcm_query_drops(zcm_t* zcm, uint64_t* out_drops); + NOTE: This may be unimplemented, in which case it will return ZCM_EUNSUPPORTED + and out_drops will be disregarded. */ +int zcm_query_drops(zcm_t *zcm, uint64_t *out_drops); /****************************************************************************/ /* NOT FOR GENERAL USE. USED FOR LANGUAGE-SPECIFIC BINDINGS WITH VERY */ @@ -180,15 +178,6 @@ zcm_sub_t* zcm_try_subscribe(zcm_t* zcm, const char* channel, zcm_msg_handler_t Returns ZCM_EOK on success, error code on failure Can fail to subscribe if zcm is already running */ int zcm_try_unsubscribe(zcm_t* zcm, zcm_sub_t* sub); -/* Nonblocking version of flush (ZCM_EAGAIN if fail, ZCM_EOK if success) as defined - above. If you want to guarantee that this function returns ZCM_EOK at some point, - you should zcm_pause() first. */ -int zcm_try_flush(zcm_t* zcm); -#ifndef ZCM_EMBEDDED -int zcm_try_stop(zcm_t* zcm); /* returns ZCM_EOK on success, error code on failure */ -int zcm_try_set_queue_size(zcm_t* zcm, - uint32_t numMsgs); /* returns ZCM_EOK or ZCM_EAGAIN */ -#endif /****************************************************************************/ #ifdef __cplusplus From d90943fde2c73e0d4e59cc7f0cb206742e343857 Mon Sep 17 00:00:00 2001 From: Jonathan Bendes Date: Wed, 27 Mar 2024 13:56:47 -0400 Subject: [PATCH 2/4] Added num dropped messages to all languages --- zcm/blocking.cpp | 10 +++++----- zcm/blocking.h | 2 +- zcm/julia/ZCM.jl | 4 ++++ zcm/nonblocking.c | 4 ++-- zcm/nonblocking.h | 2 +- zcm/python/zerocm.pyx | 21 ++++++++++++--------- zcm/transport.h | 12 ++++++------ zcm/transport/transport.cpp.template | 8 ++++---- zcm/transport/transport_ipcshm.cpp | 12 +++++------- zcm/zcm-cpp-impl.hpp | 4 ++-- zcm/zcm-cpp.hpp | 2 +- zcm/zcm.c | 10 +++------- zcm/zcm.h | 5 +++-- 13 files changed, 49 insertions(+), 47 deletions(-) diff --git a/zcm/blocking.cpp b/zcm/blocking.cpp index 3b6aeb069..d0efd88c5 100644 --- a/zcm/blocking.cpp +++ b/zcm/blocking.cpp @@ -78,7 +78,7 @@ struct zcm_blocking zcm_sub_t* subscribe(const string& channel, zcm_msg_handler_t cb, void* usr, bool block); int unsubscribe(zcm_sub_t* sub, bool block); - int queryDrops(uint64_t *outDrops); + int getNumDroppedMessages(); int writeTopology(string name); private: @@ -316,9 +316,9 @@ int zcm_blocking_t::flush() return ret; } -int zcm_blocking_t::queryDrops(uint64_t *outDrops) +int zcm_blocking_t::getNumDroppedMessages() { - return zcm_trans_query_drops(zt, outDrops); + return zcm_trans_get_num_dropped_messages(zt); } void zcm_blocking_t::recvThreadFunc() @@ -511,9 +511,9 @@ int zcm_blocking_set_queue_size(zcm_blocking_t* zcm, unsigned num_messages) return zcm->setQueueSize(num_messages); } -int zcm_blocking_query_drops(zcm_blocking_t *zcm, uint64_t *out_drops) +int zcm_blocking_get_num_dropped_messages(zcm_blocking_t *zcm) { - return zcm->queryDrops(out_drops); + return zcm->getNumDroppedMessages(); } int zcm_blocking_write_topology(zcm_blocking_t* zcm, const char* name) diff --git a/zcm/blocking.h b/zcm/blocking.h index 5067eb100..52efc92cf 100644 --- a/zcm/blocking.h +++ b/zcm/blocking.h @@ -30,7 +30,7 @@ void zcm_blocking_pause(zcm_blocking_t* zcm); void zcm_blocking_resume(zcm_blocking_t* zcm); int zcm_blocking_handle(zcm_blocking_t* zcm, unsigned timeout); int zcm_blocking_set_queue_size(zcm_blocking_t* zcm, unsigned num_messages); -int zcm_blocking_query_drops(zcm_blocking_t *zcm, uint64_t *out_drops); +int zcm_blocking_get_num_dropped_messages(zcm_blocking_t *zcm); int zcm_blocking_write_topology(zcm_blocking_t* zcm, const char* name); diff --git a/zcm/julia/ZCM.jl b/zcm/julia/ZCM.jl index 1c2a9d03a..c999e4de4 100644 --- a/zcm/julia/ZCM.jl +++ b/zcm/julia/ZCM.jl @@ -295,6 +295,10 @@ function write_topology(zcm::Zcm, name::AbstractString) zcm, convert(String, name)) end +function get_num_dropped_messages(zcm::Zcm) + ccall(("zcm_get_num_dropped_messages", "libzcm"), Cint, (Ptr{Native.Zcm}), zcm) +end + function read_bits(T::Type, buf::IOBuffer, numbits::Int, offset_bit::Int, signExtend::Bool) ret = T(0) bits_left = numbits diff --git a/zcm/nonblocking.c b/zcm/nonblocking.c index 5bb9478ab..566fa06fb 100644 --- a/zcm/nonblocking.c +++ b/zcm/nonblocking.c @@ -165,9 +165,9 @@ int zcm_nonblocking_unsubscribe(zcm_nonblocking_t* zcm, zcm_sub_t* sub) return rc; } -int zcm_nonblocking_query_drops(zcm_nonblocking_t *zcm, uint64_t *out_drops) +int zcm_nonblocking_get_num_dropped_messages(zcm_nonblocking_t *zcm) { - return zcm_trans_query_drops(zcm->zt, out_drops); + return zcm_trans_get_num_dropped_messages(zcm->zt); } static void dispatch_message(zcm_nonblocking_t* zcm, zcm_msg_t* msg) diff --git a/zcm/nonblocking.h b/zcm/nonblocking.h index e593763d8..823856f8d 100644 --- a/zcm/nonblocking.h +++ b/zcm/nonblocking.h @@ -27,7 +27,7 @@ int zcm_nonblocking_handle(zcm_nonblocking_t* zcm); int zcm_nonblocking_flush(zcm_nonblocking_t* zcm); -int zcm_nonblocking_query_drops(zcm_nonblocking_t *zcm, uint64_t *out_drops); +int zcm_nonblocking_get_num_dropped_messages(zcm_nonblocking_t *zcm); int zcm_nonblocking_set_queue_size(zcm_nonblocking_t* zcm, unsigned num_messages); diff --git a/zcm/python/zerocm.pyx b/zcm/python/zerocm.pyx index 4c252f996..1cb6b3bf2 100644 --- a/zcm/python/zerocm.pyx +++ b/zcm/python/zerocm.pyx @@ -40,15 +40,16 @@ cdef extern from "zcm/zcm.h": int zcm_publish(zcm_t* zcm, const char* channel, const uint8_t* data, uint32_t dlen) - void zcm_run (zcm_t* zcm) - void zcm_start (zcm_t* zcm) - void zcm_stop (zcm_t* zcm) - void zcm_pause (zcm_t* zcm) - void zcm_resume (zcm_t* zcm) - int zcm_handle (zcm_t* zcm, unsigned timeout) - int zcm_flush (zcm_t* zcm) - int zcm_set_queue_size(zcm_t* zcm, unsigned num_messages) - int zcm_write_topology(zcm_t* zcm, const char* name) + void zcm_run (zcm_t* zcm) + void zcm_start (zcm_t* zcm) + void zcm_stop (zcm_t* zcm) + void zcm_pause (zcm_t* zcm) + void zcm_resume (zcm_t* zcm) + int zcm_handle (zcm_t* zcm, unsigned timeout) + int zcm_flush (zcm_t* zcm) + int zcm_set_queue_size (zcm_t* zcm, unsigned num_messages) + int zcm_get_num_dropped_messages(zcm_t* zcm) + int zcm_write_topology (zcm_t* zcm, const char* name) ctypedef struct zcm_eventlog_t: pass @@ -169,6 +170,8 @@ cdef class ZCM: return zcm_set_queue_size(self.zcm, numMsgs) def writeTopology(self, str name): return zcm_write_topology(self.zcm, name.encode('utf-8')) + def getNumDroppedMessages(self): + return zcm_get_num_dropped_messages(self.zcm) cdef class LogEvent: cdef int64_t eventnum diff --git a/zcm/transport.h b/zcm/transport.h index d4b35824c..38a6c5a63 100644 --- a/zcm/transport.h +++ b/zcm/transport.h @@ -90,7 +90,7 @@ * and users should only expect accuracy within a few milliseconds. Users * should *not* attempt to use this timing mechanism for real-time events. * - * int query_drops(zcm_trans_t* zt, uint64_t *out_drops); + * int get_num_dropped_messages(zcm_trans_t* zt); * -------------------------------------------------------------------- * This method provides the caller access to an internal transport drop counter * Implementing this is not required. If unimplemented, it should @@ -173,7 +173,7 @@ * NOTE: This method does NOT have to work concurrently with recvmsg_enable() * NOTE: The 'timeout' field is ignored * - * int query_drops(zcm_trans_t* zt, uint64_t *out_drops); + * int get_num_dropped_messages(zcm_trans_t* zt); * -------------------------------------------------------------------- * This method provides the caller access to an internal transport drop counter * Implementing this is not required. If unimplemented, it should @@ -254,7 +254,7 @@ struct zcm_trans_methods_t int (*sendmsg)(zcm_trans_t* zt, zcm_msg_t msg); int (*recvmsg_enable)(zcm_trans_t* zt, const char* channel, bool enable); int (*recvmsg)(zcm_trans_t* zt, zcm_msg_t* msg, unsigned timeout); - int (*query_drops)(zcm_trans_t *zt, uint64_t *out_drops); + int (*get_num_dropped_messages)(zcm_trans_t *zt); int (*set_queue_size)(zcm_trans_t* zt, unsigned num_messages); int (*update)(zcm_trans_t* zt); void (*destroy)(zcm_trans_t* zt); @@ -273,11 +273,11 @@ static ZCM_TRANSPORT_INLINE int zcm_trans_recvmsg_enable(zcm_trans_t* zt, const static ZCM_TRANSPORT_INLINE int zcm_trans_recvmsg(zcm_trans_t* zt, zcm_msg_t* msg, unsigned timeout) { return zt->vtbl->recvmsg(zt, msg, timeout); } -static ZCM_TRANSPORT_INLINE int zcm_trans_query_drops(zcm_trans_t* zt, uint64_t *out_drops) +static ZCM_TRANSPORT_INLINE int zcm_trans_get_num_dropped_messages(zcm_trans_t* zt) { /* Possibly unimplemented, return ZCM_EUNSUPPORTED */ - if (!zt->vtbl->query_drops) return ZCM_EUNSUPPORTED; - return zt->vtbl->query_drops(zt, out_drops); + if (!zt->vtbl->get_num_dropped_messages) return ZCM_EUNSUPPORTED; + return zt->vtbl->get_num_dropped_messages(zt); } static ZCM_TRANSPORT_INLINE int zcm_trans_set_queue_size(zcm_trans_t* zt, unsigned num_messages) diff --git a/zcm/transport/transport.cpp.template b/zcm/transport/transport.cpp.template index f25a7f369..1ea15b404 100644 --- a/zcm/transport/transport.cpp.template +++ b/zcm/transport/transport.cpp.template @@ -52,7 +52,7 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t assert(0); } - int queryDrops(uint64_t *outDrops) + int getNumDroppedMessages() { // WRITE ME assert(0); @@ -90,8 +90,8 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } - static int _queryDrops(zcm_trans_t *zt, uint64_t *outDrops) - { return cast(zt)->queryDrops(outDrops); } + static int _getNumDroppedMessages(zcm_trans_t *zt) + { return cast(zt)->getNumDroppedMessages(); } static int _setQueueSize(zcm_trans_t *zt, unsigned numMessages) { return cast(zt)->setQueueSize(numMessages); } @@ -111,7 +111,7 @@ zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { &ZCM_TRANS_CLASSNAME::_sendmsg, &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, - &ZCM_TRANS_CLASSNAME::_queryDrops, + &ZCM_TRANS_CLASSNAME::_getNumDroppedMessages, &ZCM_TRANS_CLASSNAME::_setQueueSize, &ZCM_TRANS_CLASSNAME::_update, &ZCM_TRANS_CLASSNAME::_destroy, diff --git a/zcm/transport/transport_ipcshm.cpp b/zcm/transport/transport_ipcshm.cpp index 7897355ed..959f234e4 100644 --- a/zcm/transport/transport_ipcshm.cpp +++ b/zcm/transport/transport_ipcshm.cpp @@ -302,12 +302,10 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t return ZCM_EOK; } - int queryDrops(uint64_t *outDrops) + int getNumDroppedMessages() { - if (!outDrops) return ZCM_EINVALID; uint64_t drops = lf_bcast_sub_drops(sub); - *outDrops = drops; - return ZCM_EOK; + return drops & INT_MAX; } /********************** STATICS **********************/ @@ -330,8 +328,8 @@ struct ZCM_TRANS_CLASSNAME : public zcm_trans_t static int _recvmsg(zcm_trans_t *zt, zcm_msg_t *msg, unsigned timeout) { return cast(zt)->recvmsg(msg, timeout); } - static int _queryDrops(zcm_trans_t *zt, uint64_t *outDrops) - { return cast(zt)->queryDrops(outDrops); } + static int _getNumDroppedMessages(zcm_trans_t *zt) + { return cast(zt)->getNumDroppedMessages(); } static void _destroy(zcm_trans_t *zt) { delete cast(zt); } @@ -345,7 +343,7 @@ zcm_trans_methods_t ZCM_TRANS_CLASSNAME::methods = { &ZCM_TRANS_CLASSNAME::_sendmsg, &ZCM_TRANS_CLASSNAME::_recvmsgEnable, &ZCM_TRANS_CLASSNAME::_recvmsg, - &ZCM_TRANS_CLASSNAME::_queryDrops, + &ZCM_TRANS_CLASSNAME::_getNumDroppedMessages, NULL, // set_queue_size NULL, // update &ZCM_TRANS_CLASSNAME::_destroy, diff --git a/zcm/zcm-cpp-impl.hpp b/zcm/zcm-cpp-impl.hpp index 54bc3e471..705850e12 100644 --- a/zcm/zcm-cpp-impl.hpp +++ b/zcm/zcm-cpp-impl.hpp @@ -145,9 +145,9 @@ inline int ZCM::setQueueSize(uint32_t sz) return zcm_set_queue_size(zcm, sz); } -inline int ZCM::queryDrops(uint64_t& outDrops) +inline int ZCM::getNumDroppedMessages() { - return zcm_query_drops(zcm, &outDrops); + return zcm_get_num_dropped_messages(zcm); } #ifndef ZCM_EMBEDDED diff --git a/zcm/zcm-cpp.hpp b/zcm/zcm-cpp.hpp index 173883cae..500edc9c6 100644 --- a/zcm/zcm-cpp.hpp +++ b/zcm/zcm-cpp.hpp @@ -42,7 +42,7 @@ class ZCM virtual inline void pause(); virtual inline void resume(); virtual inline int setQueueSize(unsigned sz); - virtual inline int queryDrops(uint64_t& outDrops); + virtual inline int getNumDroppedMessages(); virtual inline int writeTopology(const std::string& name); #endif virtual inline int handle(unsigned timeout); diff --git a/zcm/zcm.c b/zcm/zcm.c index 62b5ac866..40beb5b6a 100644 --- a/zcm/zcm.c +++ b/zcm/zcm.c @@ -343,17 +343,13 @@ int zcm_write_topology(zcm_t* zcm, const char* name) } #endif -int zcm_query_drops(zcm_t *zcm, uint64_t *out_drops) +int zcm_get_num_dropped_messages(zcm_t *zcm) { int ret = ZCM_EUNKNOWN; #ifndef ZCM_EMBEDDED switch (zcm->type) { - case ZCM_BLOCKING: - ret = zcm_blocking_query_drops(zcm->impl, out_drops); - break; - case ZCM_NONBLOCKING: - ret = zcm_nonblocking_query_drops(zcm->impl, out_drops); - break; + case ZCM_BLOCKING: return zcm_blocking_get_num_dropped_messages(zcm->impl); + case ZCM_NONBLOCKING: return zcm_nonblocking_get_num_dropped_messages(zcm->impl); } #else ZCM_ASSERT(zcm->type == ZCM_NONBLOCKING); diff --git a/zcm/zcm.h b/zcm/zcm.h index 5595123af..c05d16a60 100644 --- a/zcm/zcm.h +++ b/zcm/zcm.h @@ -160,10 +160,11 @@ void zcm_resume(zcm_t* zcm); int zcm_write_topology(zcm_t* zcm, const char* name); #endif -/* Query the drop counter on the underlying transport +/* Query the receive drop counter on the underlying transport + * Returns number of dropped messages on success zcm error code on failure NOTE: This may be unimplemented, in which case it will return ZCM_EUNSUPPORTED and out_drops will be disregarded. */ -int zcm_query_drops(zcm_t *zcm, uint64_t *out_drops); +int zcm_get_num_dropped_messages(zcm_t *zcm); /****************************************************************************/ /* NOT FOR GENERAL USE. USED FOR LANGUAGE-SPECIFIC BINDINGS WITH VERY */ From cdd70d3cd481061ee3ff42ac073f3f700eb67411 Mon Sep 17 00:00:00 2001 From: Jonathan Bendes Date: Wed, 27 Mar 2024 18:19:21 -0400 Subject: [PATCH 3/4] Fixed small bug --- zcm/blocking.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcm/blocking.cpp b/zcm/blocking.cpp index d0efd88c5..2b036e5a1 100644 --- a/zcm/blocking.cpp +++ b/zcm/blocking.cpp @@ -170,7 +170,7 @@ void zcm_blocking_t::stop() if (runState != RunState::RUNNING && runState != RunState::PAUSED) return; runState = RunState::STOP; pauseCond.notify_all(); - recvThread.join(); + if (recvThread.joinable()) recvThread.join(); } void zcm_blocking_t::pause() From 7f655737557fde74afc194e1352ff4c42f08589c Mon Sep 17 00:00:00 2001 From: Jonathan Bendes Date: Fri, 29 Mar 2024 16:08:59 -0400 Subject: [PATCH 4/4] Fixed spelling --- zcm/transport/lockfree/lf_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcm/transport/lockfree/lf_shm.c b/zcm/transport/lockfree/lf_shm.c index 98e64ba17..2de564d0f 100644 --- a/zcm/transport/lockfree/lf_shm.c +++ b/zcm/transport/lockfree/lf_shm.c @@ -80,7 +80,7 @@ void * lf_shm_open(const char *path, int flags, size_t * _opt_size, int *_opt_er if (flags & LF_SHM_FLAG_MLOCK) { int ret = mlock(mem, size); if (ret != 0) { - printf("MLCOK FAILED: %d (%s)\n", errno, strerror(errno)); + printf("MLOCK FAILED: %d (%s)\n", errno, strerror(errno)); munmap(mem, size); close(shm_fd); if (_opt_err) *_opt_err = LF_SHM_ERR_MLOCK;