Mercurial > public > mercurial-scm > hg-stable
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 |