Skip to content

Conversation

cottsay
Copy link
Member

@cottsay cottsay commented Mar 24, 2025

This new function and associated functions provide a cross-platform mechanism for invoking a command as a subprocess of the currently running process.

There is some nuanced behavior for how process IDs are freed on Linux and Windows that influenced this API:

  1. On Windows, the PID of a subprocess is released when the process exits and there are no open handles to it. Therefore, we must maintain an open handle to the subprocess until we've obtained the exit code and no longer need it.
  2. On Linux, the PID is released when the parent process has read the exit code once.

Requires #490

@cottsay cottsay added the enhancement New feature or request label Mar 24, 2025
@cottsay cottsay requested review from mjcarroll and ahcorde March 24, 2025 20:51
@cottsay cottsay self-assigned this Mar 24, 2025
@cottsay
Copy link
Member Author

cottsay commented Mar 24, 2025

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

src/process.c Outdated

int exec_ret = execvp(cmd, argv.data);

RCUTILS_SAFE_FWRITE_TO_STDERR_WITH_FORMAT_STRING("Failed to execute process: %d", exec_ret);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RCUTILS_SAFE_FWRITE_TO_STDERR_WITH_FORMAT_STRING("Failed to execute process: %d", exec_ret);
if (exec_ret == -1) {
int error = errno;
RCUTILS_SAFE_FWRITE_TO_STDERR_WITH_FORMAT_STRING("Failed to execute process: %d (%s)", error, strerror(error));
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

execvp will only return if there is an error. The docs say it will return -1, but I'm not really sure what we'd do in this process if it returned anything else anyway - we'd still want to let the user know that something went wrong. I'll take the strerror change though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the error string portion in 7d97d42.

src/process.c Outdated
Comment on lines 189 to 191
for(size_t i = 0; i < argv.size; i++) {
argv.data[i] = NULL;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i do not think we need to do this, because rcutils_string_array_fini does that with deallocation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually "borrowing" the individual strings from the passed argument array, and if they're still referenced in argv when I deallocate it, they will be deallocated as well.

This really shouldn't be an rcutils_string_array_t anyway. I think I'll switch to just a calloc/memcpy to simplify the code and make that clearer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to a standalone zero_allocate in 7d97d42.

&allocator, "allocator is invalid", return );

#if defined _WIN32 || defined __CYGWIN__
CloseHandle(process->handle);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious here, Linux doesn't require to close the process ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux doesn't actually open any handles to the process, as far as I can tell. The PID of the child process will remain allocated until the parent process reads the exit code from that process once, at which point the PID is released for reuse elsewhere.

This is why I note in the docs:

Upon successful invocation of this function, subsequent calls will produce undefined behavior. It should typically be followed by a call to rcutils_process_close().

Base automatically changed from cottsay/string_join to rolling March 26, 2025 08:04
This new function and associated functions provide a cross-platform
mechanism for invoking a command as a subprocess of the currently
running process.

Signed-off-by: Scott K Logan <logans@cottsay.net>
@cottsay cottsay force-pushed the cottsay/subprocess branch from b0b8737 to 7d97d42 Compare March 26, 2025 16:29
@cottsay
Copy link
Member Author

cottsay commented Mar 26, 2025

Alright, I rebased to take the string join change from rolling and addressed all feedback (thank you).

@cottsay cottsay requested review from ahcorde and fujitatomoya March 26, 2025 16:30
Signed-off-by: Scott K Logan <logans@cottsay.net>
@cottsay cottsay force-pushed the cottsay/subprocess branch from 13872ec to 5f893a3 Compare March 26, 2025 16:35
@cottsay
Copy link
Member Author

cottsay commented Mar 26, 2025

Fresh CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@cottsay cottsay marked this pull request as ready for review March 26, 2025 16:44
Copy link
Collaborator

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm with green CI

@cottsay cottsay merged commit 937efe2 into rolling Mar 26, 2025
2 checks passed
@cottsay cottsay deleted the cottsay/subprocess branch March 26, 2025 20:17
@ahcorde ahcorde mentioned this pull request Mar 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants