Skip to content

Commit

Permalink
simplify GIL handling (#530)
Browse files Browse the repository at this point in the history
Co-authored-by: Christopher Doris <github.com/cjdoris>
  • Loading branch information
cjdoris authored Jul 24, 2024
1 parent 40fdbb9 commit ce8bd7a
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 144 deletions.
2 changes: 1 addition & 1 deletion pysrc/juliacall/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def args_from_config():
os.environ['PATH'] = libdir

# Open the library
CONFIG['lib'] = lib = c.CDLL(libpath, mode=c.RTLD_GLOBAL)
CONFIG['lib'] = lib = c.PyDLL(libpath, mode=c.RTLD_GLOBAL)

# parse options
argc, argv = args_from_config()
Expand Down
1 change: 0 additions & 1 deletion src/C/C.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ include("consts.jl")
include("pointers.jl")
include("extras.jl")
include("context.jl")
include("gil.jl")
include("api.jl")

function __init__()
Expand Down
120 changes: 57 additions & 63 deletions src/C/context.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,63 +145,61 @@ function init_context()
@require PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" init_pycall(PyCall)

# Initialize the interpreter
with_gil() do
CTX.is_preinitialized = Py_IsInitialized() != 0
if CTX.is_preinitialized
@assert CTX.which == :PyCall || CTX.matches_pycall isa Bool
CTX.is_preinitialized = Py_IsInitialized() != 0
if CTX.is_preinitialized
@assert CTX.which == :PyCall || CTX.matches_pycall isa Bool
else
@assert CTX.which != :PyCall
# Find ProgramName and PythonHome
script = if Sys.iswindows()
"""
import sys
print(sys.executable)
if hasattr(sys, "base_exec_prefix"):
sys.stdout.write(sys.base_exec_prefix)
else:
sys.stdout.write(sys.exec_prefix)
"""
else
@assert CTX.which != :PyCall
# Find ProgramName and PythonHome
script = if Sys.iswindows()
"""
import sys
print(sys.executable)
if hasattr(sys, "base_exec_prefix"):
sys.stdout.write(sys.base_exec_prefix)
else:
sys.stdout.write(sys.exec_prefix)
"""
else
"""
import sys
print(sys.executable)
if hasattr(sys, "base_exec_prefix"):
sys.stdout.write(sys.base_prefix)
sys.stdout.write(":")
sys.stdout.write(sys.base_exec_prefix)
else:
sys.stdout.write(sys.prefix)
sys.stdout.write(":")
sys.stdout.write(sys.exec_prefix)
"""
end
CTX.pyprogname, CTX.pyhome = readlines(python_cmd(["-c", script]))
"""
import sys
print(sys.executable)
if hasattr(sys, "base_exec_prefix"):
sys.stdout.write(sys.base_prefix)
sys.stdout.write(":")
sys.stdout.write(sys.base_exec_prefix)
else:
sys.stdout.write(sys.prefix)
sys.stdout.write(":")
sys.stdout.write(sys.exec_prefix)
"""
end
CTX.pyprogname, CTX.pyhome = readlines(python_cmd(["-c", script]))

# Set PythonHome
CTX.pyhome_w = Base.cconvert(Cwstring, CTX.pyhome)
Py_SetPythonHome(pointer(CTX.pyhome_w))
# Set PythonHome
CTX.pyhome_w = Base.cconvert(Cwstring, CTX.pyhome)
Py_SetPythonHome(pointer(CTX.pyhome_w))

# Set ProgramName
CTX.pyprogname_w = Base.cconvert(Cwstring, CTX.pyprogname)
Py_SetProgramName(pointer(CTX.pyprogname_w))
# Set ProgramName
CTX.pyprogname_w = Base.cconvert(Cwstring, CTX.pyprogname)
Py_SetProgramName(pointer(CTX.pyprogname_w))

# Start the interpreter and register exit hooks
Py_InitializeEx(0)
atexit() do
CTX.is_initialized = false
if CTX.version === missing || CTX.version < v"3.6"
Py_Finalize()
else
if Py_FinalizeEx() == -1
@warn "Py_FinalizeEx() error"
end
# Start the interpreter and register exit hooks
Py_InitializeEx(0)
atexit() do
CTX.is_initialized = false
if CTX.version === missing || CTX.version < v"3.6"
Py_Finalize()
else
if Py_FinalizeEx() == -1
@warn "Py_FinalizeEx() error"
end
end
end
CTX.is_initialized = true
if Py_AtExit(@cfunction(_atpyexit, Cvoid, ())) == -1
@warn "Py_AtExit() error"
end
end
CTX.is_initialized = true
if Py_AtExit(@cfunction(_atpyexit, Cvoid, ())) == -1
@warn "Py_AtExit() error"
end
end

Expand All @@ -218,20 +216,16 @@ function init_context()
ENV["JULIA_PYTHONCALL_EXE"] = CTX.exe_path::String
end

with_gil() do

# Get the python version
verstr = Base.unsafe_string(Py_GetVersion())
vermatch = match(r"^[0-9.]+", verstr)
if vermatch === nothing
error("Cannot parse version from version string: $(repr(verstr))")
end
CTX.version = VersionNumber(vermatch.match)
v"3.5" CTX.version < v"4" || error(
"Only Python 3.5+ is supported, this is Python $(CTX.version) at $(CTX.exe_path===missing ? "unknown location" : CTX.exe_path).",
)

# Get the python version
verstr = Base.unsafe_string(Py_GetVersion())
vermatch = match(r"^[0-9.]+", verstr)
if vermatch === nothing
error("Cannot parse version from version string: $(repr(verstr))")
end
CTX.version = VersionNumber(vermatch.match)
v"3.5" CTX.version < v"4" || error(
"Only Python 3.5+ is supported, this is Python $(CTX.version) at $(CTX.exe_path===missing ? "unknown location" : CTX.exe_path).",
)

@debug "Initialized PythonCall.jl" CTX.is_embedded CTX.is_initialized CTX.exe_path CTX.lib_path CTX.lib_ptr CTX.pyprogname CTX.pyhome CTX.version

Expand Down
24 changes: 0 additions & 24 deletions src/C/gil.jl

This file was deleted.

2 changes: 2 additions & 0 deletions src/C/pointers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const CAPI_FUNC_SIGS = Dict{Symbol,Pair{Tuple,Type}}(
:PyEval_RestoreThread => (Ptr{Cvoid},) => Cvoid,
:PyGILState_Ensure => () => PyGILState_STATE,
:PyGILState_Release => (PyGILState_STATE,) => Cvoid,
:PyGILState_GetThisThreadState => () => Ptr{Cvoid},
:PyGILState_Check => () => Cint,
# IMPORT
:PyImport_ImportModule => (Ptr{Cchar},) => PyPtr,
:PyImport_Import => (PyPtr,) => PyPtr,
Expand Down
8 changes: 3 additions & 5 deletions src/Compat/Compat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@ include("tables.jl")
include("pycall.jl")

function __init__()
C.with_gil() do
init_gui()
init_pyshow()
init_tables()
end
init_gui()
init_pyshow()
init_tables()
@require PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" init_pycall(PyCall)
end
end
10 changes: 4 additions & 6 deletions src/Convert/Convert.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ include("numpy.jl")
include("pandas.jl")

function __init__()
C.with_gil() do
init_pyconvert()
init_ctypes()
init_numpy()
init_pandas()
end
init_pyconvert()
init_ctypes()
init_numpy()
init_pandas()
end

end
10 changes: 4 additions & 6 deletions src/Core/Core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,10 @@ include("juliacall.jl")
include("pyconst_macro.jl")

function __init__()
C.with_gil() do
init_consts()
init_datetime()
init_stdlib()
init_juliacall()
end
init_consts()
init_datetime()
init_stdlib()
init_juliacall()
end

end
20 changes: 7 additions & 13 deletions src/GC/GC.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@ Like most PythonCall functions, you must only call this from the main thread.
function enable()
ENABLED[] = true
if !isempty(QUEUE)
C.with_gil(false) do
for ptr in QUEUE
if ptr != C.PyNULL
C.Py_DecRef(ptr)
end
for ptr in QUEUE
if ptr != C.PyNULL
C.Py_DecRef(ptr)
end
end
end
Expand All @@ -55,9 +53,7 @@ end
function enqueue(ptr::C.PyPtr)
if ptr != C.PyNULL && C.CTX.is_initialized
if ENABLED[]
C.with_gil(false) do
C.Py_DecRef(ptr)
end
C.Py_DecRef(ptr)
else
push!(QUEUE, ptr)
end
Expand All @@ -68,11 +64,9 @@ end
function enqueue_all(ptrs)
if C.CTX.is_initialized
if ENABLED[]
C.with_gil(false) do
for ptr in ptrs
if ptr != C.PyNULL
C.Py_DecRef(ptr)
end
for ptr in ptrs
if ptr != C.PyNULL
C.Py_DecRef(ptr)
end
end
else
Expand Down
4 changes: 1 addition & 3 deletions src/JlWrap/C.jl
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,7 @@ function init_c()
end

function __init__()
C.with_gil() do
init_c()
end
init_c()
end

PyJuliaValue_IsNull(o::C.PyPtr) = UnsafePtr{PyJuliaValueObject}(o).value[] == 0
Expand Down
42 changes: 20 additions & 22 deletions src/JlWrap/JlWrap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,26 @@ include("set.jl")
include("callback.jl")

function __init__()
Cjl.C.with_gil() do
init_base()
init_raw()
init_any()
init_iter()
init_type()
init_module()
init_io()
init_number()
init_array()
init_vector()
init_dict()
init_set()
init_callback()
# add packages to juliacall
jl = pyjuliacallmodule
jl.Core = Base.Core
jl.Base = Base
jl.Main = Main
jl.Pkg = Pkg
jl.PythonCall = PythonCall
end
init_base()
init_raw()
init_any()
init_iter()
init_type()
init_module()
init_io()
init_number()
init_array()
init_vector()
init_dict()
init_set()
init_callback()
# add packages to juliacall
jl = pyjuliacallmodule
jl.Core = Base.Core
jl.Base = Base
jl.Main = Main
jl.Pkg = Pkg
jl.PythonCall = PythonCall
end

end

0 comments on commit ce8bd7a

Please sign in to comment.