FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Python 3.14 free-threaded support by greateggsgreg · Pull Request #2721 · pythonnet/pythonnet · GitHub

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1d93b30
Thread-safety prep for free-threading builds
greateggsgreg May 9, 2026
e029137
Initialise pythonnet on free-threaded Python (#2720)
greateggsgreg May 9, 2026
fd56aa4
Make extension/CLR-object registries thread-safe
greateggsgreg May 9, 2026
b727a15
Atomic type creation in ReflectedClrType.GetOrCreate / TypeManager.Ge…
greateggsgreg May 9, 2026
debba0d
Add free-threaded thread-stress tests and 3.14t to CI matrix
greateggsgreg May 9, 2026
585916e
Atomic GCHandle ownership and finalizer-thread shutdown guards
greateggsgreg May 10, 2026
c0ddb29
Make additional internal registries thread-safe
greateggsgreg May 10, 2026
096e466
test_thread: join worker threads before returning
greateggsgreg May 10, 2026
5005f36
test_thread: cover ModuleObject thread-safe registries
greateggsgreg May 10, 2026
3ad586a
Wider thread-safety audit fixes for free-threaded Python
greateggsgreg May 10, 2026
6b1f3f4
Document lock acquisition sites and strong->weak GCHandle swap
greateggsgreg May 10, 2026
732d97e
Preserve InternString single-write invariant under DEBUG
greateggsgreg May 10, 2026
3d56611
test_thread: cover real-world consumer patterns
greateggsgreg May 10, 2026
7094b12
Auto-detect free-threaded libpython in venv home
greateggsgreg May 12, 2026
f49ed1b
Snapshot pypath, use ConcurrentDictionary for thunks and slot holders
greateggsgreg May 13, 2026
fa89ff0
Fix handling of python runtime suffixes m/t
greateggsgreg May 13, 2026
a373e79
Fix threadtest race
greateggsgreg May 13, 2026
43a4237
Fix double-free in chained ClassDerived Finalize
greateggsgreg May 13, 2026
b42377e
Enable Mono CI jobs on free-threaded Python 3.14
greateggsgreg May 13, 2026
526a297
Inline freethreaded_only as pytest.mark.skipif at call sites
greateggsgreg May 13, 2026
8485837
Fix InterruptTest assertion on free-threaded Python 3.14
greateggsgreg May 13, 2026
a459772
Add concurrent stress tests for PyBuffer.Dispose and CLR-cycle gc.col…
greateggsgreg May 15, 2026
3bf6d68
Trim concurrent overhead on hot paths from free-threading prep
greateggsgreg May 15, 2026
3433446
Pre-warm ctor binder in concurrent-gc test to avoid first-call race
greateggsgreg May 15, 2026
4a034f7
Make MethodBinder.GetMethods lazy init thread-safe under free-threading
greateggsgreg May 15, 2026
afd5799
Precompute method precedence to avoid quadratic GetParameters allocat…
greateggsgreg May 15, 2026
95e4811
Zero the slot in ClassDerived.tp_dealloc when tp_clear already ran to…
greateggsgreg May 15, 2026
a799c44
Keep ClassDerived wrapper alive across the NewObjectToPython slot dem…
greateggsgreg May 15, 2026
220b4b4
Document private helpers added during free-threading prep
greateggsgreg May 16, 2026
dfda807
Add debug echoes and a 6-minute step timeout to the Mono test job
greateggsgreg May 16, 2026
1130f6a
Drop the per-loop CLR GC.Collect from concurrent-gc test to avoid Mon…
greateggsgreg May 16, 2026
f75cbab
Add temporary Mono-step diagnostics on Linux/macOS to locate the x64-…
greateggsgreg May 16, 2026
64ea342
Revert temporary Mono-step diagnostics now that the underlying race i…
greateggsgreg May 16, 2026
fda8211
Add user-facing threading guide covering GIL, free-threading, and com…
greateggsgreg May 18, 2026
356d76f
Harden CollectBasicObject against .NET-GC timing differences
greateggsgreg May 18, 2026
f0ee4b2
Adjust the header offset on 32bit systems
filmor Aug 21, 2026
3445b08
Drop broken and unnecessary exclude
filmor Aug 22, 2026
8e6861e
Be strict about not loading on Python 3.13
filmor Aug 22, 2026
File filter

Filter by extension

Filter by extension .cs  (4) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
Prev Previous commit
Next Next commit
Document lock acquisition sites and strong->weak GCHandle swap
Comment-only changes. Adds inline notes at lock acquisitions where the
"why" is not obvious from the field declaration alone:

- ClassDerivedObject.Reset / GetModuleBuilder: explain that both
  builder caches must update atomically and that DefineDynamicAssembly
  / DefineDynamicModule produce duplicates under contention.
- TypeManager.GetType: note that CreateType + cache write must be
  atomic.
- ReflectedClrType.GetOrCreate: cross-file lock; mention it also
  serialises ClassManager.cache and TypeManager._slotsHolders writes.
- Runtime.ResetPyMembers: explain the snapshot-then-dispose pattern
  (Dispose() callbacks would deadlock if invoked under the lock).

Expands the strong->weak GCHandle swap in ClassDerivedObject.tp_dealloc
to spell out:

1. Why the PyObject is not freed at refcount 0 (C# wrapper may still
   reference it; ToPython() resurrects via _Py_NewReference).
2. Why the handle is demoted to weak (lets the C# wrapper be GC'd; on
   collection PyFinalize enqueues the real PyObject_GC_Del).
3. Why the swap uses Interlocked.Exchange (tp_clear may race on the
   same slot under FT / finalizer thread; without atomic claim both
   threads could observe and double-free the same handle).
  • Loading branch information
greateggsgreg authored and filmor committed Aug 18, 2026
commit 6b1f3f4c862901c0724a65006dc10fe2617aced8
2 changes: 2 additions & 0 deletions src/runtime/Runtime.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ private static void SetPyMemberTypeOf(out PyObject obj, StolenReference value)

private static void ResetPyMembers()
{
// Snapshot under lock; Dispose() runs outside it so a callback that
// re-enters SetPyMember does not deadlock.
PyObject[] snapshot;
lock (_pyRefsLock)
{
Expand Down
1 change: 1 addition & 0 deletions src/runtime/TypeManager.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ internal static PyType GetType(Type type)
{
// Cached with refcount 1; effectively lives until the CPython runtime is finalised.
if (cache.TryGetValue(type, out var pyType)) return pyType;
// CreateType + cache write must be atomic so two threads do not both allocate.
lock (_cacheCreateLock)
{
if (cache.TryGetValue(type, out pyType)) return pyType;
Expand Down
20 changes: 19 additions & 1 deletion src/runtime/Types/ClassDerived.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ static ClassDerivedObject()

public static void Reset()
{
// Atomic replacement of both builder caches so a concurrent
// GetModuleBuilder cannot observe one new + one old.
lock (_buildersLock)
{
assemblyBuilders = new Dictionary<string, AssemblyBuilder>();
Expand Down Expand Up @@ -84,7 +86,21 @@ protected override void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slo
// self may be null after Shutdown begun
if (self is not null)
{
// Atomically swap strong->weak; concurrent tp_clear may also clear the slot.
// Python refcount has reached 0 but we do NOT free the PyObject:
// the C# wrapper (via IPythonDerivedType.__pyobj__) may still
// reference it, and a later ToPython() on that wrapper will
// resurrect a strong handle and call _Py_NewReference.
//
// Demote the GCHandle from strong -> weak so the C# wrapper can
// be collected; when it is, PyFinalize enqueues the actual
// PyObject_GC_Del. Until then the slot keeps the wrapper
// reachable from Python -> C# but not the other way.
//
// Interlocked.Exchange: under FT (or .NET-finalizer-thread
// races) tp_clear can hit the same slot. Whichever thread
// observes the original handle frees it; the loser frees the
// weak handle it just allocated. Without the atomic swap both
// threads could see and free the same handle (double-free).
GCHandle weak = GCHandle.Alloc(self, GCHandleType.Weak);
BorrowedReference borrow = ob.Borrow();
int offset = Util.ReadInt32(Runtime.PyObject_TYPE(borrow), Offsets.tp_clr_inst_offset);
Expand Down Expand Up @@ -715,6 +731,8 @@ private static void AddPythonProperty(string propertyName, PyObject func, TypeBu
private static ModuleBuilder GetModuleBuilder(string assemblyName, string moduleName)
{
var key = Tuple.Create(assemblyName, moduleName);
// Cache check-and-create must be atomic; DefineDynamicAssembly /
// DefineDynamicModule produce duplicate builders under contention.
lock (_buildersLock)
{
if (moduleBuilders.TryGetValue(key, out ModuleBuilder? existing))
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/Types/ReflectedClrType.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public static ReflectedClrType GetOrCreate(Type type)
if (ClassManager.cache.TryGetValue(type, out var pyType))
return pyType;

// Shared with ClassManager.cache + TypeManager._slotsHolders writes
// so the multi-step type build below is atomic.
lock (ClassManager._cacheCreateLock)
{
// Re-check now that we hold the lock; another thread may have finished.
Expand Down

Back | FazBrowse Home | New Git URL