@@ -20,8 +20,12 @@ extern "C"
2020#include "rcl/graph.h"
2121
2222#include "rcl/error_handling.h"
23+ #include "rcl/guard_condition.h"
24+ #include "rcl/wait.h"
2325#include "rcutils/allocator.h"
26+ #include "rcutils/error_handling.h"
2427#include "rcutils/macros.h"
28+ #include "rcutils/time.h"
2529#include "rcutils/types.h"
2630#include "rmw/error_handling.h"
2731#include "rmw/get_node_info_and_types.h"
@@ -452,6 +456,179 @@ rcl_count_subscribers(
452456 return rcl_convert_rmw_ret_to_rcl_ret (rmw_ret );
453457}
454458
459+ typedef rcl_ret_t (* count_entities_func_t )(
460+ const rcl_node_t * node ,
461+ const char * topic_name ,
462+ size_t * count );
463+
464+ rcl_ret_t
465+ _rcl_wait_for_entities (
466+ const rcl_node_t * node ,
467+ rcl_allocator_t * allocator ,
468+ const char * topic_name ,
469+ const size_t expected_count ,
470+ rcutils_duration_value_t timeout ,
471+ bool * success ,
472+ count_entities_func_t count_entities_func )
473+ {
474+ if (!rcl_node_is_valid (node )) {
475+ return RCL_RET_NODE_INVALID ;
476+ }
477+ RCL_CHECK_ALLOCATOR_WITH_MSG (allocator , "invalid allocator" , return RCL_RET_INVALID_ARGUMENT );
478+ RCL_CHECK_ARGUMENT_FOR_NULL (topic_name , RCL_RET_INVALID_ARGUMENT );
479+ RCL_CHECK_ARGUMENT_FOR_NULL (success , RCL_RET_INVALID_ARGUMENT );
480+
481+ rcl_ret_t ret = RCL_RET_OK ;
482+ * success = false;
483+
484+ // We can avoid waiting if there are already the expected number of publishers
485+ size_t count = 0u ;
486+ ret = count_entities_func (node , topic_name , & count );
487+ if (ret != RCL_RET_OK ) {
488+ // Error message already set
489+ return ret ;
490+ }
491+ if (expected_count <= count ) {
492+ * success = true;
493+ return RCL_RET_OK ;
494+ }
495+
496+ // Create a wait set and add the node graph guard condition to it
497+ rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set ();
498+ ret = rcl_wait_set_init (
499+ & wait_set , 0 , 1 , 0 , 0 , 0 , 0 , node -> context , * allocator );
500+ if (ret != RCL_RET_OK ) {
501+ // Error message already set
502+ return ret ;
503+ }
504+
505+ const rcl_guard_condition_t * guard_condition = rcl_node_get_graph_guard_condition (node );
506+ if (!guard_condition ) {
507+ // Error message already set
508+ ret = RCL_RET_ERROR ;
509+ goto cleanup ;
510+ }
511+
512+ // Add it to the wait set
513+ ret = rcl_wait_set_add_guard_condition (& wait_set , guard_condition , NULL );
514+ if (ret != RCL_RET_OK ) {
515+ // Error message already set
516+ goto cleanup ;
517+ }
518+
519+ // Get current time
520+ // We use system time to be consistent with the clock used by rcl_wait()
521+ rcutils_time_point_value_t start ;
522+ rcutils_ret_t time_ret = rcutils_system_time_now (& start );
523+ if (time_ret != RCUTILS_RET_OK ) {
524+ rcutils_error_string_t error = rcutils_get_error_string ();
525+ rcutils_reset_error ();
526+ RCL_SET_ERROR_MSG (error .str );
527+ ret = RCL_RET_ERROR ;
528+ goto cleanup ;
529+ }
530+
531+ // Wait for expected count or timeout
532+ rcl_ret_t wait_ret ;
533+ while (true) {
534+ // Use separate 'wait_ret' code to avoid returning spurious TIMEOUT value
535+ wait_ret = rcl_wait (& wait_set , timeout );
536+ if (wait_ret != RCL_RET_OK && wait_ret != RCL_RET_TIMEOUT ) {
537+ // Error message already set
538+ ret = wait_ret ;
539+ break ;
540+ }
541+
542+ // Check count
543+ ret = count_entities_func (node , topic_name , & count );
544+ if (ret != RCL_RET_OK ) {
545+ // Error already set
546+ break ;
547+ }
548+ if (expected_count <= count ) {
549+ * success = true;
550+ break ;
551+ }
552+
553+ // If we're not waiting indefinitely, compute time remaining
554+ if (timeout >= 0 ) {
555+ rcutils_time_point_value_t now ;
556+ time_ret = rcutils_system_time_now (& now );
557+ if (time_ret != RCUTILS_RET_OK ) {
558+ rcutils_error_string_t error = rcutils_get_error_string ();
559+ rcutils_reset_error ();
560+ RCL_SET_ERROR_MSG (error .str );
561+ ret = RCL_RET_ERROR ;
562+ break ;
563+ }
564+ timeout = timeout - (now - start );
565+ if (timeout <= 0 ) {
566+ ret = RCL_RET_TIMEOUT ;
567+ break ;
568+ }
569+ }
570+
571+ // Clear wait set for next iteration
572+ ret = rcl_wait_set_clear (& wait_set );
573+ if (ret != RCL_RET_OK ) {
574+ // Error message already set
575+ break ;
576+ }
577+ }
578+
579+ rcl_ret_t cleanup_ret ;
580+ cleanup :
581+ // Cleanup
582+ cleanup_ret = rcl_wait_set_fini (& wait_set );
583+ if (cleanup_ret != RCL_RET_OK ) {
584+ // If we got two unexpected errors, return the earlier error
585+ if (ret != RCL_RET_OK && ret != RCL_RET_TIMEOUT ) {
586+ // Error message already set
587+ ret = cleanup_ret ;
588+ }
589+ }
590+
591+ return ret ;
592+ }
593+
594+ rcl_ret_t
595+ rcl_wait_for_publishers (
596+ const rcl_node_t * node ,
597+ rcl_allocator_t * allocator ,
598+ const char * topic_name ,
599+ const size_t expected_count ,
600+ rcutils_duration_value_t timeout ,
601+ bool * success )
602+ {
603+ return _rcl_wait_for_entities (
604+ node ,
605+ allocator ,
606+ topic_name ,
607+ expected_count ,
608+ timeout ,
609+ success ,
610+ rcl_count_publishers );
611+ }
612+
613+ rcl_ret_t
614+ rcl_wait_for_subscribers (
615+ const rcl_node_t * node ,
616+ rcl_allocator_t * allocator ,
617+ const char * topic_name ,
618+ const size_t expected_count ,
619+ rcutils_duration_value_t timeout ,
620+ bool * success )
621+ {
622+ return _rcl_wait_for_entities (
623+ node ,
624+ allocator ,
625+ topic_name ,
626+ expected_count ,
627+ timeout ,
628+ success ,
629+ rcl_count_subscribers );
630+ }
631+
455632typedef rmw_ret_t (* get_topic_endpoint_info_func_t )(
456633 const rmw_node_t * node ,
457634 rcutils_allocator_t * allocator ,
0 commit comments