##// END OF EJS Templates
rust-cpython: move borrow_mut() to PySharedRefCell...
Yuya Nishihara -
r43444:1c675c5f default
parent child Browse files
Show More
@@ -1,405 +1,411 b''
1 1 // ref_sharing.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 6 // of this software and associated documentation files (the "Software"), to
7 7 // deal in the Software without restriction, including without limitation the
8 8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 9 // sell copies of the Software, and to permit persons to whom the Software is
10 10 // furnished to do so, subject to the following conditions:
11 11 //
12 12 // The above copyright notice and this permission notice shall be included in
13 13 // all copies or substantial portions of the Software.
14 14 //
15 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 21 // IN THE SOFTWARE.
22 22
23 23 //! Macros for use in the `hg-cpython` bridge library.
24 24
25 25 use crate::exceptions::AlreadyBorrowed;
26 26 use cpython::{PyResult, Python};
27 27 use std::cell::{Cell, Ref, RefCell, RefMut};
28 28
29 29 /// Manages the shared state between Python and Rust
30 30 #[derive(Debug, Default)]
31 31 pub struct PySharedState {
32 32 leak_count: Cell<usize>,
33 33 mutably_borrowed: Cell<bool>,
34 34 }
35 35
36 36 impl PySharedState {
37 37 pub fn borrow_mut<'a, T>(
38 38 &'a self,
39 39 py: Python<'a>,
40 40 pyrefmut: RefMut<'a, T>,
41 41 ) -> PyResult<PyRefMut<'a, T>> {
42 42 if self.mutably_borrowed.get() {
43 43 return Err(AlreadyBorrowed::new(
44 44 py,
45 45 "Cannot borrow mutably while there exists another \
46 46 mutable reference in a Python object",
47 47 ));
48 48 }
49 49 match self.leak_count.get() {
50 50 0 => {
51 51 self.mutably_borrowed.replace(true);
52 52 Ok(PyRefMut::new(py, pyrefmut, self))
53 53 }
54 54 // TODO
55 55 // For now, this works differently than Python references
56 56 // in the case of iterators.
57 57 // Python does not complain when the data an iterator
58 58 // points to is modified if the iterator is never used
59 59 // afterwards.
60 60 // Here, we are stricter than this by refusing to give a
61 61 // mutable reference if it is already borrowed.
62 62 // While the additional safety might be argued for, it
63 63 // breaks valid programming patterns in Python and we need
64 64 // to fix this issue down the line.
65 65 _ => Err(AlreadyBorrowed::new(
66 66 py,
67 67 "Cannot borrow mutably while there are \
68 68 immutable references in Python objects",
69 69 )),
70 70 }
71 71 }
72 72
73 73 /// Return a reference to the wrapped data with an artificial static
74 74 /// lifetime.
75 75 /// We need to be protected by the GIL for thread-safety.
76 76 ///
77 77 /// # Safety
78 78 ///
79 79 /// This is highly unsafe since the lifetime of the given data can be
80 80 /// extended. Do not call this function directly.
81 81 pub unsafe fn leak_immutable<T>(
82 82 &self,
83 83 py: Python,
84 84 data: &PySharedRefCell<T>,
85 85 ) -> PyResult<&'static T> {
86 86 if self.mutably_borrowed.get() {
87 87 return Err(AlreadyBorrowed::new(
88 88 py,
89 89 "Cannot borrow immutably while there is a \
90 90 mutable reference in Python objects",
91 91 ));
92 92 }
93 93 let ptr = data.as_ptr();
94 94 self.leak_count.replace(self.leak_count.get() + 1);
95 95 Ok(&*ptr)
96 96 }
97 97
98 98 /// # Safety
99 99 ///
100 100 /// It's unsafe to update the reference count without knowing the
101 101 /// reference is deleted. Do not call this function directly.
102 102 pub unsafe fn decrease_leak_count(&self, _py: Python, mutable: bool) {
103 103 if mutable {
104 104 assert_eq!(self.leak_count.get(), 0);
105 105 assert!(self.mutably_borrowed.get());
106 106 self.mutably_borrowed.replace(false);
107 107 } else {
108 108 let count = self.leak_count.get();
109 109 assert!(count > 0);
110 110 self.leak_count.replace(count - 1);
111 111 }
112 112 }
113 113 }
114 114
115 115 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
116 116 ///
117 117 /// Only immutable operation is allowed through this interface.
118 118 #[derive(Debug)]
119 119 pub struct PySharedRefCell<T> {
120 120 inner: RefCell<T>,
121 121 pub py_shared_state: PySharedState, // TODO: remove pub
122 122 }
123 123
124 124 impl<T> PySharedRefCell<T> {
125 125 pub fn new(value: T) -> PySharedRefCell<T> {
126 126 Self {
127 127 inner: RefCell::new(value),
128 128 py_shared_state: PySharedState::default(),
129 129 }
130 130 }
131 131
132 132 pub fn borrow(&self) -> Ref<T> {
133 133 // py_shared_state isn't involved since
134 134 // - inner.borrow() would fail if self is mutably borrowed,
135 135 // - and inner.borrow_mut() would fail while self is borrowed.
136 136 self.inner.borrow()
137 137 }
138 138
139 139 pub fn as_ptr(&self) -> *mut T {
140 140 self.inner.as_ptr()
141 141 }
142 142
143 pub unsafe fn borrow_mut(&self) -> RefMut<T> {
144 // must be borrowed by self.py_shared_state(py).borrow_mut().
145 self.inner.borrow_mut()
143 // TODO: maybe this should be named as try_borrow_mut(), and use
144 // inner.try_borrow_mut(). The current implementation panics if
145 // self.inner has been borrowed, but returns error if py_shared_state
146 // refuses to borrow.
147 pub fn borrow_mut<'a>(
148 &'a self,
149 py: Python<'a>,
150 ) -> PyResult<PyRefMut<'a, T>> {
151 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
146 152 }
147 153 }
148 154
149 155 /// Holds a mutable reference to data shared between Python and Rust.
150 156 pub struct PyRefMut<'a, T> {
151 157 inner: RefMut<'a, T>,
152 158 py_shared_state: &'a PySharedState,
153 159 }
154 160
155 161 impl<'a, T> PyRefMut<'a, T> {
156 162 // Must be constructed by PySharedState after checking its leak_count.
157 163 // Otherwise, drop() would incorrectly update the state.
158 164 fn new(
159 165 _py: Python<'a>,
160 166 inner: RefMut<'a, T>,
161 167 py_shared_state: &'a PySharedState,
162 168 ) -> Self {
163 169 Self {
164 170 inner,
165 171 py_shared_state,
166 172 }
167 173 }
168 174 }
169 175
170 176 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
171 177 type Target = RefMut<'a, T>;
172 178
173 179 fn deref(&self) -> &Self::Target {
174 180 &self.inner
175 181 }
176 182 }
177 183 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
178 184 fn deref_mut(&mut self) -> &mut Self::Target {
179 185 &mut self.inner
180 186 }
181 187 }
182 188
183 189 impl<'a, T> Drop for PyRefMut<'a, T> {
184 190 fn drop(&mut self) {
185 191 let gil = Python::acquire_gil();
186 192 let py = gil.python();
187 193 unsafe {
188 194 self.py_shared_state.decrease_leak_count(py, true);
189 195 }
190 196 }
191 197 }
192 198
193 199 /// Allows a `py_class!` generated struct to share references to one of its
194 200 /// data members with Python.
195 201 ///
196 202 /// # Warning
197 203 ///
198 204 /// TODO allow Python container types: for now, integration with the garbage
199 205 /// collector does not extend to Rust structs holding references to Python
200 206 /// objects. Should the need surface, `__traverse__` and `__clear__` will
201 207 /// need to be written as per the `rust-cpython` docs on GC integration.
202 208 ///
203 209 /// # Parameters
204 210 ///
205 211 /// * `$name` is the same identifier used in for `py_class!` macro call.
206 212 /// * `$inner_struct` is the identifier of the underlying Rust struct
207 213 /// * `$data_member` is the identifier of the data member of `$inner_struct`
208 214 /// that will be shared.
209 215 /// * `$leaked` is the identifier to give to the struct that will manage
210 216 /// references to `$name`, to be used for example in other macros like
211 217 /// `py_shared_iterator`.
212 218 ///
213 219 /// # Example
214 220 ///
215 221 /// ```
216 222 /// struct MyStruct {
217 223 /// inner: Vec<u32>;
218 224 /// }
219 225 ///
220 226 /// py_class!(pub class MyType |py| {
221 227 /// data inner: PySharedRefCell<MyStruct>;
222 228 /// });
223 229 ///
224 230 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
225 231 /// ```
226 232 macro_rules! py_shared_ref {
227 233 (
228 234 $name: ident,
229 235 $inner_struct: ident,
230 236 $data_member: ident,
231 237 $leaked: ident,
232 238 ) => {
233 239 impl $name {
240 // TODO: remove this function in favor of inner(py).borrow_mut()
234 241 fn borrow_mut<'a>(
235 242 &'a self,
236 243 py: Python<'a>,
237 244 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
238 245 {
239 246 // assert $data_member type
240 247 use crate::ref_sharing::PySharedRefCell;
241 248 let data: &PySharedRefCell<_> = self.$data_member(py);
242 data.py_shared_state
243 .borrow_mut(py, unsafe { data.borrow_mut() })
249 data.borrow_mut(py)
244 250 }
245 251
246 252 /// Returns a leaked reference and its management object.
247 253 ///
248 254 /// # Safety
249 255 ///
250 256 /// It's up to you to make sure that the management object lives
251 257 /// longer than the leaked reference. Otherwise, you'll get a
252 258 /// dangling reference.
253 259 unsafe fn leak_immutable<'a>(
254 260 &'a self,
255 261 py: Python<'a>,
256 262 ) -> PyResult<($leaked, &'static $inner_struct)> {
257 263 // assert $data_member type
258 264 use crate::ref_sharing::PySharedRefCell;
259 265 let data: &PySharedRefCell<_> = self.$data_member(py);
260 266 let static_ref =
261 267 data.py_shared_state.leak_immutable(py, data)?;
262 268 let leak_handle = $leaked::new(py, self);
263 269 Ok((leak_handle, static_ref))
264 270 }
265 271 }
266 272
267 273 /// Manage immutable references to `$name` leaked into Python
268 274 /// iterators.
269 275 ///
270 276 /// In truth, this does not represent leaked references themselves;
271 277 /// it is instead useful alongside them to manage them.
272 278 pub struct $leaked {
273 279 inner: $name,
274 280 }
275 281
276 282 impl $leaked {
277 283 // Marked as unsafe so client code wouldn't construct $leaked
278 284 // struct by mistake. Its drop() is unsafe.
279 285 unsafe fn new(py: Python, inner: &$name) -> Self {
280 286 Self {
281 287 inner: inner.clone_ref(py),
282 288 }
283 289 }
284 290 }
285 291
286 292 impl Drop for $leaked {
287 293 fn drop(&mut self) {
288 294 let gil = Python::acquire_gil();
289 295 let py = gil.python();
290 296 let state = &self.inner.$data_member(py).py_shared_state;
291 297 unsafe {
292 298 state.decrease_leak_count(py, false);
293 299 }
294 300 }
295 301 }
296 302 };
297 303 }
298 304
299 305 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
300 306 ///
301 307 /// TODO: this is a bit awkward to use, and a better (more complicated)
302 308 /// procedural macro would simplify the interface a lot.
303 309 ///
304 310 /// # Parameters
305 311 ///
306 312 /// * `$name` is the identifier to give to the resulting Rust struct.
307 313 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
308 314 /// * `$iterator_type` is the type of the Rust iterator.
309 315 /// * `$success_func` is a function for processing the Rust `(key, value)`
310 316 /// tuple on iteration success, turning it into something Python understands.
311 317 /// * `$success_func` is the return type of `$success_func`
312 318 ///
313 319 /// # Example
314 320 ///
315 321 /// ```
316 322 /// struct MyStruct {
317 323 /// inner: HashMap<Vec<u8>, Vec<u8>>;
318 324 /// }
319 325 ///
320 326 /// py_class!(pub class MyType |py| {
321 327 /// data inner: PySharedRefCell<MyStruct>;
322 328 ///
323 329 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
324 330 /// let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
325 331 /// MyTypeItemsIterator::from_inner(
326 332 /// py,
327 333 /// leak_handle,
328 334 /// leaked_ref.iter(),
329 335 /// )
330 336 /// }
331 337 /// });
332 338 ///
333 339 /// impl MyType {
334 340 /// fn translate_key_value(
335 341 /// py: Python,
336 342 /// res: (&Vec<u8>, &Vec<u8>),
337 343 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
338 344 /// let (f, entry) = res;
339 345 /// Ok(Some((
340 346 /// PyBytes::new(py, f),
341 347 /// PyBytes::new(py, entry),
342 348 /// )))
343 349 /// }
344 350 /// }
345 351 ///
346 352 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
347 353 ///
348 354 /// py_shared_iterator!(
349 355 /// MyTypeItemsIterator,
350 356 /// MyTypeLeakedRef,
351 357 /// HashMap<'static, Vec<u8>, Vec<u8>>,
352 358 /// MyType::translate_key_value,
353 359 /// Option<(PyBytes, PyBytes)>
354 360 /// );
355 361 /// ```
356 362 macro_rules! py_shared_iterator {
357 363 (
358 364 $name: ident,
359 365 $leaked: ident,
360 366 $iterator_type: ty,
361 367 $success_func: expr,
362 368 $success_type: ty
363 369 ) => {
364 370 py_class!(pub class $name |py| {
365 371 data inner: RefCell<Option<$leaked>>;
366 372 data it: RefCell<$iterator_type>;
367 373
368 374 def __next__(&self) -> PyResult<$success_type> {
369 375 let mut inner_opt = self.inner(py).borrow_mut();
370 376 if inner_opt.is_some() {
371 377 match self.it(py).borrow_mut().next() {
372 378 None => {
373 379 // replace Some(inner) by None, drop $leaked
374 380 inner_opt.take();
375 381 Ok(None)
376 382 }
377 383 Some(res) => {
378 384 $success_func(py, res)
379 385 }
380 386 }
381 387 } else {
382 388 Ok(None)
383 389 }
384 390 }
385 391
386 392 def __iter__(&self) -> PyResult<Self> {
387 393 Ok(self.clone_ref(py))
388 394 }
389 395 });
390 396
391 397 impl $name {
392 398 pub fn from_inner(
393 399 py: Python,
394 400 leaked: $leaked,
395 401 it: $iterator_type
396 402 ) -> PyResult<Self> {
397 403 Self::create_instance(
398 404 py,
399 405 RefCell::new(Some(leaked)),
400 406 RefCell::new(it)
401 407 )
402 408 }
403 409 }
404 410 };
405 411 }
General Comments 0
You need to be logged in to leave comments. Login now