-
Notifications
You must be signed in to change notification settings - Fork 163
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
Increase rcl_lifecycle test coverage and add more safety checks #649
Changes from 4 commits
44dbbed
70465b7
152aaa3
71607bc
f269c23
a4961f1
661b4b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -650,7 +650,7 @@ _register_transitions( | |
return ret; | ||
} | ||
|
||
// default implementation as despicted on | ||
// default implementation as depicted on | ||
// design.ros2.org | ||
rcl_ret_t | ||
rcl_lifecycle_init_default_state_machine( | ||
|
@@ -690,10 +690,27 @@ rcl_lifecycle_init_default_state_machine( | |
|
||
return ret; | ||
|
||
fail: | ||
// Semicolon handles the "a label can only be part of a statement..." error | ||
fail:; | ||
// If rcl_lifecycle_transition_map_fini() fails, it will clobber the error string here, twice | ||
// Here, we concatenate the error strings if that happens | ||
const char * current_error = (rcl_error_is_set()) ? rcl_get_error_string().str : ""; | ||
rcl_reset_error(); | ||
|
||
if (rcl_lifecycle_transition_map_fini(&state_machine->transition_map, allocator) != RCL_RET_OK) { | ||
RCL_SET_ERROR_MSG("could not free lifecycle transition map. Leaking memory!\n"); | ||
const char * fini_error = (rcl_error_is_set()) ? rcl_get_error_string().str : ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if I interpret this correctly this line will always result in an empty string, given that you reset the error message just above. |
||
rcl_reset_error(); | ||
|
||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING( | ||
"Freeing transition map failed while handling a previous error. Leaking memory!" | ||
"\nOriginal error:\n\t%s\nError encountered in rcl_lifecycle_transition_map_fini():\n\t%s\n", | ||
current_error, fini_error); | ||
} else if (strcmp(current_error, "") != 0) { | ||
RCL_SET_ERROR_MSG(current_error); | ||
brawner marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry to iterate again over this, but I think that something like this: if (rcl_lifecycle_transition_map_fini(&state_machine->transition_map, allocator) != RCL_RET_OK) {
const char * fini_error = "";
if (rcl_error_is_set()) {
fini_error = rcl_get_error_string().str;
rcl_reset_error();
}
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Freeing transition map failed while handling a previous error. Leaking memory!"
"\nOriginal error:\n\t%s\nError encountered in rcl_lifecycle_transition_map_fini():\n\t%s\n",
current_error, fini_error);
}
if (!rcl_error_is_set()) {
RCL_SET_ERROR_MSG("Unspecified error ..");
}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem originally was that the error message was already set. But if However, if Originally when I submitted this, I just relied on rcutils to log the transition of error messages, since it warns if a new error message is set while a previous one had already existed. However, since I'm pretty new to the ROS 2 C codebase, I'm really not confident about any particular approach here 🤷♂️ |
||
} else { | ||
RCL_SET_ERROR_MSG("Unspecified in default_state_machine _register_transitions()"); | ||
} | ||
|
||
return RCL_RET_ERROR; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -40,6 +40,8 @@ rcl_lifecycle_get_zero_initialized_state() | |||||
rcl_lifecycle_state_t state; | ||||||
state.id = 0; | ||||||
state.label = NULL; | ||||||
state.valid_transitions = NULL; | ||||||
state.valid_transition_size = 0; | ||||||
return state; | ||||||
} | ||||||
|
||||||
|
@@ -58,6 +60,10 @@ rcl_lifecycle_state_init( | |||||
RCL_SET_ERROR_MSG("state pointer is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
} | ||||||
if (!label) { | ||||||
RCL_SET_ERROR_MSG("State label is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
state->id = id; | ||||||
state->label = rcutils_strndup(label, strlen(label), *allocator); | ||||||
|
@@ -118,7 +124,22 @@ rcl_lifecycle_transition_init( | |||||
|
||||||
if (!transition) { | ||||||
RCL_SET_ERROR_MSG("transition pointer is null\n"); | ||||||
return RCL_RET_OK; | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
if (!label) { | ||||||
RCL_SET_ERROR_MSG("label pointer is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
if (!start) { | ||||||
RCL_SET_ERROR_MSG("start state pointer is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
if (!goal) { | ||||||
RCL_SET_ERROR_MSG("goal state pointer is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
transition->start = start; | ||||||
|
@@ -140,7 +161,7 @@ rcl_lifecycle_transition_fini( | |||||
const rcl_allocator_t * allocator) | ||||||
{ | ||||||
if (!allocator) { | ||||||
RCL_SET_ERROR_MSG("can't initialize transition, no allocator given\n"); | ||||||
RCL_SET_ERROR_MSG("can't finalize transition, no allocator given\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
// it is already NULL | ||||||
|
@@ -192,6 +213,14 @@ rcl_lifecycle_state_machine_init( | |||||
bool default_states, | ||||||
const rcl_allocator_t * allocator) | ||||||
{ | ||||||
if (!state_machine) { | ||||||
RCL_SET_ERROR_MSG("State machine is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
if (!node_handle) { | ||||||
RCL_SET_ERROR_MSG("Node handle is null\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
if (!allocator) { | ||||||
RCL_SET_ERROR_MSG("can't initialize state machine, no allocator given\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
@@ -207,15 +236,12 @@ rcl_lifecycle_state_machine_init( | |||||
} | ||||||
|
||||||
if (default_states) { | ||||||
rcl_ret_t ret = | ||||||
rcl_lifecycle_init_default_state_machine(state_machine, allocator); | ||||||
ret = rcl_lifecycle_init_default_state_machine(state_machine, allocator); | ||||||
if (ret != RCL_RET_OK) { | ||||||
// init default state machine might have allocated memory, | ||||||
// so we have to call fini | ||||||
if (rcl_lifecycle_state_machine_fini(state_machine, node_handle, allocator) != RCL_RET_OK) { | ||||||
// error already set | ||||||
return RCL_RET_ERROR; | ||||||
} | ||||||
ret = rcl_lifecycle_state_machine_fini(state_machine, node_handle, allocator); | ||||||
return RCL_RET_ERROR; | ||||||
} | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -53,6 +53,11 @@ rcl_lifecycle_transition_map_fini( | |||||
rcl_lifecycle_transition_map_t * transition_map, | ||||||
const rcutils_allocator_t * allocator) | ||||||
{ | ||||||
if (!allocator) { | ||||||
RCL_SET_ERROR_MSG("can't free transition map, no allocator given\n"); | ||||||
return RCL_RET_ERROR; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
rcl_ret_t fcn_ret = RCL_RET_OK; | ||||||
|
||||||
// free valid transitions for all states | ||||||
|
@@ -87,15 +92,16 @@ rcl_lifecycle_register_state( | |||||
allocator, "invalid allocator", return RCUTILS_RET_INVALID_ARGUMENT) | ||||||
|
||||||
// add new primary state memory | ||||||
transition_map->states_size += 1; | ||||||
unsigned int new_states_size = transition_map->states_size + 1; | ||||||
rcl_lifecycle_state_t * new_states = allocator->reallocate( | ||||||
transition_map->states, | ||||||
transition_map->states_size * sizeof(rcl_lifecycle_state_t), | ||||||
new_states_size * sizeof(rcl_lifecycle_state_t), | ||||||
allocator->state); | ||||||
if (!new_states) { | ||||||
RCL_SET_ERROR_MSG("failed to reallocate memory for new states"); | ||||||
return RCL_RET_ERROR; | ||||||
} | ||||||
transition_map->states_size = new_states_size; | ||||||
transition_map->states = new_states; | ||||||
transition_map->states[transition_map->states_size - 1] = state; | ||||||
|
||||||
|
@@ -117,32 +123,39 @@ rcl_lifecycle_register_transition( | |||||
return RCL_RET_ERROR; | ||||||
} | ||||||
|
||||||
// we add a new transition, so increase the size | ||||||
transition_map->transitions_size += 1; | ||||||
rcl_lifecycle_state_t * goal = rcl_lifecycle_get_state(transition_map, transition.goal->id); | ||||||
if (!goal) { | ||||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING("state %u is not registered\n", transition.goal->id); | ||||||
return RCL_RET_ERROR; | ||||||
} | ||||||
// Attempt to add new transition, don't update map if it fails | ||||||
unsigned int new_transitions_size = transition_map->transitions_size + 1; | ||||||
rcl_lifecycle_transition_t * new_transitions = allocator->reallocate( | ||||||
transition_map->transitions, | ||||||
transition_map->transitions_size * sizeof(rcl_lifecycle_transition_t), | ||||||
new_transitions_size * sizeof(rcl_lifecycle_transition_t), | ||||||
allocator->state); | ||||||
if (!new_transitions) { | ||||||
RCL_SET_ERROR_MSG("failed to reallocate memory for new transitions"); | ||||||
return RCL_RET_BAD_ALLOC; | ||||||
} | ||||||
transition_map->transitions_size = new_transitions_size; | ||||||
transition_map->transitions = new_transitions; | ||||||
// finally set the new transition to the end of the array | ||||||
transition_map->transitions[transition_map->transitions_size - 1] = transition; | ||||||
|
||||||
// we have to copy the transitons here once more to the actual state | ||||||
// as we can't assign only the pointer. This pointer gets invalidated whenever | ||||||
// we add a new transition and re-shuffle/re-allocate new memory for it. | ||||||
state->valid_transition_size += 1; | ||||||
unsigned int new_valid_transitions_size = state->valid_transition_size + 1; | ||||||
rcl_lifecycle_transition_t * new_valid_transitions = allocator->reallocate( | ||||||
state->valid_transitions, | ||||||
state->valid_transition_size * sizeof(rcl_lifecycle_transition_t), | ||||||
new_valid_transitions_size * sizeof(rcl_lifecycle_transition_t), | ||||||
allocator->state); | ||||||
if (!new_valid_transitions) { | ||||||
RCL_SET_ERROR_MSG("failed to reallocate memory for new transitions on state"); | ||||||
return RCL_RET_ERROR; | ||||||
} | ||||||
state->valid_transition_size = new_valid_transitions_size; | ||||||
state->valid_transitions = new_valid_transitions; | ||||||
|
||||||
state->valid_transitions[state->valid_transition_size - 1] = transition; | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or was that semicolon intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler wouldn't let me declare
current_error
directly afterfail:
without it. The semicolon effectively creates a new statement. Should I move the variable declarations to the top?