implement `Send` and `Sync` for `PyBackedStr` and `PyBackedBytes` (#4007)
This commit is contained in:
parent
dd1710256d
commit
b053e83c08
|
@ -0,0 +1 @@
|
||||||
|
Implement `Send` and `Sync` for `PyBackedStr` and `PyBackedBytes`.
|
|
@ -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>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue