Conversation
|
|
||
| using Preferences: @load_preference | ||
|
|
||
| # Minimal package-local equivalents of Base.OncePerThread and Base.OncePerTask. The Base |
There was a problem hiding this comment.
Only define these on old Julia versions. Use the actual types when available from Base.
| return | ||
| end | ||
|
|
||
| function enter_break() |
There was a problem hiding this comment.
The enter/exit functions should take the current task and task state as args.
| end | ||
| end | ||
|
|
||
| function __init__() |
There was a problem hiding this comment.
This init and the start runtime code could happen in the init function in context.jl.
|
|
||
| const THREAD_STATE = Utils.OncePerThread{ThreadState}(ThreadState) | ||
| const TASK_STATE = Utils.OncePerTask{TaskState}(TaskState) | ||
| const INTERP = Ref{Ptr{Cvoid}}(C_NULL) |
There was a problem hiding this comment.
The interpreter pointer can go in C.CTX.
There was a problem hiding this comment.
And be initialised in the init context function.
| return ts | ||
| end | ||
|
|
||
| check_thread(s::TaskState) = (@assert current_task().sticky && Threads.threadid() == s.tid) |
There was a problem hiding this comment.
Make it 2 separate asserts.
| pyisnull(exc._b) && pycopy!(exc._b, pybuiltins.None) | ||
| pyisnone(exc._v) || (exc._v.__traceback__ = exc._b) | ||
| exc._isnormalized = true | ||
| @pyregion begin |
There was a problem hiding this comment.
Prob only need a pyregion inside the first branch?
| a = @pyconvert Union{Int,Nothing} k.start begin | ||
| errset(pybuiltins.TypeError, "slice components must be integers") | ||
| pythrow() | ||
| @pyregion errset(pybuiltins.TypeError, "slice components must be integers") |
There was a problem hiding this comment.
An errset and pythrow should be in the same region as they need the same threadstate.
There was a problem hiding this comment.
Similarly any error checks should occur in the same region as the code that set the error, as the error state is on the threadstate. Check this throughout the codebase.
| return pyjl_handle_error(f, self, exc) | ||
| else | ||
| errset(pyJuliaError, pytuple((pyjl(exc), pyjl(catch_backtrace())))) | ||
| @pyregion begin |
There was a problem hiding this comment.
This pyregion shouldn't be needed as you can only reach this by calling the method from python which will already have the threadstate.
| """ | ||
| macro py(ex) | ||
| esc(py_macro(ex, __module__, __source__)) | ||
| lowered = py_macro(ex, __module__, __source__) |
There was a problem hiding this comment.
For now revert this and don't bother putting a pyregion around the py macro. It messes with the semantics of py.
| include("JlWrap/JlWrap.jl") | ||
| include("Compat/Compat.jl") | ||
|
|
||
| function __init__() |
There was a problem hiding this comment.
Shouldn't be needed, any initialisation can occur in the init context function.
|
@codex work on all the pr comments |
Summary
Testing
|
Motivation
@pyregion/@pyregionbreak) that amortize attach/detach transitions without exposing GIL details.Description
Regionthat implements per-OS-thread persistent PythonCall-ownedPyThreadState+ cooperative semaphore, and per-task dynamicTaskStatewith stickiness and token-based nested enter/exit semantics; exported macros@pyregionand@pyregionbreakuse this machinery. (src/Region/Region.jl,src/API/macros.jl,src/API/exports.jl)Py_FinalizeEx(). (src/C/context.jl,src/Region/Region.jl,src/PythonCall.jl)PyGILState_*usage internally for region semantics and exposed macros; provided a compatibility layer for the existingPythonCall.GILAPI by routing it through the new region enter/exit functions. (src/GIL/GIL.jl,src/C/pointers.jl)@pyregion/@pyregionbreakthroughout the core C-API call sites to ensure correct attach/detach behavior while keeping regions tight; updated@pylowering so generated code has one outer@pyregionand each@jlsplice becomes a@pyregionbreak. (bulk edits undersrc/Core/*,src/PyMacro/PyMacro.jl,src/JlWrap/*,src/GC/GC.jl,src/Core/builtins.jl)PythonCall.GC.gc()can be used to drain the queue inside a region. (src/GC/GC.jl,test/GC.jl)PyThreadState_GetUnchecked/_PyThreadState_UncheckedGet) andPyThreadState_Newusage for persistent per-thread states. (src/C/pointers.jl,src/C/C.jl)test/Region.jl)1.12to rely onOncePerThread/OncePerTaskprimitives and updated julia/juliapkg metadata where appropriate.Testing
JULIA_NUM_THREADS=4 julia --project -e 'using Pkg; Pkg.test()'— all tests passed (4,095 tests passed).JULIA_NUM_THREADS=4 julia --project -e 'using Pkg; Pkg.test(coverage=true)'— suite passed (4,095 tests passed); uncovered lines are limited to defensive rollback/impossible-token branches and the Python-originated root-break path that requires JuliaCall-specific scenarios.Threads.@spawnto verify detached initialization and cross-thread Python calls — succeeded (simple runtime checks demonstratedPyThreadState_GetUnchecked()is NULL outside regions and that@pyregionamortizes transitions).uv run pytest -s --nbval ./pytest) but they failed to run in this environment due to an external constraint: the environment's Python 3.14 OpenSSL/juliapkg interaction restricts Julia to ≤1.11 and therefore conflicts with the new Julia ≥1.12 requirement; the Python tests are otherwise intended to run in a matching environment and were not failing due to region logic.docs/make.jl; Documenter ran but could not infer repository remote source links because this checkout lacks a valid origin remote (non-test blocking).If you want, I can (1) open follow-ups to address remaining minor uncovered branches and defensive assertions, (2) add CI matrix notes to run the Python tests under a Python/Julia combination that satisfies OpenSSL/juliapkg constraints, or (3) narrow/expand any
@pyregionplacements based on preferred granularity.Codex Task