comparison rust/pyo3-sharedref/src/lib.rs @ 52610:c25d345f5aa5

rust-pyo3-sharedref: renamed PySharedRefCell to PyShareable There were two problems with the naming: - the PyO3 version is not based on `RefCell`. Rather than calling this `SomethingRwLock` we decided it was best to consider this an implementation detail. We mentioned it in the doc earlier on, but that is merely just bringing some reassuring context to the reader, not intended to be a needed structural explanation. - the data is not shared: it is ready to be shared To keep the changeset readable, we only make the very minimal changes to the tests to make them pass, i.e., not renaming local variables (this will be done in a later move).
author Georges Racinet <georges.racinet@cloudcrane.io>
date Sun, 15 Dec 2024 15:03:27 +0100
parents d1e304025b90
children 4a73eb3923ac
comparison
equal deleted inserted replaced
52609:d1e304025b90 52610:c25d345f5aa5
45 /// iterator: since a Python object cannot hold a lifetime-bound object, 45 /// iterator: since a Python object cannot hold a lifetime-bound object,
46 /// `Iter<'a, T>` cannot be a data field of the Python iterator object. 46 /// `Iter<'a, T>` cannot be a data field of the Python iterator object.
47 /// While `&'a T` can be replaced with [`std::sync::Arc`], this is typically 47 /// While `&'a T` can be replaced with [`std::sync::Arc`], this is typically
48 /// not suited for more complex objects that are created from such references 48 /// not suited for more complex objects that are created from such references
49 /// and re-expose the lifetime on their types, such as iterators. 49 /// and re-expose the lifetime on their types, such as iterators.
50 /// The [`PySharedRef::leak_immutable()`] and [`UnsafePyLeaked::map()`] methods 50 /// The [`PyShareableRef::leak_immutable()`] and [`UnsafePyLeaked::map()`]
51 /// provide a way around this issue. 51 /// methods provide a way around this issue.
52 /// 52 ///
53 /// [`PySharedRefCell`] is [`Sync`]. It works internally with locks and 53 /// [`PyShareable`] is [`Sync`]. It works internally with locks and
54 /// a "generation" counter that keeps track of mutations. 54 /// a "generation" counter that keeps track of mutations.
55 /// 55 ///
56 /// [`PySharedRefCell`] is merely a data struct to be stored in its 56 /// [`PyShareable`] is merely a data struct to be stored in its
57 /// owner Python object. 57 /// owner Python object.
58 /// Any further operation will be performed through [`PySharedRef`], which is 58 /// Any further operation will be performed through [`PyShareableRef`], which
59 /// a lifetime-bound reference to the [`PySharedRefCell`]. 59 /// is a lifetime-bound reference to the [`PyShareable`].
60 /// 60 ///
61 /// # Example 61 /// # Example
62 /// 62 ///
63 /// ``` 63 /// ```
64 /// use pyo3::prelude::*; 64 /// use pyo3::prelude::*;
72 /// use std::ffi::CStr; 72 /// use std::ffi::CStr;
73 /// use std::vec::Vec; 73 /// use std::vec::Vec;
74 /// 74 ///
75 /// #[pyclass(sequence)] 75 /// #[pyclass(sequence)]
76 /// struct Set { 76 /// struct Set {
77 /// rust_set: PySharedRefCell<HashSet<i32>>, 77 /// rust_set: PyShareable<HashSet<i32>>,
78 /// } 78 /// }
79 /// 79 ///
80 /// #[pymethods] 80 /// #[pymethods]
81 /// impl Set { 81 /// impl Set {
82 /// #[new] 82 /// #[new]
165 /// # .expect("This example should not return an error"); 165 /// # .expect("This example should not return an error");
166 /// ``` 166 /// ```
167 /// 167 ///
168 /// The borrow rules are enforced dynamically in a similar manner to the 168 /// The borrow rules are enforced dynamically in a similar manner to the
169 /// Python iterator. 169 /// Python iterator.
170 ///
171 /// [`PyShareable`] is merely a data struct to be stored in a Python object.
172 /// Any further operation will be performed through [PyShareableRef], which is
173 /// a lifetime-bound reference to the [`PyShareable`].
170 #[derive(Debug)] 174 #[derive(Debug)]
171 pub struct PySharedRefCell<T: ?Sized> { 175 pub struct PyShareable<T: ?Sized> {
172 state: PySharedState, 176 state: PySharedState,
173 data: RwLock<T>, 177 data: RwLock<T>,
174 } 178 }
175 179
176 impl<T> PySharedRefCell<T> { 180 impl<T> PyShareable<T> {
177 /// Borrows the shared data and its state, keeping a reference 181 /// Borrows the shared data and its state, keeping a reference
178 /// on the owner Python object. 182 /// on the owner Python object.
179 /// 183 ///
180 /// # Safety 184 /// # Safety
181 /// 185 ///
182 /// The `data` must be owned by the `owner`. Otherwise, calling 186 /// The `data` must be owned by the `owner`. Otherwise, calling
183 /// `leak_immutable()` on the shared ref would create an invalid reference. 187 /// `leak_immutable()` on the shared ref would create an invalid reference.
184 pub unsafe fn borrow_with_owner<'py>( 188 pub unsafe fn borrow_with_owner<'py>(
185 &'py self, 189 &'py self,
186 owner: &'py Bound<'py, PyAny>, 190 owner: &'py Bound<'py, PyAny>,
187 ) -> PySharedRef<'py, T> { 191 ) -> PyShareableRef<'py, T> {
188 PySharedRef { 192 PyShareableRef {
189 owner, 193 owner,
190 state: &self.state, 194 state: &self.state,
191 data: &self.data, 195 data: &self.data,
192 } 196 }
193 } 197 }
194 } 198 }
195 199
196 impl<T> From<T> for PySharedRefCell<T> { 200 impl<T> From<T> for PyShareable<T> {
197 fn from(value: T) -> Self { 201 fn from(value: T) -> Self {
198 Self { 202 Self {
199 state: PySharedState::new(), 203 state: PySharedState::new(),
200 data: value.into(), 204 data: value.into(),
201 } 205 }
218 TryLockError::WouldBlock => Self::InnerLockWouldBlock, 222 TryLockError::WouldBlock => Self::InnerLockWouldBlock,
219 } 223 }
220 } 224 }
221 } 225 }
222 226
223 /// A reference to [`PySharedRefCell`] owned by a Python object. 227 /// A reference to [`PyShareable`] and its legit owner Python object.
224 /// 228 ///
225 /// This is a lifetime-bound reference to the [`PySharedRefCell`] data field. 229 /// This is a lifetime-bound reference to the [PyShareable] data field,
226 pub struct PySharedRef<'py, T: 'py + ?Sized> { 230 /// and could be created by an automatically generated accessor when
231 /// we make one.
232 pub struct PyShareableRef<'py, T: 'py + ?Sized> {
227 owner: &'py Bound<'py, PyAny>, 233 owner: &'py Bound<'py, PyAny>,
228 state: &'py PySharedState, 234 state: &'py PySharedState,
229 data: &'py RwLock<T>, // TODO perhaps this needs Pin 235 data: &'py RwLock<T>, // TODO perhaps this needs Pin
230 } 236 }
231 237
232 impl<'py, T: ?Sized> PySharedRef<'py, T> { 238 impl<'py, T: ?Sized> PyShareableRef<'py, T> {
233 /// Take the lock on the wrapped value for read-only operations. 239 /// Take the lock on the wrapped value for read-only operations.
234 /// 240 ///
235 /// # Panics 241 /// # Panics
236 /// 242 ///
237 /// Panics if the lock is currently held for write operations. 243 /// Panics if the lock is currently held for write operations.
315 } 321 }
316 } 322 }
317 323
318 /// The shared state between Python and Rust 324 /// The shared state between Python and Rust
319 /// 325 ///
320 /// `PySharedState` is owned by `PySharedRefCell`, and is shared across its 326 /// `PySharedState` is owned by `PyShareable`, and is shared across its
321 /// derived references. The consistency of these references are guaranteed 327 /// derived references. The consistency of these references are guaranteed
322 /// as follows: 328 /// as follows:
323 /// 329 ///
324 /// - The immutability of `PycCass` object fields. Any mutation of 330 /// - The immutability of `PycCass` object fields. Any mutation of
325 /// [`PySharedRefCell`] is allowed only through its `write()`. 331 /// [`PyShareable`] is allowed only through its `write()`.
326 /// - The `py: Python<'_>` token, which makes sure that any data access is 332 /// - The `py: Python<'_>` token, which makes sure that any data access is
327 /// synchronized by the GIL. 333 /// synchronized by the GIL.
328 /// - The underlying `RefCell`, which prevents `PySharedRefCell` value from 334 /// - The underlying `RefCell`, which prevents `PyShareable` value from being
329 /// being directly borrowed or leaked while it is mutably borrowed. 335 /// directly borrowed or leaked while it is mutably borrowed.
330 /// - The `borrow_count`, which is the number of references borrowed from 336 /// - The `borrow_count`, which is the number of references borrowed from
331 /// `UnsafePyLeaked`. Just like `RefCell`, mutation is prohibited while 337 /// `UnsafePyLeaked`. Just like `RefCell`, mutation is prohibited while
332 /// `UnsafePyLeaked` is borrowed. 338 /// `UnsafePyLeaked` is borrowed.
333 /// - The `generation` counter, which increments on `write()`. `UnsafePyLeaked` 339 /// - The `generation` counter, which increments on `write()`. `UnsafePyLeaked`
334 /// reference is valid only if the `current_generation()` equals to the 340 /// reference is valid only if the `current_generation()` equals to the
380 self.generation.fetch_add(1, Ordering::Relaxed); 386 self.generation.fetch_add(1, Ordering::Relaxed);
381 } 387 }
382 } 388 }
383 389
384 /// Helper to keep the borrow count updated while the shared object is 390 /// Helper to keep the borrow count updated while the shared object is
385 /// immutably borrowed without using the `RefCell` interface. 391 /// immutably borrowed without using the `RwLock` interface.
386 struct BorrowPyShared<'a> { 392 struct BorrowPyShared<'a> {
387 py: Python<'a>, 393 py: Python<'a>,
388 state: &'a PySharedState, 394 state: &'a PySharedState,
389 } 395 }
390 396
399 fn drop(&mut self) { 405 fn drop(&mut self) {
400 self.state.decrease_borrow_count(self.py); 406 self.state.decrease_borrow_count(self.py);
401 } 407 }
402 } 408 }
403 409
404 /// An immutable reference to [`PySharedRefCell`] value, not bound to lifetime. 410 /// An immutable reference to [`PyShareable`] value, not bound to lifetime.
405 /// 411 ///
406 /// The reference will be invalidated once the original value is mutably 412 /// The reference will be invalidated once the original value is mutably
407 /// borrowed. 413 /// borrowed.
408 /// 414 ///
409 /// # Safety 415 /// # Safety