Mercurial > public > mercurial-scm > hg
comparison rust/hg-cpython/src/ref_sharing.rs @ 43285:ffc1fbd7d1f5
rust-cpython: make PyLeakedRef operations relatively safe
This patch encapsulates the access to the leaked reference to make most
leaked-ref operations safe. The only exception is leaked_ref.map(). I
couldn't figure out how to allow arbitrary map operation safely over an
unsafe static reference. See the docstring and inline comment for details.
Now leak_immutable() can be safely implemented as the PyLeakedRef owns
its inner data.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sun, 15 Sep 2019 22:19:10 +0900 |
parents | ce6dd1cee4c8 |
children | f8c114f20d2d |
comparison
equal
deleted
inserted
replaced
43284:ce6dd1cee4c8 | 43285:ffc1fbd7d1f5 |
---|---|
185 | 185 |
186 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> { | 186 pub fn borrow_mut(&self) -> PyResult<PyRefMut<'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 |
211 /// Holds a mutable reference to data shared between Python and Rust. | 206 /// Holds a mutable reference to data shared between Python and Rust. |
212 pub struct PyRefMut<'a, T> { | 207 pub struct PyRefMut<'a, T> { |
314 } | 309 } |
315 }; | 310 }; |
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 } |
319 | |
320 // DO NOT implement Deref for PyLeakedRef<T>! Dereferencing PyLeakedRef | |
321 // without taking Python GIL wouldn't be safe. | |
327 | 322 |
328 impl<T> PyLeakedRef<T> { | 323 impl<T> PyLeakedRef<T> { |
329 /// # Safety | 324 /// # Safety |
330 /// | 325 /// |
331 /// The `py_shared_state` must be owned by the `inner` Python object. | 326 /// The `py_shared_state` must be owned by the `inner` Python object. |
336 inner: &PyObject, | 331 inner: &PyObject, |
337 data: T, | 332 data: 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, |
339 } | |
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, | |
344 } | 380 } |
345 } | 381 } |
346 } | 382 } |
347 | 383 |
348 impl<T> Drop for PyLeakedRef<T> { | 384 impl<T> Drop for PyLeakedRef<T> { |
350 // py_shared_state should be alive since we do have | 386 // py_shared_state should be alive since we do have |
351 // a Python reference to the owner object. Taking GIL makes | 387 // a Python reference to the owner object. Taking GIL makes |
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 } |
358 } | 397 } |
359 } | 398 } |
381 /// | 420 /// |
382 /// py_class!(pub class MyType |py| { | 421 /// py_class!(pub class MyType |py| { |
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 /// }); |
396 /// | 432 /// |
397 /// impl MyType { | 433 /// impl MyType { |
409 /// | 445 /// |
410 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef); | 446 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef); |
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 /// ); |
419 /// ``` | 454 /// ``` |
420 macro_rules! py_shared_iterator { | 455 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(); |
439 Ok(None) | 472 Ok(None) |
440 } | 473 } |
454 | 487 |
455 impl $name { | 488 impl $name { |
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 } |
468 }; | 499 }; |
469 } | 500 } |