diff --git a/README.md b/README.md index 8bebe6c..f6f2d3d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ In this repo it will be implemented an Arduino library wrapper for RPClite to be Including Arduino_RouterBridge.h gives the user access to a Bridge object that can be used both as a RPC client and/or server to execute and serve RPCs to/from the CPU Host running a GOLANG router. - The Bridge object is pre-defined on Serial1 and automatically initialized inside the main setup() -- The Bridge.call method is non-blocking and returns a RpcResult async object -- RpcResult class implements a blocking .result method that waits for the RPC response and returns true if the RPC returned with no errors +- The Bridge.call method is non-blocking and returns a RpcCall async object +- RpcCall class implements a blocking .result method that waits for the RPC response and returns true if the RPC returned with no errors +- The RpcCall.result will return - by reference - the result value of that call *exactly once*. Subsequent calls to .result will return an error condition - The Bridge can provide callbacks to incoming RPC requests both in a thread-unsafe and thread-safe fashion (provide & provide_safe) - Thread-safe methods execution is granted in the main loop thread where update_safe is called. By design users cannot access .update_safe() freely - Thread-unsafe methods are served in an update callback, whose execution is granted in a separate thread. Nonetheless users can access .update() freely with caution @@ -52,7 +53,7 @@ void loop() { }; // Async call - RpcResult async_rpc = Bridge.call("add", 3.0, 4.5); + RpcCall async_rpc = Bridge.call("add", 3.0, 4.5); if (!async_rpc.result(sum)) { Monitor.println("Error calling method: add"); Monitor.print("Error code: "); diff --git a/examples/simple_bridge/simple_bridge.ino b/examples/simple_bridge/simple_bridge.ino index 973aeec..508098f 100644 --- a/examples/simple_bridge/simple_bridge.ino +++ b/examples/simple_bridge/simple_bridge.ino @@ -52,7 +52,7 @@ void loop() { }; // Call with deferred response check - RpcResult outcome = Bridge.call("multiply", 5.0, 7.0); + RpcCall outcome = Bridge.call("multiply", 5.0, 7.0); Serial.println("RPC called"); delay(10); if (outcome.result(res)) { diff --git a/examples/test/test.ino b/examples/test/test.ino index 3e3b459..619bfaa 100644 --- a/examples/test/test.ino +++ b/examples/test/test.ino @@ -60,7 +60,7 @@ void loop() { } float pow; - RpcResult async_res = Bridge.call("1_args_float_result", 2.0, 3.0); // passing 2 args to a function expecting 1 + RpcCall async_res = Bridge.call("1_args_float_result", 2.0, 3.0); // passing 2 args to a function expecting 1 if (async_res.result(pow)) { Monitor.println("Result of assignment and then result: "+String(pow)); // returns true, so the right result } else { @@ -69,7 +69,7 @@ void loop() { } float div = 0; - RpcResult async_res1 = Bridge.call("2_args_float_result", 2.0); // passing 1 arg when 2 are expected + RpcCall async_res1 = Bridge.call("2_args_float_result", 2.0); // passing 1 arg when 2 are expected if (async_res1.result(div)) { Monitor.println("Result of assignment and then result: "+String(div)); // returns true, so the right result } else { @@ -78,7 +78,7 @@ void loop() { } div = 0; - RpcResult async_res2 = Bridge.call("2_args_float_result", 2.0, "invalid"); // passing a wrong type arg + RpcCall async_res2 = Bridge.call("2_args_float_result", 2.0, "invalid"); // passing a wrong type arg if (async_res2.result(div)) { Monitor.println("Result of assignment and then result: "+String(div)); // returns true, so the right result } else { @@ -87,7 +87,7 @@ void loop() { } x = false; - RpcResult async_res3 = Bridge.call("0_args_bool_result"); + RpcCall async_res3 = Bridge.call("0_args_bool_result"); if (async_res3.result(x)) { Monitor.println("Result of assignment and then result: "+String(x)); // returns true, so the right result } else { @@ -96,7 +96,7 @@ void loop() { // Avoid the following: // the call happens in the destructor falling back to the "no_result" case (a type mismatch here) - RpcResult async_res4 = Bridge.call("0_args_bool_result"); + RpcCall async_res4 = Bridge.call("0_args_bool_result"); delay(1000); } \ No newline at end of file diff --git a/src/bridge.h b/src/bridge.h index 81f4ca4..0a86ace 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -24,21 +24,27 @@ #define DEFAULT_SERIAL_BAUD 115200 #include +#include #include void updateEntryPoint(void *, void *, void *); template -class RpcResult { - +class RpcCall { public: - RpcError error; + RpcError error{GENERIC_ERR, "This call is not executed yet"}; - RpcResult(const MsgPack::str_t& m, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, Args&&... args): method(m), client(c), read_mutex(rm), write_mutex(wm), callback_params(std::forward_as_tuple(std::forward(args)...)) {} + RpcCall(const MsgPack::str_t& m, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, Args&&... args): method(m), client(c), read_mutex(rm), write_mutex(wm), callback_params(std::forward_as_tuple(std::forward(args)...)) {} template bool result(RType& result) { - if (_executed) return error.code == NO_ERR; + + if (!atomic_cas(&_executed, 0, 1)){ + // this thread lost the race + error.code = GENERIC_ERR; + error.traceback = "This call result is no longer available"; + return false; + } while (true) { if (k_mutex_lock(write_mutex, K_MSEC(10)) == 0) { @@ -69,7 +75,7 @@ class RpcResult { k_yield(); } } - _executed = true; + return error.code == NO_ERR; } @@ -78,7 +84,7 @@ class RpcResult { return result(nil); } - ~RpcResult(){ + ~RpcCall(){ result(); } @@ -88,7 +94,7 @@ class RpcResult { private: uint32_t msg_id_wait{}; - bool _executed = false; + atomic_t _executed = ATOMIC_INIT(0);; MsgPack::str_t method; RPCClient* client; @@ -210,8 +216,8 @@ class BridgeClass { } template - RpcResult call(const MsgPack::str_t& method, Args&&... args) { - return RpcResult(method, client, &read_mutex, &write_mutex, std::forward(args)...); + RpcCall call(const MsgPack::str_t& method, Args&&... args) { + return RpcCall(method, client, &read_mutex, &write_mutex, std::forward(args)...); } template diff --git a/src/monitor.h b/src/monitor.h index 02b9d16..574d5f7 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -137,7 +137,7 @@ class BridgeMonitor: public Stream { k_mutex_lock(&monitor_mutex, K_FOREVER); MsgPack::arr_t message; - RpcResult async_rpc = bridge->call(MON_READ_METHOD, size); + RpcCall async_rpc = bridge->call(MON_READ_METHOD, size); const bool ret = _connected && async_rpc.result(message); if (ret) { diff --git a/src/tcp_client.h b/src/tcp_client.h index 5931827..8421d41 100644 --- a/src/tcp_client.h +++ b/src/tcp_client.h @@ -179,7 +179,7 @@ class BridgeTCPClient : public Client { k_mutex_lock(&client_mutex, K_FOREVER); MsgPack::arr_t message; - RpcResult async_rpc = bridge->call(TCP_READ_METHOD, connection_id, size); + RpcCall async_rpc = bridge->call(TCP_READ_METHOD, connection_id, size); const bool ret = _connected && async_rpc.result(message); if (ret) {