Package: src:python-pyinstrument
Version: 5.1.2+ds-1
User: [email protected]
Usertags: python3.15
Tags: patch, ftbfs, forky, sid
Severity: important

Hi!

While rebuilding the python related packages against the Python 3.15rc2
version we found that python-pyinstrument fails to build from source
[1].

The error is:
test/low_level/test_timing_thread.py Fatal Python error: Aborted

An immediate abort is triggered due to a double release_lock, in Python
3.15 this is checked while in previous versions it wasn't. To fix it,
I've added a patch that guards the call to PyThread_release_lock,
checking the removals value.

I've applied this fix in the sandbox [2] to verify that it builds
successfully, please consider applying the patch to support the upcoming
3.15 version. This patch should also be forwarded upstream.

Setting the severity to important for now. Once Python 3.15 is released,
it will be added to python3-defaults and this bug will become release
critical.

Happy hacking,

[1]: https://debusine.debian.net/debian/r-python-python3.15/artifact/4598672/
[2]: https://debusine.debian.net/debian/r-python-python3.15/

--
"Can you imagine what I would do if I could do all I can?" -- Sun Tzu
Saludos /\/\ /\ >< `/
From: Maximiliano Curia <[email protected]>
Date: Tue, 8 Sep 2026 11:52:35 +0200
Subject: timing_thread: Only release lock if a subscriber was removed

Avoid releasing update_lock when unsubscribe is called on an already
removed ID or when subscriber_count is already zero, which causes a crash
on Python 3.15 due to unlocking an unlocked mutex.
---
 pyinstrument/low_level/pyi_timing_thread.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyinstrument/low_level/pyi_timing_thread.c b/pyinstrument/low_level/pyi_timing_thread.c
index ebb9394..9c0ca82 100644
--- a/pyinstrument/low_level/pyi_timing_thread.c
+++ b/pyinstrument/low_level/pyi_timing_thread.c
@@ -124,7 +124,7 @@ int pyi_timing_thread_unsubscribe(int id) {
     }
 
     // if the last subscriber was removed, stop the thread
-    if (subscriber_count == 0) {
+    if (removals > 0 && subscriber_count == 0) {
         thread_should_exit = 1;
         PyThread_release_lock(update_lock);
         thread_alive = 0;

Reply via email to