From 63a3e60e7aa3e600256f6d2c9715cbc52a45f6c3 Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Tue, 7 Nov 2023 17:35:46 +0000 Subject: [PATCH] make PythonCall.GC more like Base.GC (#413) Co-authored-by: Christopher Doris --- docs/src/faq.md | 4 ++-- docs/src/releasenotes.md | 1 + src/GC/GC.jl | 40 +++++++++++++++++++++++----------------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/docs/src/faq.md b/docs/src/faq.md index d6b7d117..5c1830a7 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -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, diff --git a/docs/src/releasenotes.md b/docs/src/releasenotes.md index 38710c76..e189f102 100644 --- a/docs/src/releasenotes.md +++ b/docs/src/releasenotes.md @@ -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. diff --git a/src/GC/GC.jl b/src/GC/GC.jl index 0d1fa9a8..35c091ae 100644 --- a/src/GC/GC.jl +++ b/src/GC/GC.jl @@ -3,7 +3,7 @@ Garbage collection of Python objects. -See `disable` and `enable`. +See [`enable`](@ref) and [`gc`](@ref). """ module GC @@ -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 @@ -47,9 +54,8 @@ function enable() end end end + empty!(QUEUE) end - empty!(QUEUE) - return end function enqueue(ptr::C.PyPtr)