##// 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 use crate::dirstate::dirstate_map::DirstateMap;
14 use crate::dirstate::dirstate_map::DirstateMap;
15 use crate::ref_sharing::PyLeakedRef;
15 use crate::ref_sharing::PyLeakedRef;
16 use hg::DirstateMap as RustDirstateMap;
17 use hg::{utils::hg_path::HgPathBuf, CopyMapIter};
16 use hg::{utils::hg_path::HgPathBuf, CopyMapIter};
18
17
19 py_class!(pub class CopyMap |py| {
18 py_class!(pub class CopyMap |py| {
@@ -105,16 +104,14 b' impl CopyMap {'
105
104
106 py_shared_iterator!(
105 py_shared_iterator!(
107 CopyMapKeysIterator,
106 CopyMapKeysIterator,
108 PyLeakedRef<&'static RustDirstateMap>,
107 PyLeakedRef<CopyMapIter<'static>>,
109 CopyMapIter<'static>,
110 CopyMap::translate_key,
108 CopyMap::translate_key,
111 Option<PyBytes>
109 Option<PyBytes>
112 );
110 );
113
111
114 py_shared_iterator!(
112 py_shared_iterator!(
115 CopyMapItemsIterator,
113 CopyMapItemsIterator,
116 PyLeakedRef<&'static RustDirstateMap>,
114 PyLeakedRef<CopyMapIter<'static>>,
117 CopyMapIter<'static>,
118 CopyMap::translate_key_value,
115 CopyMap::translate_key_value,
119 Option<(PyBytes, PyBytes)>
116 Option<(PyBytes, PyBytes)>
120 );
117 );
@@ -92,13 +92,10 b' py_class!(pub class Dirs |py| {'
92 })
92 })
93 }
93 }
94 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
94 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
95 let mut leak_handle =
95 let leaked_ref = self.inner_shared(py).leak_immutable()?;
96 unsafe { self.inner_shared(py).leak_immutable()? };
97 let leaked_ref = leak_handle.data.take().unwrap();
98 DirsMultisetKeysIterator::from_inner(
96 DirsMultisetKeysIterator::from_inner(
99 py,
97 py,
100 leak_handle,
98 unsafe { leaked_ref.map(py, |o| o.iter()) },
101 leaked_ref.iter(),
102 )
99 )
103 }
100 }
104
101
@@ -126,8 +123,7 b' impl Dirs {'
126
123
127 py_shared_iterator!(
124 py_shared_iterator!(
128 DirsMultisetKeysIterator,
125 DirsMultisetKeysIterator,
129 PyLeakedRef<&'static DirsMultiset>,
126 PyLeakedRef<DirsMultisetIter<'static>>,
130 DirsMultisetIter<'static>,
131 Dirs::translate_key,
127 Dirs::translate_key,
132 Option<PyBytes>
128 Option<PyBytes>
133 );
129 );
@@ -304,35 +304,26 b' py_class!(pub class DirstateMap |py| {'
304 }
304 }
305
305
306 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
306 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
307 let mut leak_handle =
307 let leaked_ref = self.inner_shared(py).leak_immutable()?;
308 unsafe { self.inner_shared(py).leak_immutable()? };
309 let leaked_ref = leak_handle.data.take().unwrap();
310 DirstateMapKeysIterator::from_inner(
308 DirstateMapKeysIterator::from_inner(
311 py,
309 py,
312 leak_handle,
310 unsafe { leaked_ref.map(py, |o| o.iter()) },
313 leaked_ref.iter(),
314 )
311 )
315 }
312 }
316
313
317 def items(&self) -> PyResult<DirstateMapItemsIterator> {
314 def items(&self) -> PyResult<DirstateMapItemsIterator> {
318 let mut leak_handle =
315 let leaked_ref = self.inner_shared(py).leak_immutable()?;
319 unsafe { self.inner_shared(py).leak_immutable()? };
320 let leaked_ref = leak_handle.data.take().unwrap();
321 DirstateMapItemsIterator::from_inner(
316 DirstateMapItemsIterator::from_inner(
322 py,
317 py,
323 leak_handle,
318 unsafe { leaked_ref.map(py, |o| o.iter()) },
324 leaked_ref.iter(),
325 )
319 )
326 }
320 }
327
321
328 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
322 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
329 let mut leak_handle =
323 let leaked_ref = self.inner_shared(py).leak_immutable()?;
330 unsafe { self.inner_shared(py).leak_immutable()? };
331 let leaked_ref = leak_handle.data.take().unwrap();
332 DirstateMapKeysIterator::from_inner(
324 DirstateMapKeysIterator::from_inner(
333 py,
325 py,
334 leak_handle,
326 unsafe { leaked_ref.map(py, |o| o.iter()) },
335 leaked_ref.iter(),
336 )
327 )
337 }
328 }
338
329
@@ -446,24 +437,18 b' py_class!(pub class DirstateMap |py| {'
446 }
437 }
447
438
448 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
439 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
449 let mut leak_handle =
440 let leaked_ref = self.inner_shared(py).leak_immutable()?;
450 unsafe { self.inner_shared(py).leak_immutable()? };
451 let leaked_ref = leak_handle.data.take().unwrap();
452 CopyMapKeysIterator::from_inner(
441 CopyMapKeysIterator::from_inner(
453 py,
442 py,
454 leak_handle,
443 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
455 leaked_ref.copy_map.iter(),
456 )
444 )
457 }
445 }
458
446
459 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
447 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
460 let mut leak_handle =
448 let leaked_ref = self.inner_shared(py).leak_immutable()?;
461 unsafe { self.inner_shared(py).leak_immutable()? };
462 let leaked_ref = leak_handle.data.take().unwrap();
463 CopyMapItemsIterator::from_inner(
449 CopyMapItemsIterator::from_inner(
464 py,
450 py,
465 leak_handle,
451 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
466 leaked_ref.copy_map.iter(),
467 )
452 )
468 }
453 }
469
454
@@ -498,16 +483,14 b' py_shared_ref!(DirstateMap, RustDirstate'
498
483
499 py_shared_iterator!(
484 py_shared_iterator!(
500 DirstateMapKeysIterator,
485 DirstateMapKeysIterator,
501 PyLeakedRef<&'static RustDirstateMap>,
486 PyLeakedRef<StateMapIter<'static>>,
502 StateMapIter<'static>,
503 DirstateMap::translate_key,
487 DirstateMap::translate_key,
504 Option<PyBytes>
488 Option<PyBytes>
505 );
489 );
506
490
507 py_shared_iterator!(
491 py_shared_iterator!(
508 DirstateMapItemsIterator,
492 DirstateMapItemsIterator,
509 PyLeakedRef<&'static RustDirstateMap>,
493 PyLeakedRef<StateMapIter<'static>>,
510 StateMapIter<'static>,
511 DirstateMap::translate_key_value,
494 DirstateMap::translate_key_value,
512 Option<(PyBytes, PyObject)>
495 Option<(PyBytes, PyObject)>
513 );
496 );
@@ -187,24 +187,19 b" impl<'a, T> PySharedRef<'a, T> {"
187 self.data.borrow_mut(self.py)
187 self.data.borrow_mut(self.py)
188 }
188 }
189
189
190 /// Returns a leaked reference temporarily held by its management object.
190 /// Returns a leaked reference.
191 ///
191 pub fn leak_immutable(&self) -> PyResult<PyLeakedRef<&'static T>> {
192 /// # Safety
192 let state = &self.data.py_shared_state;
193 ///
193 unsafe {
194 /// It's up to you to make sure that the management object lives
194 let (static_ref, static_state_ref) =
195 /// longer than the leaked reference. Otherwise, you'll get a
195 state.leak_immutable(self.py, self.data)?;
196 /// dangling reference.
196 Ok(PyLeakedRef::new(
197 pub unsafe fn leak_immutable(&self) -> PyResult<PyLeakedRef<&'static T>> {
197 self.py,
198 let (static_ref, static_state_ref) = self
198 self.owner,
199 .data
199 static_ref,
200 .py_shared_state
200 static_state_ref,
201 .leak_immutable(self.py, self.data)?;
201 ))
202 Ok(PyLeakedRef::new(
202 }
203 self.py,
204 self.owner,
205 static_ref,
206 static_state_ref,
207 ))
208 }
203 }
209 }
204 }
210
205
@@ -316,15 +311,15 b' macro_rules! py_shared_ref {'
316 }
311 }
317
312
318 /// Manage immutable references to `PyObject` leaked into Python iterators.
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 pub struct PyLeakedRef<T> {
314 pub struct PyLeakedRef<T> {
323 _inner: PyObject,
315 inner: PyObject,
324 pub data: Option<T>, // TODO: remove pub
316 data: Option<T>,
325 py_shared_state: &'static PySharedState,
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 impl<T> PyLeakedRef<T> {
323 impl<T> PyLeakedRef<T> {
329 /// # Safety
324 /// # Safety
330 ///
325 ///
@@ -338,11 +333,52 b' impl<T> PyLeakedRef<T> {'
338 py_shared_state: &'static PySharedState,
333 py_shared_state: &'static PySharedState,
339 ) -> Self {
334 ) -> Self {
340 Self {
335 Self {
341 _inner: inner.clone_ref(py),
336 inner: inner.clone_ref(py),
342 data: Some(data),
337 data: Some(data),
343 py_shared_state,
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 impl<T> Drop for PyLeakedRef<T> {
384 impl<T> Drop for PyLeakedRef<T> {
@@ -352,6 +388,9 b' impl<T> Drop for PyLeakedRef<T> {'
352 // sure that the state is only accessed by this thread.
388 // sure that the state is only accessed by this thread.
353 let gil = Python::acquire_gil();
389 let gil = Python::acquire_gil();
354 let py = gil.python();
390 let py = gil.python();
391 if self.data.is_none() {
392 return; // moved to another PyLeakedRef
393 }
355 unsafe {
394 unsafe {
356 self.py_shared_state.decrease_leak_count(py, false);
395 self.py_shared_state.decrease_leak_count(py, false);
357 }
396 }
@@ -383,13 +422,10 b' impl<T> Drop for PyLeakedRef<T> {'
383 /// data inner: PySharedRefCell<MyStruct>;
422 /// data inner: PySharedRefCell<MyStruct>;
384 ///
423 ///
385 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
424 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
386 /// let mut leak_handle =
425 /// let leaked_ref = self.inner_shared(py).leak_immutable()?;
387 /// unsafe { self.inner_shared(py).leak_immutable()? };
388 /// let leaked_ref = leak_handle.data.take().unwrap();
389 /// MyTypeItemsIterator::from_inner(
426 /// MyTypeItemsIterator::from_inner(
390 /// py,
427 /// py,
391 /// leak_handle,
428 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
392 /// leaked_ref.iter(),
393 /// )
429 /// )
394 /// }
430 /// }
395 /// });
431 /// });
@@ -411,8 +447,7 b' impl<T> Drop for PyLeakedRef<T> {'
411 ///
447 ///
412 /// py_shared_iterator!(
448 /// py_shared_iterator!(
413 /// MyTypeItemsIterator,
449 /// MyTypeItemsIterator,
414 /// PyLeakedRef<&'static MyStruct>,
450 /// PyLeakedRef<HashMap<'static, Vec<u8>, Vec<u8>>>,
415 /// HashMap<'static, Vec<u8>, Vec<u8>>,
416 /// MyType::translate_key_value,
451 /// MyType::translate_key_value,
417 /// Option<(PyBytes, PyBytes)>
452 /// Option<(PyBytes, PyBytes)>
418 /// );
453 /// );
@@ -421,18 +456,16 b' macro_rules! py_shared_iterator {'
421 (
456 (
422 $name: ident,
457 $name: ident,
423 $leaked: ty,
458 $leaked: ty,
424 $iterator_type: ty,
425 $success_func: expr,
459 $success_func: expr,
426 $success_type: ty
460 $success_type: ty
427 ) => {
461 ) => {
428 py_class!(pub class $name |py| {
462 py_class!(pub class $name |py| {
429 data inner: RefCell<Option<$leaked>>;
463 data inner: RefCell<Option<$leaked>>;
430 data it: RefCell<$iterator_type>;
431
464
432 def __next__(&self) -> PyResult<$success_type> {
465 def __next__(&self) -> PyResult<$success_type> {
433 let mut inner_opt = self.inner(py).borrow_mut();
466 let mut inner_opt = self.inner(py).borrow_mut();
434 if inner_opt.is_some() {
467 if let Some(leaked) = inner_opt.as_mut() {
435 match self.it(py).borrow_mut().next() {
468 match leaked.get_mut(py).next() {
436 None => {
469 None => {
437 // replace Some(inner) by None, drop $leaked
470 // replace Some(inner) by None, drop $leaked
438 inner_opt.take();
471 inner_opt.take();
@@ -456,12 +489,10 b' macro_rules! py_shared_iterator {'
456 pub fn from_inner(
489 pub fn from_inner(
457 py: Python,
490 py: Python,
458 leaked: $leaked,
491 leaked: $leaked,
459 it: $iterator_type
460 ) -> PyResult<Self> {
492 ) -> PyResult<Self> {
461 Self::create_instance(
493 Self::create_instance(
462 py,
494 py,
463 RefCell::new(Some(leaked)),
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