Skip to content

Commit

Permalink
Merge pull request #655 from kngwyu/unsendable
Browse files Browse the repository at this point in the history
Make &Py~ types unsendable
  • Loading branch information
kngwyu committed Oct 31, 2019
2 parents fe22975 + b5157b0 commit 72debed
Show file tree
Hide file tree
Showing 22 changed files with 111 additions and 41 deletions.
16 changes: 8 additions & 8 deletions src/gil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<rc::Rc<()>>,
// Stable solution for impl !Send
no_send: Unsendable,
}

/// The Drop implementation for `GILGuard` will release the GIL.
Expand Down Expand Up @@ -181,7 +181,7 @@ pub struct GILPool<'p> {
owned: usize,
borrowed: usize,
pointers: bool,
no_send: marker::PhantomData<rc::Rc<()>>,
no_send: Unsendable,
}

impl<'p> GILPool<'p> {
Expand All @@ -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]
Expand All @@ -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(),
}
}
}
Expand Down Expand Up @@ -260,7 +260,7 @@ impl GILGuard {
owned: pool.owned.len(),
borrowed: pool.borrowed.len(),
gstate,
no_send: marker::PhantomData,
no_send: Unsendable::default(),
}
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
///
Expand Down Expand Up @@ -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<Rc<()>>);
pub struct PyRef<'a, T: PyTypeInfo>(&'a T, Unsendable);

#[allow(clippy::cast_ptr_alignment)]
fn ref_to_ptr<T>(t: &T) -> *mut ffi::PyObject
Expand All @@ -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())
}
}

Expand Down Expand Up @@ -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<Rc<()>>);
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())
}
}

Expand Down Expand Up @@ -267,7 +267,7 @@ pub trait AsPyRef<T: PyTypeInfo>: Sized {
/// `Py<T>` is thread-safe, because any python related operations require a Python<'p> token.
#[derive(Debug)]
#[repr(transparent)]
pub struct Py<T>(NonNull<ffi::PyObject>, std::marker::PhantomData<T>);
pub struct Py<T>(NonNull<ffi::PyObject>, PhantomData<T>);

unsafe impl<T> Send for Py<T> {}

Expand Down Expand Up @@ -295,7 +295,7 @@ impl<T> Py<T> {
!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<T>` instance for the given FFI pointer.
Expand All @@ -304,7 +304,7 @@ impl<T> Py<T> {
#[inline]
pub unsafe fn from_owned_ptr_or_panic(ptr: *mut ffi::PyObject) -> Py<T> {
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();
}
Expand All @@ -317,7 +317,7 @@ impl<T> Py<T> {
/// Unsafe because the pointer might be invalid.
pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Py<T>> {
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)),
}
}
Expand All @@ -332,7 +332,7 @@ impl<T> Py<T> {
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.
Expand Down
6 changes: 6 additions & 0 deletions src/internal_tricks.rs
Original file line number Diff line number Diff line change
@@ -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<Rc<()>>;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ pub mod exceptions;
pub mod freelist;
mod gil;
mod instance;
mod internal_tricks;
pub mod marshal;
mod object;
mod objectprotocol;
Expand Down
47 changes: 47 additions & 0 deletions src/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>, query: String) -> PyResult<usize> {
/// 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::<RuntimeError, _>(()))?;
/// }
/// 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<T, F>(self, f: F) -> T
where
F: Send + FnOnce() -> T,
Expand Down
3 changes: 2 additions & 1 deletion src/types/any.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -21,7 +22,7 @@ use crate::{ffi, PyObject, PyRef, PyRefMut, PyTryFrom, PyTypeInfo};
/// assert!(any.downcast_ref::<PyList>().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);

Expand Down
3 changes: 2 additions & 1 deletion src/types/boolobject.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions src/types/bytearray.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/types/bytes.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion src/types/complex.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);

Expand Down
11 changes: 6 additions & 5 deletions src/types/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion src/types/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/types/floatob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand Down
Loading

0 comments on commit 72debed

Please sign in to comment.