comparison rust/pyo3-sharedref/src/lib.rs @ 52634:d1e304025b90

rust-pyo3-sharedref: replaced borrow/borrow_mut with RwLock namings The "borrow" word had way too many uses in this context. Originally, it came from the rust-cpython version, which is based on `RefCell`. Before this change, we had untracktable stacks of borrows (first the `Bound`, then the `PySharedRefCell`). In any case, the change to `RwLock` is far from being neutral, and we may need in a future without GIL to also expose blocking methods, to account for in-Rust possible concurrency. Using `read()` and `write()` right now matches our PyO3 habits anyway, where all non-Sync objects have to be wrapped in a RwLock.
author Georges Racinet <georges.racinet@cloudcrane.io>
date Sun, 15 Dec 2024 13:58:31 +0100
parents d85514a88706
children c25d345f5aa5
comparison
equal deleted inserted replaced
52633:d85514a88706 52634:d1e304025b90
91 /// } 91 /// }
92 /// 92 ///
93 /// fn add(slf: &Bound<'_, Self>, i: i32) -> PyResult<()> { 93 /// fn add(slf: &Bound<'_, Self>, i: i32) -> PyResult<()> {
94 /// let rust_set = &slf.borrow().rust_set; 94 /// let rust_set = &slf.borrow().rust_set;
95 /// let shared_ref = unsafe { rust_set.borrow_with_owner(slf) }; 95 /// let shared_ref = unsafe { rust_set.borrow_with_owner(slf) };
96 /// let mut set_ref = shared_ref.borrow_mut(); 96 /// let mut set_ref = shared_ref.write();
97 /// set_ref.insert(i); 97 /// set_ref.insert(i);
98 /// Ok(()) 98 /// Ok(())
99 /// } 99 /// }
100 /// } 100 /// }
101 /// 101 ///
228 state: &'py PySharedState, 228 state: &'py PySharedState,
229 data: &'py RwLock<T>, // TODO perhaps this needs Pin 229 data: &'py RwLock<T>, // TODO perhaps this needs Pin
230 } 230 }
231 231
232 impl<'py, T: ?Sized> PySharedRef<'py, T> { 232 impl<'py, T: ?Sized> PySharedRef<'py, T> {
233 /// Immutably borrows the wrapped value. 233 /// Take the lock on the wrapped value for read-only operations.
234 /// 234 ///
235 /// # Panics 235 /// # Panics
236 /// 236 ///
237 /// Panics if the value is currently mutably borrowed. 237 /// Panics if the lock is currently held for write operations.
238 pub fn borrow(&self) -> RwLockReadGuard<'py, T> { 238 pub fn read(&self) -> RwLockReadGuard<'py, T> {
239 self.try_borrow().expect("already mutably borrowed") 239 self.try_read().expect("already mutably borrowed")
240 } 240 }
241 241
242 /// Immutably borrows the wrapped value, returning an error if the value 242 /// Immutably borrows the wrapped value, returning an error if the value
243 /// is currently mutably borrowed. 243 /// is currently mutably borrowed.
244 pub fn try_borrow(&self) -> TryLockResult<RwLockReadGuard<'py, T>> { 244 pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'py, T>> {
245 // state isn't involved since 245 // state isn't involved since
246 // - data.try_borrow() would fail if self is mutably borrowed, 246 // - data.try_read() would fail if self is mutably borrowed,
247 // - and data.try_borrow_mut() would fail while self is borrowed. 247 // - and data.try_write() would fail while self is borrowed.
248 self.data.try_read() 248 self.data.try_read()
249 } 249 }
250 250
251 /// Mutably borrows the wrapped value. 251 /// Take the lock on the wrapped value for write operations.
252 /// 252 ///
253 /// Any existing leaked references will be invalidated. 253 /// Any existing leaked references will be invalidated.
254 /// 254 ///
255 /// # Panics 255 /// # Panics
256 /// 256 ///
257 /// Panics if the value is currently borrowed. 257 /// Panics if the lock is currently held.
258 pub fn borrow_mut(&self) -> RwLockWriteGuard<'py, T> { 258 pub fn write(&self) -> RwLockWriteGuard<'py, T> {
259 self.try_borrow_mut().expect("already borrowed") 259 self.try_write().expect("already borrowed")
260 } 260 }
261 261
262 /// Mutably borrows the wrapped value, returning an error if the value 262 /// Mutably borrows the wrapped value, returning an error if the value
263 /// is currently borrowed. 263 /// is currently borrowed.
264 pub fn try_borrow_mut(&self) -> TryLockResult<RwLockWriteGuard<'py, T>> { 264 pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'py, T>> {
265 // the value may be immutably borrowed through UnsafePyLeaked 265 // the value may be immutably borrowed through UnsafePyLeaked
266 if self.state.current_borrow_count(self.py()) > 0 { 266 if self.state.current_borrow_count(self.py()) > 0 {
267 // propagate borrow-by-leaked state to data to get BorrowMutError 267 // propagate borrow-by-leaked state to data to get BorrowMutError
268 let _dummy = self.data.read(); 268 let _dummy = self.data.read();
269 let _unused = self.data.try_write()?; 269 let _unused = self.data.try_write()?;
289 pub fn try_leak_immutable( 289 pub fn try_leak_immutable(
290 &self, 290 &self,
291 ) -> Result<UnsafePyLeaked<&'static T>, TryLeakError> { 291 ) -> Result<UnsafePyLeaked<&'static T>, TryLeakError> {
292 // make sure self.data isn't mutably borrowed; otherwise the 292 // make sure self.data isn't mutably borrowed; otherwise the
293 // generation number wouldn't be trusted. 293 // generation number wouldn't be trusted.
294 let data_ref = self.try_borrow()?; 294 let data_ref = self.try_read()?;
295 295
296 // keep reference to the owner so the data and state are alive, 296 // keep reference to the owner so the data and state are alive,
297 // but the data pointer can be invalidated by borrow_mut(). 297 // but the data pointer can be invalidated by write().
298 // the state wouldn't since it is immutable. 298 // the state wouldn't since it is immutable.
299 let state_ptr: *const PySharedState = self.state; 299 let state_ptr: *const PySharedState = self.state;
300 let data_ptr: *const T = &*data_ref; 300 let data_ptr: *const T = &*data_ref;
301 Ok(UnsafePyLeaked::<&'static T> { 301 Ok(UnsafePyLeaked::<&'static T> {
302 owner: self.owner.clone().unbind(), 302 owner: self.owner.clone().unbind(),
320 /// `PySharedState` is owned by `PySharedRefCell`, and is shared across its 320 /// `PySharedState` is owned by `PySharedRefCell`, and is shared across its
321 /// derived references. The consistency of these references are guaranteed 321 /// derived references. The consistency of these references are guaranteed
322 /// as follows: 322 /// as follows:
323 /// 323 ///
324 /// - The immutability of `PycCass` object fields. Any mutation of 324 /// - The immutability of `PycCass` object fields. Any mutation of
325 /// [`PySharedRefCell`] is allowed only through its `borrow_mut()`. 325 /// [`PySharedRefCell`] is allowed only through its `write()`.
326 /// - The `py: Python<'_>` token, which makes sure that any data access is 326 /// - The `py: Python<'_>` token, which makes sure that any data access is
327 /// synchronized by the GIL. 327 /// synchronized by the GIL.
328 /// - The underlying `RefCell`, which prevents `PySharedRefCell` value from 328 /// - The underlying `RefCell`, which prevents `PySharedRefCell` value from
329 /// being directly borrowed or leaked while it is mutably borrowed. 329 /// being directly borrowed or leaked while it is mutably borrowed.
330 /// - The `borrow_count`, which is the number of references borrowed from 330 /// - The `borrow_count`, which is the number of references borrowed from
331 /// `UnsafePyLeaked`. Just like `RefCell`, mutation is prohibited while 331 /// `UnsafePyLeaked`. Just like `RefCell`, mutation is prohibited while
332 /// `UnsafePyLeaked` is borrowed. 332 /// `UnsafePyLeaked` is borrowed.
333 /// - The `generation` counter, which increments on `borrow_mut()`. 333 /// - The `generation` counter, which increments on `write()`. `UnsafePyLeaked`
334 /// `UnsafePyLeaked` reference is valid only if the `current_generation()` 334 /// reference is valid only if the `current_generation()` equals to the
335 /// equals to the `generation` at the time of `leak_immutable()`. 335 /// `generation` at the time of `leak_immutable()`.
336 #[derive(Debug)] 336 #[derive(Debug)]
337 struct PySharedState { 337 struct PySharedState {
338 // The counter variable could be Cell<usize> since any operation on 338 // The counter variable could be Cell<usize> since any operation on
339 // PySharedState is synchronized by the GIL, but being "atomic" makes 339 // PySharedState is synchronized by the GIL, but being "atomic" makes
340 // PySharedState inherently Sync. The ordering requirement doesn't 340 // PySharedState inherently Sync. The ordering requirement doesn't