Skip to content

Acquire lock in separate thread - #802

Open
peytondmurray wants to merge 17 commits into
benfred:masterfrom
peytondmurray:732-timeout-acquire-lock
Open

Acquire lock in separate thread#802
peytondmurray wants to merge 17 commits into
benfred:masterfrom
peytondmurray:732-timeout-acquire-lock

Conversation

@peytondmurray

@peytondmurray peytondmurray commented Sep 10, 2025

Copy link
Copy Markdown
Contributor

This PR modifies the way that the process to be inspected gets locked.

Before, py-spy would first check the process for active threads before locking the process (otherwise all threads are reported as being idle). If the process exited before a lock could be acquired, py-spy would hang while waiting for the status of the PID to change.

With this PR, acquiring the lock on the process happens in a separate thread; if the thread doesn't acquire a lock in a specified time (default is 1s, but this is a config variable. If this is too short we can increase it) we simply error out instead of hanging. Closes #732.

I've also fixed an issue with installing the numpy>=2 optional test requirement in the test workflow.

@peytondmurray
peytondmurray force-pushed the 732-timeout-acquire-lock branch from 3f1139e to e55e340 Compare September 30, 2025 16:28
@peytondmurray

peytondmurray commented Sep 30, 2025

Copy link
Copy Markdown
Contributor Author

Okay, this ended up being trickier than I thought, because not only do you need a locker thread responsible for locking the target process, but you also need to keep the remoteprocess::Lock that gets acquired in that thread alive until samples are taken. If you don't do this, the remoteprocess::Lock will unpause the target thread when it gets dropped as the locker thread exits, screwing up the sampling.

So my original solution in which the locker thread finishes execution immediately, it would just instantly unpause the target thread before samples could be taken. Happily this is something we're testing for with test_recursive, so I was able to catch it.

Please let me know if you have any comments or if you feel this could be handled more gracefully, I'd be grateful for any feedback.

@peytondmurray peytondmurray changed the title [WIP] Acquire lock in separate thread Acquire lock in separate thread Sep 30, 2025
@peytondmurray
peytondmurray marked this pull request as ready for review September 30, 2025 22:23
@peytondmurray
peytondmurray force-pushed the 732-timeout-acquire-lock branch from a7c93d8 to 92fd140 Compare September 30, 2025 22:26
@peytondmurray

peytondmurray commented Oct 11, 2025

Copy link
Copy Markdown
Contributor Author

Ack, I pushed the last commit after testing (and passing locally on linux), and then was away for the past couple of weeks. I see now the Windows tests are failing, but I don't know why. I'll try spinning up a VM to test this out, but any help here would be appreciated. Moving back into draft until this gets resolved.

@peytondmurray
peytondmurray marked this pull request as draft October 11, 2025 20:51
@peytondmurray peytondmurray changed the title Acquire lock in separate thread [WIP] Acquire lock in separate thread Oct 11, 2025
@Carreau

Carreau commented Feb 10, 2026

Copy link
Copy Markdown

I pushed an empty commit to re-trigger CI, but workflow needs approval again; as far as I can tell the failing test now pass on my fork I think (https://github.com/Carreau/py-spy/actions/runs/21870162508/job/63122773473)

@peytondmurray

peytondmurray commented Feb 10, 2026

Copy link
Copy Markdown
Contributor Author

☝️ Thanks for double checking about this. Let me mark this as ready for review.

@peytondmurray
peytondmurray marked this pull request as ready for review February 10, 2026 19:47
@peytondmurray peytondmurray changed the title [WIP] Acquire lock in separate thread Acquire lock in separate thread Mar 20, 2026
@peytondmurray

Copy link
Copy Markdown
Contributor Author

Hey all, it would be great to see this merged at some point - would it be possible to retrigger CI?

@pezosanta

pezosanta commented Jul 2, 2026

Copy link
Copy Markdown

Hello @peytondmurray !

When can we expect this feature to be merged and released? :)

@peytondmurray

Copy link
Copy Markdown
Contributor Author

Hey @pezosanta, thanks for the tag: looks like I need to resolve these conflicts. I'll take care of that today, but I don't have the power to approve workflows or merge these changes.

@peytondmurray

Copy link
Copy Markdown
Contributor Author

Hi all,

In resolving the conflicts with the current master I realized that test_busy_loop would intermittently fail with my previous implementation. This happened because the TestRunner struct used in that test would attempt to get stack traces from a busylooping python thread, sometimes multiple times in a row.

Previously with the locking mechanism that I implemented above, the separate locker thread would only block the main thread when the main thread requested the target process to be locked, but not when it was released. When the TestRunner tried to get stack traces from the busylooping python thread multiple times (e.g. while the interpreter started up), it would in some cases try to acquire a lock on the thread before the previous call's locker thread had released it.

Now I've implemented an additional check: the main thread will now block until it receives an acknowledgement that the locker thread has released the lock on the target python thread. That seems to have resolved the issue, and I've documented how it works in a diagram in PythonSpy::lock_process_with_timeout.

I'd appreciate a second look if anyone has time. And please let me know if there's anything I can do to get this across the finish line!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multiprocessing Deadlock with v0.4.0

3 participants