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