1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python};
use std::os::raw::{c_int, c_void};
#[repr(transparent)]
pub struct PyTraverseError(c_int);
pub trait PyGCProtocol<'p>: PyClass {
fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>;
fn __clear__(&'p mut self);
}
pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {}
pub trait PyGCClearProtocol<'p>: PyGCProtocol<'p> {}
#[derive(Default)]
pub struct PyGCMethods {
pub tp_traverse: Option<ffi::traverseproc>,
pub tp_clear: Option<ffi::inquiry>,
}
#[doc(hidden)]
impl PyGCMethods {
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
type_object.tp_traverse = self.tp_traverse;
type_object.tp_clear = self.tp_clear;
}
pub fn set_traverse<T>(&mut self)
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
self.tp_traverse = tp_traverse::<T>();
}
pub fn set_clear<T>(&mut self)
where
T: for<'p> PyGCClearProtocol<'p>,
{
self.tp_clear = tp_clear::<T>();
}
}
#[derive(Copy, Clone)]
pub struct PyVisit<'p> {
visit: ffi::visitproc,
arg: *mut c_void,
_py: Python<'p>,
}
impl<'p> PyVisit<'p> {
pub fn call<T>(&self, obj: &T) -> Result<(), PyTraverseError>
where
T: AsPyPointer,
{
let r = unsafe { (self.visit)(obj.as_ptr(), self.arg) };
if r == 0 {
Ok(())
} else {
Err(PyTraverseError(r))
}
}
}
fn tp_traverse<T>() -> Option<ffi::traverseproc>
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
unsafe extern "C" fn tp_traverse<T>(
slf: *mut ffi::PyObject,
visit: ffi::visitproc,
arg: *mut c_void,
) -> c_int
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
let pool = crate::GILPool::new();
let py = pool.python();
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
let visit = PyVisit {
visit,
arg,
_py: py,
};
let borrow = slf.try_borrow();
if let Ok(borrow) = borrow {
match borrow.__traverse__(visit) {
Ok(()) => 0,
Err(PyTraverseError(code)) => code,
}
} else {
0
}
}
Some(tp_traverse::<T>)
}
fn tp_clear<T>() -> Option<ffi::inquiry>
where
T: for<'p> PyGCClearProtocol<'p>,
{
unsafe extern "C" fn tp_clear<T>(slf: *mut ffi::PyObject) -> c_int
where
T: for<'p> PyGCClearProtocol<'p>,
{
let pool = crate::GILPool::new();
let py = pool.python();
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
slf.borrow_mut().__clear__();
0
}
Some(tp_clear::<T>)
}