##// END OF EJS Templates
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
Yuya Nishihara -
r43604:945d4dba default
parent child Browse files
Show More
@@ -1,523 +1,593 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::{PyClone, 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
29
29 /// Manages the shared state between Python and Rust
30 /// Manages the shared state between Python and Rust
30 #[derive(Debug, Default)]
31 #[derive(Debug, Default)]
31 struct PySharedState {
32 struct PySharedState {
32 leak_count: Cell<usize>,
33 leak_count: Cell<usize>,
33 mutably_borrowed: Cell<bool>,
34 mutably_borrowed: Cell<bool>,
34 }
35 }
35
36
36 // &PySharedState can be Send because any access to inner cells is
37 // &PySharedState can be Send because any access to inner cells is
37 // synchronized by the GIL.
38 // synchronized by the GIL.
38 unsafe impl Sync for PySharedState {}
39 unsafe impl Sync for PySharedState {}
39
40
40 impl PySharedState {
41 impl PySharedState {
41 fn borrow_mut<'a, T>(
42 fn borrow_mut<'a, T>(
42 &'a self,
43 &'a self,
43 py: Python<'a>,
44 py: Python<'a>,
44 pyrefmut: RefMut<'a, T>,
45 pyrefmut: RefMut<'a, T>,
45 ) -> PyResult<PyRefMut<'a, T>> {
46 ) -> PyResult<PyRefMut<'a, T>> {
46 if self.mutably_borrowed.get() {
47 if self.mutably_borrowed.get() {
47 return Err(AlreadyBorrowed::new(
48 return Err(AlreadyBorrowed::new(
48 py,
49 py,
49 "Cannot borrow mutably while there exists another \
50 "Cannot borrow mutably while there exists another \
50 mutable reference in a Python object",
51 mutable reference in a Python object",
51 ));
52 ));
52 }
53 }
53 match self.leak_count.get() {
54 match self.leak_count.get() {
54 0 => {
55 0 => {
55 self.mutably_borrowed.replace(true);
56 self.mutably_borrowed.replace(true);
56 Ok(PyRefMut::new(py, pyrefmut, self))
57 Ok(PyRefMut::new(py, pyrefmut, self))
57 }
58 }
58 // TODO
59 // TODO
59 // For now, this works differently than Python references
60 // For now, this works differently than Python references
60 // in the case of iterators.
61 // in the case of iterators.
61 // Python does not complain when the data an iterator
62 // Python does not complain when the data an iterator
62 // points to is modified if the iterator is never used
63 // points to is modified if the iterator is never used
63 // afterwards.
64 // afterwards.
64 // Here, we are stricter than this by refusing to give a
65 // Here, we are stricter than this by refusing to give a
65 // mutable reference if it is already borrowed.
66 // mutable reference if it is already borrowed.
66 // While the additional safety might be argued for, it
67 // While the additional safety might be argued for, it
67 // breaks valid programming patterns in Python and we need
68 // breaks valid programming patterns in Python and we need
68 // to fix this issue down the line.
69 // to fix this issue down the line.
69 _ => Err(AlreadyBorrowed::new(
70 _ => Err(AlreadyBorrowed::new(
70 py,
71 py,
71 "Cannot borrow mutably while there are \
72 "Cannot borrow mutably while there are \
72 immutable references in Python objects",
73 immutable references in Python objects",
73 )),
74 )),
74 }
75 }
75 }
76 }
76
77
77 /// Return a reference to the wrapped data and its state with an
78 /// Return a reference to the wrapped data and its state with an
78 /// artificial static lifetime.
79 /// artificial static lifetime.
79 /// We need to be protected by the GIL for thread-safety.
80 /// We need to be protected by the GIL for thread-safety.
80 ///
81 ///
81 /// # Safety
82 /// # Safety
82 ///
83 ///
83 /// This is highly unsafe since the lifetime of the given data can be
84 /// This is highly unsafe since the lifetime of the given data can be
84 /// extended. Do not call this function directly.
85 /// extended. Do not call this function directly.
85 unsafe fn leak_immutable<T>(
86 unsafe fn leak_immutable<T>(
86 &self,
87 &self,
87 py: Python,
88 py: Python,
88 data: &PySharedRefCell<T>,
89 data: &PySharedRefCell<T>,
89 ) -> PyResult<(&'static T, &'static PySharedState)> {
90 ) -> PyResult<(&'static T, &'static PySharedState)> {
90 if self.mutably_borrowed.get() {
91 if self.mutably_borrowed.get() {
91 return Err(AlreadyBorrowed::new(
92 return Err(AlreadyBorrowed::new(
92 py,
93 py,
93 "Cannot borrow immutably while there is a \
94 "Cannot borrow immutably while there is a \
94 mutable reference in Python objects",
95 mutable reference in Python objects",
95 ));
96 ));
96 }
97 }
97 // TODO: it's weird that self is data.py_shared_state. Maybe we
98 // TODO: it's weird that self is data.py_shared_state. Maybe we
98 // can move stuff to PySharedRefCell?
99 // can move stuff to PySharedRefCell?
99 let ptr = data.as_ptr();
100 let ptr = data.as_ptr();
100 let state_ptr: *const PySharedState = &data.py_shared_state;
101 let state_ptr: *const PySharedState = &data.py_shared_state;
101 self.leak_count.replace(self.leak_count.get() + 1);
102 self.leak_count.replace(self.leak_count.get() + 1);
102 Ok((&*ptr, &*state_ptr))
103 Ok((&*ptr, &*state_ptr))
103 }
104 }
104
105
105 /// # Safety
106 /// # Safety
106 ///
107 ///
107 /// It's up to you to make sure the reference is about to be deleted
108 /// It's up to you to make sure the reference is about to be deleted
108 /// when updating the leak count.
109 /// when updating the leak count.
109 fn decrease_leak_count(&self, _py: Python, mutable: bool) {
110 fn decrease_leak_count(&self, _py: Python, mutable: bool) {
110 if mutable {
111 if mutable {
111 assert_eq!(self.leak_count.get(), 0);
112 assert_eq!(self.leak_count.get(), 0);
112 assert!(self.mutably_borrowed.get());
113 assert!(self.mutably_borrowed.get());
113 self.mutably_borrowed.replace(false);
114 self.mutably_borrowed.replace(false);
114 } else {
115 } else {
115 let count = self.leak_count.get();
116 let count = self.leak_count.get();
116 assert!(count > 0);
117 assert!(count > 0);
117 self.leak_count.replace(count - 1);
118 self.leak_count.replace(count - 1);
118 }
119 }
119 }
120 }
120 }
121 }
121
122
122 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
123 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
123 ///
124 ///
124 /// This object can be stored in a `py_class!` object as a data field. Any
125 /// This object can be stored in a `py_class!` object as a data field. Any
125 /// operation is allowed through the `PySharedRef` interface.
126 /// operation is allowed through the `PySharedRef` interface.
126 #[derive(Debug)]
127 #[derive(Debug)]
127 pub struct PySharedRefCell<T> {
128 pub struct PySharedRefCell<T> {
128 inner: RefCell<T>,
129 inner: RefCell<T>,
129 py_shared_state: PySharedState,
130 py_shared_state: PySharedState,
130 }
131 }
131
132
132 impl<T> PySharedRefCell<T> {
133 impl<T> PySharedRefCell<T> {
133 pub fn new(value: T) -> PySharedRefCell<T> {
134 pub fn new(value: T) -> PySharedRefCell<T> {
134 Self {
135 Self {
135 inner: RefCell::new(value),
136 inner: RefCell::new(value),
136 py_shared_state: PySharedState::default(),
137 py_shared_state: PySharedState::default(),
137 }
138 }
138 }
139 }
139
140
140 fn borrow<'a>(&'a self, _py: Python<'a>) -> Ref<'a, T> {
141 fn borrow<'a>(&'a self, _py: Python<'a>) -> Ref<'a, T> {
141 // py_shared_state isn't involved since
142 // py_shared_state isn't involved since
142 // - inner.borrow() would fail if self is mutably borrowed,
143 // - inner.borrow() would fail if self is mutably borrowed,
143 // - and inner.borrow_mut() would fail while self is borrowed.
144 // - and inner.borrow_mut() would fail while self is borrowed.
144 self.inner.borrow()
145 self.inner.borrow()
145 }
146 }
146
147
147 fn as_ptr(&self) -> *mut T {
148 fn as_ptr(&self) -> *mut T {
148 self.inner.as_ptr()
149 self.inner.as_ptr()
149 }
150 }
150
151
151 // TODO: maybe this should be named as try_borrow_mut(), and use
152 // TODO: maybe this should be named as try_borrow_mut(), and use
152 // inner.try_borrow_mut(). The current implementation panics if
153 // inner.try_borrow_mut(). The current implementation panics if
153 // self.inner has been borrowed, but returns error if py_shared_state
154 // self.inner has been borrowed, but returns error if py_shared_state
154 // refuses to borrow.
155 // refuses to borrow.
155 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<PyRefMut<'a, T>> {
156 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<PyRefMut<'a, T>> {
156 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
157 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
157 }
158 }
158 }
159 }
159
160
160 /// Sharable data member of type `T` borrowed from the `PyObject`.
161 /// Sharable data member of type `T` borrowed from the `PyObject`.
161 pub struct PySharedRef<'a, T> {
162 pub struct PySharedRef<'a, T> {
162 py: Python<'a>,
163 py: Python<'a>,
163 owner: &'a PyObject,
164 owner: &'a PyObject,
164 data: &'a PySharedRefCell<T>,
165 data: &'a PySharedRefCell<T>,
165 }
166 }
166
167
167 impl<'a, T> PySharedRef<'a, T> {
168 impl<'a, T> PySharedRef<'a, T> {
168 /// # Safety
169 /// # Safety
169 ///
170 ///
170 /// The `data` must be owned by the `owner`. Otherwise, the leak count
171 /// The `data` must be owned by the `owner`. Otherwise, the leak count
171 /// would get wrong.
172 /// would get wrong.
172 pub unsafe fn new(
173 pub unsafe fn new(
173 py: Python<'a>,
174 py: Python<'a>,
174 owner: &'a PyObject,
175 owner: &'a PyObject,
175 data: &'a PySharedRefCell<T>,
176 data: &'a PySharedRefCell<T>,
176 ) -> Self {
177 ) -> Self {
177 Self { py, owner, data }
178 Self { py, owner, data }
178 }
179 }
179
180
180 pub fn borrow(&self) -> Ref<'a, T> {
181 pub fn borrow(&self) -> Ref<'a, T> {
181 self.data.borrow(self.py)
182 self.data.borrow(self.py)
182 }
183 }
183
184
184 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> {
185 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> {
185 self.data.borrow_mut(self.py)
186 self.data.borrow_mut(self.py)
186 }
187 }
187
188
188 /// Returns a leaked reference.
189 /// Returns a leaked reference.
189 pub fn leak_immutable(&self) -> PyResult<PyLeaked<&'static T>> {
190 pub fn leak_immutable(&self) -> PyResult<PyLeaked<&'static T>> {
190 let state = &self.data.py_shared_state;
191 let state = &self.data.py_shared_state;
191 unsafe {
192 unsafe {
192 let (static_ref, static_state_ref) =
193 let (static_ref, static_state_ref) =
193 state.leak_immutable(self.py, self.data)?;
194 state.leak_immutable(self.py, self.data)?;
194 Ok(PyLeaked::new(
195 Ok(PyLeaked::new(
195 self.py,
196 self.py,
196 self.owner,
197 self.owner,
197 static_ref,
198 static_ref,
198 static_state_ref,
199 static_state_ref,
199 ))
200 ))
200 }
201 }
201 }
202 }
202 }
203 }
203
204
204 /// Holds a mutable reference to data shared between Python and Rust.
205 /// Holds a mutable reference to data shared between Python and Rust.
205 pub struct PyRefMut<'a, T> {
206 pub struct PyRefMut<'a, T> {
206 py: Python<'a>,
207 py: Python<'a>,
207 inner: RefMut<'a, T>,
208 inner: RefMut<'a, T>,
208 py_shared_state: &'a PySharedState,
209 py_shared_state: &'a PySharedState,
209 }
210 }
210
211
211 impl<'a, T> PyRefMut<'a, T> {
212 impl<'a, T> PyRefMut<'a, T> {
212 // Must be constructed by PySharedState after checking its leak_count.
213 // Must be constructed by PySharedState after checking its leak_count.
213 // Otherwise, drop() would incorrectly update the state.
214 // Otherwise, drop() would incorrectly update the state.
214 fn new(
215 fn new(
215 py: Python<'a>,
216 py: Python<'a>,
216 inner: RefMut<'a, T>,
217 inner: RefMut<'a, T>,
217 py_shared_state: &'a PySharedState,
218 py_shared_state: &'a PySharedState,
218 ) -> Self {
219 ) -> Self {
219 Self {
220 Self {
220 py,
221 py,
221 inner,
222 inner,
222 py_shared_state,
223 py_shared_state,
223 }
224 }
224 }
225 }
225 }
226 }
226
227
227 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
228 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
228 type Target = RefMut<'a, T>;
229 type Target = RefMut<'a, T>;
229
230
230 fn deref(&self) -> &Self::Target {
231 fn deref(&self) -> &Self::Target {
231 &self.inner
232 &self.inner
232 }
233 }
233 }
234 }
234 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
235 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
235 fn deref_mut(&mut self) -> &mut Self::Target {
236 fn deref_mut(&mut self) -> &mut Self::Target {
236 &mut self.inner
237 &mut self.inner
237 }
238 }
238 }
239 }
239
240
240 impl<'a, T> Drop for PyRefMut<'a, T> {
241 impl<'a, T> Drop for PyRefMut<'a, T> {
241 fn drop(&mut self) {
242 fn drop(&mut self) {
242 self.py_shared_state.decrease_leak_count(self.py, true);
243 self.py_shared_state.decrease_leak_count(self.py, true);
243 }
244 }
244 }
245 }
245
246
246 /// Allows a `py_class!` generated struct to share references to one of its
247 /// Allows a `py_class!` generated struct to share references to one of its
247 /// data members with Python.
248 /// data members with Python.
248 ///
249 ///
249 /// # Warning
250 /// # Warning
250 ///
251 ///
251 /// TODO allow Python container types: for now, integration with the garbage
252 /// TODO allow Python container types: for now, integration with the garbage
252 /// collector does not extend to Rust structs holding references to Python
253 /// collector does not extend to Rust structs holding references to Python
253 /// objects. Should the need surface, `__traverse__` and `__clear__` will
254 /// objects. Should the need surface, `__traverse__` and `__clear__` will
254 /// need to be written as per the `rust-cpython` docs on GC integration.
255 /// need to be written as per the `rust-cpython` docs on GC integration.
255 ///
256 ///
256 /// # Parameters
257 /// # Parameters
257 ///
258 ///
258 /// * `$name` is the same identifier used in for `py_class!` macro call.
259 /// * `$name` is the same identifier used in for `py_class!` macro call.
259 /// * `$inner_struct` is the identifier of the underlying Rust struct
260 /// * `$inner_struct` is the identifier of the underlying Rust struct
260 /// * `$data_member` is the identifier of the data member of `$inner_struct`
261 /// * `$data_member` is the identifier of the data member of `$inner_struct`
261 /// that will be shared.
262 /// that will be shared.
262 /// * `$shared_accessor` is the function name to be generated, which allows
263 /// * `$shared_accessor` is the function name to be generated, which allows
263 /// safe access to the data member.
264 /// safe access to the data member.
264 ///
265 ///
265 /// # Safety
266 /// # Safety
266 ///
267 ///
267 /// `$data_member` must persist while the `$name` object is alive. In other
268 /// `$data_member` must persist while the `$name` object is alive. In other
268 /// words, it must be an accessor to a data field of the Python object.
269 /// words, it must be an accessor to a data field of the Python object.
269 ///
270 ///
270 /// # Example
271 /// # Example
271 ///
272 ///
272 /// ```
273 /// ```
273 /// struct MyStruct {
274 /// struct MyStruct {
274 /// inner: Vec<u32>;
275 /// inner: Vec<u32>;
275 /// }
276 /// }
276 ///
277 ///
277 /// py_class!(pub class MyType |py| {
278 /// py_class!(pub class MyType |py| {
278 /// data inner: PySharedRefCell<MyStruct>;
279 /// data inner: PySharedRefCell<MyStruct>;
279 /// });
280 /// });
280 ///
281 ///
281 /// py_shared_ref!(MyType, MyStruct, inner, inner_shared);
282 /// py_shared_ref!(MyType, MyStruct, inner, inner_shared);
282 /// ```
283 /// ```
283 macro_rules! py_shared_ref {
284 macro_rules! py_shared_ref {
284 (
285 (
285 $name: ident,
286 $name: ident,
286 $inner_struct: ident,
287 $inner_struct: ident,
287 $data_member: ident,
288 $data_member: ident,
288 $shared_accessor: ident
289 $shared_accessor: ident
289 ) => {
290 ) => {
290 impl $name {
291 impl $name {
291 /// Returns a safe reference to the shared `$data_member`.
292 /// Returns a safe reference to the shared `$data_member`.
292 ///
293 ///
293 /// This function guarantees that `PySharedRef` is created with
294 /// This function guarantees that `PySharedRef` is created with
294 /// the valid `self` and `self.$data_member(py)` pair.
295 /// the valid `self` and `self.$data_member(py)` pair.
295 fn $shared_accessor<'a>(
296 fn $shared_accessor<'a>(
296 &'a self,
297 &'a self,
297 py: Python<'a>,
298 py: Python<'a>,
298 ) -> $crate::ref_sharing::PySharedRef<'a, $inner_struct> {
299 ) -> $crate::ref_sharing::PySharedRef<'a, $inner_struct> {
299 use cpython::PythonObject;
300 use cpython::PythonObject;
300 use $crate::ref_sharing::PySharedRef;
301 use $crate::ref_sharing::PySharedRef;
301 let owner = self.as_object();
302 let owner = self.as_object();
302 let data = self.$data_member(py);
303 let data = self.$data_member(py);
303 unsafe { PySharedRef::new(py, owner, data) }
304 unsafe { PySharedRef::new(py, owner, data) }
304 }
305 }
305 }
306 }
306 };
307 };
307 }
308 }
308
309
309 /// Manage immutable references to `PyObject` leaked into Python iterators.
310 /// Manage immutable references to `PyObject` leaked into Python iterators.
310 pub struct PyLeaked<T> {
311 pub struct PyLeaked<T> {
311 inner: PyObject,
312 inner: PyObject,
312 data: Option<T>,
313 data: Option<T>,
313 py_shared_state: &'static PySharedState,
314 py_shared_state: &'static PySharedState,
314 }
315 }
315
316
316 // DO NOT implement Deref for PyLeaked<T>! Dereferencing PyLeaked
317 // DO NOT implement Deref for PyLeaked<T>! Dereferencing PyLeaked
317 // without taking Python GIL wouldn't be safe.
318 // without taking Python GIL wouldn't be safe.
318
319
319 impl<T> PyLeaked<T> {
320 impl<T> PyLeaked<T> {
320 /// # Safety
321 /// # Safety
321 ///
322 ///
322 /// The `py_shared_state` must be owned by the `inner` Python object.
323 /// The `py_shared_state` must be owned by the `inner` Python object.
323 fn new(
324 fn new(
324 py: Python,
325 py: Python,
325 inner: &PyObject,
326 inner: &PyObject,
326 data: T,
327 data: T,
327 py_shared_state: &'static PySharedState,
328 py_shared_state: &'static PySharedState,
328 ) -> Self {
329 ) -> Self {
329 Self {
330 Self {
330 inner: inner.clone_ref(py),
331 inner: inner.clone_ref(py),
331 data: Some(data),
332 data: Some(data),
332 py_shared_state,
333 py_shared_state,
333 }
334 }
334 }
335 }
335
336
336 /// Returns an immutable reference to the inner value.
337 /// Immutably borrows the wrapped value.
337 pub fn get_ref<'a>(&'a self, _py: Python<'a>) -> &'a T {
338 pub fn try_borrow<'a>(
338 self.data.as_ref().unwrap()
339 &'a self,
340 py: Python<'a>,
341 ) -> PyResult<PyLeakedRef<'a, T>> {
342 Ok(PyLeakedRef {
343 _py: py,
344 data: self.data.as_ref().unwrap(),
345 })
339 }
346 }
340
347
341 /// Returns a mutable reference to the inner value.
348 /// Mutably borrows the wrapped value.
342 ///
349 ///
343 /// Typically `T` is an iterator. If `T` is an immutable reference,
350 /// Typically `T` is an iterator. If `T` is an immutable reference,
344 /// `get_mut()` is useless since the inner value can't be mutated.
351 /// `get_mut()` is useless since the inner value can't be mutated.
345 pub fn get_mut<'a>(&'a mut self, _py: Python<'a>) -> &'a mut T {
352 pub fn try_borrow_mut<'a>(
346 self.data.as_mut().unwrap()
353 &'a mut self,
354 py: Python<'a>,
355 ) -> PyResult<PyLeakedRefMut<'a, T>> {
356 Ok(PyLeakedRefMut {
357 _py: py,
358 data: self.data.as_mut().unwrap(),
359 })
347 }
360 }
348
361
349 /// Converts the inner value by the given function.
362 /// Converts the inner value by the given function.
350 ///
363 ///
351 /// Typically `T` is a static reference to a container, and `U` is an
364 /// Typically `T` is a static reference to a container, and `U` is an
352 /// iterator of that container.
365 /// iterator of that container.
353 ///
366 ///
354 /// # Safety
367 /// # Safety
355 ///
368 ///
356 /// The lifetime of the object passed in to the function `f` is cheated.
369 /// The lifetime of the object passed in to the function `f` is cheated.
357 /// It's typically a static reference, but is valid only while the
370 /// It's typically a static reference, but is valid only while the
358 /// corresponding `PyLeaked` is alive. Do not copy it out of the
371 /// corresponding `PyLeaked` is alive. Do not copy it out of the
359 /// function call.
372 /// function call.
360 pub unsafe fn map<U>(
373 pub unsafe fn map<U>(
361 mut self,
374 mut self,
362 py: Python,
375 py: Python,
363 f: impl FnOnce(T) -> U,
376 f: impl FnOnce(T) -> U,
364 ) -> PyLeaked<U> {
377 ) -> PyLeaked<U> {
365 // f() could make the self.data outlive. That's why map() is unsafe.
378 // f() could make the self.data outlive. That's why map() is unsafe.
366 // In order to make this function safe, maybe we'll need a way to
379 // In order to make this function safe, maybe we'll need a way to
367 // temporarily restrict the lifetime of self.data and translate the
380 // temporarily restrict the lifetime of self.data and translate the
368 // returned object back to Something<'static>.
381 // returned object back to Something<'static>.
369 let new_data = f(self.data.take().unwrap());
382 let new_data = f(self.data.take().unwrap());
370 PyLeaked {
383 PyLeaked {
371 inner: self.inner.clone_ref(py),
384 inner: self.inner.clone_ref(py),
372 data: Some(new_data),
385 data: Some(new_data),
373 py_shared_state: self.py_shared_state,
386 py_shared_state: self.py_shared_state,
374 }
387 }
375 }
388 }
376 }
389 }
377
390
378 impl<T> Drop for PyLeaked<T> {
391 impl<T> Drop for PyLeaked<T> {
379 fn drop(&mut self) {
392 fn drop(&mut self) {
380 // py_shared_state should be alive since we do have
393 // py_shared_state should be alive since we do have
381 // a Python reference to the owner object. Taking GIL makes
394 // a Python reference to the owner object. Taking GIL makes
382 // sure that the state is only accessed by this thread.
395 // sure that the state is only accessed by this thread.
383 let gil = Python::acquire_gil();
396 let gil = Python::acquire_gil();
384 let py = gil.python();
397 let py = gil.python();
385 if self.data.is_none() {
398 if self.data.is_none() {
386 return; // moved to another PyLeaked
399 return; // moved to another PyLeaked
387 }
400 }
388 self.py_shared_state.decrease_leak_count(py, false);
401 self.py_shared_state.decrease_leak_count(py, false);
389 }
402 }
390 }
403 }
391
404
405 /// Immutably borrowed reference to a leaked value.
406 pub struct PyLeakedRef<'a, T> {
407 _py: Python<'a>,
408 data: &'a T,
409 }
410
411 impl<T> Deref for PyLeakedRef<'_, T> {
412 type Target = T;
413
414 fn deref(&self) -> &T {
415 self.data
416 }
417 }
418
419 /// Mutably borrowed reference to a leaked value.
420 pub struct PyLeakedRefMut<'a, T> {
421 _py: Python<'a>,
422 data: &'a mut T,
423 }
424
425 impl<T> Deref for PyLeakedRefMut<'_, T> {
426 type Target = T;
427
428 fn deref(&self) -> &T {
429 self.data
430 }
431 }
432
433 impl<T> DerefMut for PyLeakedRefMut<'_, T> {
434 fn deref_mut(&mut self) -> &mut T {
435 self.data
436 }
437 }
438
392 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
439 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
393 ///
440 ///
394 /// TODO: this is a bit awkward to use, and a better (more complicated)
441 /// TODO: this is a bit awkward to use, and a better (more complicated)
395 /// procedural macro would simplify the interface a lot.
442 /// procedural macro would simplify the interface a lot.
396 ///
443 ///
397 /// # Parameters
444 /// # Parameters
398 ///
445 ///
399 /// * `$name` is the identifier to give to the resulting Rust struct.
446 /// * `$name` is the identifier to give to the resulting Rust struct.
400 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
447 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
401 /// * `$iterator_type` is the type of the Rust iterator.
448 /// * `$iterator_type` is the type of the Rust iterator.
402 /// * `$success_func` is a function for processing the Rust `(key, value)`
449 /// * `$success_func` is a function for processing the Rust `(key, value)`
403 /// tuple on iteration success, turning it into something Python understands.
450 /// tuple on iteration success, turning it into something Python understands.
404 /// * `$success_func` is the return type of `$success_func`
451 /// * `$success_func` is the return type of `$success_func`
405 ///
452 ///
406 /// # Example
453 /// # Example
407 ///
454 ///
408 /// ```
455 /// ```
409 /// struct MyStruct {
456 /// struct MyStruct {
410 /// inner: HashMap<Vec<u8>, Vec<u8>>;
457 /// inner: HashMap<Vec<u8>, Vec<u8>>;
411 /// }
458 /// }
412 ///
459 ///
413 /// py_class!(pub class MyType |py| {
460 /// py_class!(pub class MyType |py| {
414 /// data inner: PySharedRefCell<MyStruct>;
461 /// data inner: PySharedRefCell<MyStruct>;
415 ///
462 ///
416 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
463 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
417 /// let leaked_ref = self.inner_shared(py).leak_immutable()?;
464 /// let leaked_ref = self.inner_shared(py).leak_immutable()?;
418 /// MyTypeItemsIterator::from_inner(
465 /// MyTypeItemsIterator::from_inner(
419 /// py,
466 /// py,
420 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
467 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
421 /// )
468 /// )
422 /// }
469 /// }
423 /// });
470 /// });
424 ///
471 ///
425 /// impl MyType {
472 /// impl MyType {
426 /// fn translate_key_value(
473 /// fn translate_key_value(
427 /// py: Python,
474 /// py: Python,
428 /// res: (&Vec<u8>, &Vec<u8>),
475 /// res: (&Vec<u8>, &Vec<u8>),
429 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
476 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
430 /// let (f, entry) = res;
477 /// let (f, entry) = res;
431 /// Ok(Some((
478 /// Ok(Some((
432 /// PyBytes::new(py, f),
479 /// PyBytes::new(py, f),
433 /// PyBytes::new(py, entry),
480 /// PyBytes::new(py, entry),
434 /// )))
481 /// )))
435 /// }
482 /// }
436 /// }
483 /// }
437 ///
484 ///
438 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
485 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
439 ///
486 ///
440 /// py_shared_iterator!(
487 /// py_shared_iterator!(
441 /// MyTypeItemsIterator,
488 /// MyTypeItemsIterator,
442 /// PyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>,
489 /// PyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>,
443 /// MyType::translate_key_value,
490 /// MyType::translate_key_value,
444 /// Option<(PyBytes, PyBytes)>
491 /// Option<(PyBytes, PyBytes)>
445 /// );
492 /// );
446 /// ```
493 /// ```
447 macro_rules! py_shared_iterator {
494 macro_rules! py_shared_iterator {
448 (
495 (
449 $name: ident,
496 $name: ident,
450 $leaked: ty,
497 $leaked: ty,
451 $success_func: expr,
498 $success_func: expr,
452 $success_type: ty
499 $success_type: ty
453 ) => {
500 ) => {
454 py_class!(pub class $name |py| {
501 py_class!(pub class $name |py| {
455 data inner: RefCell<Option<$leaked>>;
502 data inner: RefCell<Option<$leaked>>;
456
503
457 def __next__(&self) -> PyResult<$success_type> {
504 def __next__(&self) -> PyResult<$success_type> {
458 let mut inner_opt = self.inner(py).borrow_mut();
505 let mut inner_opt = self.inner(py).borrow_mut();
459 if let Some(leaked) = inner_opt.as_mut() {
506 if let Some(leaked) = inner_opt.as_mut() {
460 match leaked.get_mut(py).next() {
507 let mut iter = leaked.try_borrow_mut(py)?;
508 match iter.next() {
461 None => {
509 None => {
462 // replace Some(inner) by None, drop $leaked
510 // replace Some(inner) by None, drop $leaked
463 inner_opt.take();
511 inner_opt.take();
464 Ok(None)
512 Ok(None)
465 }
513 }
466 Some(res) => {
514 Some(res) => {
467 $success_func(py, res)
515 $success_func(py, res)
468 }
516 }
469 }
517 }
470 } else {
518 } else {
471 Ok(None)
519 Ok(None)
472 }
520 }
473 }
521 }
474
522
475 def __iter__(&self) -> PyResult<Self> {
523 def __iter__(&self) -> PyResult<Self> {
476 Ok(self.clone_ref(py))
524 Ok(self.clone_ref(py))
477 }
525 }
478 });
526 });
479
527
480 impl $name {
528 impl $name {
481 pub fn from_inner(
529 pub fn from_inner(
482 py: Python,
530 py: Python,
483 leaked: $leaked,
531 leaked: $leaked,
484 ) -> PyResult<Self> {
532 ) -> PyResult<Self> {
485 Self::create_instance(
533 Self::create_instance(
486 py,
534 py,
487 RefCell::new(Some(leaked)),
535 RefCell::new(Some(leaked)),
488 )
536 )
489 }
537 }
490 }
538 }
491 };
539 };
492 }
540 }
493
541
494 #[cfg(test)]
542 #[cfg(test)]
495 #[cfg(any(feature = "python27-bin", feature = "python3-bin"))]
543 #[cfg(any(feature = "python27-bin", feature = "python3-bin"))]
496 mod test {
544 mod test {
497 use super::*;
545 use super::*;
498 use cpython::{GILGuard, Python};
546 use cpython::{GILGuard, Python};
499
547
500 py_class!(class Owner |py| {
548 py_class!(class Owner |py| {
501 data string: PySharedRefCell<String>;
549 data string: PySharedRefCell<String>;
502 });
550 });
503 py_shared_ref!(Owner, String, string, string_shared);
551 py_shared_ref!(Owner, String, string, string_shared);
504
552
505 fn prepare_env() -> (GILGuard, Owner) {
553 fn prepare_env() -> (GILGuard, Owner) {
506 let gil = Python::acquire_gil();
554 let gil = Python::acquire_gil();
507 let py = gil.python();
555 let py = gil.python();
508 let owner =
556 let owner =
509 Owner::create_instance(py, PySharedRefCell::new("new".to_owned()))
557 Owner::create_instance(py, PySharedRefCell::new("new".to_owned()))
510 .unwrap();
558 .unwrap();
511 (gil, owner)
559 (gil, owner)
512 }
560 }
513
561
514 #[test]
562 #[test]
563 fn test_leaked_borrow() {
564 let (gil, owner) = prepare_env();
565 let py = gil.python();
566 let leaked = owner.string_shared(py).leak_immutable().unwrap();
567 let leaked_ref = leaked.try_borrow(py).unwrap();
568 assert_eq!(*leaked_ref, "new");
569 }
570
571 #[test]
572 fn test_leaked_borrow_mut() {
573 let (gil, owner) = prepare_env();
574 let py = gil.python();
575 let leaked = owner.string_shared(py).leak_immutable().unwrap();
576 let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
577 let mut leaked_ref = leaked_iter.try_borrow_mut(py).unwrap();
578 assert_eq!(leaked_ref.next(), Some('n'));
579 assert_eq!(leaked_ref.next(), Some('e'));
580 assert_eq!(leaked_ref.next(), Some('w'));
581 assert_eq!(leaked_ref.next(), None);
582 }
583
584 #[test]
515 fn test_borrow_mut_while_leaked() {
585 fn test_borrow_mut_while_leaked() {
516 let (gil, owner) = prepare_env();
586 let (gil, owner) = prepare_env();
517 let py = gil.python();
587 let py = gil.python();
518 assert!(owner.string_shared(py).borrow_mut().is_ok());
588 assert!(owner.string_shared(py).borrow_mut().is_ok());
519 let _leaked = owner.string_shared(py).leak_immutable().unwrap();
589 let _leaked = owner.string_shared(py).leak_immutable().unwrap();
520 // TODO: will be allowed
590 // TODO: will be allowed
521 assert!(owner.string_shared(py).borrow_mut().is_err());
591 assert!(owner.string_shared(py).borrow_mut().is_err());
522 }
592 }
523 }
593 }
General Comments 0
You need to be logged in to leave comments. Login now