Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Thread configuration prototype #1075

Open
wants to merge 7 commits into
base: rolling
Choose a base branch
from
1 change: 1 addition & 0 deletions rcl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ set(${PROJECT_NAME}_sources
src/rcl/service.c
src/rcl/service_event_publisher.c
src/rcl/subscription.c
src/rcl/thread_attr.c
src/rcl/time.c
src/rcl/timer.c
src/rcl/type_hash.c
Expand Down
25 changes: 25 additions & 0 deletions rcl/include/rcl/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "rcl/types.h"
#include "rcl/visibility_control.h"
#include "rcl_yaml_param_parser/types.h"
#include "rcutils/thread_attr.h"

#ifdef __cplusplus
extern "C"
Expand Down Expand Up @@ -83,6 +84,12 @@ typedef struct rcl_arguments_s
/// logging (must be preceded with --enable- or --disable-).
#define RCL_LOG_EXT_LIB_FLAG_SUFFIX "external-lib-logs"

/// The ROS flag that precedes the ROS thread attribute file path.
#define RCL_THREAD_ATTRS_FILE_FLAG "--thread-attrs-file"

/// The ROS flag that precedes the ROS thread attribute.
#define RCL_THREAD_ATTRS_VALUE_FLAG "--thread-attrs-value"

/// Return a rcl_arguments_t struct with members initialized to `NULL`.
RCL_PUBLIC
RCL_WARN_UNUSED
Expand Down Expand Up @@ -447,6 +454,24 @@ rcl_ret_t
rcl_arguments_fini(
rcl_arguments_t * args);

/// Return thread attribute parsed from the command line.
/**
* Thread attribute are parsed directly from command line arguments and
* thread attribute files provided in the command line.
*
* \param[in] arguments An arguments structure that has been parsed.
* \param[out] thread_attrs thread attribute as parsed from command line arguments.
* This structure must be finalized by the caller.
* \return #RCL_RET_OK if everything goes correctly, or
* \return #RCL_RET_INVALID_ARGUMENT if any function arguments are invalid, or
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_arguments_get_thread_attrs(
const rcl_arguments_t * arguments,
rcutils_thread_attrs_t ** thread_attrs);

#ifdef __cplusplus
}
#endif
Expand Down
10 changes: 10 additions & 0 deletions rcl/include/rcl/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,16 @@ RCL_WARN_UNUSED
rmw_context_t *
rcl_context_get_rmw_context(rcl_context_t * context);

/// Returns pointer to the thread attribute list.
/**
* \param[in] context object from which the thread attribute list should be retrieved.
* \return pointer to thread attribute list if valid, otherwise `NULL`
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcutils_thread_attrs_t *
rcl_context_get_thread_attrs(const rcl_context_t * context);

#ifdef __cplusplus
}
#endif
Expand Down
66 changes: 66 additions & 0 deletions rcl/include/rcl/thread_attr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2023 eSOL Co.,Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// @file

#ifndef RCL__THREAD_ATTR_H_
#define RCL__THREAD_ATTR_H_

#ifdef __cplusplus
extern "C"
{
#endif

#include <stddef.h>

#include "rcl/allocator.h"
#include "rcl/macros.h"
#include "rcl/types.h"
#include "rcl/visibility_control.h"
#include "rcutils/thread_attr.h"

extern const char * const RCL_THREAD_ATTR_VALUE_ENV_VAR;
extern const char * const RCL_THREAD_ATTR_FILE_ENV_VAR;

/// Determine the default thread attribute from string, based on the environment.
/// \param[out] thread_attrs Must not be NULL.
/// \param[in] allocator memory allocator to be used
/// \return #RCL_RET_INVALID_ARGUMENT if an argument is invalid, or
/// \return #RCL_RET_ERROR in case of an unexpected error, or
/// \return #RCL_RET_BAD_ALLOC if allocating memory failed, or
/// \return #RCL_RET_OK.
RCL_PUBLIC
rcl_ret_t
rcl_get_default_thread_attrs_from_value(
rcutils_thread_attrs_t * thread_attrs,
rcl_allocator_t allocator);

/// Determine the default thread attribute from file path, based on the environment.
/// \param[out] thread_attrs Must not be NULL.
/// \param[in] allocator memory allocator to be used
/// \return #RCL_RET_INVALID_ARGUMENT if an argument is invalid, or
/// \return #RCL_RET_ERROR in case of an unexpected error, or
/// \return #RCL_RET_BAD_ALLOC if allocating memory failed, or
/// \return #RCL_RET_OK.
RCL_PUBLIC
rcl_ret_t
rcl_get_default_thread_attrs_from_file(
rcutils_thread_attrs_t * thread_attrs,
rcl_allocator_t allocator);

#ifdef __cplusplus
}
#endif

#endif // RCL__THREAD_ATTR_H_
2 changes: 2 additions & 0 deletions rcl/include/rcl/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ typedef rmw_ret_t rcl_ret_t;
#define RCL_RET_INVALID_PARAM_RULE 1010
/// Argument is not a valid log level rule
#define RCL_RET_INVALID_LOG_LEVEL_RULE 1020
/// Argument is not a valid thread attr rule
#define RCL_RET_INVALID_THREAD_ATTRS 1030

// rcl event specific ret codes in 20XX
/// Invalid rcl_event_t given return code.
Expand Down
115 changes: 115 additions & 0 deletions rcl/src/rcl/arguments.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
#include "rcl/lexer_lookahead.h"
#include "rcl/validate_topic_name.h"
#include "rcl_yaml_param_parser/parser.h"
#include "rcl_yaml_param_parser/parser_thread_attr.h"
#include "rcl_yaml_param_parser/types.h"
#include "rcutils/allocator.h"
#include "rcutils/error_handling.h"
#include "rcutils/format_string.h"
#include "rcutils/logging.h"
#include "rcutils/logging_macros.h"
#include "rcutils/strdup.h"
#include "rcutils/thread_attr.h"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"

Expand Down Expand Up @@ -286,6 +288,12 @@ rcl_parse_arguments(
goto fail;
}

args_impl->thread_attrs = rcutils_get_zero_initialized_thread_attrs();
ret = rcutils_thread_attrs_init(&args_impl->thread_attrs, allocator);
if (RCL_RET_OK != ret) {
goto fail;
}

args_impl->parameter_overrides = rcl_yaml_node_struct_init(allocator);
if (NULL == args_impl->parameter_overrides) {
ret = RCL_RET_BAD_ALLOC;
Expand Down Expand Up @@ -559,6 +567,75 @@ rcl_parse_arguments(
RCL_DISABLE_FLAG_PREFIX, RCL_LOG_EXT_LIB_FLAG_SUFFIX, rcl_get_error_string().str);
rcl_reset_error();

// Attempt to parse argument as thread attribute flag
if (strcmp(RCL_THREAD_ATTRS_VALUE_FLAG, argv[i]) == 0) {
if (i + 1 < argc) {
if (args_impl->thread_attrs.num_attributes != 0) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Thread attributes already set: '%s %s'.", argv[i], argv[i + 1]);
++i;
continue;
}
// Attempt to parse next argument as thread attribute rule
if (RCL_RET_OK ==
rcl_parse_yaml_thread_attrs_value(argv[i + 1], &args_impl->thread_attrs))
{
RCUTILS_LOG_DEBUG_NAMED(
ROS_PACKAGE_NAME, "Got thread attributes value : %s\n", argv[i + 1]);
++i; // Skip flag here, for loop will skip rule.
continue;
}
rcl_error_string_t prev_error_string = rcl_get_error_string();
rcl_reset_error();
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse thread attributes value: '%s %s'. Error: %s", argv[i], argv[i + 1],
prev_error_string.str);
} else {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse trailing %s flag. No thread attributes value found.", argv[i]);
}
ret = RCL_RET_INVALID_ROS_ARGS;
goto fail;
}
RCUTILS_LOG_DEBUG_NAMED(
ROS_PACKAGE_NAME, "Arg %d (%s) is not a %s flag.",
i, argv[i], RCL_THREAD_ATTRS_VALUE_FLAG);

// Attempt to parse argument as thread attribute file rule
if (strcmp(RCL_THREAD_ATTRS_FILE_FLAG, argv[i]) == 0) {
if (i + 1 < argc) {
if (args_impl->thread_attrs.num_attributes != 0) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Thread attributes already setted: '%s %s'.", argv[i], argv[i + 1]);
++i;
continue;
}
// Attempt to parse next argument as thread attribute file rule
if (
RCL_RET_OK == rcl_parse_yaml_thread_attrs_file(
argv[i + 1], &args_impl->thread_attrs))
{
RCUTILS_LOG_DEBUG_NAMED(
ROS_PACKAGE_NAME, "Got thread attributes file : %s\n", argv[i + 1]);
++i; // Skip flag here, for loop will skip rule.
continue;
}
rcl_error_string_t prev_error_string = rcl_get_error_string();
rcl_reset_error();
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse thread attributes file: '%s %s'. Error: %s", argv[i], argv[i + 1],
prev_error_string.str);
} else {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse trailing %s flag. No file path provided.", argv[i]);
}
ret = RCL_RET_INVALID_ROS_ARGS;
goto fail;
}
RCUTILS_LOG_DEBUG_NAMED(
ROS_PACKAGE_NAME, "Arg %d (%s) is not a %s flag.",
i, argv[i], RCL_THREAD_ATTRS_FILE_FLAG);

// Argument is an unknown ROS specific argument
args_impl->unparsed_ros_args[args_impl->num_unparsed_ros_args] = i;
++(args_impl->num_unparsed_ros_args);
Expand Down Expand Up @@ -925,6 +1002,18 @@ rcl_arguments_copy(
return RCL_RET_BAD_ALLOC;
}
args_out->impl->enclave = enclave_copy;

// Copy thread attributes
if (0 < args->impl->thread_attrs.num_attributes) {
rcutils_ret_t thread_attrs_ret =
rcutils_thread_attrs_copy(&args->impl->thread_attrs, &args_out->impl->thread_attrs);
if (RCUTILS_RET_OK != thread_attrs_ret) {
if (RCL_RET_OK != rcl_arguments_fini(args_out)) {
RCL_SET_ERROR_MSG("Error while finalizing arguments due to another error");
}
return RCL_RET_BAD_ALLOC;
}
}
return RCL_RET_OK;
}

Expand Down Expand Up @@ -988,6 +1077,14 @@ rcl_arguments_fini(
args->impl->external_log_config_file = NULL;
}

rcl_ret_t thread_ret = rcutils_thread_attrs_fini(&args->impl->thread_attrs);
if (RCL_RET_OK != thread_ret) {
ret = thread_ret;
RCUTILS_LOG_ERROR_NAMED(
ROS_PACKAGE_NAME,
"Failed to finalize thread attribute while finalizing arguments. Continuing...");
}

args->impl->allocator.deallocate(args->impl, args->impl->allocator.state);
args->impl = NULL;
return ret;
Expand Down Expand Up @@ -2067,11 +2164,29 @@ _rcl_allocate_initialized_arguments_impl(rcl_arguments_t * args, rcl_allocator_t
args_impl->log_rosout_disabled = false;
args_impl->log_ext_lib_disabled = false;
args_impl->enclave = NULL;
args_impl->thread_attrs = rcutils_get_zero_initialized_thread_attrs();
args_impl->allocator = *allocator;

return RCL_RET_OK;
}

rcl_ret_t
rcl_arguments_get_thread_attrs(
const rcl_arguments_t * arguments,
rcutils_thread_attrs_t ** thread_attrs)
{
RCL_CHECK_ARGUMENT_FOR_NULL(arguments, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(arguments->impl, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(thread_attrs, RCL_RET_INVALID_ARGUMENT);

if (0 < arguments->impl->thread_attrs.num_attributes) {
*thread_attrs = &arguments->impl->thread_attrs;
return RCL_RET_OK;
} else {
return RCL_RET_ERROR;
}
}

#ifdef __cplusplus
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions rcl/src/rcl/arguments_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ struct rcl_arguments_impl_s
/// Length of remap_rules.
int num_remap_rules;

/// thread attribute.
rcutils_thread_attrs_t thread_attrs;

/// Log levels parsed from arguments.
rcl_log_levels_t log_levels;
/// A file used to configure the external logging library
Expand Down
32 changes: 32 additions & 0 deletions rcl/src/rcl/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern "C"
#include "./common.h"
#include "./context_impl.h"
#include "rcutils/stdatomic_helper.h"
#include "rcl_yaml_param_parser/parser_thread_attr.h"

rcl_context_t
rcl_get_zero_initialized_context(void)
Expand Down Expand Up @@ -105,6 +106,22 @@ rcl_context_get_rmw_context(rcl_context_t * context)
return &(context->impl->rmw_context);
}

rcutils_thread_attrs_t *
rcl_context_get_thread_attrs(const rcl_context_t * context)
{
RCL_CHECK_ARGUMENT_FOR_NULL(context, NULL);
RCL_CHECK_FOR_NULL_WITH_MSG(context->impl, "context is zero-initialized", return NULL);

rcutils_thread_attrs_t * attrs = NULL;
rcl_ret_t ret = rcl_arguments_get_thread_attrs(&context->global_arguments, &attrs);
if (RCL_RET_OK != ret) {
if (0 < context->impl->thread_attrs.num_attributes) {
attrs = &context->impl->thread_attrs;
}
}
return attrs;
}

rcl_ret_t
__cleanup_context(rcl_context_t * context)
{
Expand Down Expand Up @@ -146,6 +163,21 @@ __cleanup_context(rcl_context_t * context)
}
}

// clean up thread_attrs_context
rcl_ret_t thread_attrs_context_fini_ret =
rcutils_thread_attrs_fini(&(context->impl->thread_attrs));
if (RCL_RET_OK != thread_attrs_context_fini_ret) {
if (RCL_RET_OK == ret) {
ret = thread_attrs_context_fini_ret;
}
RCUTILS_SAFE_FWRITE_TO_STDERR(
"[rcl|context.c:" RCUTILS_STRINGIFY(__LINE__)
"] failed to finalize attr context while cleaning up context, memory may be leaked: ");
RCUTILS_SAFE_FWRITE_TO_STDERR(rcutils_get_error_string().str);
RCUTILS_SAFE_FWRITE_TO_STDERR("\n");
rcutils_reset_error();
}

// clean up rmw_context
if (NULL != context->impl->rmw_context.implementation_identifier) {
rmw_ret_t rmw_context_fini_ret = rmw_context_fini(&(context->impl->rmw_context));
Expand Down
3 changes: 3 additions & 0 deletions rcl/src/rcl/context_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef RCL__CONTEXT_IMPL_H_
#define RCL__CONTEXT_IMPL_H_

#include "rcl_yaml_param_parser/parser_thread_attr.h"
#include "rcl/context.h"
#include "rcl/error_handling.h"

Expand All @@ -38,6 +39,8 @@ struct rcl_context_impl_s
char ** argv;
/// rmw context.
rmw_context_t rmw_context;
/// thread attributes.
rcutils_thread_attrs_t thread_attrs;
};

RCL_LOCAL
Expand Down
Loading