##// END OF EJS Templates
rust-cpython: make PyLeakedRef operations relatively safe...
Yuya Nishihara -
r43579:ffc1fbd7 default
parent child Browse files
Show More
@@ -13,7 +13,6 b' use std::cell::RefCell;'
13 13
14 14 use crate::dirstate::dirstate_map::DirstateMap;
15 15 use crate::ref_sharing::PyLeakedRef;
16 use hg::DirstateMap as RustDirstateMap;
17 16 use hg::{utils::hg_path::HgPathBuf, CopyMapIter};
18 17
19 18 py_class!(pub class CopyMap |py| {
@@ -105,16 +104,14 b' impl CopyMap {'
105 104
106 105 py_shared_iterator!(
107 106 CopyMapKeysIterator,
108 PyLeakedRef<&'static RustDirstateMap>,
109 CopyMapIter<'static>,
107 PyLeakedRef<CopyMapIter<'static>>,
110 108 CopyMap::translate_key,
111 109 Option<PyBytes>
112 110 );
113 111
114 112 py_shared_iterator!(
115 113 CopyMapItemsIterator,
116 PyLeakedRef<&'static RustDirstateMap>,
117 CopyMapIter<'static>,
114 PyLeakedRef<CopyMapIter<'static>>,
118 115 CopyMap::translate_key_value,
119 116 Option<(PyBytes, PyBytes)>
120 117 );
@@ -92,13 +92,10 b' py_class!(pub class Dirs |py| {'
92 92 })
93 93 }
94 94 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
95 let mut leak_handle =
96 unsafe { self.inner_shared(py).leak_immutable()? };
97 let leaked_ref = leak_handle.data.take().unwrap();
95 let leaked_ref = self.inner_shared(py).leak_immutable()?;
98 96 DirsMultisetKeysIterator::from_inner(
99 97 py,
100 leak_handle,
101 leaked_ref.iter(),
98 unsafe { leaked_ref.map(py, |o| o.iter()) },
102 99 )
103 100 }
104 101
@@ -126,8 +123,7 b' impl Dirs {'
126 123
127 124 py_shared_iterator!(
128 125 DirsMultisetKeysIterator,
129 PyLeakedRef<&'static DirsMultiset>,
130 DirsMultisetIter<'static>,
126 PyLeakedRef<DirsMultisetIter<'static>>,
131 127 Dirs::translate_key,
132 128 Option<PyBytes>
133 129 );
@@ -304,35 +304,26 b' py_class!(pub class DirstateMap |py| {'
304 304 }
305 305
306 306 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
307 let mut leak_handle =
308 unsafe { self.inner_shared(py).leak_immutable()? };
309 let leaked_ref = leak_handle.data.take().unwrap();
307 let leaked_ref = self.inner_shared(py).leak_immutable()?;
310 308 DirstateMapKeysIterator::from_inner(
311 309 py,
312 leak_handle,
313 leaked_ref.iter(),
310 unsafe { leaked_ref.map(py, |o| o.iter()) },
314 311 )
315 312 }
316 313
317 314 def items(&self) -> PyResult<DirstateMapItemsIterator> {
318 let mut leak_handle =
319 unsafe { self.inner_shared(py).leak_immutable()? };
320 let leaked_ref = leak_handle.data.take().unwrap();
315 let leaked_ref = self.inner_shared(py).leak_immutable()?;
321 316 DirstateMapItemsIterator::from_inner(
322 317 py,
323 leak_handle,
324 leaked_ref.iter(),
318 unsafe { leaked_ref.map(py, |o| o.iter()) },
325 319 )
326 320 }
327 321
328 322 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
329 let mut leak_handle =
330 unsafe { self.inner_shared(py).leak_immutable()? };
331 let leaked_ref = leak_handle.data.take().unwrap();
323 let leaked_ref = self.inner_shared(py).leak_immutable()?;
332 324 DirstateMapKeysIterator::from_inner(
333 325 py,
334 leak_handle,
335 leaked_ref.iter(),
326 unsafe { leaked_ref.map(py, |o| o.iter()) },
336 327 )
337 328 }
338 329
@@ -446,24 +437,18 b' py_class!(pub class DirstateMap |py| {'
446 437 }
447 438
448 439 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
449 let mut leak_handle =
450 unsafe { self.inner_shared(py).leak_immutable()? };
451 let leaked_ref = leak_handle.data.take().unwrap();
440 let leaked_ref = self.inner_shared(py).leak_immutable()?;
452 441 CopyMapKeysIterator::from_inner(
453 442 py,
454 leak_handle,
455 leaked_ref.copy_map.iter(),
443 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
456 444 )
457 445 }
458 446
459 447 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
460 let mut leak_handle =
461 unsafe { self.inner_shared(py).leak_immutable()? };
462 let leaked_ref = leak_handle.data.take().unwrap();
448 let leaked_ref = self.inner_shared(py).leak_immutable()?;
463 449 CopyMapItemsIterator::from_inner(
464 450 py,
465 leak_handle,
466 leaked_ref.copy_map.iter(),
451 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
467 452 )
468 453 }
469 454
@@ -498,16 +483,14 b' py_shared_ref!(DirstateMap, RustDirstate'
498 483
499 484 py_shared_iterator!(
500 485 DirstateMapKeysIterator,
501 PyLeakedRef<&'static RustDirstateMap>,
502 StateMapIter<'static>,
486 PyLeakedRef<StateMapIter<'static>>,
503 487 DirstateMap::translate_key,
504 488 Option<PyBytes>
505 489 );
506 490
507 491 py_shared_iterator!(
508 492 DirstateMapItemsIterator,
509 PyLeakedRef<&'static RustDirstateMap>,
510 StateMapIter<'static>,
493 PyLeakedRef<StateMapIter<'static>>,
511 494 DirstateMap::translate_key_value,
512 495 Option<(PyBytes, PyObject)>
513 496 );
@@ -187,18 +187,12 b" impl<'a, T> PySharedRef<'a, T> {"
187 187 self.data.borrow_mut(self.py)
188 188 }
189 189
190 /// Returns a leaked reference temporarily held by its management object.
191 ///
192 /// # Safety
193 ///
194 /// It's up to you to make sure that the management object lives
195 /// longer than the leaked reference. Otherwise, you'll get a
196 /// dangling reference.
197 pub unsafe fn leak_immutable(&self) -> PyResult<PyLeakedRef<&'static T>> {
198 let (static_ref, static_state_ref) = self
199 .data
200 .py_shared_state
201 .leak_immutable(self.py, self.data)?;
190 /// Returns a leaked reference.
191 pub fn leak_immutable(&self) -> PyResult<PyLeakedRef<&'static T>> {
192 let state = &self.data.py_shared_state;
193 unsafe {
194 let (static_ref, static_state_ref) =
195 state.leak_immutable(self.py, self.data)?;
202 196 Ok(PyLeakedRef::new(
203 197 self.py,
204 198 self.owner,
@@ -207,6 +201,7 b" impl<'a, T> PySharedRef<'a, T> {"
207 201 ))
208 202 }
209 203 }
204 }
210 205
211 206 /// Holds a mutable reference to data shared between Python and Rust.
212 207 pub struct PyRefMut<'a, T> {
@@ -316,15 +311,15 b' macro_rules! py_shared_ref {'
316 311 }
317 312
318 313 /// Manage immutable references to `PyObject` leaked into Python iterators.
319 ///
320 /// In truth, this does not represent leaked references themselves;
321 /// it is instead useful alongside them to manage them.
322 314 pub struct PyLeakedRef<T> {
323 _inner: PyObject,
324 pub data: Option<T>, // TODO: remove pub
315 inner: PyObject,
316 data: Option<T>,
325 317 py_shared_state: &'static PySharedState,
326 318 }
327 319
320 // DO NOT implement Deref for PyLeakedRef<T>! Dereferencing PyLeakedRef
321 // without taking Python GIL wouldn't be safe.
322
328 323 impl<T> PyLeakedRef<T> {
329 324 /// # Safety
330 325 ///
@@ -338,11 +333,52 b' impl<T> PyLeakedRef<T> {'
338 333 py_shared_state: &'static PySharedState,
339 334 ) -> Self {
340 335 Self {
341 _inner: inner.clone_ref(py),
336 inner: inner.clone_ref(py),
342 337 data: Some(data),
343 338 py_shared_state,
344 339 }
345 340 }
341
342 /// Returns an immutable reference to the inner value.
343 pub fn get_ref<'a>(&'a self, _py: Python<'a>) -> &'a T {
344 self.data.as_ref().unwrap()
345 }
346
347 /// Returns a mutable reference to the inner value.
348 ///
349 /// Typically `T` is an iterator. If `T` is an immutable reference,
350 /// `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 {
352 self.data.as_mut().unwrap()
353 }
354
355 /// Converts the inner value by the given function.
356 ///
357 /// Typically `T` is a static reference to a container, and `U` is an
358 /// iterator of that container.
359 ///
360 /// # Safety
361 ///
362 /// 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
364 /// corresponding `PyLeakedRef` is alive. Do not copy it out of the
365 /// function call.
366 pub unsafe fn map<U>(
367 mut self,
368 py: Python,
369 f: impl FnOnce(T) -> U,
370 ) -> PyLeakedRef<U> {
371 // 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
373 // temporarily restrict the lifetime of self.data and translate the
374 // returned object back to Something<'static>.
375 let new_data = f(self.data.take().unwrap());
376 PyLeakedRef {
377 inner: self.inner.clone_ref(py),
378 data: Some(new_data),
379 py_shared_state: self.py_shared_state,
380 }
381 }
346 382 }
347 383
348 384 impl<T> Drop for PyLeakedRef<T> {
@@ -352,6 +388,9 b' impl<T> Drop for PyLeakedRef<T> {'
352 388 // sure that the state is only accessed by this thread.
353 389 let gil = Python::acquire_gil();
354 390 let py = gil.python();
391 if self.data.is_none() {
392 return; // moved to another PyLeakedRef
393 }
355 394 unsafe {
356 395 self.py_shared_state.decrease_leak_count(py, false);
357 396 }
@@ -383,13 +422,10 b' impl<T> Drop for PyLeakedRef<T> {'
383 422 /// data inner: PySharedRefCell<MyStruct>;
384 423 ///
385 424 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
386 /// let mut leak_handle =
387 /// unsafe { self.inner_shared(py).leak_immutable()? };
388 /// let leaked_ref = leak_handle.data.take().unwrap();
425 /// let leaked_ref = self.inner_shared(py).leak_immutable()?;
389 426 /// MyTypeItemsIterator::from_inner(
390 427 /// py,
391 /// leak_handle,
392 /// leaked_ref.iter(),
428 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
393 429 /// )
394 430 /// }
395 431 /// });
@@ -411,8 +447,7 b' impl<T> Drop for PyLeakedRef<T> {'
411 447 ///
412 448 /// py_shared_iterator!(
413 449 /// MyTypeItemsIterator,
414 /// PyLeakedRef<&'static MyStruct>,
415 /// HashMap<'static, Vec<u8>, Vec<u8>>,
450 /// PyLeakedRef<HashMap<'static, Vec<u8>, Vec<u8>>>,
416 451 /// MyType::translate_key_value,
417 452 /// Option<(PyBytes, PyBytes)>
418 453 /// );
@@ -421,18 +456,16 b' macro_rules! py_shared_iterator {'
421 456 (
422 457 $name: ident,
423 458 $leaked: ty,
424 $iterator_type: ty,
425 459 $success_func: expr,
426 460 $success_type: ty
427 461 ) => {
428 462 py_class!(pub class $name |py| {
429 463 data inner: RefCell<Option<$leaked>>;
430 data it: RefCell<$iterator_type>;
431 464
432 465 def __next__(&self) -> PyResult<$success_type> {
433 466 let mut inner_opt = self.inner(py).borrow_mut();
434 if inner_opt.is_some() {
435 match self.it(py).borrow_mut().next() {
467 if let Some(leaked) = inner_opt.as_mut() {
468 match leaked.get_mut(py).next() {
436 469 None => {
437 470 // replace Some(inner) by None, drop $leaked
438 471 inner_opt.take();
@@ -456,12 +489,10 b' macro_rules! py_shared_iterator {'
456 489 pub fn from_inner(
457 490 py: Python,
458 491 leaked: $leaked,
459 it: $iterator_type
460 492 ) -> PyResult<Self> {
461 493 Self::create_instance(
462 494 py,
463 495 RefCell::new(Some(leaked)),
464 RefCell::new(it)
465 496 )
466 497 }
467 498 }
General Comments 0
You need to be logged in to leave comments. Login now