##// END OF EJS Templates
rust-cpython: add generation counter to leaked reference...
Yuya Nishihara -
r43605:0836efe4 default
parent child Browse files
Show More
@@ -1,593 +1,691 b''
1 // ref_sharing.rs
1 // ref_sharing.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to
6 // of this software and associated documentation files (the "Software"), to
7 // deal in the Software without restriction, including without limitation the
7 // deal in the Software without restriction, including without limitation the
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 // sell copies of the Software, and to permit persons to whom the Software is
9 // sell copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
10 // furnished to do so, subject to the following conditions:
11 //
11 //
12 // The above copyright notice and this permission notice shall be included in
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
13 // all copies or substantial portions of the Software.
14 //
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 // IN THE SOFTWARE.
21 // IN THE SOFTWARE.
22
22
23 //! Macros for use in the `hg-cpython` bridge library.
23 //! Macros for use in the `hg-cpython` bridge library.
24
24
25 use crate::exceptions::AlreadyBorrowed;
25 use crate::exceptions::AlreadyBorrowed;
26 use cpython::{PyClone, PyObject, PyResult, Python};
26 use cpython::{exc, PyClone, PyErr, PyObject, PyResult, Python};
27 use std::cell::{Cell, Ref, RefCell, RefMut};
27 use std::cell::{Cell, Ref, RefCell, RefMut};
28 use std::ops::{Deref, DerefMut};
28 use std::ops::{Deref, DerefMut};
29 use std::sync::atomic::{AtomicUsize, Ordering};
29
30
30 /// Manages the shared state between Python and Rust
31 /// Manages the shared state between Python and Rust
32 ///
33 /// `PySharedState` is owned by `PySharedRefCell`, and is shared across its
34 /// derived references. The consistency of these references are guaranteed
35 /// as follows:
36 ///
37 /// - The immutability of `py_class!` object fields. Any mutation of
38 /// `PySharedRefCell` is allowed only through its `borrow_mut()`.
39 /// - The `py: Python<'_>` token, which makes sure that any data access is
40 /// synchronized by the GIL.
41 /// - The `generation` counter, which increments on `borrow_mut()`. `PyLeaked`
42 /// reference is valid only if the `current_generation()` equals to the
43 /// `generation` at the time of `leak_immutable()`.
31 #[derive(Debug, Default)]
44 #[derive(Debug, Default)]
32 struct PySharedState {
45 struct PySharedState {
33 leak_count: Cell<usize>,
46 leak_count: Cell<usize>,
34 mutably_borrowed: Cell<bool>,
47 mutably_borrowed: Cell<bool>,
48 // The counter variable could be Cell<usize> since any operation on
49 // PySharedState is synchronized by the GIL, but being "atomic" makes
50 // PySharedState inherently Sync. The ordering requirement doesn't
51 // matter thanks to the GIL.
52 generation: AtomicUsize,
35 }
53 }
36
54
37 // &PySharedState can be Send because any access to inner cells is
55 // &PySharedState can be Send because any access to inner cells is
38 // synchronized by the GIL.
56 // synchronized by the GIL.
39 unsafe impl Sync for PySharedState {}
57 unsafe impl Sync for PySharedState {}
40
58
41 impl PySharedState {
59 impl PySharedState {
42 fn borrow_mut<'a, T>(
60 fn borrow_mut<'a, T>(
43 &'a self,
61 &'a self,
44 py: Python<'a>,
62 py: Python<'a>,
45 pyrefmut: RefMut<'a, T>,
63 pyrefmut: RefMut<'a, T>,
46 ) -> PyResult<PyRefMut<'a, T>> {
64 ) -> PyResult<PyRefMut<'a, T>> {
47 if self.mutably_borrowed.get() {
65 if self.mutably_borrowed.get() {
48 return Err(AlreadyBorrowed::new(
66 return Err(AlreadyBorrowed::new(
49 py,
67 py,
50 "Cannot borrow mutably while there exists another \
68 "Cannot borrow mutably while there exists another \
51 mutable reference in a Python object",
69 mutable reference in a Python object",
52 ));
70 ));
53 }
71 }
54 match self.leak_count.get() {
72 match self.leak_count.get() {
55 0 => {
73 0 => {
56 self.mutably_borrowed.replace(true);
74 self.mutably_borrowed.replace(true);
75 // Note that this wraps around to the same value if mutably
76 // borrowed more than usize::MAX times, which wouldn't happen
77 // in practice.
78 self.generation.fetch_add(1, Ordering::Relaxed);
57 Ok(PyRefMut::new(py, pyrefmut, self))
79 Ok(PyRefMut::new(py, pyrefmut, self))
58 }
80 }
59 // TODO
81 // TODO
60 // For now, this works differently than Python references
82 // For now, this works differently than Python references
61 // in the case of iterators.
83 // in the case of iterators.
62 // Python does not complain when the data an iterator
84 // Python does not complain when the data an iterator
63 // points to is modified if the iterator is never used
85 // points to is modified if the iterator is never used
64 // afterwards.
86 // afterwards.
65 // Here, we are stricter than this by refusing to give a
87 // Here, we are stricter than this by refusing to give a
66 // mutable reference if it is already borrowed.
88 // mutable reference if it is already borrowed.
67 // While the additional safety might be argued for, it
89 // While the additional safety might be argued for, it
68 // breaks valid programming patterns in Python and we need
90 // breaks valid programming patterns in Python and we need
69 // to fix this issue down the line.
91 // to fix this issue down the line.
70 _ => Err(AlreadyBorrowed::new(
92 _ => Err(AlreadyBorrowed::new(
71 py,
93 py,
72 "Cannot borrow mutably while there are \
94 "Cannot borrow mutably while there are \
73 immutable references in Python objects",
95 immutable references in Python objects",
74 )),
96 )),
75 }
97 }
76 }
98 }
77
99
78 /// Return a reference to the wrapped data and its state with an
100 /// Return a reference to the wrapped data and its state with an
79 /// artificial static lifetime.
101 /// artificial static lifetime.
80 /// We need to be protected by the GIL for thread-safety.
102 /// We need to be protected by the GIL for thread-safety.
81 ///
103 ///
82 /// # Safety
104 /// # Safety
83 ///
105 ///
84 /// This is highly unsafe since the lifetime of the given data can be
106 /// This is highly unsafe since the lifetime of the given data can be
85 /// extended. Do not call this function directly.
107 /// extended. Do not call this function directly.
86 unsafe fn leak_immutable<T>(
108 unsafe fn leak_immutable<T>(
87 &self,
109 &self,
88 py: Python,
110 py: Python,
89 data: &PySharedRefCell<T>,
111 data: &PySharedRefCell<T>,
90 ) -> PyResult<(&'static T, &'static PySharedState)> {
112 ) -> PyResult<(&'static T, &'static PySharedState)> {
91 if self.mutably_borrowed.get() {
113 if self.mutably_borrowed.get() {
92 return Err(AlreadyBorrowed::new(
114 return Err(AlreadyBorrowed::new(
93 py,
115 py,
94 "Cannot borrow immutably while there is a \
116 "Cannot borrow immutably while there is a \
95 mutable reference in Python objects",
117 mutable reference in Python objects",
96 ));
118 ));
97 }
119 }
98 // TODO: it's weird that self is data.py_shared_state. Maybe we
120 // TODO: it's weird that self is data.py_shared_state. Maybe we
99 // can move stuff to PySharedRefCell?
121 // can move stuff to PySharedRefCell?
100 let ptr = data.as_ptr();
122 let ptr = data.as_ptr();
101 let state_ptr: *const PySharedState = &data.py_shared_state;
123 let state_ptr: *const PySharedState = &data.py_shared_state;
102 self.leak_count.replace(self.leak_count.get() + 1);
124 self.leak_count.replace(self.leak_count.get() + 1);
103 Ok((&*ptr, &*state_ptr))
125 Ok((&*ptr, &*state_ptr))
104 }
126 }
105
127
106 /// # Safety
128 /// # Safety
107 ///
129 ///
108 /// It's up to you to make sure the reference is about to be deleted
130 /// It's up to you to make sure the reference is about to be deleted
109 /// when updating the leak count.
131 /// when updating the leak count.
110 fn decrease_leak_count(&self, _py: Python, mutable: bool) {
132 fn decrease_leak_count(&self, _py: Python, mutable: bool) {
111 if mutable {
133 if mutable {
112 assert_eq!(self.leak_count.get(), 0);
134 assert_eq!(self.leak_count.get(), 0);
113 assert!(self.mutably_borrowed.get());
135 assert!(self.mutably_borrowed.get());
114 self.mutably_borrowed.replace(false);
136 self.mutably_borrowed.replace(false);
115 } else {
137 } else {
116 let count = self.leak_count.get();
138 let count = self.leak_count.get();
117 assert!(count > 0);
139 assert!(count > 0);
118 self.leak_count.replace(count - 1);
140 self.leak_count.replace(count - 1);
119 }
141 }
120 }
142 }
143
144 fn current_generation(&self, _py: Python) -> usize {
145 self.generation.load(Ordering::Relaxed)
146 }
121 }
147 }
122
148
123 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
149 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
124 ///
150 ///
125 /// This object can be stored in a `py_class!` object as a data field. Any
151 /// This object can be stored in a `py_class!` object as a data field. Any
126 /// operation is allowed through the `PySharedRef` interface.
152 /// operation is allowed through the `PySharedRef` interface.
127 #[derive(Debug)]
153 #[derive(Debug)]
128 pub struct PySharedRefCell<T> {
154 pub struct PySharedRefCell<T> {
129 inner: RefCell<T>,
155 inner: RefCell<T>,
130 py_shared_state: PySharedState,
156 py_shared_state: PySharedState,
131 }
157 }
132
158
133 impl<T> PySharedRefCell<T> {
159 impl<T> PySharedRefCell<T> {
134 pub fn new(value: T) -> PySharedRefCell<T> {
160 pub fn new(value: T) -> PySharedRefCell<T> {
135 Self {
161 Self {
136 inner: RefCell::new(value),
162 inner: RefCell::new(value),
137 py_shared_state: PySharedState::default(),
163 py_shared_state: PySharedState::default(),
138 }
164 }
139 }
165 }
140
166
141 fn borrow<'a>(&'a self, _py: Python<'a>) -> Ref<'a, T> {
167 fn borrow<'a>(&'a self, _py: Python<'a>) -> Ref<'a, T> {
142 // py_shared_state isn't involved since
168 // py_shared_state isn't involved since
143 // - inner.borrow() would fail if self is mutably borrowed,
169 // - inner.borrow() would fail if self is mutably borrowed,
144 // - and inner.borrow_mut() would fail while self is borrowed.
170 // - and inner.borrow_mut() would fail while self is borrowed.
145 self.inner.borrow()
171 self.inner.borrow()
146 }
172 }
147
173
148 fn as_ptr(&self) -> *mut T {
174 fn as_ptr(&self) -> *mut T {
149 self.inner.as_ptr()
175 self.inner.as_ptr()
150 }
176 }
151
177
152 // TODO: maybe this should be named as try_borrow_mut(), and use
178 // TODO: maybe this should be named as try_borrow_mut(), and use
153 // inner.try_borrow_mut(). The current implementation panics if
179 // inner.try_borrow_mut(). The current implementation panics if
154 // self.inner has been borrowed, but returns error if py_shared_state
180 // self.inner has been borrowed, but returns error if py_shared_state
155 // refuses to borrow.
181 // refuses to borrow.
156 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<PyRefMut<'a, T>> {
182 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<PyRefMut<'a, T>> {
157 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
183 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
158 }
184 }
159 }
185 }
160
186
161 /// Sharable data member of type `T` borrowed from the `PyObject`.
187 /// Sharable data member of type `T` borrowed from the `PyObject`.
162 pub struct PySharedRef<'a, T> {
188 pub struct PySharedRef<'a, T> {
163 py: Python<'a>,
189 py: Python<'a>,
164 owner: &'a PyObject,
190 owner: &'a PyObject,
165 data: &'a PySharedRefCell<T>,
191 data: &'a PySharedRefCell<T>,
166 }
192 }
167
193
168 impl<'a, T> PySharedRef<'a, T> {
194 impl<'a, T> PySharedRef<'a, T> {
169 /// # Safety
195 /// # Safety
170 ///
196 ///
171 /// The `data` must be owned by the `owner`. Otherwise, the leak count
197 /// The `data` must be owned by the `owner`. Otherwise, the leak count
172 /// would get wrong.
198 /// would get wrong.
173 pub unsafe fn new(
199 pub unsafe fn new(
174 py: Python<'a>,
200 py: Python<'a>,
175 owner: &'a PyObject,
201 owner: &'a PyObject,
176 data: &'a PySharedRefCell<T>,
202 data: &'a PySharedRefCell<T>,
177 ) -> Self {
203 ) -> Self {
178 Self { py, owner, data }
204 Self { py, owner, data }
179 }
205 }
180
206
181 pub fn borrow(&self) -> Ref<'a, T> {
207 pub fn borrow(&self) -> Ref<'a, T> {
182 self.data.borrow(self.py)
208 self.data.borrow(self.py)
183 }
209 }
184
210
185 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> {
211 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> {
186 self.data.borrow_mut(self.py)
212 self.data.borrow_mut(self.py)
187 }
213 }
188
214
189 /// Returns a leaked reference.
215 /// Returns a leaked reference.
190 pub fn leak_immutable(&self) -> PyResult<PyLeaked<&'static T>> {
216 pub fn leak_immutable(&self) -> PyResult<PyLeaked<&'static T>> {
191 let state = &self.data.py_shared_state;
217 let state = &self.data.py_shared_state;
192 unsafe {
218 unsafe {
193 let (static_ref, static_state_ref) =
219 let (static_ref, static_state_ref) =
194 state.leak_immutable(self.py, self.data)?;
220 state.leak_immutable(self.py, self.data)?;
195 Ok(PyLeaked::new(
221 Ok(PyLeaked::new(
196 self.py,
222 self.py,
197 self.owner,
223 self.owner,
198 static_ref,
224 static_ref,
199 static_state_ref,
225 static_state_ref,
200 ))
226 ))
201 }
227 }
202 }
228 }
203 }
229 }
204
230
205 /// Holds a mutable reference to data shared between Python and Rust.
231 /// Holds a mutable reference to data shared between Python and Rust.
206 pub struct PyRefMut<'a, T> {
232 pub struct PyRefMut<'a, T> {
207 py: Python<'a>,
233 py: Python<'a>,
208 inner: RefMut<'a, T>,
234 inner: RefMut<'a, T>,
209 py_shared_state: &'a PySharedState,
235 py_shared_state: &'a PySharedState,
210 }
236 }
211
237
212 impl<'a, T> PyRefMut<'a, T> {
238 impl<'a, T> PyRefMut<'a, T> {
213 // Must be constructed by PySharedState after checking its leak_count.
239 // Must be constructed by PySharedState after checking its leak_count.
214 // Otherwise, drop() would incorrectly update the state.
240 // Otherwise, drop() would incorrectly update the state.
215 fn new(
241 fn new(
216 py: Python<'a>,
242 py: Python<'a>,
217 inner: RefMut<'a, T>,
243 inner: RefMut<'a, T>,
218 py_shared_state: &'a PySharedState,
244 py_shared_state: &'a PySharedState,
219 ) -> Self {
245 ) -> Self {
220 Self {
246 Self {
221 py,
247 py,
222 inner,
248 inner,
223 py_shared_state,
249 py_shared_state,
224 }
250 }
225 }
251 }
226 }
252 }
227
253
228 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
254 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
229 type Target = RefMut<'a, T>;
255 type Target = RefMut<'a, T>;
230
256
231 fn deref(&self) -> &Self::Target {
257 fn deref(&self) -> &Self::Target {
232 &self.inner
258 &self.inner
233 }
259 }
234 }
260 }
235 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
261 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
236 fn deref_mut(&mut self) -> &mut Self::Target {
262 fn deref_mut(&mut self) -> &mut Self::Target {
237 &mut self.inner
263 &mut self.inner
238 }
264 }
239 }
265 }
240
266
241 impl<'a, T> Drop for PyRefMut<'a, T> {
267 impl<'a, T> Drop for PyRefMut<'a, T> {
242 fn drop(&mut self) {
268 fn drop(&mut self) {
243 self.py_shared_state.decrease_leak_count(self.py, true);
269 self.py_shared_state.decrease_leak_count(self.py, true);
244 }
270 }
245 }
271 }
246
272
247 /// Allows a `py_class!` generated struct to share references to one of its
273 /// Allows a `py_class!` generated struct to share references to one of its
248 /// data members with Python.
274 /// data members with Python.
249 ///
275 ///
250 /// # Warning
276 /// # Warning
251 ///
277 ///
252 /// TODO allow Python container types: for now, integration with the garbage
278 /// TODO allow Python container types: for now, integration with the garbage
253 /// collector does not extend to Rust structs holding references to Python
279 /// collector does not extend to Rust structs holding references to Python
254 /// objects. Should the need surface, `__traverse__` and `__clear__` will
280 /// objects. Should the need surface, `__traverse__` and `__clear__` will
255 /// need to be written as per the `rust-cpython` docs on GC integration.
281 /// need to be written as per the `rust-cpython` docs on GC integration.
256 ///
282 ///
257 /// # Parameters
283 /// # Parameters
258 ///
284 ///
259 /// * `$name` is the same identifier used in for `py_class!` macro call.
285 /// * `$name` is the same identifier used in for `py_class!` macro call.
260 /// * `$inner_struct` is the identifier of the underlying Rust struct
286 /// * `$inner_struct` is the identifier of the underlying Rust struct
261 /// * `$data_member` is the identifier of the data member of `$inner_struct`
287 /// * `$data_member` is the identifier of the data member of `$inner_struct`
262 /// that will be shared.
288 /// that will be shared.
263 /// * `$shared_accessor` is the function name to be generated, which allows
289 /// * `$shared_accessor` is the function name to be generated, which allows
264 /// safe access to the data member.
290 /// safe access to the data member.
265 ///
291 ///
266 /// # Safety
292 /// # Safety
267 ///
293 ///
268 /// `$data_member` must persist while the `$name` object is alive. In other
294 /// `$data_member` must persist while the `$name` object is alive. In other
269 /// words, it must be an accessor to a data field of the Python object.
295 /// words, it must be an accessor to a data field of the Python object.
270 ///
296 ///
271 /// # Example
297 /// # Example
272 ///
298 ///
273 /// ```
299 /// ```
274 /// struct MyStruct {
300 /// struct MyStruct {
275 /// inner: Vec<u32>;
301 /// inner: Vec<u32>;
276 /// }
302 /// }
277 ///
303 ///
278 /// py_class!(pub class MyType |py| {
304 /// py_class!(pub class MyType |py| {
279 /// data inner: PySharedRefCell<MyStruct>;
305 /// data inner: PySharedRefCell<MyStruct>;
280 /// });
306 /// });
281 ///
307 ///
282 /// py_shared_ref!(MyType, MyStruct, inner, inner_shared);
308 /// py_shared_ref!(MyType, MyStruct, inner, inner_shared);
283 /// ```
309 /// ```
284 macro_rules! py_shared_ref {
310 macro_rules! py_shared_ref {
285 (
311 (
286 $name: ident,
312 $name: ident,
287 $inner_struct: ident,
313 $inner_struct: ident,
288 $data_member: ident,
314 $data_member: ident,
289 $shared_accessor: ident
315 $shared_accessor: ident
290 ) => {
316 ) => {
291 impl $name {
317 impl $name {
292 /// Returns a safe reference to the shared `$data_member`.
318 /// Returns a safe reference to the shared `$data_member`.
293 ///
319 ///
294 /// This function guarantees that `PySharedRef` is created with
320 /// This function guarantees that `PySharedRef` is created with
295 /// the valid `self` and `self.$data_member(py)` pair.
321 /// the valid `self` and `self.$data_member(py)` pair.
296 fn $shared_accessor<'a>(
322 fn $shared_accessor<'a>(
297 &'a self,
323 &'a self,
298 py: Python<'a>,
324 py: Python<'a>,
299 ) -> $crate::ref_sharing::PySharedRef<'a, $inner_struct> {
325 ) -> $crate::ref_sharing::PySharedRef<'a, $inner_struct> {
300 use cpython::PythonObject;
326 use cpython::PythonObject;
301 use $crate::ref_sharing::PySharedRef;
327 use $crate::ref_sharing::PySharedRef;
302 let owner = self.as_object();
328 let owner = self.as_object();
303 let data = self.$data_member(py);
329 let data = self.$data_member(py);
304 unsafe { PySharedRef::new(py, owner, data) }
330 unsafe { PySharedRef::new(py, owner, data) }
305 }
331 }
306 }
332 }
307 };
333 };
308 }
334 }
309
335
310 /// Manage immutable references to `PyObject` leaked into Python iterators.
336 /// Manage immutable references to `PyObject` leaked into Python iterators.
337 ///
338 /// This reference will be invalidated once the original value is mutably
339 /// borrowed.
311 pub struct PyLeaked<T> {
340 pub struct PyLeaked<T> {
312 inner: PyObject,
341 inner: PyObject,
313 data: Option<T>,
342 data: Option<T>,
314 py_shared_state: &'static PySharedState,
343 py_shared_state: &'static PySharedState,
344 /// Generation counter of data `T` captured when PyLeaked is created.
345 generation: usize,
315 }
346 }
316
347
317 // DO NOT implement Deref for PyLeaked<T>! Dereferencing PyLeaked
348 // DO NOT implement Deref for PyLeaked<T>! Dereferencing PyLeaked
318 // without taking Python GIL wouldn't be safe.
349 // without taking Python GIL wouldn't be safe. Also, the underling reference
350 // is invalid if generation != py_shared_state.generation.
319
351
320 impl<T> PyLeaked<T> {
352 impl<T> PyLeaked<T> {
321 /// # Safety
353 /// # Safety
322 ///
354 ///
323 /// The `py_shared_state` must be owned by the `inner` Python object.
355 /// The `py_shared_state` must be owned by the `inner` Python object.
324 fn new(
356 fn new(
325 py: Python,
357 py: Python,
326 inner: &PyObject,
358 inner: &PyObject,
327 data: T,
359 data: T,
328 py_shared_state: &'static PySharedState,
360 py_shared_state: &'static PySharedState,
329 ) -> Self {
361 ) -> Self {
330 Self {
362 Self {
331 inner: inner.clone_ref(py),
363 inner: inner.clone_ref(py),
332 data: Some(data),
364 data: Some(data),
333 py_shared_state,
365 py_shared_state,
366 generation: py_shared_state.current_generation(py),
334 }
367 }
335 }
368 }
336
369
337 /// Immutably borrows the wrapped value.
370 /// Immutably borrows the wrapped value.
371 ///
372 /// Borrowing fails if the underlying reference has been invalidated.
338 pub fn try_borrow<'a>(
373 pub fn try_borrow<'a>(
339 &'a self,
374 &'a self,
340 py: Python<'a>,
375 py: Python<'a>,
341 ) -> PyResult<PyLeakedRef<'a, T>> {
376 ) -> PyResult<PyLeakedRef<'a, T>> {
377 self.validate_generation(py)?;
342 Ok(PyLeakedRef {
378 Ok(PyLeakedRef {
343 _py: py,
379 _py: py,
344 data: self.data.as_ref().unwrap(),
380 data: self.data.as_ref().unwrap(),
345 })
381 })
346 }
382 }
347
383
348 /// Mutably borrows the wrapped value.
384 /// Mutably borrows the wrapped value.
349 ///
385 ///
386 /// Borrowing fails if the underlying reference has been invalidated.
387 ///
350 /// Typically `T` is an iterator. If `T` is an immutable reference,
388 /// Typically `T` is an iterator. If `T` is an immutable reference,
351 /// `get_mut()` is useless since the inner value can't be mutated.
389 /// `get_mut()` is useless since the inner value can't be mutated.
352 pub fn try_borrow_mut<'a>(
390 pub fn try_borrow_mut<'a>(
353 &'a mut self,
391 &'a mut self,
354 py: Python<'a>,
392 py: Python<'a>,
355 ) -> PyResult<PyLeakedRefMut<'a, T>> {
393 ) -> PyResult<PyLeakedRefMut<'a, T>> {
394 self.validate_generation(py)?;
356 Ok(PyLeakedRefMut {
395 Ok(PyLeakedRefMut {
357 _py: py,
396 _py: py,
358 data: self.data.as_mut().unwrap(),
397 data: self.data.as_mut().unwrap(),
359 })
398 })
360 }
399 }
361
400
362 /// Converts the inner value by the given function.
401 /// Converts the inner value by the given function.
363 ///
402 ///
364 /// Typically `T` is a static reference to a container, and `U` is an
403 /// Typically `T` is a static reference to a container, and `U` is an
365 /// iterator of that container.
404 /// iterator of that container.
366 ///
405 ///
406 /// # Panics
407 ///
408 /// Panics if the underlying reference has been invalidated.
409 ///
410 /// This is typically called immediately after the `PyLeaked` is obtained.
411 /// In which case, the reference must be valid and no panic would occur.
412 ///
367 /// # Safety
413 /// # Safety
368 ///
414 ///
369 /// The lifetime of the object passed in to the function `f` is cheated.
415 /// The lifetime of the object passed in to the function `f` is cheated.
370 /// It's typically a static reference, but is valid only while the
416 /// It's typically a static reference, but is valid only while the
371 /// corresponding `PyLeaked` is alive. Do not copy it out of the
417 /// corresponding `PyLeaked` is alive. Do not copy it out of the
372 /// function call.
418 /// function call.
373 pub unsafe fn map<U>(
419 pub unsafe fn map<U>(
374 mut self,
420 mut self,
375 py: Python,
421 py: Python,
376 f: impl FnOnce(T) -> U,
422 f: impl FnOnce(T) -> U,
377 ) -> PyLeaked<U> {
423 ) -> PyLeaked<U> {
424 // Needs to test the generation value to make sure self.data reference
425 // is still intact.
426 self.validate_generation(py)
427 .expect("map() over invalidated leaked reference");
428
378 // f() could make the self.data outlive. That's why map() is unsafe.
429 // f() could make the self.data outlive. That's why map() is unsafe.
379 // In order to make this function safe, maybe we'll need a way to
430 // In order to make this function safe, maybe we'll need a way to
380 // temporarily restrict the lifetime of self.data and translate the
431 // temporarily restrict the lifetime of self.data and translate the
381 // returned object back to Something<'static>.
432 // returned object back to Something<'static>.
382 let new_data = f(self.data.take().unwrap());
433 let new_data = f(self.data.take().unwrap());
383 PyLeaked {
434 PyLeaked {
384 inner: self.inner.clone_ref(py),
435 inner: self.inner.clone_ref(py),
385 data: Some(new_data),
436 data: Some(new_data),
386 py_shared_state: self.py_shared_state,
437 py_shared_state: self.py_shared_state,
438 generation: self.generation,
439 }
440 }
441
442 fn validate_generation(&self, py: Python) -> PyResult<()> {
443 if self.py_shared_state.current_generation(py) == self.generation {
444 Ok(())
445 } else {
446 Err(PyErr::new::<exc::RuntimeError, _>(
447 py,
448 "Cannot access to leaked reference after mutation",
449 ))
387 }
450 }
388 }
451 }
389 }
452 }
390
453
391 impl<T> Drop for PyLeaked<T> {
454 impl<T> Drop for PyLeaked<T> {
392 fn drop(&mut self) {
455 fn drop(&mut self) {
393 // py_shared_state should be alive since we do have
456 // py_shared_state should be alive since we do have
394 // a Python reference to the owner object. Taking GIL makes
457 // a Python reference to the owner object. Taking GIL makes
395 // sure that the state is only accessed by this thread.
458 // sure that the state is only accessed by this thread.
396 let gil = Python::acquire_gil();
459 let gil = Python::acquire_gil();
397 let py = gil.python();
460 let py = gil.python();
398 if self.data.is_none() {
461 if self.data.is_none() {
399 return; // moved to another PyLeaked
462 return; // moved to another PyLeaked
400 }
463 }
401 self.py_shared_state.decrease_leak_count(py, false);
464 self.py_shared_state.decrease_leak_count(py, false);
402 }
465 }
403 }
466 }
404
467
405 /// Immutably borrowed reference to a leaked value.
468 /// Immutably borrowed reference to a leaked value.
406 pub struct PyLeakedRef<'a, T> {
469 pub struct PyLeakedRef<'a, T> {
407 _py: Python<'a>,
470 _py: Python<'a>,
408 data: &'a T,
471 data: &'a T,
409 }
472 }
410
473
411 impl<T> Deref for PyLeakedRef<'_, T> {
474 impl<T> Deref for PyLeakedRef<'_, T> {
412 type Target = T;
475 type Target = T;
413
476
414 fn deref(&self) -> &T {
477 fn deref(&self) -> &T {
415 self.data
478 self.data
416 }
479 }
417 }
480 }
418
481
419 /// Mutably borrowed reference to a leaked value.
482 /// Mutably borrowed reference to a leaked value.
420 pub struct PyLeakedRefMut<'a, T> {
483 pub struct PyLeakedRefMut<'a, T> {
421 _py: Python<'a>,
484 _py: Python<'a>,
422 data: &'a mut T,
485 data: &'a mut T,
423 }
486 }
424
487
425 impl<T> Deref for PyLeakedRefMut<'_, T> {
488 impl<T> Deref for PyLeakedRefMut<'_, T> {
426 type Target = T;
489 type Target = T;
427
490
428 fn deref(&self) -> &T {
491 fn deref(&self) -> &T {
429 self.data
492 self.data
430 }
493 }
431 }
494 }
432
495
433 impl<T> DerefMut for PyLeakedRefMut<'_, T> {
496 impl<T> DerefMut for PyLeakedRefMut<'_, T> {
434 fn deref_mut(&mut self) -> &mut T {
497 fn deref_mut(&mut self) -> &mut T {
435 self.data
498 self.data
436 }
499 }
437 }
500 }
438
501
439 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
502 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
440 ///
503 ///
441 /// TODO: this is a bit awkward to use, and a better (more complicated)
504 /// TODO: this is a bit awkward to use, and a better (more complicated)
442 /// procedural macro would simplify the interface a lot.
505 /// procedural macro would simplify the interface a lot.
443 ///
506 ///
444 /// # Parameters
507 /// # Parameters
445 ///
508 ///
446 /// * `$name` is the identifier to give to the resulting Rust struct.
509 /// * `$name` is the identifier to give to the resulting Rust struct.
447 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
510 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
448 /// * `$iterator_type` is the type of the Rust iterator.
511 /// * `$iterator_type` is the type of the Rust iterator.
449 /// * `$success_func` is a function for processing the Rust `(key, value)`
512 /// * `$success_func` is a function for processing the Rust `(key, value)`
450 /// tuple on iteration success, turning it into something Python understands.
513 /// tuple on iteration success, turning it into something Python understands.
451 /// * `$success_func` is the return type of `$success_func`
514 /// * `$success_func` is the return type of `$success_func`
452 ///
515 ///
453 /// # Example
516 /// # Example
454 ///
517 ///
455 /// ```
518 /// ```
456 /// struct MyStruct {
519 /// struct MyStruct {
457 /// inner: HashMap<Vec<u8>, Vec<u8>>;
520 /// inner: HashMap<Vec<u8>, Vec<u8>>;
458 /// }
521 /// }
459 ///
522 ///
460 /// py_class!(pub class MyType |py| {
523 /// py_class!(pub class MyType |py| {
461 /// data inner: PySharedRefCell<MyStruct>;
524 /// data inner: PySharedRefCell<MyStruct>;
462 ///
525 ///
463 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
526 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
464 /// let leaked_ref = self.inner_shared(py).leak_immutable()?;
527 /// let leaked_ref = self.inner_shared(py).leak_immutable()?;
465 /// MyTypeItemsIterator::from_inner(
528 /// MyTypeItemsIterator::from_inner(
466 /// py,
529 /// py,
467 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
530 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
468 /// )
531 /// )
469 /// }
532 /// }
470 /// });
533 /// });
471 ///
534 ///
472 /// impl MyType {
535 /// impl MyType {
473 /// fn translate_key_value(
536 /// fn translate_key_value(
474 /// py: Python,
537 /// py: Python,
475 /// res: (&Vec<u8>, &Vec<u8>),
538 /// res: (&Vec<u8>, &Vec<u8>),
476 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
539 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
477 /// let (f, entry) = res;
540 /// let (f, entry) = res;
478 /// Ok(Some((
541 /// Ok(Some((
479 /// PyBytes::new(py, f),
542 /// PyBytes::new(py, f),
480 /// PyBytes::new(py, entry),
543 /// PyBytes::new(py, entry),
481 /// )))
544 /// )))
482 /// }
545 /// }
483 /// }
546 /// }
484 ///
547 ///
485 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
548 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
486 ///
549 ///
487 /// py_shared_iterator!(
550 /// py_shared_iterator!(
488 /// MyTypeItemsIterator,
551 /// MyTypeItemsIterator,
489 /// PyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>,
552 /// PyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>,
490 /// MyType::translate_key_value,
553 /// MyType::translate_key_value,
491 /// Option<(PyBytes, PyBytes)>
554 /// Option<(PyBytes, PyBytes)>
492 /// );
555 /// );
493 /// ```
556 /// ```
494 macro_rules! py_shared_iterator {
557 macro_rules! py_shared_iterator {
495 (
558 (
496 $name: ident,
559 $name: ident,
497 $leaked: ty,
560 $leaked: ty,
498 $success_func: expr,
561 $success_func: expr,
499 $success_type: ty
562 $success_type: ty
500 ) => {
563 ) => {
501 py_class!(pub class $name |py| {
564 py_class!(pub class $name |py| {
502 data inner: RefCell<Option<$leaked>>;
565 data inner: RefCell<Option<$leaked>>;
503
566
504 def __next__(&self) -> PyResult<$success_type> {
567 def __next__(&self) -> PyResult<$success_type> {
505 let mut inner_opt = self.inner(py).borrow_mut();
568 let mut inner_opt = self.inner(py).borrow_mut();
506 if let Some(leaked) = inner_opt.as_mut() {
569 if let Some(leaked) = inner_opt.as_mut() {
507 let mut iter = leaked.try_borrow_mut(py)?;
570 let mut iter = leaked.try_borrow_mut(py)?;
508 match iter.next() {
571 match iter.next() {
509 None => {
572 None => {
510 // replace Some(inner) by None, drop $leaked
573 // replace Some(inner) by None, drop $leaked
511 inner_opt.take();
574 inner_opt.take();
512 Ok(None)
575 Ok(None)
513 }
576 }
514 Some(res) => {
577 Some(res) => {
515 $success_func(py, res)
578 $success_func(py, res)
516 }
579 }
517 }
580 }
518 } else {
581 } else {
519 Ok(None)
582 Ok(None)
520 }
583 }
521 }
584 }
522
585
523 def __iter__(&self) -> PyResult<Self> {
586 def __iter__(&self) -> PyResult<Self> {
524 Ok(self.clone_ref(py))
587 Ok(self.clone_ref(py))
525 }
588 }
526 });
589 });
527
590
528 impl $name {
591 impl $name {
529 pub fn from_inner(
592 pub fn from_inner(
530 py: Python,
593 py: Python,
531 leaked: $leaked,
594 leaked: $leaked,
532 ) -> PyResult<Self> {
595 ) -> PyResult<Self> {
533 Self::create_instance(
596 Self::create_instance(
534 py,
597 py,
535 RefCell::new(Some(leaked)),
598 RefCell::new(Some(leaked)),
536 )
599 )
537 }
600 }
538 }
601 }
539 };
602 };
540 }
603 }
541
604
542 #[cfg(test)]
605 #[cfg(test)]
543 #[cfg(any(feature = "python27-bin", feature = "python3-bin"))]
606 #[cfg(any(feature = "python27-bin", feature = "python3-bin"))]
544 mod test {
607 mod test {
545 use super::*;
608 use super::*;
546 use cpython::{GILGuard, Python};
609 use cpython::{GILGuard, Python};
547
610
548 py_class!(class Owner |py| {
611 py_class!(class Owner |py| {
549 data string: PySharedRefCell<String>;
612 data string: PySharedRefCell<String>;
550 });
613 });
551 py_shared_ref!(Owner, String, string, string_shared);
614 py_shared_ref!(Owner, String, string, string_shared);
552
615
553 fn prepare_env() -> (GILGuard, Owner) {
616 fn prepare_env() -> (GILGuard, Owner) {
554 let gil = Python::acquire_gil();
617 let gil = Python::acquire_gil();
555 let py = gil.python();
618 let py = gil.python();
556 let owner =
619 let owner =
557 Owner::create_instance(py, PySharedRefCell::new("new".to_owned()))
620 Owner::create_instance(py, PySharedRefCell::new("new".to_owned()))
558 .unwrap();
621 .unwrap();
559 (gil, owner)
622 (gil, owner)
560 }
623 }
561
624
562 #[test]
625 #[test]
563 fn test_leaked_borrow() {
626 fn test_leaked_borrow() {
564 let (gil, owner) = prepare_env();
627 let (gil, owner) = prepare_env();
565 let py = gil.python();
628 let py = gil.python();
566 let leaked = owner.string_shared(py).leak_immutable().unwrap();
629 let leaked = owner.string_shared(py).leak_immutable().unwrap();
567 let leaked_ref = leaked.try_borrow(py).unwrap();
630 let leaked_ref = leaked.try_borrow(py).unwrap();
568 assert_eq!(*leaked_ref, "new");
631 assert_eq!(*leaked_ref, "new");
569 }
632 }
570
633
571 #[test]
634 #[test]
572 fn test_leaked_borrow_mut() {
635 fn test_leaked_borrow_mut() {
573 let (gil, owner) = prepare_env();
636 let (gil, owner) = prepare_env();
574 let py = gil.python();
637 let py = gil.python();
575 let leaked = owner.string_shared(py).leak_immutable().unwrap();
638 let leaked = owner.string_shared(py).leak_immutable().unwrap();
576 let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
639 let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
577 let mut leaked_ref = leaked_iter.try_borrow_mut(py).unwrap();
640 let mut leaked_ref = leaked_iter.try_borrow_mut(py).unwrap();
578 assert_eq!(leaked_ref.next(), Some('n'));
641 assert_eq!(leaked_ref.next(), Some('n'));
579 assert_eq!(leaked_ref.next(), Some('e'));
642 assert_eq!(leaked_ref.next(), Some('e'));
580 assert_eq!(leaked_ref.next(), Some('w'));
643 assert_eq!(leaked_ref.next(), Some('w'));
581 assert_eq!(leaked_ref.next(), None);
644 assert_eq!(leaked_ref.next(), None);
582 }
645 }
583
646
584 #[test]
647 #[test]
648 fn test_leaked_borrow_after_mut() {
649 let (gil, owner) = prepare_env();
650 let py = gil.python();
651 let leaked = owner.string_shared(py).leak_immutable().unwrap();
652 owner.string(py).py_shared_state.leak_count.replace(0); // XXX cheat
653 owner.string_shared(py).borrow_mut().unwrap().clear();
654 owner.string(py).py_shared_state.leak_count.replace(1); // XXX cheat
655 assert!(leaked.try_borrow(py).is_err());
656 }
657
658 #[test]
659 fn test_leaked_borrow_mut_after_mut() {
660 let (gil, owner) = prepare_env();
661 let py = gil.python();
662 let leaked = owner.string_shared(py).leak_immutable().unwrap();
663 let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
664 owner.string(py).py_shared_state.leak_count.replace(0); // XXX cheat
665 owner.string_shared(py).borrow_mut().unwrap().clear();
666 owner.string(py).py_shared_state.leak_count.replace(1); // XXX cheat
667 assert!(leaked_iter.try_borrow_mut(py).is_err());
668 }
669
670 #[test]
671 #[should_panic(expected = "map() over invalidated leaked reference")]
672 fn test_leaked_map_after_mut() {
673 let (gil, owner) = prepare_env();
674 let py = gil.python();
675 let leaked = owner.string_shared(py).leak_immutable().unwrap();
676 owner.string(py).py_shared_state.leak_count.replace(0); // XXX cheat
677 owner.string_shared(py).borrow_mut().unwrap().clear();
678 owner.string(py).py_shared_state.leak_count.replace(1); // XXX cheat
679 let _leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
680 }
681
682 #[test]
585 fn test_borrow_mut_while_leaked() {
683 fn test_borrow_mut_while_leaked() {
586 let (gil, owner) = prepare_env();
684 let (gil, owner) = prepare_env();
587 let py = gil.python();
685 let py = gil.python();
588 assert!(owner.string_shared(py).borrow_mut().is_ok());
686 assert!(owner.string_shared(py).borrow_mut().is_ok());
589 let _leaked = owner.string_shared(py).leak_immutable().unwrap();
687 let _leaked = owner.string_shared(py).leak_immutable().unwrap();
590 // TODO: will be allowed
688 // TODO: will be allowed
591 assert!(owner.string_shared(py).borrow_mut().is_err());
689 assert!(owner.string_shared(py).borrow_mut().is_err());
592 }
690 }
593 }
691 }
General Comments 0
You need to be logged in to leave comments. Login now