implement `Send` and `Sync` for `PyBackedStr` and `PyBackedBytes` (#4007)

This commit is contained in:
David Hewitt 2024-03-29 11:28:42 +00:00 committed by GitHub
parent dd1710256d
commit b053e83c08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 6 deletions

View File

@ -0,0 +1 @@
Implement `Send` and `Sync` for `PyBackedStr` and `PyBackedBytes`.

View File

@ -16,14 +16,14 @@ use crate::{
pub struct PyBackedStr { pub struct PyBackedStr {
#[allow(dead_code)] // only held so that the storage is not dropped #[allow(dead_code)] // only held so that the storage is not dropped
storage: Py<PyAny>, storage: Py<PyAny>,
data: NonNull<[u8]>, data: NonNull<str>,
} }
impl Deref for PyBackedStr { impl Deref for PyBackedStr {
type Target = str; type Target = str;
fn deref(&self) -> &str { fn deref(&self) -> &str {
// Safety: `data` is known to be immutable utf8 string and owned by self // Safety: `data` is known to be immutable and owned by self
unsafe { std::str::from_utf8_unchecked(self.data.as_ref()) } unsafe { self.data.as_ref() }
} }
} }
@ -39,13 +39,18 @@ impl AsRef<[u8]> for PyBackedStr {
} }
} }
// Safety: the underlying Python str (or bytes) is immutable and
// safe to share between threads
unsafe impl Send for PyBackedStr {}
unsafe impl Sync for PyBackedStr {}
impl TryFrom<Bound<'_, PyString>> for PyBackedStr { impl TryFrom<Bound<'_, PyString>> for PyBackedStr {
type Error = PyErr; type Error = PyErr;
fn try_from(py_string: Bound<'_, PyString>) -> Result<Self, Self::Error> { fn try_from(py_string: Bound<'_, PyString>) -> Result<Self, Self::Error> {
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))] #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
{ {
let s = py_string.to_str()?; let s = py_string.to_str()?;
let data = NonNull::from(s.as_bytes()); let data = NonNull::from(s);
Ok(Self { Ok(Self {
storage: py_string.as_any().to_owned().unbind(), storage: py_string.as_any().to_owned().unbind(),
data, data,
@ -54,8 +59,8 @@ impl TryFrom<Bound<'_, PyString>> for PyBackedStr {
#[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))] #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
{ {
let bytes = py_string.encode_utf8()?; let bytes = py_string.encode_utf8()?;
let b = bytes.as_bytes(); let s = unsafe { std::str::from_utf8_unchecked(bytes.as_bytes()) };
let data = NonNull::from(b); let data = NonNull::from(s);
Ok(Self { Ok(Self {
storage: bytes.into_any().unbind(), storage: bytes.into_any().unbind(),
data, data,
@ -100,6 +105,11 @@ impl AsRef<[u8]> for PyBackedBytes {
} }
} }
// Safety: the underlying Python bytes or Rust bytes is immutable and
// safe to share between threads
unsafe impl Send for PyBackedBytes {}
unsafe impl Sync for PyBackedBytes {}
impl From<Bound<'_, PyBytes>> for PyBackedBytes { impl From<Bound<'_, PyBytes>> for PyBackedBytes {
fn from(py_bytes: Bound<'_, PyBytes>) -> Self { fn from(py_bytes: Bound<'_, PyBytes>) -> Self {
let b = py_bytes.as_bytes(); let b = py_bytes.as_bytes();
@ -201,4 +211,16 @@ mod test {
assert_eq!(&*py_backed_bytes, b"abcde"); assert_eq!(&*py_backed_bytes, b"abcde");
}); });
} }
#[test]
fn test_backed_types_send_sync() {
fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}
is_send::<PyBackedStr>();
is_sync::<PyBackedStr>();
is_send::<PyBackedBytes>();
is_sync::<PyBackedBytes>();
}
} }