Project update

Free-threaded PySide locking

From wrapper-wide serialization to short, explicit state transactions

August 19, 2026

The problem

The GIL no longer protects binding state

Shared state

Wrapper lifetime, ownership, parent links, maps, signals and metadata

New exposure

True parallel execution makes latent races and lock interactions observable

Initial design

One PyMutex around every wrapper call

Enter PySide
acquire lock
Call Qt / C++
Possible Python callback
Return
release lock
Simple, but it can deadlock with locks held by callers or acquired inside Qt—and it serializes nearly all PySide work.
Phase 0

CPython critical section: a safer interim step

What it fixes

CPython can suspend the section around Python-visible waits, imports and thread-state detaches.

What remains

Qt and C++ locks are invisible to CPython. Broad serialization also remains.

Final direction

One lock, held only for short state mutations

Lock

Validate and update PySide/Shiboken shared bookkeeping.

Unlock

Before calling Qt or Python, waiting, decrefing or destroying.

Preserve

Qt’s existing thread-affinity and thread-safety rules still apply.

Object lifetime

Keep calls safe without locking across Qt

Acquire short
call lease
Call Qt
without state lock
Release lease
Deletion never waits for an active call. It is marked pending and performed after the final lease is released.
Status, cost and risk

Where we are now

Phase 0 — complete

Observed deadlocks fixed; critical-section milestone implemented

Phase 1 is the main feasibility gate. The audit is bounded to a small set of binding data structures.