From 61acf98aec8e4304bff88a73bc8ca463234d285b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 4 Jan 2015 20:11:18 +0100 Subject: [PATCH] Fix threading --- src/conversion.rs | 19 ++++++++++--------- src/err.rs | 1 - src/lib.rs | 1 + src/pythonrun.rs | 31 +++++++++++++++++++++++++------ 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/conversion.rs b/src/conversion.rs index 486514be..2659beb1 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -79,9 +79,9 @@ impl <'p, 's, T : PythonObject<'p>> ToPyObject<'p, 's> for T { } } -impl <'p, 'a, T : PythonObject<'p>> FromPyObject<'p, 'a> for &'a T { +impl <'p, 's, T : PythonObject<'p>> FromPyObject<'p, 's> for &'s T { #[inline] - fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, &'a T> { + fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, &'s T> { s.downcast() } } @@ -100,23 +100,24 @@ impl <'p, 's, T : PythonObject<'p>> ToPyObject<'p, 's> for PyPtr<'p, T> { } } -impl <'p, 'a, T : PythonObject<'p>> FromPyObject<'p, 'a> for PyPtr<'p, T> { +impl <'p, 's, T : PythonObject<'p>> FromPyObject<'p, 's> for PyPtr<'p, T> { #[inline] - fn from_py_object(s : &'a PyObject<'p>) -> PyResult<'p, PyPtr<'p, T>> { + fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, PyPtr<'p, T>> { PyPtr::new(s).downcast_into() } } // bool -/* -impl <'p, T : PythonObject<'p>> ToPyObject<'p> for bool { + +impl <'p, 's> ToPyObject<'p, 's> for bool { type PointerType = &'p PyObject<'p>; - fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, Self::PointerType>; - if *self { py.True() } else { py.False() } + #[inline] + fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'p PyObject<'p>> { + Ok(if *self { py.True() } else { py.False() }) + } } -*/ impl <'p, 'a> FromPyObject<'p, 'a> for bool { fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, bool> { diff --git a/src/err.rs b/src/err.rs index ea02f2fc..5948e3a1 100644 --- a/src/err.rs +++ b/src/err.rs @@ -91,7 +91,6 @@ pub fn error_on_minusone(py : Python, result : libc::c_int) -> PyResult<()> { } #[cfg(test)] -#[allow(experimental)] mod tests { use {Python, PyType, PyErr}; diff --git a/src/lib.rs b/src/lib.rs index 1380068c..50e74316 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ #![allow(unused_imports, dead_code, unused_variables)] #![feature(associated_types)] #![feature(globs)] +#![feature(slicing_syntax)] extern crate libc; extern crate "libpython27-sys" as ffi; diff --git a/src/pythonrun.rs b/src/pythonrun.rs index 466da97d..7c2a3a5b 100644 --- a/src/pythonrun.rs +++ b/src/pythonrun.rs @@ -7,15 +7,34 @@ static START: Once = ONCE_INIT; /// Prepares the use of python in a free-threaded context. pub fn prepare_freethreaded_python() { + // Protect against race conditions when python is not yet initialized + // and multiple threads concurrently call 'prepare_freethreaded_python()'. + // Note that we do not protect against concurrent initialization of the python runtime + // by other users of the python C API. START.call_once(|| unsafe { - ::ffi::Py_InitializeEx(0); - if ::ffi::PyEval_ThreadsInitialized() == 0 { - ::ffi::PyEval_InitThreads(); - // InitThreads() will acquire the GIL, - // but we don't want to acquire it at this point + if ffi::Py_IsInitialized() != 0 { + // If python is already initialized, we expect python threading to also be initialized, + // as we can't make the existing python main thread acquire the GIL. + assert!(ffi::PyEval_ThreadsInitialized() != 0); + } else { + // If python isn't initialized yet, we expect that python threading isn't initialized either. + assert!(ffi::PyEval_ThreadsInitialized() == 0); + // Initialize python. + // We use Py_InitializeEx() with initsigs=0 to disable Python signal handling. + // Signal handling depends on the notion of a 'main thread', which doesn't exist in this case. + // Note that the 'main thread' notion in python isn't documented properly; + // and running python without one is not officially supported. + ffi::Py_InitializeEx(0); + ffi::PyEval_InitThreads(); + // PyEval_InitThreads() will acquire the GIL, + // but we don't want to hold it at this point // (it's not acquired in the other code paths) - ::ffi::PyEval_ReleaseLock(); + // So immediately release the GIL: + let _thread_state = ffi::PyEval_SaveThread(); + // Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the python runtime, + // and will be restored by PyGILState_Ensure. } }); } +