From be0c8302457d9b7d5eb9a9bc34efe21dd18c2cc9 Mon Sep 17 00:00:00 2001 From: Dave Kleinschmidt Date: Wed, 2 Sep 2026 13:09:09 -0400 Subject: [PATCH] protect GIL with Julia reentrant mutex --- Project.toml | 2 +- src/GIL/GIL.jl | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 14acaaf3..4942699c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PythonCall" uuid = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" authors = ["Christopher Doris "] -version = "0.9.35" +version = "0.9.36" [deps] CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab" diff --git a/src/GIL/GIL.jl b/src/GIL/GIL.jl index f4b386ce..6590610f 100644 --- a/src/GIL/GIL.jl +++ b/src/GIL/GIL.jl @@ -25,6 +25,12 @@ if Base.VERSION ≥ v"1.11" ) end +const LOCK = ReentrantLock() + +function __init__() + Base.lock(LOCK) + return nothing +end """ lock(f) @@ -42,11 +48,16 @@ See [`@lock`](@ref) for the macro form. This function is experimental. Its semantics may be changed without notice. """ function lock(f) + Base.lock(LOCK) + task = current_task() + sticky, task.sticky = task.sticky, true state = C.PyGILState_Ensure() try f() finally C.PyGILState_Release(state) + task.sticky = sticky + Base.unlock(LOCK) end end @@ -67,11 +78,16 @@ The macro equivalent of [`lock`](@ref). """ macro lock(expr) quote + Base.lock($LOCK) + task = current_task() + sticky, task.sticky = task.sticky, true state = C.PyGILState_Ensure() try $(esc(expr)) finally C.PyGILState_Release(state) + task.sticky = sticky + Base.unlock($LOCK) end end end @@ -92,11 +108,17 @@ See [`@unlock`](@ref) for the macro form. This function is experimental. Its semantics may be changed without notice. """ function unlock(f) + if !islocked(LOCK) + error("GIL is not held") + end + + Base.unlock(LOCK) state = C.PyEval_SaveThread() try f() finally C.PyEval_RestoreThread(state) + Base.lock(LOCK) end end @@ -117,11 +139,17 @@ The macro equivalent of [`unlock`](@ref). """ macro unlock(expr) quote + if !islocked($LOCK) + error("GIL is not held") + end + + Base.unlock($LOCK) state = C.PyEval_SaveThread() try $(esc(expr)) finally C.PyEval_RestoreThread(state) + Base.lock($LOCK) end end end