Skip to content

fix(sidecar)!: make Windows RC notifications unload-safe - #2569

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 6 commits into
mainfrom
glopes/rc-win-notification
Sep 29, 2026
Merged

gh-worker-dd-mergequeue-cf854d[bot] merged 6 commits into
mainfrom
glopes/rc-win-notification

Conversation

@cataphract

@cataphract cataphract commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Replace the Windows remote-config notification mechanism that called
CreateRemoteThread in the client process with a client-owned auto-reset event
and Windows thread-pool wait.

The event handle is transferred to the sidecar over IPC. The sidecar signals
the event when configuration may have changed, and the client callback runs on
the process thread pool. Dropping the registration disarms the wait, cancels
callbacks that have not started, and drains any callback already running.

A stable notification ID lets the sidecar deduplicate repeated transfers of
the same event even when they have different sidecar-local HANDLE values.

Motivation

The previous sidecar implementation passed an extension DLL entry point to
CreateRemoteThread. A notification racing PHP module shutdown could execute
after the DLL was unloaded. The Windows VM Red scenario reproduced this as an
immediate c0000005 access violation in <Unloaded_php_ddtrace.dll>.

The new client-owned registration makes module shutdown independent of sidecar
work while ensuring that callback code and state are no longer in use before
the DLL is unloaded. It also avoids creating a remote thread for every
notification.

Additional Notes

This PR is stacked on #2567 and uses its optional-handle IPC support. Review the
single commit on top of glopes/ipc-changes. It should be retargeted to main
after #2567 merges.

How to test the change?

Windows Red/Green integration validation

I prepared an adopting dd-trace-php checkout which:

  • creates one process-wide notification registration during PHP MINIT;
  • gives the registration to ddog_sidecar_session_set_config for each sidecar
    connection; and
  • drops the registration during PHP MSHUTDOWN, before the extension DLL is
    unloaded.

I built that extension on the Windows Server 2019 VM with MSVC. Each stress
iteration launches a short-lived PHP CLI process which starts tracing, asks a
test agent to publish an APM_TRACING configuration update, and waits for the
sidecar's next /v0.7/config poll. The test agent records that poll, and the PHP
script prints the client.client_tracer.process_tags sent in its request body.
Adding the configuration gives the sidecar an update to notify the extension
about; terminating the process immediately afterwards exercises that
notification against extension shutdown and DLL unload.

The harness runs those PHP processes under CDB with a subprocess sidecar and a
100 ms remote-config polling interval. Every iteration must both exit
successfully and print runtime.sapi:cli among the recorded process tags. The
tag assertion proves that the sidecar made the remote-config request; a clean
process exit without exercising remote config would not pass. CDB was
configured to catch access violations and failures in unloaded module code.

  • Red: with the pre-fix libdatadog implementation, the same workload failed
    on its first iteration with c0000005 in
    <Unloaded_php_ddtrace.dll>+0x1118030. This reproduces the shutdown race: a
    sidecar-created remote thread was still executing an extension entry point
    after Windows had unloaded the extension DLL.
  • Green: with this PR and the adopting caller, all 321/321 iterations exited
    successfully and produced the expected tag. CDB reported no access violation,
    execution in <Unloaded_php_ddtrace.dll>, or other captured exception.

Finally, I ran one process with CDB breakpoints on dd-trace-php's remote-config
MSHUTDOWN function and on
ddog_sidecar_remote_config_notification_drop. Both breakpoints fired, in that
order, before the extension unloaded, and shutdown completed without an
exception. This verifies that the adopting caller invokes the draining drop
path at the point whose lifetime the fix is intended to protect.

@cataphract
cataphract requested review from a team as code owners September 24, 2026 07:38
@cataphract
cataphract added this pull request to stack #2570 September 24, 2026 07:40
@cataphract cataphract changed the title fix(sidecar): make Windows RC notifications unload-safe fix(sidecar)!: make Windows RC notifications unload-safe Sep 24, 2026
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Clippy Allow Annotation Report

Tracked Clippy allow annotations changed vs main: ✅ -2 (12 → 10)

Rule Base PR Δ
unwrap_used 9 7 ✅ -2
By file and crate

By file

File Base PR Δ
datadog-sidecar/src/service/sidecar_server.rs 6 4 ✅ -2

By crate

Crate Base PR Δ
datadog-sidecar 43 41 ✅ -2

About This Report

This report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality. Panic-inducing macros in particular should be avoided. In the future, this report may become a PR-blocking quality gate.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 609a80ddbd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread datadog-sidecar/src/windows/remote_config_notification.rs
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-24T07:44:15.004866Z 609a80d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@datadog-official

datadog-official Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Tests

✅ All CI checks and tests passed.

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
• Patch Coverage: 70.00%
• Overall Coverage: 78.95% (-0.07%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 63de94e | Docs | View more details | Give us feedback!

@pr-commenter

pr-commenter Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Benchmarks

Comparison

Benchmark execution time: 2026-09-29 15:56:29

Comparing candidate commit 63de94e in PR branch glopes/rc-win-notification with baseline commit 0c8b6fd in branch main.

📊 Benchmarking dashboard

Found 4 performance improvements and 2 performance regressions! Performance is the same for 171 metrics, 0 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:ddsketch_read/ordered_bins/clustered_near_zero

  • 🟩 execution_time [-395.001ns; -391.804ns] or [-6.771%; -6.716%]

scenario:ddsketch_read/ordered_bins/collapsing

  • 🟩 execution_time [-3.041µs; -3.028µs] or [-11.202%; -11.154%]

scenario:ddsketch_read/ordered_bins/large_values

  • 🟩 execution_time [-731.543ns; -727.713ns] or [-9.502%; -9.452%]

scenario:ddsketch_read/ordered_bins/mixed

  • 🟩 execution_time [-887.919ns; -883.280ns] or [-7.704%; -7.664%]

scenario:msgpack_decoder::v05/high_sharing/10000

  • 🟥 execution_time [+439.021µs; +441.461µs] or [+5.801%; +5.834%]
  • 🟥 throughput [-72847.839op/s; -72450.889op/s] or [-5.513%; -5.483%]

Benchmark execution time: 2026-09-29 15:48:43

Comparing candidate commit 63de94e in PR branch glopes/rc-win-notification with baseline commit 0c8b6fd in branch main.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 166 metrics, 10 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:datadog_sample_span/parent_not_sampled_short_circuit/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+555.735%; -555.735%]

scenario:datadog_sample_span/parent_sampled_short_circuit/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+550.529%; -553.292%]

scenario:glob_matcher/ascii_case_insensitive_match/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+555.735%; -555.735%]

scenario:glob_matcher/ascii_exact_match/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+547.464%; -551.859%]

scenario:glob_matcher/ascii_exact_miss/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+545.651%; -551.015%]

scenario:glob_matcher/ascii_wildcard_backtrack_match/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+555.735%; -555.735%]

scenario:glob_matcher/ascii_wildcard_heavy_backtrack/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+556.598%; -556.141%]

scenario:glob_matcher/ascii_wildcard_question_match/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+555.735%; -555.735%]

scenario:glob_matcher/ascii_wildcard_star_match/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+558.075%; -556.838%]

scenario:glob_matcher/star_short_circuit/allocated_bytes

  • unstable execution_time [-0.000ns; +0.000ns] or [+556.505%; -556.097%]

Candidate

Omitted due to size.

Baseline

Omitted due to size.

@dd-octo-sts

dd-octo-sts Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 9.08 MB 9.08 MB 0% (0 B) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 96.24 MB 96.24 MB 0% (0 B) 👌
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 107.62 MB 107.62 MB 0% (0 B) 👌
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 12.20 MB 12.20 MB 0% (0 B) 👌
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 29.13 MB 29.13 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 97.60 KB 97.60 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 191.88 MB 191.90 MB +0% (+16.00 KB) 👌
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 817.94 MB 817.82 MB --.01% (-120.70 KB) 💪
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 9.74 MB 9.74 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 97.60 KB 97.60 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 27.57 MB 27.57 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 55.74 MB 55.74 MB -0% (-180 B) 👌
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 25.47 MB 25.47 MB -0% (-512 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 99.13 KB 99.13 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 197.13 MB 197.16 MB +.01% (+24.00 KB) 🔍
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 804.90 MB 804.11 MB --.09% (-806.37 KB) 💪
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 7.56 MB 7.56 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 99.13 KB 99.13 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 29.69 MB 29.69 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 52.67 MB 52.67 MB +0% (+202 B) 👌
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 86.23 MB 86.23 MB 0% (0 B) 👌
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 10.07 MB 10.07 MB 0% (0 B) 👌
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 102.08 MB 102.08 MB 0% (0 B) 👌
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 12.31 MB 12.31 MB 0% (0 B) 👌

@bwoebi bwoebi left a comment

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 haven't tested this out, but seeing CI working, and the fundamental idea being in line with the expectations I have towards the notification system, this gets a thumbs up from me!

Thank you Gustavo!

Base automatically changed from glopes/ipc-changes to main September 28, 2026 16:40
The sidecar previously called CreateRemoteThread in the client process
with an entry point inside the extension DLL. A notification racing
module shutdown could therefore execute code after the DLL was unloaded,
causing an access violation in Unloaded_php_ddtrace.dll.

Replace the remote thread with an auto-reset event transferred to the
sidecar. The sidecar signals the event, while a client-owned thread-pool
wait invokes the callback. Dropping the registration disarms the wait,
cancels callbacks that have not started, and waits for any callback
already running before releasing its state. It does not wait for sidecar
work during shutdown.

A stable ID lets the sidecar deduplicate repeated transfers of one
notification. Each transfer may produce a different sidecar-local
HANDLE value even though all duplicates refer to the same event, so
the HANDLE value alone is not a stable identity.

This removes the unload race and avoids creating a remote thread for
every notification.
@cataphract
cataphract force-pushed the glopes/rc-win-notification branch from 609a80d to 6117b96 Compare September 29, 2026 11:05
@cataphract

Copy link
Copy Markdown
Contributor Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-09-29 11:12:53 UTC ℹ️ Start processing command /merge


2026-09-29 11:13:00 UTC ℹ️ MergeQueue: Pull request is not mergeable yet

It will be processed automatically as soon as GitHub reports it as mergeable. View in MergeQueue UI.

  • Run /code blockers to see what is blocking it.
  • Run /remove to cancel it.

2026-09-29 14:39:13 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in main is approximately 47m (p90).


2026-09-29 14:55:00 UTC ❌ MergeQueue: This merge request was updated

This PR is rejected because it was updated

@cataphract

Copy link
Copy Markdown
Contributor Author

/merge -c

Still missing an approval

@gh-worker-devflow-routing-ef8351

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-09-29 11:38:06 UTC ℹ️ Start processing command `/merge -c

Still missing an approval`
If you need support, contact us on Slack #devflow.!


2026-09-29 11:38:07 UTC 🚨 Devflow: *`/merge -c

Still missing an approval`*

Failed to generate input: unknown flag: -

If you need support, contact us on Slack #devflow. with those details!

winapi-rs is effectively unmaintained and has not received a commit
since November 2021. Migrate direct consumers to the actively maintained
windows-sys crate.

The existing page_size and sysinfo dependencies are retained, so
winapi remains in the lockfile transitively through both crates.

See the upstream supersession discussion:

retep998/winapi-rs#1055

@bantonsson bantonsson left a comment

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.

Solid fix. Only minor doc comment since the doc threw me off a bit.

Comment thread datadog-sidecar-ffi/src/remote_config_notification.rs Outdated
@cataphract
cataphract requested a review from a team as a code owner September 29, 2026 13:48
@cataphract

Copy link
Copy Markdown
Contributor Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-09-29 14:59:14 UTC ℹ️ Start processing command /merge


2026-09-29 14:59:21 UTC ℹ️ MergeQueue: Pull request is not mergeable yet

It will be processed automatically as soon as GitHub reports it as mergeable. View in MergeQueue UI.

  • Run /code blockers to see what is blocking it.
  • Run /remove to cancel it.

2026-09-29 16:01:30 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in main is approximately 47m (p90).


2026-09-29 16:39:53 UTC ℹ️ MergeQueue: This merge request was merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants