GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-144763: Don't detach the GIL in tracemalloc by vstinner · Pull Request #144779 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Feb 13, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

tracemalloc no longer detachs the GIL to acquire its internal lock.

  • Issue: Crash in _io_TextIOWrapper_write_impl when running tracemalloc.clear_traces in threads #144763

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
pythongh-144763: Don't detach the GIL in tracemalloc
00c69d2
tracemalloc no longer detachs the GIL to acquire its internal lock.
@bedevere-app bedevere-app Bot added the awaiting core review label Feb 13, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Feb 13, 2026
Crash in _io_TextIOWrapper_write_impl when running tracemalloc.clear_traces in threads #144763
Closed
@vstinner
vstinner marked this pull request as draft February 13, 2026 10:18
@bedevere-app bedevere-app Bot removed the awaiting core review label Feb 13, 2026
@vstinner

vstinner commented Feb 13, 2026

Copy link
Copy Markdown
Member Author

This change basically reverts commit 01157e0 (PR gh-139449) which fixed another deadlock on tracemalloc.stop() :-) See issue gh-139116.

cc @ZeroIntensity @encukou

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Fix race condition in _PyTraceMalloc_Stop()
a91fc22
Don't call PyRefTracer_SetTracer() while holding TABLES_LOCK().
@vstinner

vstinner commented Feb 13, 2026

Copy link
Copy Markdown
Member Author

This change basically reverts commit 01157e0 (PR #139449) which fixed another deadlock on tracemalloc.stop() :-) See issue #139116.

I pushed a change to avoid the deadlock on tracemalloc.stop(): don't call PyRefTracer_SetTracer() while holding TABLES_LOCK().

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner marked this pull request as ready for review February 13, 2026 16:20
@bedevere-app bedevere-app Bot added the awaiting core review label Feb 13, 2026
kumaraditya303
kumaraditya303 reviewed Feb 14, 2026
View reviewed changes
Comment thread Python/tracemalloc.c
int lineno = -1;
PyCodeObject *code = _PyFrame_GetCode(pyframe);
// PyUnstable_InterpreterFrame_GetLine() cannot but used, since it uses
// a critical section which can trigger a deadlock.

@kumaraditya303 kumaraditya303 Feb 14, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I think the problem is that critical sections requires an active thread state and this code can be called with detached thread state iirc.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Feb 16, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

tracemalloc_get_frame() is called with an attached thread state, see the caller traceback_get_frames() which has the code:

    PyThreadState *tstate = _PyThreadState_GET();
    assert(tstate != NULL);

Example of deadlock when running #144763 (comment) reproducer on a free-threaded build:

  • Thread A: _PyCode_GetTLBC() => ... => tracemalloc_alloc(): TABLES_LOCK()
  • Thread B: _PyTraceMalloc_TraceRef() => ... => tracemalloc_get_frame() => PyUnstable_InterpreterFrame_GetLine() => PyCode_Addr2Line(): Py_BEGIN_CRITICAL_SECTION(co)

Locks:

  • Thread A is in a Py_BEGIN_CRITICAL_SECTION(co) critical section (_PyCode_GetTLBC()) and waits for TABLES_LOCK().
  • Thread B has TABLES_LOCK() lock and waits for Py_BEGIN_CRITICAL_SECTION(co) critical section.

Thread A and thread B want to use Py_BEGIN_CRITICAL_SECTION(co) on the same code object (0x20000844810).

=> deadlock :-(

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@colesbury colesbury Feb 17, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

There are probably some thread-safety issues here if the code object is being concurrently instrumented or deinstrumented. I don't see a better way of handling it, so let's leave a TODO or note.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
ZeroIntensity
ZeroIntensity reviewed Feb 16, 2026
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Is it necessary to keep the attached thread state in TABLES_LOCK? It seems the bulk of the fix is just calling PyRefTracer_SetTracer without holding the lock.

Alternatively, we could just use Py_BEGIN_CRITICAL_SECTION_MUTEX instead of PyMutex_Lock.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Misc/NEWS.d/next/Library/2026-02-13-11-14-18.gh-issue-144763.cDwnEE.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 16, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Is it necessary to keep the attached thread state in TABLES_LOCK? It seems the bulk of the fix is just calling PyRefTracer_SetTracer without holding the lock.

There are two problems:

  • (A) detaching the thread state in TABLES_LOCK() breaks a lot of assumptions in the _io module about critical sections, the GC and memory allocators leading to race conditions. See Crash in _io_TextIOWrapper_write_impl when running tracemalloc.clear_traces in threads #144763 (comment) for the problem on the _io.TextIOWrapper.write() method.
  • (B) Deadlock involving PyRefTracer_SetTracer() in tracemalloc.stop(): see issue Potential deadlock in test_tracemalloc with the FT build and tail-call dispatch. #139116. My fix is to call PyRefTracer_SetTracer() without holding TABLES_LOCK() (which is safe at this point in _PyTraceMalloc_Stop()).

Yes, I think that it's necessary to keep the attached thread state in TABLES_LOCK (not detach the thread state).

Alternatively, we could just use Py_BEGIN_CRITICAL_SECTION_MUTEX instead of PyMutex_Lock.

What would be the benefits?

ZeroIntensity reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Rephase the NEWS entry using "attached thread state"
78ceae3
@colesbury

colesbury commented Feb 17, 2026

Copy link
Copy Markdown
Contributor

Using _Py_LOCK_DONT_DETACH in TABLES_LOCK() definitely seems like the right approach for the allocator calls (i.e., tracemalloc_alloc/tracemalloc_free). We don't want to detach the thread state there.

I'm not really sure about _PyTraceMalloc_Stop() and _PyTraceMalloc_Start(). Should these be safe to call concurrently? The implementation isn't thread-safe for concurrent calls to tracemalloc.start() or tracemalloc.stop(). Maybe that's fine?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 17, 2026

Copy link
Copy Markdown
Member Author

I'm not really sure about _PyTraceMalloc_Stop() and _PyTraceMalloc_Start(). Should these be safe to call concurrently? The implementation isn't thread-safe for concurrent calls to tracemalloc.start() or tracemalloc.stop(). Maybe that's fine?

I'm only aware of #128679 : concurrent calls to PyTraceMalloc_Track() and tracemalloc.stop() (case tested by test_tracemalloc.test_tracemalloc_track_race()).

I don't think that two threads should be able to call tracemalloc.stop() and tracemalloc.start() in parallel. I expect that only a single thread (the main thread?) would call these two methods.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ZeroIntensity

ZeroIntensity commented Feb 18, 2026

Copy link
Copy Markdown
Member

Could we use _Py_LOCK_DONT_DETACH in TABLES_LOCK, but use a normal (detaching) call in start/stop? That should avoid the reftracer deadlock without needing to move the call to PyRefTracer_SetTracer.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ZeroIntensity

ZeroIntensity commented Feb 18, 2026

Copy link
Copy Markdown
Member

Nevermind, that would lead to a lock-ordering deadlock:

  1. Thread A releases the GIL and acquires TABLES_LOCK.
  2. Thread B acquires the GIL and begins waiting on TABLES_LOCK.
  3. Thread A is waiting on the GIL, which thread B holds.
  4. Deadlock!

If I remember correctly, tracemalloc still needs more work to avoid crashes when running concurrently (particularly in subinterpreters), so I think it would be fine to leave start/stop as is in this PR and address it later.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 18, 2026

Copy link
Copy Markdown
Member Author

@kumaraditya303 @colesbury @ZeroIntensity: So what do you think of this change? Does it look good to you?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

kumaraditya303
kumaraditya303 reviewed Feb 18, 2026
View reviewed changes
Comment thread Python/tracemalloc.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity
ZeroIntensity approved these changes Feb 18, 2026
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I think this makes sense.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting core review labels Feb 18, 2026
@vstinner @kumaraditya303
Update Python/tracemalloc.c
609f7fb
Co-authored-by: Kumar Aditya <[email protected]>
colesbury
colesbury approved these changes Feb 18, 2026
View reviewed changes
colesbury
colesbury reviewed Feb 18, 2026
View reviewed changes
Comment thread Python/tracemalloc.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

colesbury
colesbury reviewed Feb 18, 2026
View reviewed changes
Comment thread Misc/NEWS.d/next/Library/2026-02-13-11-14-18.gh-issue-144763.cDwnEE.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Fix typos
ff3241a
kumaraditya303
kumaraditya303 approved these changes Feb 18, 2026
View reviewed changes
@vstinner
vstinner enabled auto-merge (squash) February 18, 2026 15:35
@vstinner
vstinner merged commit 83f4fff into python:main Feb 18, 2026
47 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner deleted the tracemalloc_dont_detach branch February 18, 2026 15:57
@bedevere-app bedevere-app Bot removed the awaiting merge label Feb 18, 2026
vstinner added a commit to vstinner/cpython that referenced this pull request Feb 18, 2026
@vstinner @kumaraditya303
pythongh-144763: Don't detach the GIL in tracemalloc (python#144779)
20009b2
tracemalloc no longer detaches the GIL to acquire its internal lock.

Co-authored-by: Kumar Aditya <[email protected]>
(cherry picked from commit 83f4fff)
vstinner added a commit to vstinner/cpython that referenced this pull request Feb 18, 2026
@vstinner @kumaraditya303
pythongh-144763: Fix race conditions in tracemalloc (python#144779)
de4f844
Avoid PyUnstable_InterpreterFrame_GetLine() since it uses a critical
section which can lead to a deadlock.

_PyTraceMalloc_Stop() now also calls PyRefTracer_SetTracer() without
holding TABLES_LOCK() to prevent another deadlock.

Co-authored-by: Kumar Aditya <[email protected]>
(cherry picked from commit 83f4fff)
@bedevere-app

bedevere-app Bot commented Feb 18, 2026

Copy link
Copy Markdown

GH-144965 is a backport of this pull request to the 3.14 branch.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Feb 18, 2026

Copy link
Copy Markdown
Member Author

Backport of the fix to 3.14: #144965. The backport is different since the 3.14 code already avoided to detach the thread safe.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner added a commit that referenced this pull request Feb 18, 2026
@vstinner @kumaraditya303
[3.14] gh-144763: Fix race conditions in tracemalloc (#144779) (#144965)
f67cf83
gh-144763: Fix race conditions in tracemalloc (#144779)

Avoid PyUnstable_InterpreterFrame_GetLine() since it uses a critical
section which can lead to a deadlock.

_PyTraceMalloc_Stop() now also calls PyRefTracer_SetTracer() without
holding TABLES_LOCK() to prevent another deadlock.

(cherry picked from commit 83f4fff)

Co-authored-by: Kumar Aditya <[email protected]>
@bedevere-bot

bedevere-bot commented Feb 18, 2026

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 FreeBSD Refleaks 3.14 (tier-3) has failed when building commit f67cf83.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1800/builds/1004) and take a look at the build logs.
  4. Check if the failure is related to this commit (f67cf83) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1800/builds/1004

Test leaking resources:

  • test_events: references
  • test_events: memory blocks

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/test/support/__init__.py", line 859, in gc_collect
    gc.collect()
ResourceWarning: unclosed <socket.socket fd=9, family=2, type=1, proto=6, laddr=('127.0.0.1', 15216), raddr=('127.0.0.1', 15217)>
Task was destroyed but it is pending!
task: <Task pending name='Task-4077' coro=<BaseSelectorEventLoop._accept_connection2() done, defined at /buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py:217> wait_for=<Future finished exception=ConnectionResetError()>>
Future exception was never retrieved
future: <Future finished exception=ConnectionResetError()>
Traceback (most recent call last):
  File "/buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/asyncio/sslproto.py", line 581, in _on_handshake_complete
    raise handshake_exc
ConnectionResetError
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x847dcc4d0>:
Traceback (most recent call last):
  File "/buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py", line 873, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=9>
k


Traceback (most recent call last):
  File "/buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/test/support/__init__.py", line 859, in gc_collect
    gc.collect()
ResourceWarning: unclosed <socket.socket fd=8, family=2, type=1, proto=6, laddr=('127.0.0.1', 11443), raddr=('127.0.0.1', 11444)>
Task was destroyed but it is pending!
task: <Task pending name='Task-4078' coro=<BaseSelectorEventLoop._accept_connection2() done, defined at /buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py:217> wait_for=<Future pending cb=[Task.task_wakeup()]>>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x844ba84d0>:
Traceback (most recent call last):
  File "/buildbot/buildarea/3.14.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py", line 873, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=8>
k
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

brijkapadia pushed a commit to brijkapadia/cpython that referenced this pull request Feb 28, 2026
@vstinner @kumaraditya303 @brijkapadia
pythongh-144763: Don't detach the GIL in tracemalloc (python#144779)
e19d9ae
tracemalloc no longer detaches the GIL to acquire its internal lock.

Co-authored-by: Kumar Aditya <[email protected]>
ljfp pushed a commit to ljfp/cpython that referenced this pull request Apr 25, 2026
@vstinner @kumaraditya303 @ljfp
pythongh-144763: Don't detach the GIL in tracemalloc (python#144779)
d3cd2e7
tracemalloc no longer detaches the GIL to acquire its internal lock.

Co-authored-by: Kumar Aditya <[email protected]>
@vstinner vstinner mentioned this pull request Apr 30, 2026
gh-139116: Skip test_tracemalloc_track_race under ASAN #139448
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@colesbury colesbury colesbury approved these changes
@ZeroIntensity ZeroIntensity ZeroIntensity approved these changes
@kumaraditya303 kumaraditya303 kumaraditya303 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@vstinner @colesbury @ZeroIntensity @bedevere-bot @kumaraditya303
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.