Skip to content

Python 3.14 free-threaded support #2720

@greateggsgreg

Description

@greateggsgreg

Tracking issue for free-threaded Python (Py_GIL_DISABLED) support. Companion to the umbrella 3.14 tracker #2610.

Why FT is different from a normal GIL build

  • PyObject_HEAD is 16 bytes larger; the refcount is split across ob_ref_local + ob_ref_shared and must be read via Py_REFCNT (a real exported symbol on 3.14+).
  • Build detection: sys._is_gil_enabled() (added in 3.13) returns False on FT.
  • The GIL has been silently serialising every pythonnet static cache, lock-free counter, finalizer-thread interaction, and Reflection.Emit use site. All of those need explicit synchronisation under FT.

Hazards in pythonnet (resolved by PR #2721)

Area What broke How it was fixed
Refcount / ABI Single-offset read Py_REFCNT P/Invoke + ObjectHeadOffset = 16 for FT
Type-creation race Duplicate cache.Add; partial-type visibility Two-cache (cache + _inProgressCache) under _cacheCreateLock
GCHandle in tp_clear/tp_dealloc Double-free under main-thread + finalizer-thread race Interlocked.Exchange on the slot
Reflection.Emit Concurrent DefineType corrupts IL / throws "Duplicate type name" Lock both CreateDerivedType and DelegateManager.GetDispatcher
Static collections Plain Dictionary/HashSet/List thread-safety issues ConcurrentDictionary where possible; Interlocked/Volatile for single-cell state; locks for nested mutation and ordered list semantics
Finalizer-thread / Py_Finalize interaction Stale ob_ref_local reads after teardown crash the process Runtime._Py_IsFinalizing() guards on all decref-from-finalizer paths

See PR #2721 for the per-file detail.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions