From 7977fe68e20a7d0d6dc854c88a549784deaf6804 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 27 Oct 2019 18:03:01 +0900 Subject: [PATCH 1/3] Use Unsendable alias instead of Phantomdata> --- src/gil.rs | 16 ++++++++-------- src/instance.rs | 20 ++++++++++---------- src/internal_tricks.rs | 6 ++++++ src/lib.rs | 1 + 4 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 src/internal_tricks.rs diff --git a/src/gil.rs b/src/gil.rs index 7034af6c182..e56b7eb51f2 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -3,11 +3,12 @@ //! Interaction with python's global interpreter lock use crate::ffi; +use crate::internal_tricks::Unsendable; use crate::types::PyAny; use crate::Python; use spin; use std::ptr::NonNull; -use std::{any, marker, rc, sync}; +use std::{any, sync}; static START: sync::Once = sync::Once::new(); static START_PYO3: sync::Once = sync::Once::new(); @@ -101,9 +102,8 @@ pub struct GILGuard { owned: usize, borrowed: usize, gstate: ffi::PyGILState_STATE, - // hack to opt out of Send on stable rust, which doesn't - // have negative impls - no_send: marker::PhantomData>, + // Stable solution for impl !Send + no_send: Unsendable, } /// The Drop implementation for `GILGuard` will release the GIL. @@ -181,7 +181,7 @@ pub struct GILPool<'p> { owned: usize, borrowed: usize, pointers: bool, - no_send: marker::PhantomData>, + no_send: Unsendable, } impl<'p> GILPool<'p> { @@ -193,7 +193,7 @@ impl<'p> GILPool<'p> { owned: p.owned.len(), borrowed: p.borrowed.len(), pointers: true, - no_send: marker::PhantomData, + no_send: Unsendable::default(), } } #[inline] @@ -204,7 +204,7 @@ impl<'p> GILPool<'p> { owned: p.owned.len(), borrowed: p.borrowed.len(), pointers: false, - no_send: marker::PhantomData, + no_send: Unsendable::default(), } } } @@ -260,7 +260,7 @@ impl GILGuard { owned: pool.owned.len(), borrowed: pool.borrowed.len(), gstate, - no_send: marker::PhantomData, + no_send: Unsendable::default(), } } } diff --git a/src/instance.rs b/src/instance.rs index 779d186170c..100710279bb 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -2,6 +2,7 @@ use crate::err::{PyErr, PyResult}; use crate::gil; use crate::instance; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; use crate::type_object::PyTypeCreate; @@ -13,7 +14,6 @@ use std::marker::PhantomData; use std::mem; use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; -use std::rc::Rc; /// Types that are built into the python interpreter. /// @@ -55,7 +55,7 @@ pub unsafe trait PyNativeType: Sized { /// py.run("assert p.length() == 12", None, Some(d)).unwrap(); /// ``` #[derive(Debug)] -pub struct PyRef<'a, T: PyTypeInfo>(&'a T, PhantomData>); +pub struct PyRef<'a, T: PyTypeInfo>(&'a T, Unsendable); #[allow(clippy::cast_ptr_alignment)] fn ref_to_ptr(t: &T) -> *mut ffi::PyObject @@ -67,7 +67,7 @@ where impl<'a, T: PyTypeInfo> PyRef<'a, T> { pub(crate) fn from_ref(r: &'a T) -> Self { - PyRef(r, PhantomData) + PyRef(r, Unsendable::default()) } } @@ -143,11 +143,11 @@ where /// py.run("assert p.length() == 100", None, Some(d)).unwrap(); /// ``` #[derive(Debug)] -pub struct PyRefMut<'a, T: PyTypeInfo>(&'a mut T, PhantomData>); +pub struct PyRefMut<'a, T: PyTypeInfo>(&'a mut T, Unsendable); impl<'a, T: PyTypeInfo> PyRefMut<'a, T> { pub(crate) fn from_mut(t: &'a mut T) -> Self { - PyRefMut(t, PhantomData) + PyRefMut(t, Unsendable::default()) } } @@ -267,7 +267,7 @@ pub trait AsPyRef: Sized { /// `Py` is thread-safe, because any python related operations require a Python<'p> token. #[derive(Debug)] #[repr(transparent)] -pub struct Py(NonNull, std::marker::PhantomData); +pub struct Py(NonNull, PhantomData); unsafe impl Send for Py {} @@ -295,7 +295,7 @@ impl Py { !ptr.is_null() && ffi::Py_REFCNT(ptr) > 0, format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr)) ); - Py(NonNull::new_unchecked(ptr), std::marker::PhantomData) + Py(NonNull::new_unchecked(ptr), PhantomData) } /// Creates a `Py` instance for the given FFI pointer. @@ -304,7 +304,7 @@ impl Py { #[inline] pub unsafe fn from_owned_ptr_or_panic(ptr: *mut ffi::PyObject) -> Py { match NonNull::new(ptr) { - Some(nonnull_ptr) => Py(nonnull_ptr, std::marker::PhantomData), + Some(nonnull_ptr) => Py(nonnull_ptr, PhantomData), None => { crate::err::panic_after_error(); } @@ -317,7 +317,7 @@ impl Py { /// Unsafe because the pointer might be invalid. pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult> { match NonNull::new(ptr) { - Some(nonnull_ptr) => Ok(Py(nonnull_ptr, std::marker::PhantomData)), + Some(nonnull_ptr) => Ok(Py(nonnull_ptr, PhantomData)), None => Err(PyErr::fetch(py)), } } @@ -332,7 +332,7 @@ impl Py { format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr)) ); ffi::Py_INCREF(ptr); - Py(NonNull::new_unchecked(ptr), std::marker::PhantomData) + Py(NonNull::new_unchecked(ptr), PhantomData) } /// Gets the reference count of the ffi::PyObject pointer. diff --git a/src/internal_tricks.rs b/src/internal_tricks.rs new file mode 100644 index 00000000000..1cf8a56a68f --- /dev/null +++ b/src/internal_tricks.rs @@ -0,0 +1,6 @@ +use std::marker::PhantomData; +use std::rc::Rc; + +/// A marker type that makes the type !Send. +/// Temporal hack until https://github.com/rust-lang/rust/issues/13231 is resolved. +pub(crate) type Unsendable = PhantomData>; diff --git a/src/lib.rs b/src/lib.rs index f5bbcdec903..f65a33e62d9 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,6 +161,7 @@ pub mod exceptions; pub mod freelist; mod gil; mod instance; +mod internal_tricks; pub mod marshal; mod object; mod objectprotocol; From 6dff93b7e93eed11d5a7771770b5f92d5bbf9122 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 27 Oct 2019 23:13:07 +0900 Subject: [PATCH 2/3] Make &Py~ types unsendable --- src/types/any.rs | 3 ++- src/types/boolobject.rs | 3 ++- src/types/bytearray.rs | 4 ++-- src/types/bytes.rs | 3 ++- src/types/complex.rs | 3 ++- src/types/datetime.rs | 11 ++++++----- src/types/dict.rs | 3 ++- src/types/floatob.rs | 3 ++- src/types/list.rs | 3 ++- src/types/module.rs | 3 ++- src/types/num.rs | 3 ++- src/types/sequence.rs | 3 ++- src/types/set.rs | 5 +++-- src/types/slice.rs | 3 ++- src/types/string.rs | 3 ++- src/types/tuple.rs | 3 ++- src/types/typeobject.rs | 3 ++- 17 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/types/any.rs b/src/types/any.rs index 58d35c4cd2f..dbbc5cf0647 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -1,5 +1,6 @@ use crate::conversion::AsPyPointer; use crate::err::PyDowncastError; +use crate::internal_tricks::Unsendable; use crate::{ffi, PyObject, PyRef, PyRefMut, PyTryFrom, PyTypeInfo}; /// Represents a python's [Any](https://docs.python.org/3/library/typing.html#typing.Any) type. @@ -21,7 +22,7 @@ use crate::{ffi, PyObject, PyRef, PyRefMut, PyTryFrom, PyTypeInfo}; /// assert!(any.downcast_ref::().is_err()); /// ``` #[repr(transparent)] -pub struct PyAny(PyObject); +pub struct PyAny(PyObject, Unsendable); pyobject_native_type_named!(PyAny); pyobject_native_type_convert!(PyAny, ffi::PyBaseObject_Type, ffi::PyObject_Check); diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index 6107e68f3ef..7204090e61c 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -1,5 +1,6 @@ // Copyright (c) 2017-present PyO3 Project and Contributors use crate::ffi; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; use crate::FromPyObject; @@ -10,7 +11,7 @@ use crate::{PyTryFrom, ToPyObject}; /// Represents a Python `bool`. #[repr(transparent)] -pub struct PyBool(PyObject); +pub struct PyBool(PyObject, Unsendable); pyobject_native_type!(PyBool, ffi::PyBool_Type, ffi::PyBool_Check); diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index b71eee19246..bdc7455102a 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -1,8 +1,8 @@ // Copyright (c) 2017-present PyO3 Project and Contributors - use crate::err::{PyErr, PyResult}; use crate::ffi; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::AsPyPointer; use crate::Python; @@ -11,7 +11,7 @@ use std::slice; /// Represents a Python `bytearray`. #[repr(transparent)] -pub struct PyByteArray(PyObject); +pub struct PyByteArray(PyObject, Unsendable); pyobject_native_type!(PyByteArray, ffi::PyByteArray_Type, ffi::PyByteArray_Check); diff --git a/src/types/bytes.rs b/src/types/bytes.rs index 9b73f18b712..2734d6912ee 100644 --- a/src/types/bytes.rs +++ b/src/types/bytes.rs @@ -1,6 +1,7 @@ use crate::conversion::FromPyObject; use crate::conversion::{PyTryFrom, ToPyObject}; use crate::err::PyResult; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; use crate::AsPyPointer; @@ -15,7 +16,7 @@ use std::str; /// /// This type is immutable #[repr(transparent)] -pub struct PyBytes(PyObject); +pub struct PyBytes(PyObject, Unsendable); pyobject_native_type!( PyBytes, diff --git a/src/types/complex.rs b/src/types/complex.rs index a9eff2c58e2..4eef6792a13 100644 --- a/src/types/complex.rs +++ b/src/types/complex.rs @@ -1,6 +1,7 @@ use crate::ffi; #[cfg(not(PyPy))] use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::AsPyPointer; use crate::PyObject; use crate::Python; @@ -10,7 +11,7 @@ use std::os::raw::c_double; /// Represents a Python `complex`. #[repr(transparent)] -pub struct PyComplex(PyObject); +pub struct PyComplex(PyObject, Unsendable); pyobject_native_type!(PyComplex, ffi::PyComplex_Type, ffi::PyComplex_Check); diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 077be5fb3a0..15f898950d7 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -25,6 +25,7 @@ use crate::ffi::{ PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE, PyDateTime_TIME_GET_SECOND, }; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyTuple; use crate::AsPyPointer; @@ -65,7 +66,7 @@ pub trait PyTimeAccess { } /// Bindings around `datetime.date` -pub struct PyDate(PyObject); +pub struct PyDate(PyObject, Unsendable); pyobject_native_type!( PyDate, *PyDateTimeAPI.DateType, @@ -120,7 +121,7 @@ impl PyDateAccess for PyDate { } /// Bindings for `datetime.datetime` -pub struct PyDateTime(PyObject); +pub struct PyDateTime(PyObject, Unsendable); pyobject_native_type!( PyDateTime, *PyDateTimeAPI.DateTimeType, @@ -229,7 +230,7 @@ impl PyTimeAccess for PyDateTime { } /// Bindings for `datetime.time` -pub struct PyTime(PyObject); +pub struct PyTime(PyObject, Unsendable); pyobject_native_type!( PyTime, *PyDateTimeAPI.TimeType, @@ -313,7 +314,7 @@ impl PyTimeAccess for PyTime { /// Bindings for `datetime.tzinfo` /// /// This is an abstract base class and should not be constructed directly. -pub struct PyTzInfo(PyObject); +pub struct PyTzInfo(PyObject, Unsendable); pyobject_native_type!( PyTzInfo, *PyDateTimeAPI.TZInfoType, @@ -322,7 +323,7 @@ pyobject_native_type!( ); /// Bindings for `datetime.timedelta` -pub struct PyDelta(PyObject); +pub struct PyDelta(PyObject, Unsendable); pyobject_native_type!( PyDelta, *PyDateTimeAPI.DeltaType, diff --git a/src/types/dict.rs b/src/types/dict.rs index 380b8ae0768..8740b7e5e4f 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -2,6 +2,7 @@ use crate::err::{self, PyErr, PyResult}; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::{PyAny, PyList}; use crate::AsPyPointer; @@ -14,7 +15,7 @@ use std::{cmp, collections, hash}; /// Represents a Python `dict`. #[repr(transparent)] -pub struct PyDict(PyObject); +pub struct PyDict(PyObject, Unsendable); pyobject_native_type!(PyDict, ffi::PyDict_Type, ffi::PyDict_Check); diff --git a/src/types/floatob.rs b/src/types/floatob.rs index a76a386296c..a27bfb5b73c 100644 --- a/src/types/floatob.rs +++ b/src/types/floatob.rs @@ -5,6 +5,7 @@ use crate::err::PyErr; use crate::ffi; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; use crate::types::PyAny; @@ -22,7 +23,7 @@ use std::os::raw::c_double; /// and [extract](struct.PyObject.html#method.extract) /// with `f32`/`f64`. #[repr(transparent)] -pub struct PyFloat(PyObject); +pub struct PyFloat(PyObject, Unsendable); pyobject_native_type!(PyFloat, ffi::PyFloat_Type, ffi::PyFloat_Check); diff --git a/src/types/list.rs b/src/types/list.rs index 039ef6d49bb..9392482d2cd 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -5,6 +5,7 @@ use crate::err::{self, PyResult}; use crate::ffi::{self, Py_ssize_t}; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; use crate::IntoPyPointer; @@ -14,7 +15,7 @@ use crate::{ToBorrowedObject, ToPyObject}; /// Represents a Python `list`. #[repr(transparent)] -pub struct PyList(PyObject); +pub struct PyList(PyObject, Unsendable); pyobject_native_type!(PyList, ffi::PyList_Type, ffi::PyList_Check); diff --git a/src/types/module.rs b/src/types/module.rs index e4c3668571b..fa5babe3747 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -6,6 +6,7 @@ use crate::err::{PyErr, PyResult}; use crate::exceptions; use crate::ffi; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; use crate::type_object::PyTypeCreate; @@ -23,7 +24,7 @@ use std::str; /// Represents a Python `module` object. #[repr(transparent)] -pub struct PyModule(PyObject); +pub struct PyModule(PyObject, Unsendable); pyobject_native_type!(PyModule, ffi::PyModule_Type, ffi::PyModule_Check); diff --git a/src/types/num.rs b/src/types/num.rs index e65820c8c06..ba191f79bbd 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -6,6 +6,7 @@ use crate::err::{PyErr, PyResult}; use crate::exceptions; use crate::ffi; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; use crate::AsPyPointer; @@ -117,7 +118,7 @@ macro_rules! int_convert_128 { /// and [extract](struct.PyObject.html#method.extract) /// with the primitive Rust integer types. #[repr(transparent)] -pub struct PyLong(PyObject); +pub struct PyLong(PyObject, Unsendable); pyobject_native_type!( PyLong, diff --git a/src/types/sequence.rs b/src/types/sequence.rs index d81ffbdd9b4..0de56dfa901 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -4,6 +4,7 @@ use crate::buffer; use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::ffi::{self, Py_ssize_t}; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; use crate::types::{PyAny, PyList, PyTuple}; @@ -12,7 +13,7 @@ use crate::{FromPyObject, PyTryFrom, ToBorrowedObject}; /// Represents a reference to a python object supporting the sequence protocol. #[repr(transparent)] -pub struct PySequence(PyObject); +pub struct PySequence(PyObject, Unsendable); pyobject_native_type_named!(PySequence); impl PySequence { diff --git a/src/types/set.rs b/src/types/set.rs index eaa4311c60f..c62ef734e32 100644 --- a/src/types/set.rs +++ b/src/types/set.rs @@ -4,6 +4,7 @@ use crate::err::{self, PyErr, PyResult}; use crate::ffi; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::AsPyPointer; use crate::Python; @@ -13,11 +14,11 @@ use std::{collections, hash}; /// Represents a Python `set` #[repr(transparent)] -pub struct PySet(PyObject); +pub struct PySet(PyObject, Unsendable); /// Represents a Python `frozenset` #[repr(transparent)] -pub struct PyFrozenSet(PyObject); +pub struct PyFrozenSet(PyObject, Unsendable); pyobject_native_type!(PySet, ffi::PySet_Type, Some("builtins"), ffi::PySet_Check); pyobject_native_type!(PyFrozenSet, ffi::PyFrozenSet_Type, ffi::PyFrozenSet_Check); diff --git a/src/types/slice.rs b/src/types/slice.rs index 41f0b104714..d618a2a903b 100644 --- a/src/types/slice.rs +++ b/src/types/slice.rs @@ -3,6 +3,7 @@ use crate::err::{PyErr, PyResult}; use crate::ffi::{self, Py_ssize_t}; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::Python; use crate::{AsPyPointer, ToPyObject}; @@ -12,7 +13,7 @@ use std::os::raw::c_long; /// /// Only `c_long` indices supported at the moment by `PySlice` object. #[repr(transparent)] -pub struct PySlice(PyObject); +pub struct PySlice(PyObject, Unsendable); pyobject_native_type!(PySlice, ffi::PySlice_Type, ffi::PySlice_Check); diff --git a/src/types/string.rs b/src/types/string.rs index f4699779adb..ad216a7e810 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -5,6 +5,7 @@ use crate::conversion::{PyTryFrom, ToPyObject}; use crate::err::{PyErr, PyResult}; use crate::gil; use crate::instance::PyNativeType; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; use crate::AsPyPointer; @@ -21,7 +22,7 @@ use std::str; /// /// This type is immutable #[repr(transparent)] -pub struct PyString(PyObject); +pub struct PyString(PyObject, Unsendable); pyobject_native_type!(PyString, ffi::PyUnicode_Type, ffi::PyUnicode_Check); diff --git a/src/types/tuple.rs b/src/types/tuple.rs index bf661010c36..e2c7eb450f4 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -5,6 +5,7 @@ use crate::err::{PyErr, PyResult}; use crate::exceptions; use crate::ffi::{self, Py_ssize_t}; use crate::instance::{AsPyRef, Py, PyNativeType}; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; use crate::AsPyPointer; @@ -15,7 +16,7 @@ use std::slice; /// Represents a Python `tuple` object. #[repr(transparent)] -pub struct PyTuple(PyObject); +pub struct PyTuple(PyObject, Unsendable); pyobject_native_type!(PyTuple, ffi::PyTuple_Type, ffi::PyTuple_Check); diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index bfb68714a5c..f079595cde9 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -5,6 +5,7 @@ use crate::err::{PyErr, PyResult}; use crate::ffi; use crate::instance::{Py, PyNativeType}; +use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::type_object::{PyTypeInfo, PyTypeObject}; use crate::AsPyPointer; @@ -14,7 +15,7 @@ use std::ffi::CStr; /// Represents a reference to a Python `type object`. #[repr(transparent)] -pub struct PyType(PyObject); +pub struct PyType(PyObject, Unsendable); pyobject_native_type!(PyType, ffi::PyType_Type, ffi::PyType_Check); From b5157b043113bca72b37ad9b9e7e40dd0153835c Mon Sep 17 00:00:00 2001 From: kngwyu Date: Mon, 28 Oct 2019 13:21:30 +0900 Subject: [PATCH 3/3] Add doctests for allow_threads --- src/python.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/python.rs b/src/python.rs index f9a0362a46c..f9164f2519b 100644 --- a/src/python.rs +++ b/src/python.rs @@ -72,6 +72,53 @@ impl<'p> Python<'p> { } /// Temporarily releases the `GIL`, thus allowing other Python threads to run. + /// + /// # Example + /// ``` + /// # use pyo3::prelude::*; use pyo3::types::IntoPyDict; use pyo3::wrap_pyfunction; + /// use pyo3::exceptions::RuntimeError; + /// use std::sync::Arc; + /// use std::thread; + /// #[pyfunction] + /// fn parallel_count(py: Python<'_>, strings: Vec, query: String) -> PyResult { + /// let query = query.chars().next().unwrap(); + /// py.allow_threads(move || { + /// let threads: Vec<_> = strings + /// .into_iter() + /// .map(|s| thread::spawn(move || s.chars().filter(|&c| c == query).count())) + /// .collect(); + /// let mut sum = 0; + /// for t in threads { + /// sum += t.join().map_err(|_| PyErr::new::(()))?; + /// } + /// Ok(sum) + /// }) + /// } + /// let gil = Python::acquire_gil(); + /// let py = gil.python(); + /// let m = PyModule::new(py, "pcount").unwrap(); + /// m.add_wrapped(wrap_pyfunction!(parallel_count)).unwrap(); + /// let locals = [("pcount", m)].into_py_dict(py); + /// py.run(r#" + /// s = ["Flow", "my", "tears", "the", "Policeman", "Said"] + /// assert pcount.parallel_count(s, "a") == 3 + /// "#, None, Some(locals)); + /// ``` + /// + /// **NOTE** + /// You cannot use all `&Py~` types in the closure that `allow_threads` takes. + /// # Example + /// ```compile_fail + /// # use pyo3::prelude::*; + /// # use pyo3::types::PyString; + /// fn parallel_print(py: Python<'_>) { + /// let s = PyString::new(py, "This object should not be shared >_<"); + /// py.allow_threads(move || { + /// println!("{:?}", s); // This causes compile error. + /// }); + /// } + /// # Example + /// ``` pub fn allow_threads(self, f: F) -> T where F: Send + FnOnce() -> T,