Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ebd0a95
Thread-safety prep for free-threading builds
greateggsgreg May 9, 2026
cf8372a
Initialise pythonnet on free-threaded Python (#2720)
greateggsgreg May 9, 2026
441954f
Make extension/CLR-object registries thread-safe
greateggsgreg May 9, 2026
19c7f59
Atomic type creation in ReflectedClrType.GetOrCreate / TypeManager.Ge…
greateggsgreg May 9, 2026
1029c62
Add free-threaded thread-stress tests and 3.14t to CI matrix
greateggsgreg May 9, 2026
3158ca5
Atomic GCHandle ownership and finalizer-thread shutdown guards
greateggsgreg May 10, 2026
1149295
Make additional internal registries thread-safe
greateggsgreg May 10, 2026
e8afa85
test_thread: join worker threads before returning
greateggsgreg May 10, 2026
3954df3
test_thread: cover ModuleObject thread-safe registries
greateggsgreg May 10, 2026
28013af
Wider thread-safety audit fixes for free-threaded Python
greateggsgreg May 10, 2026
9a3dd59
Document lock acquisition sites and strong->weak GCHandle swap
greateggsgreg May 10, 2026
cb1dcde
Preserve InternString single-write invariant under DEBUG
greateggsgreg May 10, 2026
74d12f1
test_thread: cover real-world consumer patterns
greateggsgreg May 10, 2026
a30c84e
Auto-detect free-threaded libpython in venv home
greateggsgreg May 12, 2026
be5ba34
Snapshot pypath, use ConcurrentDictionary for thunks and slot holders
greateggsgreg May 13, 2026
946785e
Fix handling of python runtime suffixes m/t
greateggsgreg May 13, 2026
77ca497
Fix threadtest race
greateggsgreg May 13, 2026
af518bb
Fix double-free in chained ClassDerived Finalize
greateggsgreg May 13, 2026
ed735ac
Enable Mono CI jobs on free-threaded Python 3.14
greateggsgreg May 13, 2026
bd3f0bf
Inline freethreaded_only as pytest.mark.skipif at call sites
greateggsgreg May 13, 2026
c4688b3
Fix InterruptTest assertion on free-threaded Python 3.14
greateggsgreg May 13, 2026
84cd07c
Add concurrent stress tests for PyBuffer.Dispose and CLR-cycle gc.col…
greateggsgreg May 15, 2026
d9b658d
Trim concurrent overhead on hot paths from free-threading prep
greateggsgreg May 15, 2026
20c51ad
Pre-warm ctor binder in concurrent-gc test to avoid first-call race
greateggsgreg May 15, 2026
f5f8ab0
Make MethodBinder.GetMethods lazy init thread-safe under free-threading
greateggsgreg May 15, 2026
c5097eb
Precompute method precedence to avoid quadratic GetParameters allocat…
greateggsgreg May 15, 2026
18e4901
Zero the slot in ClassDerived.tp_dealloc when tp_clear already ran to…
greateggsgreg May 15, 2026
e0569d0
Keep ClassDerived wrapper alive across the NewObjectToPython slot dem…
greateggsgreg May 15, 2026
2c9f861
Document private helpers added during free-threading prep
greateggsgreg May 16, 2026
ee3add4
Add debug echoes and a 6-minute step timeout to the Mono test job
greateggsgreg May 16, 2026
414ce70
Drop the per-loop CLR GC.Collect from concurrent-gc test to avoid Mon…
greateggsgreg May 16, 2026
99a99db
Add temporary Mono-step diagnostics on Linux/macOS to locate the x64-…
greateggsgreg May 16, 2026
8e7c2ba
Revert temporary Mono-step diagnostics now that the underlying race i…
greateggsgreg May 16, 2026
f8b69a2
Merge branch 'master' into freethreading-prep
greateggsgreg May 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Trim concurrent overhead on hot paths from free-threading prep
DelegateManager.GetDispatcher now takes a lock-free fast path on cache
hit (ConcurrentDictionary), avoiding the emit lock on every CLR delegate
dispatch.  InternString and ClassManager._inProgressCache revert to
plain Dictionary since they are only written under existing single-
threaded or locked paths, and ClassBase.ClearVisited becomes a per-
thread HashSet (tp_clear recursion is intra-stack).
  • Loading branch information
greateggsgreg committed May 15, 2026
commit d9b658dc283c3a9a5f291eac06730161d123e1d4
5 changes: 2 additions & 3 deletions src/runtime/ClassManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ internal class ClassManager
BindingFlags.NonPublic;

// cache: fully-initialised types (lock-free reads).
// _inProgressCache: partial types visible only to the lock-holding builder
// for self-referential definitions.
// _inProgressCache: partial types; only accessed under _cacheCreateLock.
internal static ConcurrentDictionary<MaybeType, ReflectedClrType> cache = new();
internal static readonly ConcurrentDictionary<MaybeType, ReflectedClrType> _inProgressCache = new();
internal static readonly Dictionary<MaybeType, ReflectedClrType> _inProgressCache = new();
internal static readonly object _cacheCreateLock = new();
private static readonly Type dtype;

Expand Down
8 changes: 5 additions & 3 deletions src/runtime/DelegateManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand All @@ -15,8 +16,8 @@ namespace Python.Runtime
/// </summary>
internal class DelegateManager
{
private readonly Dictionary<Type,Type> cache = new();
// Reflection.Emit is not thread-safe; serialise cache lookup + DefineType.
// Lock-free reads; Reflection.Emit (BuildDispatcher) is serialised.
private readonly ConcurrentDictionary<Type, Type> cache = new();
private readonly object _emitLock = new();
private readonly Type basetype = typeof(Dispatcher);
private readonly Type arrayType = typeof(object[]);
Expand All @@ -39,9 +40,10 @@ public DelegateManager()
/// </summary>
private Type GetDispatcher(Type dtype)
{
if (cache.TryGetValue(dtype, out Type item)) return item;
lock (_emitLock)
{
return cache.TryGetValue(dtype, out Type item) ? item : BuildDispatcher(dtype);
return cache.TryGetValue(dtype, out item) ? item : BuildDispatcher(dtype);
}
}

Expand Down
13 changes: 5 additions & 8 deletions src/runtime/InternString.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand All @@ -9,8 +8,9 @@ namespace Python.Runtime
{
static partial class InternString
{
private static readonly ConcurrentDictionary<string, PyString> _string2interns = new();
private static readonly ConcurrentDictionary<IntPtr, string> _intern2strings = new();
// Populated only by Initialize (single-threaded); immutable until Shutdown.
private static readonly Dictionary<string, PyString> _string2interns = new();
private static readonly Dictionary<IntPtr, string> _intern2strings = new();
const BindingFlags PyIdentifierFieldFlags = BindingFlags.Static | BindingFlags.NonPublic;

static InternString()
Expand Down Expand Up @@ -76,11 +76,8 @@ public static bool TryGetInterned(BorrowedReference op, out string s)

private static void SetIntern(string s, PyString op)
{
// Initialize is single-threaded; TryAdd preserves the original
// single-write invariant via Debug.Assert without crashing release.
bool a = _string2interns.TryAdd(s, op);
bool b = _intern2strings.TryAdd(op.Reference.DangerousGetAddress(), s);
Debug.Assert(a && b);
_string2interns.Add(s, op);
_intern2strings.Add(op.Reference.DangerousGetAddress(), s);
}
}
}
13 changes: 6 additions & 7 deletions src/runtime/Types/ClassBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -375,8 +374,8 @@ public static int tp_clear(BorrowedReference ob)
return 0;
}

// tp_clear re-entrancy guard.
static readonly ConcurrentDictionary<IntPtr, byte> ClearVisited = new();
// tp_clear re-entrancy guard; per-thread since recursion is intra-stack.
[ThreadStatic] static HashSet<IntPtr>? _clearVisited;

internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
{
Expand All @@ -392,12 +391,12 @@ internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
if (clearPtr == TypeManager.subtype_clear)
{
var addr = ob.DangerousGetAddress();
if (!ClearVisited.TryAdd(addr, 0))
var visited = _clearVisited ??= new HashSet<IntPtr>();
if (!visited.Add(addr))
return 0;

int res = clear(ob);
ClearVisited.TryRemove(addr, out _);
return res;
try { return clear(ob); }
finally { visited.Remove(addr); }
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Types/ReflectedClrType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static ReflectedClrType GetOrCreate(Type type)
}
finally
{
ClassManager._inProgressCache.TryRemove(type, out _);
ClassManager._inProgressCache.Remove(type);
}

return pyType;
Expand Down
Loading