Python::with_gil causes a deadlock (multithreaded environment) #3089
-
QuestionI am executing python code(below). I pass a python lambda to a Rust function which then executes it in a multithreaded way (polars is basically calling it from inside .par_iter() ) As soon as I introduce I found this: #3045 which suggests allow_threads but since I am calling a python function I don't think it's for my usecase Any thoughts would be much appreciated. Somehow exactly the same set up works in py-polars (see map_mul https://github.com/pola-rs/polars/blob/master/py-polars/src/lazy/apply.rs) Reproducible example use polars::lazy::dsl::{map_binary, GetOutput};
use polars::prelude::{DataFrame};
use polars::prelude::*;
use pyo3::{pyclass, pymodule, pymethods, Python, types::{PyModule, PyType}, PyResult, PyObject};
use pyo3::prelude::*;
#[pyclass]
pub struct DataSet {
data: DataFrame
}
#[pymethods]
impl DataSet {
#[classmethod]
fn new(_: &PyType) -> Self {
let data = df!["a" => [1, 2, 3],
"b" => [4, 5, 6],
"c" => ["a", "d", "b"]].unwrap();
Self{data}
}
fn execute(&self, e: &ExprWrapper) {
let res = self.data.clone().lazy().with_column(e.e.clone())
.collect()
.unwrap();
dbg!(res);
}
}
#[pyclass]
pub struct ExprWrapper {
pub e: Expr
}
#[pyfunction]
fn ultimeasure(pyfnc: PyObject) -> ExprWrapper{
let functor = move |_: Series, _: Series| {
let _ = pyfnc.clone();
// Here I would convert Series to Python Side Series, call lambda, then result back to Rust Series
Python::with_gil(move |_| {});
let srs = Series::new("a", [1,2]);
Ok(Some(srs))
};
let e = map_binary(col("a"), col("b"), functor, GetOutput::from_type(DataType::Float64));
ExprWrapper{e}
}
#[pymodule]
fn pyo3issue(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<DataSet>()?;
m.add_wrapped(wrap_pyfunction!(ultimeasure)).unwrap();
Ok(())
} !!! Python side, the code I am executing: import pyo3issue
import polars as pl
def calculator(a: pl.Series, b: pl.Series):
print("here")
return a**b
c = pyo3issue.DataSet.new()
m = pyo3issue.ultimeasure(lambda a,b : a**b)
print(c.execute(m)) Installed versions [lib]
name = "pyo3issue"
# "cdylib" is necessary to produce a shared library for Python to import from.
crate-type = ["cdylib"]
[dependencies]
polars = { version = "0.28.0", features = [
"lazy"
] }
pyo3-polars = "0.2.0"
polars-arrow = "0.28.0"
[dependencies.pyo3]
version = "0.18.0"
# "abi3-py37" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.7
features = ["abi3-py37"] |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 4 replies
-
I think the issue is that |
Beta Was this translation helpful? Give feedback.
-
I have to admit to not knowing the difference between |
Beta Was this translation helpful? Give feedback.
-
@adamreichold thanks alot! You mean like this: #[pyfunction]
fn ultimeasure(py: Python, pyobj: PyObject) -> ExprWrapper{
let functor = move |s1: Series, s2: Series| {
let pyobj = pyobj.clone();
Python::with_gil(move |py| {
// Here I would convert Series to Python Side Series, call lambda, then result back to Rust Series like this:
//let s1 = rust_series_to_py_series(py, &s1).unwrap();
//let s2 = rust_series_to_py_series(py, &s2).unwrap();
// call the lambda and get a python side Series wrapper
//let res = match pyobj.call(py, (s1, s2), None) {
// Ok(pyobj) => pyobj,
// Err(e) => panic!("python apply failed: {}", e.value(py)),
//};
//
//let srs = py_series_to_rust_series(res.as_ref(py)).unwrap();
//Ok(Some(srs))
let srs = Series::new("a", [1,2]);
Ok(Some(srs))
})
};
let e = py.allow_threads(||{
map_binary(col("a"), col("b"), functor, GetOutput::from_type(DataType::Float64))
});
ExprWrapper{e}
} This unfortunately is still a deadlock. The thing is ...and therefore if I try to use |
Beta Was this translation helpful? Give feedback.
@adamreichold thanks alot! You mean like this: