Skip to content

Commit 703566f

Browse files
committed
impr: thread safety of bridge, monitor and hci
test: threading test rpc call
1 parent 87402b9 commit 703566f

File tree

5 files changed

+156
-14
lines changed

5 files changed

+156
-14
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# SPDX-FileCopyrightText: Copyright (C) 2025 ARDUINO SA <http://www.arduino.cc>
2+
#
3+
# SPDX-License-Identifier: MPL-2.0
4+
5+
import time
6+
from arduino.app_utils import *
7+
8+
led_state = False
9+
10+
def loopback(message):
11+
time.sleep(1)
12+
return message
13+
14+
Bridge.provide("loopback", loopback)
15+
App.run()
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#include <Arduino_RouterBridge.h>
2+
#include <zephyr/kernel.h>
3+
4+
// Thread config
5+
#define THREAD_STACK_SIZE 500
6+
#define THREAD_PRIORITY 5
7+
8+
// 2nd thread definition
9+
struct k_thread rpc_thread;
10+
k_thread_stack_t *rpc_stack_area = k_thread_stack_alloc(THREAD_STACK_SIZE, 0);
11+
12+
// RPC call and its mutex
13+
struct k_mutex mtx;
14+
15+
16+
void rpc_thread_entry(void *p1, void *p2, void *p3) {
17+
RpcCall<MsgPack::str_t> *call = reinterpret_cast<RpcCall<MsgPack::str_t>*>(p1);
18+
19+
k_sleep(K_MSEC(400));
20+
21+
Serial.println("\n*** Second Thread ***");
22+
Serial.println("*** Calling result() again...");
23+
24+
k_mutex_lock(&mtx, K_FOREVER);
25+
26+
MsgPack::str_t msg;
27+
bool ok = call->result(msg);
28+
29+
if (ok) {
30+
Serial.println("*** Second call succeeded (unexpected!)");
31+
Serial.print("**** Message: ");
32+
Serial.println(msg.c_str());
33+
} else {
34+
Serial.println("*** Second call FAILED as expected (already executed)");
35+
Serial.print("*** Error Code: 0x");
36+
Serial.println(call->getErrorCode(), HEX);
37+
Serial.print("*** Error Message: ");
38+
Serial.println(call->getErrorMessage().c_str());
39+
}
40+
41+
k_mutex_unlock(&mtx);
42+
43+
Serial.println("*** Second Thread End ***\n");
44+
}
45+
46+
47+
void setup() {
48+
Serial.begin(115200);
49+
k_sleep(K_MSEC(2000));
50+
51+
Serial.println("\n=== Threaded RPC Test ===\n");
52+
53+
Bridge.begin();
54+
Monitor.begin();
55+
56+
k_mutex_init(&mtx);
57+
58+
// ---- First result() call in main thread ----
59+
Serial.println("--- First thread waits for the other side ---");
60+
k_sleep(K_MSEC(5000));
61+
Serial.println("--- First result() call (main thread) ---");
62+
RpcCall loopback_call = Bridge.call("loopback", "TEST");
63+
64+
if (loopback_call.isError()) {
65+
Serial.println("--- Bridge call before execution");
66+
Serial.print("--- Error Code: 0x");
67+
Serial.println(loopback_call.getErrorCode(), HEX);
68+
Serial.print("--- Error Message: ");
69+
Serial.println(loopback_call.getErrorMessage().c_str());
70+
}
71+
72+
MsgPack::str_t msg;
73+
k_mutex_lock(&mtx, K_FOREVER);
74+
bool ok = loopback_call.result(msg);
75+
k_mutex_unlock(&mtx);
76+
77+
if (ok) {
78+
Serial.println("--- First call succeeded.");
79+
Serial.print("--- Message: ");
80+
Serial.println(msg.c_str());
81+
} else {
82+
Serial.println("--- First call FAILED (unexpected).");
83+
}
84+
85+
// ---- Launch second thread ----
86+
Serial.println("\n--- Starting second thread...");
87+
88+
k_tid_t rpc_tid = k_thread_create(
89+
&rpc_thread,
90+
rpc_stack_area,
91+
THREAD_STACK_SIZE,
92+
rpc_thread_entry,
93+
&loopback_call,
94+
NULL,
95+
NULL,
96+
THREAD_PRIORITY,
97+
0,
98+
K_NO_WAIT
99+
);
100+
101+
k_thread_name_set(rpc_tid, "test");
102+
103+
Serial.println("--- Second thread launched.\n");
104+
105+
//k_thread_join(rpc_tid, K_NO_WAIT); // Works in ISRs only!
106+
k_sleep(K_MSEC(5000));
107+
108+
}
109+
110+
void loop() {
111+
k_sleep(K_MSEC(5000));
112+
}

src/bridge.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ class BridgeClass {
182182

183183
if (is_started()) return true;
184184

185+
k_mutex_lock(&bridge_mutex, K_FOREVER);
186+
185187
serial_ptr->begin(baud);
186188
transport = new SerialTransport(*serial_ptr);
187189

@@ -196,7 +198,6 @@ class BridgeClass {
196198
UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT);
197199
k_thread_name_set(upd_tid, "bridge");
198200

199-
k_mutex_lock(&bridge_mutex, K_FOREVER);
200201
bool res = false;
201202
started = call(RESET_METHOD).result(res) && res;
202203
k_mutex_unlock(&bridge_mutex);

src/hci.h

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
5353
bool begin(const char *device = "hci0") {
5454
k_mutex_init(&hci_mutex);
5555

56+
k_mutex_lock(&hci_mutex, K_FOREVER);
5657
// Pre-allocate recv buffer to avoid allocations during recv calls
5758
recv_buffer.reserve(BufferSize);
5859

5960
if (!(*bridge) && !bridge->begin()) {
61+
k_mutex_unlock(&hci_mutex);
6062
return false;
6163
}
6264

@@ -65,16 +67,18 @@ template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
6567
initialized = result;
6668
}
6769

68-
return initialized;
70+
k_mutex_unlock(&hci_mutex);
71+
return result;
6972
}
7073

7174
void end() {
75+
k_mutex_lock(&hci_mutex, K_FOREVER);
76+
7277
if (!initialized) {
78+
k_mutex_unlock(&hci_mutex);
7379
return;
7480
}
7581

76-
k_mutex_lock(&hci_mutex, K_FOREVER);
77-
7882
bool result;
7983
bridge->call(HCI_CLOSE_METHOD).result(result);
8084
initialized = false;
@@ -83,16 +87,20 @@ template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
8387
}
8488

8589
explicit operator bool() const {
86-
return initialized;
90+
k_mutex_lock(&hci_mutex, K_FOREVER);
91+
bool out = initialized;
92+
k_mutex_unlock(&hci_mutex);
93+
return out;
8794
}
8895

8996
int send(const uint8_t *buffer, size_t size) {
97+
k_mutex_lock(&hci_mutex, K_FOREVER);
98+
9099
if (!initialized) {
100+
k_mutex_unlock(&hci_mutex);
91101
return -1;
92102
}
93103

94-
k_mutex_lock(&hci_mutex, K_FOREVER);
95-
96104
BinaryView send_buffer(buffer, size);
97105
size_t bytes_sent;
98106
const bool ret = bridge->call(HCI_SEND_METHOD, send_buffer).result(bytes_sent);
@@ -106,12 +114,13 @@ template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
106114
}
107115

108116
int recv(uint8_t *buffer, size_t max_size) {
117+
k_mutex_lock(&hci_mutex, K_FOREVER);
118+
109119
if (!initialized) {
120+
k_mutex_unlock(&hci_mutex);
110121
return -1;
111122
}
112123

113-
k_mutex_lock(&hci_mutex, K_FOREVER);
114-
115124
recv_buffer.clear();
116125
bool ret = bridge->call(HCI_RECV_METHOD, max_size).result(recv_buffer);
117126

@@ -130,12 +139,14 @@ template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
130139
}
131140

132141
int available() {
142+
143+
k_mutex_lock(&hci_mutex, K_FOREVER);
144+
133145
if (!initialized) {
146+
k_mutex_unlock(&hci_mutex);
134147
return 0;
135148
}
136149

137-
k_mutex_lock(&hci_mutex, K_FOREVER);
138-
139150
bool result;
140151
bool ret = bridge->call(HCI_AVAIL_METHOD).result(result);
141152

src/monitor.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,17 @@ class BridgeMonitor: public Stream {
4242

4343
if (is_connected()) return true;
4444

45+
k_mutex_lock(&monitor_mutex, K_FOREVER);
4546
bool bridge_started = (*bridge);
4647
if (!bridge_started) {
4748
bridge_started = bridge->begin();
4849
}
4950

50-
if (!bridge_started) return false;
51+
if (!bridge_started) {
52+
k_mutex_unlock(&monitor_mutex);
53+
return false;
54+
}
5155

52-
k_mutex_lock(&monitor_mutex, K_FOREVER);
5356
bool out = false;
5457
_connected = bridge->call(MON_CONNECTED_METHOD).result(out) && out;
5558
k_mutex_unlock(&monitor_mutex);
@@ -121,9 +124,9 @@ class BridgeMonitor: public Stream {
121124
}
122125

123126
bool reset() {
127+
k_mutex_lock(&monitor_mutex, K_FOREVER);
124128
bool res;
125129
bool ok = bridge->call(MON_RESET_METHOD).result(res) && res;
126-
k_mutex_lock(&monitor_mutex, K_FOREVER);
127130
_connected = !ok;
128131
k_mutex_unlock(&monitor_mutex);
129132
return ok;

0 commit comments

Comments
 (0)