Skip to content

Commit efbd887

Browse files
mergify[bot]Acuadros95David-LP99
authored
Fix timeout for no metadata run session (backport #305) (#340)
Includes cherry-picks of #305 and #342 (which fixes the CI for older distros) Signed-off-by: acuadros95 <acuadros1995@gmail.com> Signed-off-by: David Laseca Perez <davidlaseca@eprosima.com> Co-authored-by: Antonio Cuadros <acuadros1995@gmail.com> Co-authored-by: David Laseca Perez <davidlaseca@eprosima.com>
1 parent cc5d34a commit efbd887

5 files changed

Lines changed: 450 additions & 21 deletions

File tree

‎.github/workflows/ubuntu-ci.yml‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,22 +155,55 @@ jobs:
155155
156156
# The tests establish real XRCE sessions, so an Agent has to be running before colcon test starts
157157
# The Agent must not pick up the implementation under test as its own middleware
158-
RMW_IMPLEMENTATION=rmw_fastrtps_cpp ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888 -d -v4 &
158+
RMW_IMPLEMENTATION=rmw_fastrtps_cpp install/micro_ros_agent/lib/micro_ros_agent/micro_ros_agent udp4 --port 8888 -d -v4 &
159+
agent_pid=$!
159160
sleep 1
160161
161162
colcon test \
162163
--event-handlers console_direct+ \
163164
--return-code-on-test-failure \
164165
--packages-select $TESTED_PACKAGES
165166
167+
# Kill Agent so the next step can start its own Agent
168+
kill $agent_pid
169+
wait $agent_pid || true
170+
171+
- name: Ensure wait set stability (rmw_wait)
172+
# test_wait.cpp asserts on wall clock timings, so it is the part of the suite most
173+
# exposed to flakiness, so it is repeated 100 times in a random order
174+
run: |
175+
. /opt/ros/$ROS_DISTRO/setup.bash
176+
. install/local_setup.bash
177+
178+
RMW_IMPLEMENTATION=rmw_fastrtps_cpp install/micro_ros_agent/lib/micro_ros_agent/micro_ros_agent udp4 --port 8888 -d -v4 &
179+
agent_pid=$!
180+
sleep 1
181+
182+
# --gtest_shuffle reseeds on every iteration and prints the seed it used
183+
./build/rmw_microxrcedds/test/test-wait \
184+
--gtest_repeat=100 \
185+
--gtest_shuffle
186+
187+
kill $agent_pid
188+
wait $agent_pid || true
189+
166190
- name: Static memory report
167191
# Kept separate from the test step so it still runs when a test fails,
168192
# but needs the build to succeed
169193
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
170194
run: |
171195
. /opt/ros/$ROS_DISTRO/setup.bash
196+
. install/local_setup.bash
197+
198+
RMW_IMPLEMENTATION=rmw_fastrtps_cpp install/micro_ros_agent/lib/micro_ros_agent/micro_ros_agent udp4 --port 8888 -d -v4 &
199+
agent_pid=$!
200+
sleep 1
201+
172202
./build/rmw_microxrcedds/test/test-sizes 2> memanalisys_out
173203
204+
kill $agent_pid
205+
wait $agent_pid || true
206+
174207
- name: Test results
175208
if: ${{ !cancelled() }}
176209
run: |

‎rmw_microxrcedds_c/src/rmw_microros_internal/types.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,9 @@ typedef struct rmw_uxrce_guard_condition_t
252252
{
253253
rmw_uxrce_mempool_item_t mem;
254254

255-
bool hasTriggered;
255+
// Polled by rmw_wait and can be written from arbitrary threads
256+
// by rmw_trigger_guard_condition, needs 'volatile'
257+
volatile bool hasTriggered;
256258

257259
rmw_guard_condition_t rmw_guard_condition;
258260
} rmw_uxrce_guard_condition_t;

‎rmw_microxrcedds_c/src/rmw_wait.c‎

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,25 @@
1818
#include <rmw/rmw.h>
1919
#include <rmw/time.h>
2020
#include <uxr/client/core/session/session.h>
21+
#include <uxr/client/util/time.h>
2122

2223
#include "./rmw_microros_internal/utils.h"
2324

25+
#define RMW_UXRCE_MAX_SESSION_WAIT_SLICE_MS 10
26+
27+
static bool rmw_uxrce_any_guard_condition_triggered(
28+
const rmw_guard_conditions_t * guard_conditions)
29+
{
30+
for (size_t i = 0; guard_conditions && i < guard_conditions->guard_condition_count; ++i) {
31+
rmw_uxrce_guard_condition_t * custom_guard_condition =
32+
(rmw_uxrce_guard_condition_t *)guard_conditions->guard_conditions[i];
33+
if (NULL != custom_guard_condition && custom_guard_condition->hasTriggered) {
34+
return true;
35+
}
36+
}
37+
return false;
38+
}
39+
2440
rmw_ret_t
2541
rmw_wait(
2642
rmw_subscriptions_t * subscriptions,
@@ -88,34 +104,58 @@ rmw_wait(
88104

89105
// Count sessions to be ran
90106
uint8_t available_contexts = 0;
107+
uint8_t available_sessions = 0;
91108
item = session_memory.allocateditems;
92109
while (item != NULL) {
93110
rmw_context_impl_t * custom_context = (rmw_context_impl_t *)item->data;
94111
available_contexts += custom_context->need_to_be_ran ? 1 : 0;
112+
available_sessions++;
95113
item = item->next;
96114
}
97115

98-
// There is no context that contais any of the wait set entities. Nothing to wait here.
99-
if (available_contexts != 0) {
100-
int32_t per_session_timeout =
101-
(timeout.i32 == UXR_TIMEOUT_INF) ? UXR_TIMEOUT_INF :
102-
(int32_t)((float)timeout.i32 / (float)available_contexts);
103-
116+
// Ensure spinning at least once, even if the timeout is 0
117+
bool data_available = false;
118+
item = session_memory.allocateditems;
119+
while (item != NULL) {
120+
rmw_context_impl_t * custom_context = (rmw_context_impl_t *)item->data;
121+
if (custom_context->need_to_be_ran) {
122+
data_available |= uxr_run_session_until_data(&custom_context->session, 0);
123+
} else {
124+
uxr_run_session_timeout(&custom_context->session, 0);
125+
}
126+
item = item->next;
127+
}
128+
bool guard_condition_triggered = rmw_uxrce_any_guard_condition_triggered(guard_conditions);
129+
130+
if (available_sessions != 0 && !data_available && !guard_condition_triggered &&
131+
timeout.i32 != 0)
132+
{
133+
// An infinite timeout is handled as a deadline that never expires, so that guard conditions
134+
// are still polled between slices instead of blocking inside a single XRCE receive.
135+
const int64_t deadline = (timeout.i32 == UXR_TIMEOUT_INF) ?
136+
INT64_MAX : uxr_millis() + timeout.i32;
137+
138+
// Sessions are serviced round-robin in bounded slices, so that no session monopolises the
139+
// wait, guard conditions are polled between slices and the deadline is not overshot.
104140
item = session_memory.allocateditems;
105-
while (item != NULL) {
141+
int64_t now = uxr_millis();
142+
while (!data_available && !guard_condition_triggered && now < deadline) {
106143
rmw_context_impl_t * custom_context = (rmw_context_impl_t *)item->data;
144+
const int64_t remaining = deadline - now;
145+
const int32_t slice = (remaining < RMW_UXRCE_MAX_SESSION_WAIT_SLICE_MS) ?
146+
(int32_t)remaining : RMW_UXRCE_MAX_SESSION_WAIT_SLICE_MS;
147+
107148
if (custom_context->need_to_be_ran) {
108-
uxr_run_session_until_data(&custom_context->session, per_session_timeout);
149+
// If at least one context needs to be ran, the other sessions are not serviced
150+
data_available = uxr_run_session_until_data(&custom_context->session, slice);
151+
} else if (available_contexts == 0) {
152+
// No wait set entity belongs to any session, keep every session's metatraffic alive
153+
uxr_run_session_timeout(&custom_context->session, slice);
109154
}
110-
item = item->next;
111-
}
112-
} else {
113-
// Spin with no blocking to handle session metatraffic
114-
item = session_memory.allocateditems;
115-
while (item != NULL) {
116-
rmw_context_impl_t * custom_context = (rmw_context_impl_t *)item->data;
117-
uxr_run_session_timeout(&custom_context->session, 0);
118-
item = item->next;
155+
156+
guard_condition_triggered = rmw_uxrce_any_guard_condition_triggered(guard_conditions);
157+
item = (item->next == NULL) ? session_memory.allocateditems : item->next;
158+
now = uxr_millis();
119159
}
120160
}
121161

@@ -161,7 +201,7 @@ rmw_wait(
161201
for (size_t i = 0; guard_conditions && i < guard_conditions->guard_condition_count; ++i) {
162202
rmw_uxrce_guard_condition_t * custom_guard_condition =
163203
(rmw_uxrce_guard_condition_t *)guard_conditions->guard_conditions[i];
164-
if (custom_guard_condition->hasTriggered == false) {
204+
if (NULL == custom_guard_condition || custom_guard_condition->hasTriggered == false) {
165205
guard_conditions->guard_conditions[i] = NULL;
166206
} else {
167207
custom_guard_condition->hasTriggered = false;

‎rmw_microxrcedds_c/test/CMakeLists.txt‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,5 @@ rmw_test(test-reqres test_reqres.cpp)
5757
rmw_test(test-topic test_topic.cpp)
5858
rmw_test(test-rmw test_rmw.cpp)
5959
rmw_test(test-sizes test_sizes.cpp)
60-
rmw_test(test-guardcond test_guard_condition.cpp)
60+
rmw_test(test-guardcond test_guard_condition.cpp)
61+
rmw_test(test-wait test_wait.cpp)

0 commit comments

Comments
 (0)