@@ -15,7 +15,7 @@ use std::mem::ManuallyDrop;
15
15
use std:: ops:: Deref ;
16
16
use std:: ptr:: { self , NonNull } ;
17
17
18
- // so, PyObjectRef is basically equivalent to `PyRc<PyGenericObject <dyn PyObjectPayload>>`, except it's
18
+ // so, PyObjectRef is basically equivalent to `PyRc<PyInner <dyn PyObjectPayload>>`, except it's
19
19
// only one pointer in width rather than 2. We do that by manually creating a vtable, and putting
20
20
// a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do.
21
21
// This can lead to faster code since there's just less data to pass around, as well as because of
@@ -32,18 +32,18 @@ use std::ptr::{self, NonNull};
32
32
// There has to be padding in the space between the 2 fields. But, if that field is a trait object
33
33
// (like `dyn PyObjectPayload`) we don't *know* how much padding there is between the `payload`
34
34
// field and the previous field. So, Rust has to consult the vtable to know the exact offset of
35
- // `payload` in `PyGenericObject <dyn PyObjectPayload>`, which has a huge performance impact when *every
35
+ // `payload` in `PyInner <dyn PyObjectPayload>`, which has a huge performance impact when *every
36
36
// single payload access* requires a vtable lookup. Thankfully, we're able to avoid that because of
37
37
// the way we use PyObjectRef, in that whenever we want to access the payload we (almost) always
38
38
// access it from a generic function. So, rather than doing
39
39
//
40
40
// - check vtable for payload offset
41
- // - get offset in PyGenericObject struct
41
+ // - get offset in PyInner struct
42
42
// - call as_any() method of PyObjectPayload
43
43
// - call downcast_ref() method of Any
44
44
// we can just do
45
45
// - check vtable that typeid matches
46
- // - pointer cast directly to *const PyGenericObject <T>
46
+ // - pointer cast directly to *const PyInner <T>
47
47
//
48
48
// and at that point the compiler can know the offset of `payload` for us because **we've given it a
49
49
// concrete type to work with before we ever access the `payload` field**
@@ -395,27 +395,17 @@ impl InstanceDict {
395
395
}
396
396
}
397
397
398
- /// This is an actual python object. It consists of a `typ` which is the
399
- /// python class, and carries some rust payload optionally. This rust
400
- /// payload can be a rust float or rust int in case of float and int objects.
401
- #[ repr( transparent) ]
402
- pub struct PyGenericObject < T > {
403
- inner : PyInner < T > ,
404
- }
405
-
406
- impl < T : PyObjectPayload > PyGenericObject < T > {
407
- #[ allow( clippy:: new_ret_no_self) ]
408
- pub fn new ( payload : T , typ : PyTypeRef , dict : Option < PyDictRef > ) -> PyObjectRef {
409
- let inner = PyInner {
398
+ impl < T : PyObjectPayload > PyInner < T > {
399
+ fn new ( payload : T , typ : PyTypeRef , dict : Option < PyDictRef > ) -> Box < Self > {
400
+ Box :: new ( PyInner {
410
401
refcount : RefCount :: new ( ) ,
411
402
typeid : TypeId :: of :: < T > ( ) ,
412
403
vtable : PyObjVTable :: of :: < T > ( ) ,
413
404
typ : PyRwLock :: new ( typ) ,
414
405
dict : dict. map ( InstanceDict :: new) ,
415
406
weaklist : WeakRefList :: new ( ) ,
416
407
payload,
417
- } ;
418
- PyObjectRef :: new ( PyGenericObject { inner } )
408
+ } )
419
409
}
420
410
}
421
411
@@ -501,13 +491,6 @@ impl PyObjectRef {
501
491
}
502
492
}
503
493
504
- fn new < T : PyObjectPayload > ( value : PyGenericObject < T > ) -> Self {
505
- let inner: * const PyInner < T > = Box :: into_raw ( Box :: new ( value. inner ) ) ;
506
- Self {
507
- ptr : unsafe { NonNull :: new_unchecked ( inner as * mut PyObject ) } ,
508
- }
509
- }
510
-
511
494
/// Attempt to downcast this reference to a subclass.
512
495
///
513
496
/// If the downcast fails, the original ref is returned in as `Err` so
@@ -712,12 +695,9 @@ impl PyObject {
712
695
pub fn as_raw ( & self ) -> * const PyObject {
713
696
self
714
697
}
715
- }
716
698
717
- impl PyObjectRef {
718
- #[ inline( never) ]
719
- #[ cold]
720
- fn drop_slow ( & mut self ) {
699
+ #[ inline]
700
+ fn drop_slow_inner ( & self ) -> Result < ( ) , ( ) > {
721
701
// CPython-compatible drop implementation
722
702
if let Some ( slot_del) = self . class ( ) . mro_find_map ( |cls| cls. slots . del . load ( ) ) {
723
703
let ret = crate :: vm:: thread:: with_vm ( self , |vm| {
@@ -731,7 +711,7 @@ impl PyObjectRef {
731
711
// the decref right above set refcount back to 0
732
712
Some ( true ) => { }
733
713
// we've been resurrected by __del__
734
- Some ( false ) => return ,
714
+ Some ( false ) => return Err ( ( ) ) ,
735
715
None => {
736
716
warn ! ( "couldn't run __del__ method for object" )
737
717
}
@@ -741,8 +721,20 @@ impl PyObjectRef {
741
721
wrl. clear ( ) ;
742
722
}
743
723
744
- let drop_dealloc = self . 0 . vtable . drop_dealloc ;
745
- unsafe { drop_dealloc ( self . ptr . as_ptr ( ) ) }
724
+ Ok ( ( ) )
725
+ }
726
+
727
+ /// Can only be called when refcount has dropped to zero. `ptr` must be valid
728
+ #[ inline( never) ]
729
+ #[ cold]
730
+ unsafe fn drop_slow ( ptr : NonNull < PyObject > ) {
731
+ if let Err ( ( ) ) = ptr. as_ref ( ) . drop_slow_inner ( ) {
732
+ // abort drop for whatever reason
733
+ return ;
734
+ }
735
+ let drop_dealloc = ptr. as_ref ( ) . 0 . vtable . drop_dealloc ;
736
+ // call drop only when there are no references in scope - stacked borrows stuff
737
+ drop_dealloc ( ptr. as_ptr ( ) )
746
738
}
747
739
}
748
740
@@ -805,7 +797,7 @@ impl PyObjectWeak {
805
797
impl Drop for PyObjectRef {
806
798
fn drop ( & mut self ) {
807
799
if self . 0 . refcount . dec ( ) {
808
- self . drop_slow ( )
800
+ unsafe { PyObject :: drop_slow ( self . ptr ) }
809
801
}
810
802
}
811
803
}
@@ -870,9 +862,9 @@ impl<T: PyObjectPayload> ToOwned for PyObjectView<T> {
870
862
871
863
#[ inline( always) ]
872
864
fn to_owned ( & self ) -> Self :: Owned {
865
+ self . 0 . refcount . inc ( ) ;
873
866
PyRef {
874
- obj : self . as_object ( ) . to_owned ( ) ,
875
- _marker : PhantomData ,
867
+ ptr : NonNull :: from ( self ) ,
876
868
}
877
869
}
878
870
}
@@ -911,9 +903,14 @@ impl<T: PyObjectPayload> fmt::Debug for PyObjectView<T> {
911
903
/// where a reference to the same object must be returned.
912
904
#[ repr( transparent) ]
913
905
pub struct PyRef < T : PyObjectPayload > {
914
- // invariant: this obj must always have payload of type T
915
- obj : PyObjectRef ,
916
- _marker : PhantomData < T > ,
906
+ ptr : NonNull < PyObjectView < T > > ,
907
+ }
908
+
909
+ cfg_if:: cfg_if! {
910
+ if #[ cfg( feature = "threading" ) ] {
911
+ unsafe impl <T : PyObjectPayload > Send for PyRef <T > { }
912
+ unsafe impl <T : PyObjectPayload > Sync for PyRef <T > { }
913
+ }
917
914
}
918
915
919
916
impl < T : PyObjectPayload > fmt:: Debug for PyRef < T > {
@@ -922,6 +919,15 @@ impl<T: PyObjectPayload> fmt::Debug for PyRef<T> {
922
919
}
923
920
}
924
921
922
+ impl < T : PyObjectPayload > Drop for PyRef < T > {
923
+ #[ inline]
924
+ fn drop ( & mut self ) {
925
+ if self . 0 . refcount . dec ( ) {
926
+ unsafe { PyObject :: drop_slow ( self . ptr . cast :: < PyObject > ( ) ) }
927
+ }
928
+ }
929
+ }
930
+
925
931
impl < T : PyObjectPayload > Clone for PyRef < T > {
926
932
#[ inline( always) ]
927
933
fn clone ( & self ) -> Self {
@@ -932,43 +938,47 @@ impl<T: PyObjectPayload> Clone for PyRef<T> {
932
938
impl < T : PyObjectPayload > PyRef < T > {
933
939
unsafe fn from_raw ( raw : * const PyObjectView < T > ) -> Self {
934
940
Self {
935
- obj : PyObjectRef :: from_raw ( raw as _ ) ,
936
- _marker : PhantomData ,
941
+ ptr : NonNull :: new_unchecked ( raw as * mut _ ) ,
937
942
}
938
943
}
939
944
940
945
/// Safety: payload type of `obj` must be `T`
946
+ #[ inline]
941
947
unsafe fn from_obj_unchecked ( obj : PyObjectRef ) -> Self {
942
948
debug_assert ! ( obj. payload_is:: <T >( ) ) ;
949
+ let obj = ManuallyDrop :: new ( obj) ;
943
950
Self {
944
- obj,
945
- _marker : PhantomData ,
951
+ ptr : obj. ptr . cast ( ) ,
946
952
}
947
953
}
948
954
949
- // ideally we'd be able to define this in pyobject.rs, but method visibility rules are weird
955
+ # [ inline ]
950
956
pub fn new_ref ( payload : T , typ : crate :: builtins:: PyTypeRef , dict : Option < PyDictRef > ) -> Self {
951
- let obj = PyGenericObject :: new ( payload, typ, dict) ;
952
- // SAFETY: we just created the object from a payload of type T
953
- unsafe { Self :: from_obj_unchecked ( obj) }
957
+ let inner = Box :: into_raw ( PyInner :: new ( payload, typ, dict) ) ;
958
+ Self {
959
+ ptr : unsafe { NonNull :: new_unchecked ( inner. cast :: < PyObjectView < T > > ( ) ) } ,
960
+ }
954
961
}
955
962
}
956
963
957
964
impl < T > PyObjectWrap for PyRef < T >
958
965
where
959
966
T : PyObjectPayload ,
960
967
{
968
+ #[ inline]
961
969
fn into_object ( self ) -> PyObjectRef {
962
- self . obj
970
+ let me = ManuallyDrop :: new ( self ) ;
971
+ PyObjectRef { ptr : me. ptr . cast ( ) }
963
972
}
964
973
}
965
974
966
975
impl < T > AsRef < PyObject > for PyRef < T >
967
976
where
968
977
T : PyObjectPayload ,
969
978
{
979
+ #[ inline( always) ]
970
980
fn as_ref ( & self ) -> & PyObject {
971
- & self . obj
981
+ ( * * self ) . as_object ( )
972
982
}
973
983
}
974
984
@@ -987,8 +997,9 @@ where
987
997
{
988
998
type Target = PyObjectView < T > ;
989
999
1000
+ #[ inline( always) ]
990
1001
fn deref ( & self ) -> & PyObjectView < T > {
991
- unsafe { & * ( & * self . obj as * const PyObject as * const PyObjectView < T > ) }
1002
+ unsafe { self . ptr . as_ref ( ) }
992
1003
}
993
1004
}
994
1005
@@ -1000,10 +1011,10 @@ pub struct PyWeakRef<T: PyObjectPayload> {
1000
1011
1001
1012
impl < T : PyObjectPayload > PyWeakRef < T > {
1002
1013
pub fn upgrade ( & self ) -> Option < PyRef < T > > {
1003
- self . weak . upgrade ( ) . map ( |obj| PyRef {
1004
- obj ,
1005
- _marker : PhantomData ,
1006
- } )
1014
+ self . weak
1015
+ . upgrade ( )
1016
+ // SAFETY: PyWeakRef<T> was always created from a PyRef<T>, so the object is T
1017
+ . map ( |obj| unsafe { PyRef :: from_obj_unchecked ( obj ) } )
1007
1018
}
1008
1019
}
1009
1020
0 commit comments