pyo3 icon indicating copy to clipboard operation
pyo3 copied to clipboard

`Drop` trait never called on exit

Open jimy-byerley opened this issue 1 year ago • 1 comments

Hello there, I have an issue on program exit where the Drop trait of a PyClass object doesn't get called when python is exitting. I first encountered it in an extension module I am working on, but it seems to happen also when it is rust initializing python.

Here is minimal example

use pyo3::prelude::*;

fn main() {
    println!("starting");
    let a = Python::with_gil(|py| {
        Py::new(py, A::new(1))
    });
    let b = A::new(2);
    drop(b);
    drop(a);    // commenting this changes nothing
    println!("ending");
//     Python::with_gil(|_| {});   // uncomment it to have a being freed
}

#[pyclass]
struct A {
    a: u32,
}
impl A {
    fn new(a: u32) -> Self {
        println!("creating");
        Self {a}
    }
}
impl Drop for A {
    fn drop(&mut self) {
        println!("dropping {}", self.a);
        self.a = 0;
    }
}

Using pyo3==0.21.2 or lower, and python==3.11.2 I get the following output

starting
creating 1
creating 2
dropping 2
ending

I would have expected to see drop 1 somewhere, likely after ending and main exit, when the started python interpreter stops and reacquire its GIL to drop all its objects. Instead it seems the object is never dropped.

If I reacquire the GIL before leaving main (uncommenting the last line) I finally get what's expected.

starting
creating 1
creating 2
dropping 2
ending
dropping 1

In this example, reacquire the GIL at the end of the main seems to be a workaround, but it is not in the case of an extension module where you do not know when the program will stop and thus cannot insert this GIL acquisition.

In my usecase, I need to properly stop rust threads in drop Do you have an idea of what to do in order to ensure drop will be called ?

jimy-byerley avatar Apr 18 '24 12:04 jimy-byerley

Regardless of if this instance can be fixed, relying on destructors being called at program exit is a recipe for sadness when Python is involved.

birkenfeld avatar Apr 18 '24 14:04 birkenfeld

Is there an alternative for stopping the threads owned by my rust objects when python exits ? For instance a pyo3 function for checking whether the python interpreter is still alive ?

jimy-byerley avatar Jul 10 '24 15:07 jimy-byerley

I found my issue, in the end it was not because of pyo3 at all, I had something wrong about the way I was keeping Py<T> instances So I am able to have the destructor called on exit as long as nothing (like PyQt...) is holding a ref to it !

jimy-byerley avatar Jul 15 '24 14:07 jimy-byerley