comparison rust/hg-cpython/src/ref_sharing.rs @ 43427:b7ab3a0a9e57

rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref Since the underlying value can't be mutably borrowed by PyLeaked, we don't have to manage yet another mutably-borrowed state. We can just rely on the RefCell implementation. Maybe we can add try_leak_immutable(), but this patch doesn't in order to keep the patch series not too long.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 05 Oct 2019 08:56:15 -0400
parents 6f9f15a476a4
children 6c0e47874217
comparison
equal deleted inserted replaced
43426:6f9f15a476a4 43427:b7ab3a0a9e57
36 /// 36 ///
37 /// - The immutability of `py_class!` object fields. Any mutation of 37 /// - The immutability of `py_class!` object fields. Any mutation of
38 /// `PySharedRefCell` is allowed only through its `borrow_mut()`. 38 /// `PySharedRefCell` is allowed only through its `borrow_mut()`.
39 /// - The `py: Python<'_>` token, which makes sure that any data access is 39 /// - The `py: Python<'_>` token, which makes sure that any data access is
40 /// synchronized by the GIL. 40 /// synchronized by the GIL.
41 /// - The underlying `RefCell`, which prevents `PySharedRefCell` data from
42 /// being directly borrowed or leaked while it is mutably borrowed.
41 /// - The `borrow_count`, which is the number of references borrowed from 43 /// - The `borrow_count`, which is the number of references borrowed from
42 /// `PyLeaked`. Just like `RefCell`, mutation is prohibited while `PyLeaked` 44 /// `PyLeaked`. Just like `RefCell`, mutation is prohibited while `PyLeaked`
43 /// is borrowed. 45 /// is borrowed.
44 /// - The `generation` counter, which increments on `borrow_mut()`. `PyLeaked` 46 /// - The `generation` counter, which increments on `borrow_mut()`. `PyLeaked`
45 /// reference is valid only if the `current_generation()` equals to the 47 /// reference is valid only if the `current_generation()` equals to the
97 /// This is highly unsafe since the lifetime of the given data can be 99 /// This is highly unsafe since the lifetime of the given data can be
98 /// extended. Do not call this function directly. 100 /// extended. Do not call this function directly.
99 unsafe fn leak_immutable<T>( 101 unsafe fn leak_immutable<T>(
100 &self, 102 &self,
101 py: Python, 103 py: Python,
102 data: &PySharedRefCell<T>, 104 data: Ref<T>,
103 ) -> PyResult<(&'static T, &'static PySharedState)> { 105 ) -> PyResult<(&'static T, &'static PySharedState)> {
104 if self.mutably_borrowed.get() { 106 if self.mutably_borrowed.get() {
105 return Err(AlreadyBorrowed::new( 107 return Err(AlreadyBorrowed::new(
106 py, 108 py,
107 "Cannot borrow immutably while there is a \ 109 "Cannot borrow immutably while there is a \
108 mutable reference in Python objects", 110 mutable reference in Python objects",
109 )); 111 ));
110 } 112 }
111 // TODO: it's weird that self is data.py_shared_state. Maybe we 113 let ptr: *const T = &*data;
112 // can move stuff to PySharedRefCell? 114 let state_ptr: *const PySharedState = self;
113 let ptr = data.as_ptr();
114 let state_ptr: *const PySharedState = &data.py_shared_state;
115 Ok((&*ptr, &*state_ptr)) 115 Ok((&*ptr, &*state_ptr))
116 } 116 }
117 117
118 fn current_borrow_count(&self, _py: Python) -> usize { 118 fn current_borrow_count(&self, _py: Python) -> usize {
119 self.borrow_count.load(Ordering::Relaxed) 119 self.borrow_count.load(Ordering::Relaxed)
198 // - inner.borrow() would fail if self is mutably borrowed, 198 // - inner.borrow() would fail if self is mutably borrowed,
199 // - and inner.borrow_mut() would fail while self is borrowed. 199 // - and inner.borrow_mut() would fail while self is borrowed.
200 self.inner.borrow() 200 self.inner.borrow()
201 } 201 }
202 202
203 fn as_ptr(&self) -> *mut T {
204 self.inner.as_ptr()
205 }
206
207 // TODO: maybe this should be named as try_borrow_mut(), and use 203 // TODO: maybe this should be named as try_borrow_mut(), and use
208 // inner.try_borrow_mut(). The current implementation panics if 204 // inner.try_borrow_mut(). The current implementation panics if
209 // self.inner has been borrowed, but returns error if py_shared_state 205 // self.inner has been borrowed, but returns error if py_shared_state
210 // refuses to borrow. 206 // refuses to borrow.
211 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<PyRefMut<'a, T>> { 207 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<PyRefMut<'a, T>> {
240 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> { 236 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> {
241 self.data.borrow_mut(self.py) 237 self.data.borrow_mut(self.py)
242 } 238 }
243 239
244 /// Returns a leaked reference. 240 /// Returns a leaked reference.
241 ///
242 /// # Panics
243 ///
244 /// Panics if this is mutably borrowed.
245 pub fn leak_immutable(&self) -> PyResult<PyLeaked<&'static T>> { 245 pub fn leak_immutable(&self) -> PyResult<PyLeaked<&'static T>> {
246 let state = &self.data.py_shared_state; 246 let state = &self.data.py_shared_state;
247 // make sure self.data isn't mutably borrowed; otherwise the
248 // generation number can't be trusted.
249 let data_ref = self.borrow();
247 unsafe { 250 unsafe {
248 let (static_ref, static_state_ref) = 251 let (static_ref, static_state_ref) =
249 state.leak_immutable(self.py, self.data)?; 252 state.leak_immutable(self.py, data_ref)?;
250 Ok(PyLeaked::new( 253 Ok(PyLeaked::new(
251 self.py, 254 self.py,
252 self.owner, 255 self.owner,
253 static_ref, 256 static_ref,
254 static_state_ref, 257 static_state_ref,
700 let _leaked_ref = leaked_iter.try_borrow_mut(py).unwrap(); 703 let _leaked_ref = leaked_iter.try_borrow_mut(py).unwrap();
701 assert!(owner.string_shared(py).borrow_mut().is_err()); 704 assert!(owner.string_shared(py).borrow_mut().is_err());
702 } 705 }
703 assert!(owner.string_shared(py).borrow_mut().is_ok()); 706 assert!(owner.string_shared(py).borrow_mut().is_ok());
704 } 707 }
705 } 708
709 #[test]
710 #[should_panic(expected = "mutably borrowed")]
711 fn test_leak_while_borrow_mut() {
712 let (gil, owner) = prepare_env();
713 let py = gil.python();
714 let _mut_ref = owner.string_shared(py).borrow_mut();
715 let _ = owner.string_shared(py).leak_immutable();
716 }
717 }