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