Skip to content
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

Fix ThreadError on calling stop in Signal.trap #1295

Merged
merged 2 commits into from
Sep 13, 2024
Merged

Conversation

tombruijn
Copy link
Member

@tombruijn tombruijn commented Sep 13, 2024

When Appsignal.stop is called inside a Signal.trap block, it raises a ThreadError. This is because our CheckIn::Scheduler calls Mutex#synchronize and this is not allowed in Ruby.

Unsafe methods to call in Signal.trap blocks
Mutex#lock, Mutex#synchronize and any code using them are explicitly
unsafe. This includes Monitor in the standard library which uses Mutex
to provide reentrancy.

Source: https://docs.ruby-lang.org/en/master/signals_rdoc.html

To fix this, wrap the entire Appsignal.stop method contents in a Thread, and join it at the end. This does exactly the same, but in a thread, and makes Ruby happy.

This solution is inspired by this StackOverflow answer: https://stackoverflow.com/a/28052251/543643

To test this, I didn't want to declare Signal.trap inside the RSpec process for a unit test. Instead created an integration test that runs a script that has a signal trap and calls Appsignal.stop from within that signal trap.
The Runner helper class implementation is largely copied from the Diagnose integration test submodule.


@tombruijn tombruijn added the bug label Sep 13, 2024
@tombruijn tombruijn self-assigned this Sep 13, 2024
@tombruijn tombruijn force-pushed the fix-threaderror-on-stop branch 7 times, most recently from 7a32295 to 0b7ffd7 Compare September 13, 2024 11:43
@tombruijn tombruijn marked this pull request as ready for review September 13, 2024 11:50
spec/integration/runner.rb Outdated Show resolved Hide resolved
When `Appsignal.stop` is called inside a `Signal.trap` block, it raises
a `ThreadError`. This is because our `CheckIn::Scheduler` calls
`Mutex#synchronize` and this is not allowed in Ruby.

> Unsafe methods to call in Signal.trap blocks > Mutex#lock,
Mutex#synchronize and any code using them are explicitly > unsafe. This
includes Monitor in the standard library which uses Mutex > to provide
reentrancy.

Source: https://docs.ruby-lang.org/en/master/signals_rdoc.html

To fix this, wrap the entire `Appsignal.stop` method contents in a
Thread, and join it at the end. This does exactly the same, but in a
thread, and makes Ruby happy.

This solution is inspired by this StackOverflow answer:
https://stackoverflow.com/a/28052251/543643

To test this, I didn't want to declare `Signal.trap` inside the RSpec
process for a unit test. Instead created an integration test that runs a
script that has a signal trap and calls `Appsignal.stop` from within
that signal trap.
The Runner helper class implementation is largely copied from the
Diagnose integration test submodule.
Noemi reported 0.5 seconds isn't enough on her machine so wait longer.
@tombruijn tombruijn merged commit 32323de into main Sep 13, 2024
122 checks passed
@tombruijn tombruijn deleted the fix-threaderror-on-stop branch September 13, 2024 12:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants