From 5ef7ae01fe48255279b6aea45e9d65e79fadba73 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 23 Oct 2021 17:04:56 +0200 Subject: [PATCH] Document interaction with GIL Closes #9. --- CHANGELOG.md | 4 +++ Cargo.toml | 2 +- examples/hello_world/Cargo.toml | 2 +- src/lib.rs | 52 +++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c94b8a9..022b11e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.4.1 + +* Docs: Point out the need to handle GIL in around threads. + # 0.4.0 * Upgrade to pyo3 0.14. diff --git a/Cargo.toml b/Cargo.toml index 2b7d297..3e6995f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-log" -version = "0.4.0" +version = "0.4.1" authors = ["Michal 'vorner' Vaner "] description = "Logging bridge from pyo3 native extension to python" documentation = "https://docs.rs/pyo3-log" diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml index ca141a6..d1661e9 100644 --- a/examples/hello_world/Cargo.toml +++ b/examples/hello_world/Cargo.toml @@ -12,5 +12,5 @@ crate-type = ["cdylib"] [dependencies] log = "~0.4" -pyo3 = { version = "~0.13", features = ["extension-module"] } +pyo3 = { version = "~0.14", features = ["extension-module"] } pyo3-log = { path = "../.." } diff --git a/src/lib.rs b/src/lib.rs index 5bbf4fd..b73851d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,6 +103,58 @@ //! //! Log levels are mapped to the same-named ones. The [`Trace`][Level::Trace] doesn't exist on the //! Python side, but is mapped to a level with value 5. +//! +//! # Interaction with Python GIL +//! +//! Under the hook, the logging routines call into Python. That means they need to acquire the +//! Global Interpreter Lock of Python. +//! +//! This has several consequences. One of them is the above mentioned performance considerations. +//! +//! The other is a risk of deadlocks if threads are used from within the extension code without +//! releasing the GIL. +//! +//! ```rust +//! use std::thread; +//! use log::info; +//! use pyo3::prelude::*; +//! +//! #[pyfunction] +//! fn deadlock() { +//! info!("This logs fine"); +//! +//! let background_thread = thread::spawn(|| { +//! info!("This'll deadlock"); +//! }); +//! +//! background_thread.join().unwrap(); +//! } +//! # let _ = deadlock; +//! ``` +//! +//! The above code will deadlock, because the `info` call in the background thread needs the GIL +//! that's held by the deadlock function. One needs to give up the GIL to let the other threads +//! run, something like this: +//! +//! ```rust +//! use std::thread; +//! use log::info; +//! use pyo3::prelude::*; +//! +//! #[pyfunction] +//! fn dont_deadlock(py: Python<'_>) { +//! info!("This logs fine"); +//! +//! py.allow_threads(|| { +//! let background_thread = thread::spawn(|| { +//! info!("This'll not deadlock"); +//! }); +//! +//! background_thread.join().unwrap(); +//! }); +//! } +//! # let _ = dont_deadlock; +//! ``` use std::cmp; use std::collections::HashMap;