Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make PythonCall.GC more like Base.GC #413

Merged
merged 3 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions docs/src/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ No.

Some rules if you are writing multithreaded code:
- Only call Python functions from the first thread.
- You probably also need to call `PythonCall.GC.disable()` on the main thread before any
threaded block of code. Remember to call `PythonCall.GC.enable()` again afterwards.
- You probably also need to call `on=PythonCall.GC.enable(false)` on the main thread before any
threaded block of code. Remember to call `PythonCall.GC.enable(on)` again afterwards.
(This is because Julia finalizers can be called from any thread.)
- Julia intentionally causes segmentation faults as part of the GC safepoint mechanism.
If unhandled, these segfaults will result in termination of the process. To enable signal handling,
Expand Down
1 change: 1 addition & 0 deletions docs/src/releasenotes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Release Notes

## Unreleased (v1)
* `PythonCall.GC` is now more like `Base.GC`: `enable(true)` replaces `enable()`, `enable(false)` replaces `disable()`, and `gc()` is added.

## Unreleased
* `Py` is now treated as a scalar when broadcasting.
Expand Down
40 changes: 23 additions & 17 deletions src/GC/GC.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

Garbage collection of Python objects.

See `disable` and `enable`.
See [`enable`](@ref) and [`gc`](@ref).
"""
module GC

Expand All @@ -13,32 +13,39 @@ const ENABLED = Ref(true)
const QUEUE = C.PyPtr[]

"""
PythonCall.GC.disable()
PythonCall.GC.enable(on::Bool)

Disable the PythonCall garbage collector.
Control whether garbage collection of Python objects is turned on or off.

This means that whenever a Python object owned by Julia is finalized, it is not immediately
freed but is instead added to a queue of objects to free later when `enable()` is called.
Return the previous GC state.

Disabling the GC means that whenever a Python object owned by Julia is finalized, it is not
immediately freed but is instead added to a queue of objects to free later when GC is
re-enabled.

Like most PythonCall functions, you must only call this from the main thread.
"""
function disable()
ENABLED[] = false
return
function enable(on::Bool)
was_on = ENABLED[]
if on
ENABLED[] = true
if !was_on
gc()
end
else
ENABLED[] = false
end
return ans
end

"""
PythonCall.GC.enable()

Re-enable the PythonCall garbage collector.
PythonCall.GC.gc()

This frees any Python objects which were finalized while the GC was disabled, and allows
objects finalized in the future to be freed immediately.
Perform garbage collection of Python objects.

Like most PythonCall functions, you must only call this from the main thread.
"""
function enable()
ENABLED[] = true
function gc()
if !isempty(QUEUE)
C.with_gil(false) do
for ptr in QUEUE
Expand All @@ -47,9 +54,8 @@ function enable()
end
end
end
empty!(QUEUE)
end
empty!(QUEUE)
return
end

function enqueue(ptr::C.PyPtr)
Expand Down