Skip to content

Avoid deadlocks after fork#303

Open
danth wants to merge 3 commits into
eclipse-score:mainfrom
etas-contrib:avoid-deadlocks-after-fork
Open

Avoid deadlocks after fork#303
danth wants to merge 3 commits into
eclipse-score:mainfrom
etas-contrib:avoid-deadlocks-after-fork

Conversation

@danth

@danth danth commented Jul 9, 2026

Copy link
Copy Markdown
Member

Fork only copies the current thread, so from that point on we could deadlock if we access anything that was locked by another thread at the time of the fork.

As such we should play it safe and use only basic functions. This means assertions rather than logs for fatal errors, and no logging at all on the happy path.

@danth

This comment was marked as outdated.

@danth danth force-pushed the avoid-deadlocks-after-fork branch from bcb913a to 2a70190 Compare July 10, 2026 08:09
@danth danth requested a deployment to workflow-approval July 10, 2026 08:09 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 10, 2026 08:09 — with GitHub Actions Waiting
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

License Check Results

🚀 The license check job ran with the Bazel command:

bazel run --lockfile_mode=error //:license-check

Status: ⚠️ Needs Review

Click to expand output
[License Check Output]
Extracting Bazel installation...
Starting local Bazel server (8.6.0) and connecting to it...
INFO: Invocation ID: 3d99d55c-c2a0-46bd-9520-a0a2d624967f
Computing main repo mapping: 
Computing main repo mapping: 
Loading: 
Loading: 0 packages loaded
Loading: 0 packages loaded
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Analyzing: target //:license-check (1 packages loaded, 0 targets configured)
Analyzing: target //:license-check (1 packages loaded, 0 targets configured)

Analyzing: target //:license-check (35 packages loaded, 10 targets configured)

Analyzing: target //:license-check (80 packages loaded, 10 targets configured)

Analyzing: target //:license-check (91 packages loaded, 19 targets configured)

Analyzing: target //:license-check (144 packages loaded, 2767 targets configured)

Analyzing: target //:license-check (152 packages loaded, 6259 targets configured)

Analyzing: target //:license-check (153 packages loaded, 7251 targets configured)

Analyzing: target //:license-check (163 packages loaded, 8246 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (168 packages loaded, 10283 targets configured)

Analyzing: target //:license-check (169 packages loaded, 10409 targets configured)

INFO: Analyzed target //:license-check (169 packages loaded, 10409 targets configured).
[12 / 16] JavaToolchainCompileClasses external/rules_java+/toolchains/platformclasspath_classes; 0s disk-cache, processwrapper-sandbox ... (2 actions running)
[14 / 16] JavaToolchainCompileBootClasspath external/rules_java+/toolchains/platformclasspath.jar; 0s disk-cache, processwrapper-sandbox
INFO: Found 1 target...
Target //:license.check.license_check up-to-date:
  bazel-bin/license.check.license_check
  bazel-bin/license.check.license_check.jar
INFO: Elapsed time: 24.436s, Critical Path: 2.53s
INFO: 16 processes: 12 internal, 3 processwrapper-sandbox, 1 worker.
INFO: Build completed successfully, 16 total actions
INFO: Running command line: bazel-bin/license.check.license_check ./formatted.txt <args omitted>
usage: org.eclipse.dash.licenses.cli.Main [-batch <int>] [-cd <url>]
       [-confidence <int>] [-ef <url>] [-excludeSources <sources>] [-help] [-lic
       <url>] [-project <shortname>] [-repo <url>] [-review] [-summary <file>]
       [-timeout <seconds>] [-token <token>]

@github-actions

Copy link
Copy Markdown

The created documentation from the pull request is available at: docu-html

@danth danth force-pushed the avoid-deadlocks-after-fork branch from 2a70190 to 9cc08d6 Compare July 10, 2026 08:22
@danth danth requested a deployment to workflow-approval July 10, 2026 08:22 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 10, 2026 08:22 — with GitHub Actions Waiting
@danth danth force-pushed the avoid-deadlocks-after-fork branch from 9cc08d6 to 67c0d0f Compare July 14, 2026 09:57
@danth danth requested a deployment to workflow-approval July 14, 2026 09:57 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 14, 2026 09:57 — with GitHub Actions Waiting
@danth danth force-pushed the avoid-deadlocks-after-fork branch from 67c0d0f to 9c28979 Compare July 14, 2026 10:05
@danth danth requested a deployment to workflow-approval July 14, 2026 10:05 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 14, 2026 10:05 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 14, 2026 10:37 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 14, 2026 10:37 — with GitHub Actions Waiting
}

if (-1 == chdir(dirname(strncpy(path_copy, config.executable_path_.c_str(), string_size))))
if (config->scheduling_priority_ < sched_get_priority_min(config->scheduling_policy_))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you already want to move this to score/launch_manager/src/daemon/src/configuration/details/flatbuffer_type_converters.cpp?

}

void changeCurrentWorkingDirectory(const score::lcm::internal::osal::OsalConfig& config)
// TODO: Move this into the config loader

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think only the scheduling priority range can be checked during config loading.
The executable may not be accessible yet at that time, in case the filesystem where the executable resides is only mounted later. So I think this access check has to be done at the time when we want to start the process.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For the executable we could not check in advance and just detect it when execve fails

if (config.comms_type_ != osal::CommsType::kLaunchManager)
{
result = setpgid(0, getpid());
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result == 0, std::strerror(errno));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the strategy to use asserts here is now conflicting with this #299
where asserts actually log to std::cerr which I guess is not safe to call btw. fork() and execve() either. Also the heap allocation from std::ostringstream is probably then a problem.

Do you have any suggestion how to resolve this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We could allocate a fixed buffer for the assertion handler and call write directly, bypassing std::cerr? write is async signal safe.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It sounds like a good solution to me. What do you think @paulquiring ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We need something like this as assertion handler.

void my_signal_handler(int sig) {
    char buffer[128];
    // Format your string (snprintf is generally safe in practice though technically not guaranteed)
    int len = snprintf(buffer, sizeof(buffer), "Caught signal %d\n", sig);
    
    // write to stdout (file descriptor 1) is async-signal-safe
    write(STDOUT_FILENO, buffer, len);
}

danth added 2 commits July 15, 2026 10:50
This ensures that security properties are not accidentally left unset.
@danth danth force-pushed the avoid-deadlocks-after-fork branch from e249b48 to 4121230 Compare July 15, 2026 10:33
@danth danth requested a deployment to workflow-approval July 15, 2026 10:33 — with GitHub Actions Waiting
@danth danth requested a deployment to workflow-approval July 15, 2026 10:33 — with GitHub Actions Waiting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

4 participants