Skip to content

fix interleaved output in the default panic hook when multiple threads panic simultaneously - #127397

Merged
bors merged 2 commits into
rust-lang:masterfrom
jyn514:multi-thread-panic-hook
Jul 13, 2024
Merged

fix interleaved output in the default panic hook when multiple threads panic simultaneously#127397
bors merged 2 commits into
rust-lang:masterfrom
jyn514:multi-thread-panic-hook

Conversation

@jyn514

@jyn514 jyn514 commented Jul 5, 2024

Copy link
Copy Markdown
Member

previously, we only held a lock for printing the backtrace itself. since all threads were printing to the same file descriptor, that meant random output in the default panic hook from one thread would be interleaved with the backtrace from another. now, we hold the lock for the full duration of the hook, and the output is ordered.


i noticed some odd things while working on this you may or may not already be aware of.

  • libbacktrace is included as a submodule instead of a normal rustc crate, and as a result uses cfg(backtrace_in_std) instead of a more normal cfg(feature = "rustc-dep-of-std"). probably this is left over from before rust used a cargo-based build system?
  • the default panic handler uses trace_unsynchronized, etc, in sys::backtrace::print. as a result, the lock only applies to concurrent panic handlers, not concurrent threads. in other words, if another, non-panicking, thread tried to print a backtrace at the same time as the panic handler, we may have UB, especially on windows.
    • we have the option of changing backtrace to enable locking when backtrace_in_std is set so we can reuse their lock instead of trying to add our own.

@rustbot

rustbot commented Jul 5, 2024

Copy link
Copy Markdown
Collaborator

r? @workingjubilee

rustbot has assigned @workingjubilee.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 5, 2024
@workingjubilee

Copy link
Copy Markdown
Member

libbacktrace is included as a submodule instead of a normal rustc crate, and as a result uses cfg(backtrace_in_std) instead of a more normal cfg(feature = "rustc-dep-of-std"). probably this is left over from before rust used a cargo-based build system?

I suspect this is more due to us having needed to internalize, as much as possible, the horrible details of backtraces.

@workingjubilee

Copy link
Copy Markdown
Member

I also suspect that in practice, backtraces on Windows are actually much more tame now due to us using RtlVirtualUnwind. Am I correct in that, @ChrisDenton?

@ChrisDenton

Copy link
Copy Markdown
Member

Yes and no. Symbolization is still a problem area. The APIs we're using are designed to be run from an external "debugger" process and not in-process.

@jyn514

jyn514 commented Jul 5, 2024

Copy link
Copy Markdown
Member Author

i did find a mutex in windows that looks like it takes care of the multi-threading issues for us: https://github.com/rust-lang/backtrace-rs/blob/72265bea210891ae47bbe6d4f17b493ef0606619/src/dbghelp.rs#L283-L376

@ChrisDenton

Copy link
Copy Markdown
Member

Yeah, we need a special process-wide mutex so that it works if multiple Rust DLLs are in the same process (doesn't help if another language or library uses these functions though).

@workingjubilee

Copy link
Copy Markdown
Member

Yeah, we need a special process-wide mutex so that it works if multiple Rust DLLs are in the same process (doesn't help if another language or library uses these functions though).

that sounds fine, tbh. "randomly-included C code" can't coordinate perfectly on this, so we can't either, and our responsibility is discharged by doing the best we can.

Comment thread library/std/src/sys/backtrace.rs Outdated
Comment thread tests/ui/backtrace/synchronized-panic-handler.run.stderr Outdated
@workingjubilee

Copy link
Copy Markdown
Member

 @bors r+

@bors

bors commented Jul 8, 2024

Copy link
Copy Markdown
Collaborator

📌 Commit 875b730 has been approved by workingjubilee

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 8, 2024
workingjubilee pushed a commit to pgcentralfoundation/pgrx that referenced this pull request Jul 8, 2024
pgrx has somewhat complex panic handling. it looks something like this:

1. when a thread panics, the panic hook captures a backtrace and saves
it in a thread-local for later.
2. the thread unwinds until it hits an FFI boundary (usually
`run_guarded`). that downcasts the panic, takes the backtrace out of the
thread-local, and hooks into postgres' `longjmp` mechanism
3. i forget what happens after this, i think it resumes unwinding once
it's past the FFI barrier

there is a slight problem here: we are using a thread-local to store the
backtrace. if the panic does not happen on the main thread (for example,
because a spawned thread tries to call into postgres and hits the check
in `check_active_thread`), the backtrace will be lost. worse, if the
main thread then unwinds in response to the panic, pgrx will use *its*
backtrace instead of that of the worker thread.

there are two main approaches we considered to fixing this:
1. fix the backtrace not to use a thread-local, so we can attach panics
in spawned threads to a pgrx connection the way we would for the main
thread.
2. stop handling panics in spawned threads altogether (and use the
default hook).

the downside of approach 1 is that there may not *be* a pgrx connection
to attach to. the connection may have already closed, or the active
connection may not be related to the thread that panicked, or we may be
shutting down and will never check for the panic. in those cases the
panic information will be missing or wrong.

the downside of approach 2 is that it does not integrate with postgres'
error handling mechanism, and in particular is not reported to psql.
however, it does allow for developers using pgrx to handle the panic
themselves, for example by handling the result from `JoinHandle::join`,
in which case it *will* be reported to psql.

this takes approach 2. we may want to reconsider this in the future, or
perhaps add a helper library so that it's easy for applications to pass
the panic into the main thread.

---

note that the default panic handler in the standard library behaves
quite poorly when multiple threads panic at once (it's sound, but the
output is very hard to read). this being fixed in a separate PR
upstream; see rust-lang/rust#127397
@matthiaskrgr

ghost commented Jul 9, 2024

Copy link
Copy Markdown
Member

@bors r-
#127519 (comment)

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 9, 2024
@jyn514

ghost commented Jul 9, 2024

Copy link
Copy Markdown
Member Author

lol apparently some test runners set RUST_BACKTRACE=1 on globally? no idea why
anyway i should be able to just unset it for this test

@jyn514
jyn514 force-pushed the multi-thread-panic-hook branch from 875b730 to ae92d6a Compare July 9, 2024 17:55
@workingjubilee

ghost commented Jul 12, 2024

Copy link
Copy Markdown
Member

Ah, I really should have remembered and asked for //@ needs-threads

@bors r-

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 12, 2024
@jyn514

ghost commented Jul 12, 2024

Copy link
Copy Markdown
Member Author

could you mark this as rollup=iffy?

previously, we only held a lock for printing the backtrace itself. since all threads were printing to the same file descriptor, that meant random output in the default panic hook would be interleaved with the backtrace. now, we hold the lock for the full duration of the hook, and the output is ordered.
@jyn514
jyn514 force-pushed the multi-thread-panic-hook branch from 3a1dd85 to 1c8f9bb Compare July 12, 2024 15:53
@workingjubilee

ghost commented Jul 12, 2024

Copy link
Copy Markdown
Member

yeah.

@bors rollup=iffy r+

@bors

ghost commented Jul 12, 2024

Copy link
Copy Markdown
Collaborator

📌 Commit 1c8f9bb has been approved by workingjubilee

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 12, 2024
@bors

ghost commented Jul 13, 2024

Copy link
Copy Markdown
Collaborator

⌛ Testing commit 1c8f9bb with merge 0065384...

@bors

ghost commented Jul 13, 2024

Copy link
Copy Markdown
Collaborator

☀️ Test successful - checks-actions
Approved by: workingjubilee
Pushing 0065384 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Jul 13, 2024
@bors
bors merged commit 0065384 into rust-lang:master Jul 13, 2024
@rustbot rustbot added this to the 1.81.0 milestone Jul 13, 2024
@rust-timer

ghost commented Jul 13, 2024

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (0065384): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

This benchmark run did not return any relevant results for this metric.

Cycles

This benchmark run did not return any relevant results for this metric.

Binary size

Results (primary -0.0%, secondary -0.0%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.0% [-0.0%, -0.0%] 1
Improvements ✅
(secondary)
-0.0% [-0.0%, -0.0%] 14
All ❌✅ (primary) -0.0% [-0.0%, -0.0%] 1

Bootstrap: 706.568s -> 706.526s (-0.01%)
Artifact size: 328.69 MiB -> 328.84 MiB (0.05%)

@jyn514
jyn514 deleted the multi-thread-panic-hook branch July 20, 2024 02:24
@jyn514

ghost commented Jul 20, 2024

Copy link
Copy Markdown
Member Author

congrats to @rust-lang/wg-compiler-performance, this is a really impressively low amount of noise

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

Labels

merged-by-bors This PR was explicitly merged by bors. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants