commit f273fa2ffee434accd0dafe2c2c333d867015b89
parent 07b9a3adf4612bc368f803aa90a987a6e18c6ece
Author: Johannes Lorenz <j.git@lorenz-ho.me>
Date: Sun, 26 Jan 2025 02:46:19 +0100
Use port-checker directly without network
This removes all liblo code from PortChecker and rather lets it
communicate to MW by owning two in-process "UIs". This commit should
make the `PortChecker` test
1. run on macOS
2. more stable
3. faster
Diffstat:
1 file changed, 117 insertions(+), 20 deletions(-)
diff --git a/src/Tests/PortChecker.cpp b/src/Tests/PortChecker.cpp
@@ -1,4 +1,5 @@
#include <cassert>
+#include <queue>
#include <thread>
#include <mutex>
#include <iostream>
@@ -12,6 +13,7 @@
#include "../Misc/Master.h"
#include "../Misc/MiddleWare.h"
#include "../UI/NSM.H"
+#include "rtosc/arg-val.h"
// for linking purposes only:
NSM_Client *nsm = 0;
@@ -19,6 +21,114 @@ zyn::MiddleWare *middleware = 0;
char *instance_name=(char*)"";
+//#define DEBUG_PORT_CHECKER
+
+//! non-network implementation of port_checker::server
+//! @note This is all in one thread - thus, no mutexes etc
+class direct_server : public rtosc::port_checker::server
+{
+ zyn::MiddleWare* const mw;
+ const int gui_id;
+ std::queue<std::vector<char>> received; // inbox for MW's replies
+
+public:
+ //!< UI callback, will queue message in `received`
+ void on_recv(const char* message)
+ {
+ int len = rtosc_message_length(message, -1);
+ std::vector<char> buf(len);
+ memcpy(buf.data(), message, len);
+ received.push(std::move(buf));
+ }
+
+ //!< Fetch exactly 1 msg from `received`, may call server::handle_recv
+ void fetch_received()
+ {
+ if(!received.empty())
+ {
+ const char* message = received.front().data();
+ if(waiting && exp_paths_n_args[0].size())
+ {
+ _replied_path = 0;
+ for(std::vector<const char*>* exp_strs = exp_paths_n_args; exp_strs->size();
+ ++exp_strs, ++_replied_path)
+ if(!strcmp((*exp_strs)[0], message))
+ {
+#ifdef DEBUG_PORT_CHECKER
+ std::cout << "on_recv: match:" << std::endl;
+#endif
+ int len = rtosc_message_length(message, -1);
+
+ *last_buffer = std::vector<char>(len);
+ memcpy(last_buffer->data(), message, len);
+
+ unsigned nargs = rtosc_narguments(message);
+ std::vector<char> types(nargs);
+ for (unsigned i = 0; i < nargs; ++i)
+ types[i] = rtosc_type(message, i);
+
+ server::handle_recv(nargs, types.data(), exp_strs);
+ break;
+ }
+ }
+ received.pop();
+ }
+ }
+
+ //! Let port checker send a message to MW
+ bool send_msg(const char* address,
+ size_t nargs, const rtosc_arg_val_t* args) override
+ {
+ char buf[8192];
+ int len = rtosc_avmessage(buf, sizeof(buf), address, nargs, args);
+ if(len <= 0)
+ throw std::runtime_error("Could not serialize message");
+ mw->transmitMsgGui(gui_id, buf);
+ return true;
+ }
+
+ //! Let port checker wait until matching message was replied
+ bool _wait_for_reply(std::vector<char>* buffer,
+ std::vector<rtosc_arg_val_t> * args,
+ int n0, int n1) override
+ {
+ (void)n0;
+ // TODO: most of this function is common to liblo_server
+ // => refactor into portchecker
+ assert(n1);
+ exp_paths_n_args[n1].clear();
+
+ last_args = args;
+ last_buffer = buffer;
+
+ // allow up to 1000 recv calls = 1s
+ // if there's no reply at all after 0.5 seconds, abort
+ const int timeout_initial = timeout_msecs;
+ int tries_left = 1000, timeout = timeout_initial;
+ while(tries_left-->1 && timeout-->1 && waiting) // waiting is set in "on_recv"
+ {
+ mw->tick();
+ usleep(1000);
+ if(received.size()) {
+ fetch_received();
+ timeout = timeout_initial;
+ }
+ // message will be dispatched to the server's callback
+ // server::handle_recv will unset `waiting` if a "good" message was found
+ }
+ waiting = true; // prepare for next round
+
+ return tries_left && timeout;
+ }
+
+ void vinit(const char* ) override {} // nothing to do here
+
+ direct_server(int gui_id, int timeout_msecs, zyn::MiddleWare* mw)
+ : server(timeout_msecs)
+ , mw(mw)
+ , gui_id(gui_id) {}
+};
+
class PortChecker
{
void _masterChangedCallback(zyn::Master* m)
@@ -63,22 +173,12 @@ class PortChecker
void uiCallback0(const char* msg)
{
- (void)msg;
+ sender->on_recv(msg);
}
-
+
void uiCallback1(const char* msg)
{
- (void)msg;
- }
-
- bool exit_mw = false;
- void run_mw()
- {
- while(!exit_mw) {
- if(mw)
- mw->tick();
- usleep(20000);
- }
+ other->on_recv(msg);
}
public:
@@ -89,13 +189,12 @@ class PortChecker
{
assert(mw);
- std::thread mwthread(&PortChecker::run_mw, this);
-
bool ok;
try {
int timeout_msecs = 50;
- rtosc::liblo_server sender(timeout_msecs), other(timeout_msecs);
- rtosc::port_checker pc(&sender, &other);
+ sender = new direct_server(0, timeout_msecs, mw);
+ other = new direct_server(1, timeout_msecs, mw);
+ rtosc::port_checker pc(sender, other);
ok = pc(mw->getServerPort());
if(!pc.print_sanity_checks())
@@ -113,12 +212,9 @@ class PortChecker
}
int res = ok ? EXIT_SUCCESS : EXIT_FAILURE;
- exit_mw = true;
- mwthread.join();
return res;
}
-
void start_realtime(void)
{
do_exit = false;
@@ -159,6 +255,7 @@ class PortChecker
zyn::MiddleWare* mw;
std::thread* realtime;
bool do_exit;
+ direct_server* sender, * other;
};
int main()