ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
wait.c
1 // Copyright 2015 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifdef __cplusplus
16 extern "C"
17 {
18 #endif
19 
20 #include "rcl/wait.h"
21 
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <string.h>
25 
26 #include "rcl/allocator.h"
27 #include "rcl/error_handling.h"
28 #include "rcl/time.h"
29 #include "rcl/types.h"
30 #include "rcutils/logging_macros.h"
31 #include "rmw/error_handling.h"
32 #include "rmw/ret_types.h"
33 #include "rmw/rmw.h"
34 #include "rmw/time.h"
35 #include "rmw/types.h"
36 
37 #include "./context_impl.h"
38 #include "./client_impl.h"
39 #include "./guard_condition_impl.h"
40 #include "./service_impl.h"
41 #include "./subscription_impl.h"
42 #include "./timer_impl.h"
43 
45 {
46  // number of subscriptions that have been added to the wait set
47  size_t subscription_index;
48  rmw_subscriptions_t rmw_subscriptions;
49  // number of guard_conditions that have been added to the wait set
50  size_t guard_condition_index;
51  rmw_guard_conditions_t rmw_guard_conditions;
52  // number of clients that have been added to the wait set
53  size_t client_index;
54  rmw_clients_t rmw_clients;
55  // number of services that have been added to the wait set
56  size_t service_index;
57  rmw_services_t rmw_services;
58  // number of events that have been added to the wait set
59  size_t event_index;
60  rmw_events_t rmw_events;
61 
62  rmw_wait_set_t * rmw_wait_set;
63  // number of timers that have been added to the wait set
64  size_t timer_index;
65  // context with which the wait set is associated
66  rcl_context_t * context;
67  // allocator used in the wait set
68  rcl_allocator_t allocator;
69 };
70 
73 {
74  // All members are initialized to 0 or NULL by C99 6.7.8/10.
75  static rcl_wait_set_t null_wait_set;
76  return null_wait_set;
77 }
78 
79 bool
81 {
82  return wait_set && wait_set->impl;
83 }
84 
87  rcl_wait_set_t * wait_set,
88  size_t number_of_subscriptions,
89  size_t number_of_guard_conditions,
90  size_t number_of_timers,
91  size_t number_of_clients,
92  size_t number_of_services,
93  size_t number_of_events,
94  rcl_context_t * context,
95  rcl_allocator_t allocator)
96 {
97  RCUTILS_LOG_DEBUG_NAMED(
98  ROS_PACKAGE_NAME, "Initializing wait set with "
99  "'%zu' subscriptions, '%zu' guard conditions, '%zu' timers, '%zu' clients, '%zu' services",
100  number_of_subscriptions, number_of_guard_conditions, number_of_timers, number_of_clients,
101  number_of_services);
102  rcl_ret_t rcl_ret = RCL_RET_ERROR;
103 
104  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
105  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT);
106  if (rcl_wait_set_is_valid(wait_set)) {
107  RCL_SET_ERROR_MSG("wait_set already initialized, or memory was uninitialized.");
108  return RCL_RET_ALREADY_INIT;
109  }
110  // Make sure rcl has been initialized.
111  RCL_CHECK_ARGUMENT_FOR_NULL(context, RCL_RET_INVALID_ARGUMENT);
112  if (!rcl_context_is_valid(context)) {
113  RCL_SET_ERROR_MSG(
114  "the given context is not valid, "
115  "either rcl_init() was not called or rcl_shutdown() was called.");
116  return RCL_RET_NOT_INIT;
117  }
118  // Allocate space for the implementation struct.
119  wait_set->impl = (rcl_wait_set_impl_t *)allocator.allocate(
120  sizeof(rcl_wait_set_impl_t), allocator.state);
121  RCL_CHECK_FOR_NULL_WITH_MSG(
122  wait_set->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC);
123  memset(wait_set->impl, 0, sizeof(rcl_wait_set_impl_t));
124  wait_set->impl->rmw_subscriptions.subscribers = NULL;
125  wait_set->impl->rmw_subscriptions.subscriber_count = 0;
126  wait_set->impl->rmw_guard_conditions.guard_conditions = NULL;
127  wait_set->impl->rmw_guard_conditions.guard_condition_count = 0;
128  wait_set->impl->rmw_clients.clients = NULL;
129  wait_set->impl->rmw_clients.client_count = 0;
130  wait_set->impl->rmw_services.services = NULL;
131  wait_set->impl->rmw_services.service_count = 0;
132  wait_set->impl->rmw_events.events = NULL;
133  wait_set->impl->rmw_events.event_count = 0;
134  // Set context.
135  wait_set->impl->context = context;
136  // Set allocator.
137  wait_set->impl->allocator = allocator;
138 
139  size_t num_conditions =
140  (2 * number_of_subscriptions) +
141  number_of_guard_conditions +
142  number_of_clients +
143  number_of_services +
144  number_of_events;
145 
146  wait_set->impl->rmw_wait_set = rmw_create_wait_set(&(context->impl->rmw_context), num_conditions);
147  if (!wait_set->impl->rmw_wait_set) {
148  rcl_ret = RCL_RET_BAD_ALLOC;
149  goto fail;
150  }
151 
152  // Initialize subscription space.
153  rcl_ret = rcl_wait_set_resize(
154  wait_set, number_of_subscriptions, number_of_guard_conditions, number_of_timers,
155  number_of_clients, number_of_services, number_of_events);
156  if (RCL_RET_OK != rcl_ret) {
157  goto fail;
158  }
159  return RCL_RET_OK;
160 
161 fail:
162  if (wait_set->impl->rmw_wait_set != NULL) {
163  rmw_ret_t rmw_ret = rmw_destroy_wait_set(wait_set->impl->rmw_wait_set);
164  if (rmw_ret != RMW_RET_OK) {
165  rcl_ret = RCL_RET_WAIT_SET_INVALID;
166  }
167  }
168  allocator.deallocate(wait_set->impl, wait_set->impl->allocator.state);
169  wait_set->impl = NULL;
170 
171  return rcl_ret;
172 }
173 
174 rcl_ret_t
176 {
177  rcl_ret_t result = RCL_RET_OK;
178  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT);
179 
180  if (rcl_wait_set_is_valid(wait_set)) {
181  rmw_ret_t ret = rmw_destroy_wait_set(wait_set->impl->rmw_wait_set);
182  if (ret != RMW_RET_OK) {
183  RCL_SET_ERROR_MSG(rmw_get_error_string().str);
184  result = RCL_RET_WAIT_SET_INVALID;
185  }
186 
187  rcl_ret_t resize_result = rcl_wait_set_resize(wait_set, 0, 0, 0, 0, 0, 0);
188  if (result == RCL_RET_OK) {
189  // Only return the error here if we had no earlier errors.
190  result = resize_result;
191  }
192  if (wait_set->impl) {
193  wait_set->impl->allocator.deallocate(wait_set->impl, wait_set->impl->allocator.state);
194  wait_set->impl = NULL;
195  }
196  }
197  return result;
198 }
199 
200 rcl_ret_t
202 {
203  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT);
204  if (!rcl_wait_set_is_valid(wait_set)) {
205  RCL_SET_ERROR_MSG("wait set is invalid");
207  }
208  RCL_CHECK_ARGUMENT_FOR_NULL(allocator, RCL_RET_INVALID_ARGUMENT);
209  *allocator = wait_set->impl->allocator;
210  return RCL_RET_OK;
211 }
212 
213 #define SET_ADD(Type) \
214  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT); \
215  if (!wait_set->impl) { \
216  RCL_SET_ERROR_MSG("wait set is invalid"); \
217  return RCL_RET_WAIT_SET_INVALID; \
218  } \
219  RCL_CHECK_ARGUMENT_FOR_NULL(Type, RCL_RET_INVALID_ARGUMENT); \
220  if (!(wait_set->impl->Type ## _index < wait_set->size_of_ ## Type ## s)) { \
221  RCL_SET_ERROR_MSG(#Type "s set is full"); \
222  return RCL_RET_WAIT_SET_FULL; \
223  } \
224  size_t current_index = wait_set->impl->Type ## _index++; \
225  wait_set->Type ## s[current_index] = Type; \
226  /* Set optional output argument */ \
227  if (NULL != index) { \
228  *index = current_index; \
229  }
230 
231 #define SET_ADD_RMW(Type, RMWStorage, RMWCount) \
232  /* Also place into rmw storage. */ \
233  rmw_ ## Type ## _t * rmw_handle = rcl_ ## Type ## _get_rmw_handle(Type); \
234  RCL_CHECK_FOR_NULL_WITH_MSG( \
235  rmw_handle, rcl_get_error_string().str, return RCL_RET_ERROR); \
236  wait_set->impl->RMWStorage[current_index] = rmw_handle->data; \
237  wait_set->impl->RMWCount++;
238 
239 #define SET_CLEAR(Type) \
240  do { \
241  if (NULL != wait_set->Type ## s) { \
242  memset( \
243  (void *)wait_set->Type ## s, \
244  0, \
245  sizeof(rcl_ ## Type ## _t *) * wait_set->size_of_ ## Type ## s); \
246  wait_set->impl->Type ## _index = 0; \
247  } \
248  } while (false)
249 
250 #define SET_CLEAR_RMW(Type, RMWStorage, RMWCount) \
251  do { \
252  if (NULL != wait_set->impl->RMWStorage) { \
253  /* Also clear the rmw storage. */ \
254  memset( \
255  wait_set->impl->RMWStorage, \
256  0, \
257  sizeof(void *) * wait_set->impl->RMWCount); \
258  wait_set->impl->RMWCount = 0; \
259  } \
260  } while (false)
261 
262 #define SET_RESIZE(Type, ExtraDealloc, ExtraRealloc) \
263  do { \
264  rcl_allocator_t allocator = wait_set->impl->allocator; \
265  wait_set->size_of_ ## Type ## s = 0; \
266  wait_set->impl->Type ## _index = 0; \
267  if (0 == Type ## s_size) { \
268  if (wait_set->Type ## s) { \
269  allocator.deallocate((void *)wait_set->Type ## s, allocator.state); \
270  wait_set->Type ## s = NULL; \
271  } \
272  ExtraDealloc \
273  } else { \
274  wait_set->Type ## s = (const rcl_ ## Type ## _t **)allocator.reallocate( \
275  (void *)wait_set->Type ## s, sizeof(rcl_ ## Type ## _t *) * Type ## s_size, \
276  allocator.state); \
277  RCL_CHECK_FOR_NULL_WITH_MSG( \
278  wait_set->Type ## s, "allocating memory failed", return RCL_RET_BAD_ALLOC); \
279  memset((void *)wait_set->Type ## s, 0, sizeof(rcl_ ## Type ## _t *) * Type ## s_size); \
280  wait_set->size_of_ ## Type ## s = Type ## s_size; \
281  ExtraRealloc \
282  } \
283  } while (false)
284 
285 #define SET_RESIZE_RMW_DEALLOC(RMWStorage, RMWCount) \
286  /* Also deallocate the rmw storage. */ \
287  if (wait_set->impl->RMWStorage) { \
288  allocator.deallocate((void *)wait_set->impl->RMWStorage, allocator.state); \
289  wait_set->impl->RMWStorage = NULL; \
290  wait_set->impl->RMWCount = 0; \
291  }
292 
293 #define SET_RESIZE_RMW_REALLOC(Type, RMWStorage, RMWCount) \
294  /* Also resize the rmw storage. */ \
295  wait_set->impl->RMWCount = 0; \
296  wait_set->impl->RMWStorage = (void **)allocator.reallocate( \
297  wait_set->impl->RMWStorage, sizeof(void *) * Type ## s_size, allocator.state); \
298  if (!wait_set->impl->RMWStorage) { \
299  allocator.deallocate((void *)wait_set->Type ## s, allocator.state); \
300  wait_set->Type ## s = NULL; \
301  wait_set->size_of_ ## Type ## s = 0; \
302  RCL_SET_ERROR_MSG("allocating memory failed"); \
303  return RCL_RET_BAD_ALLOC; \
304  } \
305  memset(wait_set->impl->RMWStorage, 0, sizeof(void *) * Type ## s_size);
306 
307 /*
308  * Ensure that the waitset lists do not contain duplicated entries.
309  * For example, considering `wait_set->clients`:
310  * - first we set `in_use_by_waitset` to false for each entry.
311  * - then we loop again setting `in_use_by_waitset` to true one by one
312  * - if we find an entry where `in_use_by_waitset` was already true, it
313  * means that it was present twice in the waitset and we return an error
314 */
315 #define CHECK_DOUBLE_USAGE(Type) \
316  for (size_t idx = 0; idx < wait_set->size_of_ ## Type ## s; idx++) { \
317  if (wait_set->Type ## s[idx]) { \
318  wait_set->Type ## s[idx]->impl->in_use_by_waitset = false; \
319  } \
320  } \
321  for (size_t idx = 0; idx < wait_set->size_of_ ## Type ## s; idx++) { \
322  if (wait_set->Type ## s[idx]) { \
323  if(wait_set->Type ## s[idx]->impl->in_use_by_waitset) { \
324  RCL_SET_ERROR_MSG("Entitiy of type " #Type " added multiple times to waitset."); \
325  return RCL_RET_WAIT_SET_INVALID; \
326  } \
327  wait_set->Type ## s[idx]->impl->in_use_by_waitset = true; \
328  } \
329  }
330 
331 /* Implementation-specific notes:
332  *
333  * Add the rmw representation to the underlying rmw array and increment
334  * the rmw array count.
335  */
336 rcl_ret_t
338  rcl_wait_set_t * wait_set,
339  const rcl_subscription_t * subscription,
340  size_t * index)
341 {
342  SET_ADD(subscription)
343  SET_ADD_RMW(subscription, rmw_subscriptions.subscribers, rmw_subscriptions.subscriber_count)
344  return RCL_RET_OK;
345 }
346 
347 /* Implementation-specific notes:
348  *
349  * Sets all of the entries in the underlying rmw array to null, and sets the
350  * count in the rmw array to 0.
351  */
352 rcl_ret_t
354 {
355  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT);
356  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set->impl, RCL_RET_WAIT_SET_INVALID);
357 
358  SET_CLEAR(subscription);
359  SET_CLEAR(guard_condition);
360  SET_CLEAR(client);
361  SET_CLEAR(service);
362  SET_CLEAR(event);
363  SET_CLEAR(timer);
364 
365  SET_CLEAR_RMW(
366  subscription,
367  rmw_subscriptions.subscribers,
368  rmw_subscriptions.subscriber_count);
369  SET_CLEAR_RMW(
370  guard_condition,
371  rmw_guard_conditions.guard_conditions,
372  rmw_guard_conditions.guard_condition_count);
373  SET_CLEAR_RMW(
374  clients,
375  rmw_clients.clients,
376  rmw_clients.client_count);
377  SET_CLEAR_RMW(
378  services,
379  rmw_services.services,
380  rmw_services.service_count);
381  SET_CLEAR_RMW(
382  events,
383  rmw_events.events,
384  rmw_events.event_count);
385 
386  return RCL_RET_OK;
387 }
388 
389 /* Implementation-specific notes:
390  *
391  * Similarly, the underlying rmw representation is reallocated and reset:
392  * all entries are set to null and the count is set to zero.
393  */
394 rcl_ret_t
396  rcl_wait_set_t * wait_set,
397  size_t subscriptions_size,
398  size_t guard_conditions_size,
399  size_t timers_size,
400  size_t clients_size,
401  size_t services_size,
402  size_t events_size)
403 {
404  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT);
405  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set->impl, RCL_RET_WAIT_SET_INVALID);
406  SET_RESIZE(
407  subscription,
408  SET_RESIZE_RMW_DEALLOC(
409  rmw_subscriptions.subscribers, rmw_subscriptions.subscriber_count),
410  SET_RESIZE_RMW_REALLOC(
411  subscription, rmw_subscriptions.subscribers, rmw_subscriptions.subscriber_count)
412  );
413  // Guard condition RCL size is the resize amount given
414  SET_RESIZE(guard_condition,;,;); // NOLINT
415 
416  // Guard condition RMW size needs to be guard conditions + timers
417  rmw_guard_conditions_t * rmw_gcs = &(wait_set->impl->rmw_guard_conditions);
418  const size_t num_rmw_gc = guard_conditions_size + timers_size;
419  // Clear added guard conditions
420  rmw_gcs->guard_condition_count = 0u;
421  if (0u == num_rmw_gc) {
422  if (rmw_gcs->guard_conditions) {
423  wait_set->impl->allocator.deallocate(
424  (void *)rmw_gcs->guard_conditions, wait_set->impl->allocator.state);
425  rmw_gcs->guard_conditions = NULL;
426  }
427  } else {
428  rmw_gcs->guard_conditions = (void **)wait_set->impl->allocator.reallocate(
429  rmw_gcs->guard_conditions, sizeof(void *) * num_rmw_gc, wait_set->impl->allocator.state);
430  if (!rmw_gcs->guard_conditions) {
431  // Deallocate rcl arrays to match unallocated rmw guard conditions
432  wait_set->impl->allocator.deallocate(
433  (void *)wait_set->guard_conditions, wait_set->impl->allocator.state);
434  wait_set->size_of_guard_conditions = 0u;
435  wait_set->guard_conditions = NULL;
436  wait_set->impl->allocator.deallocate(
437  (void *)wait_set->timers, wait_set->impl->allocator.state);
438  wait_set->size_of_timers = 0u;
439  wait_set->timers = NULL;
440  RCL_SET_ERROR_MSG("allocating memory failed");
441  return RCL_RET_BAD_ALLOC;
442  }
443  memset(rmw_gcs->guard_conditions, 0, sizeof(void *) * num_rmw_gc);
444  }
445 
446  SET_RESIZE(timer,;,;); // NOLINT
447  SET_RESIZE(
448  client,
449  SET_RESIZE_RMW_DEALLOC(
450  rmw_clients.clients, rmw_clients.client_count),
451  SET_RESIZE_RMW_REALLOC(
452  client, rmw_clients.clients, rmw_clients.client_count)
453  );
454  SET_RESIZE(
455  service,
456  SET_RESIZE_RMW_DEALLOC(
457  rmw_services.services, rmw_services.service_count),
458  SET_RESIZE_RMW_REALLOC(
459  service, rmw_services.services, rmw_services.service_count)
460  );
461  SET_RESIZE(
462  event,
463  SET_RESIZE_RMW_DEALLOC(
464  rmw_events.events, rmw_events.event_count),
465  SET_RESIZE_RMW_REALLOC(
466  event, rmw_events.events, rmw_events.event_count)
467  );
468 
469  return RCL_RET_OK;
470 }
471 
472 rcl_ret_t
474  rcl_wait_set_t * wait_set,
475  const rcl_guard_condition_t * guard_condition,
476  size_t * index)
477 {
478  SET_ADD(guard_condition)
479  SET_ADD_RMW(
480  guard_condition, rmw_guard_conditions.guard_conditions,
481  rmw_guard_conditions.guard_condition_count)
482 
483  return RCL_RET_OK;
484 }
485 
486 rcl_ret_t
488  rcl_wait_set_t * wait_set,
489  const rcl_timer_t * timer,
490  size_t * index)
491 {
492  SET_ADD(timer)
493  // Add timer guard conditions to end of rmw guard condtion set.
494  rcl_guard_condition_t * guard_condition = rcl_timer_get_guard_condition(timer);
495  if (NULL != guard_condition) {
496  // rcl_wait() will take care of moving these backwards and setting guard_condition_count.
497  const size_t index = wait_set->size_of_guard_conditions + (wait_set->impl->timer_index - 1);
498  rmw_guard_condition_t * rmw_handle = rcl_guard_condition_get_rmw_handle(guard_condition);
499  RCL_CHECK_FOR_NULL_WITH_MSG(
500  rmw_handle, rcl_get_error_string().str, return RCL_RET_ERROR);
501  wait_set->impl->rmw_guard_conditions.guard_conditions[index] = rmw_handle->data;
502  }
503  return RCL_RET_OK;
504 }
505 
506 rcl_ret_t
508  rcl_wait_set_t * wait_set,
509  const rcl_client_t * client,
510  size_t * index)
511 {
512  SET_ADD(client)
513  SET_ADD_RMW(client, rmw_clients.clients, rmw_clients.client_count)
514  return RCL_RET_OK;
515 }
516 
517 rcl_ret_t
519  rcl_wait_set_t * wait_set,
520  const rcl_service_t * service,
521  size_t * index)
522 {
523  SET_ADD(service)
524  SET_ADD_RMW(service, rmw_services.services, rmw_services.service_count)
525  return RCL_RET_OK;
526 }
527 
528 rcl_ret_t
530  rcl_wait_set_t * wait_set,
531  const rcl_event_t * event,
532  size_t * index)
533 {
534  SET_ADD(event)
535  SET_ADD_RMW(event, rmw_events.events, rmw_events.event_count)
536  wait_set->impl->rmw_events.events[current_index] = rmw_handle;
537  return RCL_RET_OK;
538 }
539 
540 rcl_ret_t
541 rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
542 {
543  RCL_CHECK_ARGUMENT_FOR_NULL(wait_set, RCL_RET_INVALID_ARGUMENT);
544  if (!rcl_wait_set_is_valid(wait_set)) {
545  RCL_SET_ERROR_MSG("wait set is invalid");
547  }
548  if (
549  wait_set->size_of_subscriptions == 0 &&
550  wait_set->size_of_guard_conditions == 0 &&
551  wait_set->size_of_timers == 0 &&
552  wait_set->size_of_clients == 0 &&
553  wait_set->size_of_services == 0 &&
554  wait_set->size_of_events == 0)
555  {
556  RCL_SET_ERROR_MSG("wait set is empty");
557  return RCL_RET_WAIT_SET_EMPTY;
558  }
559 
560  CHECK_DOUBLE_USAGE(client);
561  CHECK_DOUBLE_USAGE(guard_condition);
562  CHECK_DOUBLE_USAGE(service);
563  CHECK_DOUBLE_USAGE(subscription);
564  CHECK_DOUBLE_USAGE(timer);
565 
566  // Calculate the timeout argument.
567  // By default, set the timer to block indefinitely if none of the below conditions are met.
568  rmw_time_t * timeout_argument = NULL;
569  rmw_time_t temporary_timeout_storage;
570  bool is_non_blocking = timeout == 0;
571 
572  for (uint64_t t_idx = 0; t_idx < wait_set->impl->timer_index; ++t_idx) {
573  if (!wait_set->timers[t_idx]) {
574  continue; // Skip NULL timers.
575  }
576  rmw_guard_conditions_t * rmw_gcs = &(wait_set->impl->rmw_guard_conditions);
577  size_t gc_idx = wait_set->size_of_guard_conditions + t_idx;
578  if (NULL != rmw_gcs->guard_conditions[gc_idx]) {
579  // This timer has a guard condition, so move it to make a legal wait set.
580  rmw_gcs->guard_conditions[rmw_gcs->guard_condition_count] =
581  rmw_gcs->guard_conditions[gc_idx];
582  ++(rmw_gcs->guard_condition_count);
583  }
584  }
585 
586  int64_t min_next_call_time[RCL_STEADY_TIME + 1];
587  rcl_clock_t * clocks[RCL_STEADY_TIME + 1] = {NULL, NULL, NULL, NULL};
588 
589  // asserts to make sure nobody changes the ordering of RCL_ROS_TIME,
590  // RCL_SYSTEM_TIME and RCL_STEADY_TIME
591  static_assert(RCL_ROS_TIME < RCL_STEADY_TIME + 1, "RCL_ROS_TIME won't fit in the array");
592  static_assert(RCL_SYSTEM_TIME < RCL_STEADY_TIME + 1, "RCL_SYSTEM_TIME won't fit in the array");
593  static_assert(RCL_STEADY_TIME < RCL_STEADY_TIME + 1, "RCL_STEADY_TIME won't fit in the array");
594 
595  min_next_call_time[RCL_ROS_TIME] = INT64_MAX;
596  min_next_call_time[RCL_SYSTEM_TIME] = INT64_MAX;
597  min_next_call_time[RCL_STEADY_TIME] = INT64_MAX;
598 
599  if (!is_non_blocking) {
600  for (size_t t_idx = 0; t_idx < wait_set->impl->timer_index; ++t_idx) {
601  if (!wait_set->timers[t_idx]) {
602  continue; // Skip NULL timers.
603  }
604 
605  rcl_clock_t * clock;
606  rcl_ret_t ret = rcl_timer_clock(wait_set->timers[t_idx], &clock);
607  if (ret != RCL_RET_OK) {
608  // should never happen
609  RCL_EXPECT_ERROR_IS_SET(ret);
610  return RCL_RET_ERROR;
611  }
612 
613  if (clock->type == RCL_ROS_TIME) {
614  bool timer_override_active = false;
615  ret = rcl_is_enabled_ros_time_override(clock, &timer_override_active);
616  if (ret != RCL_RET_OK) {
617  // should never happen
618  RCL_EXPECT_ERROR_IS_SET(ret);
619  return RCL_RET_ERROR;
620  }
621 
622  if (timer_override_active) {
623  // we need to check, it the timer is already ready
624  bool override_timer_is_ready = false;
625  ret = rcl_timer_is_ready(wait_set->timers[t_idx], &override_timer_is_ready);
626  if (ret != RCL_RET_OK) {
627  // should never happen
628  RCL_EXPECT_ERROR_IS_SET(ret);
629  return RCL_RET_ERROR;
630  }
631 
632  if (override_timer_is_ready) {
633  // no need to search further for the timeout, we need to wake up instantly
634  is_non_blocking = true;
635  break;
636  }
637 
638  // if the timer override is active, there is no point in computing a wait time,
639  // as it might be on a total wrong time basis. In case this timer becomes ready,
640  // the guard_condition above will wake us.
641  continue;
642  }
643  }
644 
645  // get the time of the next call to the timer
646  int64_t next_call_time = INT64_MAX;
647  ret = rcl_timer_get_next_call_time(wait_set->timers[t_idx], &next_call_time);
648  if (ret == RCL_RET_TIMER_CANCELED) {
649  wait_set->timers[t_idx] = NULL;
650  continue;
651  }
652  if (ret != RCL_RET_OK) {
653  RCL_EXPECT_ERROR_IS_SET(ret);
654  return ret; // The rcl error state should already be set.
655  }
656  if (next_call_time < min_next_call_time[clock->type]) {
657  clocks[clock->type] = clock;
658  min_next_call_time[clock->type] = next_call_time;
659  }
660  }
661  }
662 
663  if (is_non_blocking) {
664  temporary_timeout_storage.sec = 0;
665  temporary_timeout_storage.nsec = 0;
666  timeout_argument = &temporary_timeout_storage;
667  } else {
668  bool has_valid_timeout = timeout > 0;
669  int64_t min_timeout = has_valid_timeout ? timeout : INT64_MAX;
670 
671  // determine the min timeout of all clocks
672  for (size_t i = RCL_ROS_TIME; i <= RCL_STEADY_TIME; i++) {
673  if (clocks[i] == NULL) {
674  continue;
675  }
676 
677  int64_t cur_time;
678  rmw_ret_t ret = rcl_clock_get_now(clocks[i], &cur_time);
679  if (ret != RCL_RET_OK) {
680  RCL_EXPECT_ERROR_IS_SET(ret);
681  return ret; // The rcl error state should already be set.
682  }
683 
684  int64_t timer_timeout = min_next_call_time[i] - cur_time;
685 
686  if (timer_timeout <= min_timeout) {
687  has_valid_timeout = true;
688  min_timeout = timer_timeout;
689  }
690  }
691 
692  // If min_timeout was negative, we need to wake up immediately.
693  if (min_timeout < 0) {
694  min_timeout = 0;
695  }
696  if (has_valid_timeout) {
697  temporary_timeout_storage.sec = RCL_NS_TO_S(min_timeout);
698  temporary_timeout_storage.nsec = min_timeout % 1000000000;
699  timeout_argument = &temporary_timeout_storage;
700  }
701  }
702 
703  // Wait.
704  rmw_ret_t ret = rmw_wait(
705  &wait_set->impl->rmw_subscriptions,
706  &wait_set->impl->rmw_guard_conditions,
707  &wait_set->impl->rmw_services,
708  &wait_set->impl->rmw_clients,
709  &wait_set->impl->rmw_events,
710  wait_set->impl->rmw_wait_set,
711  timeout_argument);
712 
713  // Items that are not ready will have been set to NULL by rmw_wait.
714  // We now update our handles accordingly.
715 
716  bool any_timer_is_ready = false;
717 
718  // Check for ready timers
719  // and set not ready timers (which includes canceled timers) to NULL.
720  size_t i;
721  for (i = 0; i < wait_set->impl->timer_index; ++i) {
722  if (!wait_set->timers[i]) {
723  continue;
724  }
725 
726  bool current_timer_is_ready = false;
727  rcl_ret_t ret = rcl_timer_is_ready(wait_set->timers[i], &current_timer_is_ready);
728  if (ret != RCL_RET_OK) {
729  RCL_EXPECT_ERROR_IS_SET(ret);
730  return ret; // The rcl error state should already be set.
731  }
732  if (!current_timer_is_ready) {
733  wait_set->timers[i] = NULL;
734  } else {
735  any_timer_is_ready = true;
736  }
737  }
738  // Check for timeout, return RCL_RET_TIMEOUT only if it wasn't a timer.
739  if (ret != RMW_RET_OK && ret != RMW_RET_TIMEOUT) {
740  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
741  "Error from rmw_wait(): %d %s", ret, rmw_get_error_string().str);
742  return RCL_RET_ERROR;
743  }
744  // Set corresponding rcl subscription handles NULL.
745  for (i = 0; i < wait_set->size_of_subscriptions; ++i) {
746  bool is_ready = wait_set->impl->rmw_subscriptions.subscribers[i] != NULL;
747  if (!is_ready) {
748  wait_set->subscriptions[i] = NULL;
749  }
750  }
751  // Set corresponding rcl guard_condition handles NULL.
752  for (i = 0; i < wait_set->size_of_guard_conditions; ++i) {
753  bool is_ready = wait_set->impl->rmw_guard_conditions.guard_conditions[i] != NULL;
754  if (!is_ready) {
755  wait_set->guard_conditions[i] = NULL;
756  }
757  }
758  // Set corresponding rcl client handles NULL.
759  for (i = 0; i < wait_set->size_of_clients; ++i) {
760  bool is_ready = wait_set->impl->rmw_clients.clients[i] != NULL;
761  if (!is_ready) {
762  wait_set->clients[i] = NULL;
763  }
764  }
765  // Set corresponding rcl service handles NULL.
766  for (i = 0; i < wait_set->size_of_services; ++i) {
767  bool is_ready = wait_set->impl->rmw_services.services[i] != NULL;
768  if (!is_ready) {
769  wait_set->services[i] = NULL;
770  }
771  }
772  // Set corresponding rcl event handles NULL.
773  for (i = 0; i < wait_set->size_of_events; ++i) {
774  bool is_ready = wait_set->impl->rmw_events.events[i] != NULL;
775  if (!is_ready) {
776  wait_set->events[i] = NULL;
777  }
778  }
779 
780  if (RMW_RET_TIMEOUT == ret && !any_timer_is_ready) {
781  return RCL_RET_TIMEOUT;
782  }
783  return RCL_RET_OK;
784 }
785 
786 #ifdef __cplusplus
787 }
788 #endif
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
Definition: allocator.h:56
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_context_is_valid(const rcl_context_t *context)
Return true if the given context is currently valid, otherwise false.
Definition: context.c:106
RCL_PUBLIC RCL_WARN_UNUSED rmw_guard_condition_t * rcl_guard_condition_get_rmw_handle(const rcl_guard_condition_t *guard_condition)
Return the rmw guard condition handle.
Structure which encapsulates a ROS Client.
Definition: client.h:47
Encapsulation of a time source.
Definition: time.h:141
rcl_clock_type_t type
Clock type.
Definition: time.h:143
rmw_context_t rmw_context
rmw context.
Definition: context_impl.h:46
Encapsulates the non-global state of an init/shutdown cycle.
Definition: context.h:117
rcl_context_impl_t * impl
Implementation specific pointer.
Definition: context.h:123
Structure which encapsulates a ROS QoS event handle.
Definition: event.h:64
Handle for a rcl guard condition.
Structure which encapsulates a ROS Service.
Definition: service.h:46
Structure which encapsulates a ROS Subscription.
Definition: subscription.h:48
Structure which encapsulates a ROS Timer.
Definition: timer.h:42
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:44
const rcl_event_t ** events
Storage for event pointers.
Definition: wait.h:66
const rcl_timer_t ** timers
Storage for timer pointers.
Definition: wait.h:54
size_t size_of_timers
Number of timers.
Definition: wait.h:56
size_t size_of_events
Number of events.
Definition: wait.h:68
size_t size_of_subscriptions
Number of subscriptions.
Definition: wait.h:48
const rcl_service_t ** services
Storage for service pointers.
Definition: wait.h:62
const rcl_client_t ** clients
Storage for client pointers.
Definition: wait.h:58
const rcl_subscription_t ** subscriptions
Storage for subscription pointers.
Definition: wait.h:46
size_t size_of_guard_conditions
Number of guard_conditions.
Definition: wait.h:52
rcl_wait_set_impl_t * impl
Implementation specific storage.
Definition: wait.h:70
size_t size_of_clients
Number of clients.
Definition: wait.h:60
const rcl_guard_condition_t ** guard_conditions
Storage for guard condition pointers.
Definition: wait.h:50
size_t size_of_services
Number of services.
Definition: wait.h:64
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_get_now(rcl_clock_t *clock, rcl_time_point_value_t *time_point_value)
Fill the time point value with the current value of the associated clock.
Definition: time.c:263
#define RCL_NS_TO_S
Convenience macro to convert nanoseconds to seconds.
Definition: time.h:42
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_is_enabled_ros_time_override(rcl_clock_t *clock, bool *is_enabled)
Check if the RCL_ROS_TIME time source has the override enabled.
Definition: time.c:343
@ RCL_ROS_TIME
Use ROS time.
Definition: time.h:69
@ RCL_SYSTEM_TIME
Use system time.
Definition: time.h:71
@ RCL_STEADY_TIME
Use a steady clock time.
Definition: time.h:73
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_get_next_call_time(const rcl_timer_t *timer, int64_t *next_call_time)
Retrieve the time when the next call to rcl_timer_call() shall occur.
Definition: timer.c:326
RCL_PUBLIC RCL_WARN_UNUSED rcl_guard_condition_t * rcl_timer_get_guard_condition(const rcl_timer_t *timer)
Retrieve a guard condition used by the timer to wake the waitset when using ROSTime.
Definition: timer.c:505
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_clock(const rcl_timer_t *timer, rcl_clock_t **clock)
Retrieve the clock of the timer.
Definition: timer.c:233
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_is_ready(const rcl_timer_t *timer, bool *is_ready)
Calculates whether or not the timer should be called.
Definition: timer.c:307
#define RCL_RET_WAIT_SET_EMPTY
Given rcl_wait_set_t is empty return code.
Definition: types.h:102
#define RCL_RET_WAIT_SET_INVALID
Invalid rcl_wait_set_t given return code.
Definition: types.h:100
#define RCL_RET_NOT_INIT
rcl_init() not yet called return code.
Definition: types.h:44
#define RCL_RET_ALREADY_INIT
rcl_init() already called return code.
Definition: types.h:42
#define RCL_RET_OK
Success return code.
Definition: types.h:28
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:34
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:36
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:30
#define RCL_RET_TIMER_CANCELED
Given timer was canceled return code.
Definition: types.h:96
#define RCL_RET_TIMEOUT
Timeout occurred return code.
Definition: types.h:32
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:25
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_subscription(rcl_wait_set_t *wait_set, const rcl_subscription_t *subscription, size_t *index)
Store a pointer to the given subscription in the next empty spot in the set.
Definition: wait.c:337
RCL_PUBLIC bool rcl_wait_set_is_valid(const rcl_wait_set_t *wait_set)
Return true if the wait set is valid, else false.
Definition: wait.c:80
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_service(rcl_wait_set_t *wait_set, const rcl_service_t *service, size_t *index)
Store a pointer to the service in the next empty spot in the set.
Definition: wait.c:518
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_init(rcl_wait_set_t *wait_set, size_t number_of_subscriptions, size_t number_of_guard_conditions, size_t number_of_timers, size_t number_of_clients, size_t number_of_services, size_t number_of_events, rcl_context_t *context, rcl_allocator_t allocator)
Initialize a rcl wait set with space for items to be waited on.
Definition: wait.c:86
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_clear(rcl_wait_set_t *wait_set)
Remove (sets to NULL) all entities in the wait set.
Definition: wait.c:353
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_timer(rcl_wait_set_t *wait_set, const rcl_timer_t *timer, size_t *index)
Store a pointer to the timer in the next empty spot in the set.
Definition: wait.c:487
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_client(rcl_wait_set_t *wait_set, const rcl_client_t *client, size_t *index)
Store a pointer to the client in the next empty spot in the set.
Definition: wait.c:507
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_fini(rcl_wait_set_t *wait_set)
Finalize a rcl wait set.
Definition: wait.c:175
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait(rcl_wait_set_t *wait_set, int64_t timeout)
Block until the wait set is ready or until the timeout has been exceeded.
Definition: wait.c:541
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_resize(rcl_wait_set_t *wait_set, size_t subscriptions_size, size_t guard_conditions_size, size_t timers_size, size_t clients_size, size_t services_size, size_t events_size)
Reallocate space for entities in the wait set.
Definition: wait.c:395
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_get_allocator(const rcl_wait_set_t *wait_set, rcl_allocator_t *allocator)
Retrieve the wait set's allocator.
Definition: wait.c:201
RCL_PUBLIC RCL_WARN_UNUSED rcl_wait_set_t rcl_get_zero_initialized_wait_set(void)
Return a rcl_wait_set_t struct with members set to NULL.
Definition: wait.c:72
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_event(rcl_wait_set_t *wait_set, const rcl_event_t *event, size_t *index)
Store a pointer to the event in the next empty spot in the set.
Definition: wait.c:529
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_guard_condition(rcl_wait_set_t *wait_set, const rcl_guard_condition_t *guard_condition, size_t *index)
Store a pointer to the guard condition in the next empty spot in the set.
Definition: wait.c:473