|
18 | 18 | #include <rmw/rmw.h> |
19 | 19 | #include <rmw/time.h> |
20 | 20 | #include <uxr/client/core/session/session.h> |
| 21 | +#include <uxr/client/util/time.h> |
21 | 22 |
|
22 | 23 | #include "./rmw_microros_internal/utils.h" |
23 | 24 |
|
| 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 | + |
24 | 40 | rmw_ret_t |
25 | 41 | rmw_wait( |
26 | 42 | rmw_subscriptions_t * subscriptions, |
@@ -88,34 +104,58 @@ rmw_wait( |
88 | 104 |
|
89 | 105 | // Count sessions to be ran |
90 | 106 | uint8_t available_contexts = 0; |
| 107 | + uint8_t available_sessions = 0; |
91 | 108 | item = session_memory.allocateditems; |
92 | 109 | while (item != NULL) { |
93 | 110 | rmw_context_impl_t * custom_context = (rmw_context_impl_t *)item->data; |
94 | 111 | available_contexts += custom_context->need_to_be_ran ? 1 : 0; |
| 112 | + available_sessions++; |
95 | 113 | item = item->next; |
96 | 114 | } |
97 | 115 |
|
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. |
104 | 140 | item = session_memory.allocateditems; |
105 | | - while (item != NULL) { |
| 141 | + int64_t now = uxr_millis(); |
| 142 | + while (!data_available && !guard_condition_triggered && now < deadline) { |
106 | 143 | 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 | + |
107 | 148 | 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); |
109 | 154 | } |
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(); |
119 | 159 | } |
120 | 160 | } |
121 | 161 |
|
@@ -161,7 +201,7 @@ rmw_wait( |
161 | 201 | for (size_t i = 0; guard_conditions && i < guard_conditions->guard_condition_count; ++i) { |
162 | 202 | rmw_uxrce_guard_condition_t * custom_guard_condition = |
163 | 203 | (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) { |
165 | 205 | guard_conditions->guard_conditions[i] = NULL; |
166 | 206 | } else { |
167 | 207 | custom_guard_condition->hasTriggered = false; |
|
0 commit comments