@@ -8,7 +8,7 @@ use std::{
8
8
fmt:: Debug ,
9
9
sync:: {
10
10
atomic:: { AtomicBool , Ordering } ,
11
- Arc , OnceLock ,
11
+ Arc , OnceLock , Weak ,
12
12
} ,
13
13
} ;
14
14
@@ -28,47 +28,50 @@ use crate::{build_tools::py_schema_err, py_gc::PyGcTraverse};
28
28
/// They get indexed by a ReferenceId, which are integer identifiers
29
29
/// that are handed out and managed by DefinitionsBuilder when the Schema{Validator,Serializer}
30
30
/// gets build.
31
- #[ derive( Clone ) ]
32
31
pub struct Definitions < T > ( AHashMap < Arc < String > , Definition < T > > ) ;
33
32
34
- /// Internal type which contains a definition to be filled
35
- pub struct Definition < T > ( Arc < DefinitionInner < T > > ) ;
36
-
37
- struct DefinitionInner < T > {
38
- value : OnceLock < T > ,
39
- name : LazyName ,
33
+ struct Definition < T > {
34
+ value : Arc < OnceLock < T > > ,
35
+ name : Arc < LazyName > ,
40
36
}
41
37
42
38
/// Reference to a definition.
43
39
pub struct DefinitionRef < T > {
44
- name : Arc < String > ,
45
- value : Definition < T > ,
40
+ reference : Arc < String > ,
41
+ // We use a weak reference to the definition to avoid a reference cycle
42
+ // when recursive definitions are used.
43
+ value : Weak < OnceLock < T > > ,
44
+ name : Arc < LazyName > ,
46
45
}
47
46
48
47
// DefinitionRef can always be cloned (#[derive(Clone)] would require T: Clone)
49
48
impl < T > Clone for DefinitionRef < T > {
50
49
fn clone ( & self ) -> Self {
51
50
Self {
52
- name : self . name . clone ( ) ,
51
+ reference : self . reference . clone ( ) ,
53
52
value : self . value . clone ( ) ,
53
+ name : self . name . clone ( ) ,
54
54
}
55
55
}
56
56
}
57
57
58
58
impl < T > DefinitionRef < T > {
59
59
pub fn id ( & self ) -> usize {
60
- Arc :: as_ptr ( & self . value . 0 ) as usize
60
+ Weak :: as_ptr ( & self . value ) as usize
61
61
}
62
62
63
63
pub fn get_or_init_name ( & self , init : impl FnOnce ( & T ) -> String ) -> & str {
64
- match self . value . 0 . value . get ( ) {
65
- Some ( value) => self . value . 0 . name . get_or_init ( || init ( value) ) ,
64
+ let Some ( definition) = self . value . upgrade ( ) else {
65
+ return "..." ;
66
+ } ;
67
+ match definition. get ( ) {
68
+ Some ( value) => self . name . get_or_init ( || init ( value) ) ,
66
69
None => "..." ,
67
70
}
68
71
}
69
72
70
- pub fn get ( & self ) -> Option < & T > {
71
- self . value . 0 . value . get ( )
73
+ pub fn read < R > ( & self , f : impl FnOnce ( Option < & T > ) -> R ) -> R {
74
+ f ( self . value . upgrade ( ) . as_ref ( ) . and_then ( | value| value . get ( ) ) )
72
75
}
73
76
}
74
77
@@ -96,15 +99,9 @@ impl<T: Debug> Debug for Definitions<T> {
96
99
}
97
100
}
98
101
99
- impl < T > Clone for Definition < T > {
100
- fn clone ( & self ) -> Self {
101
- Self ( self . 0 . clone ( ) )
102
- }
103
- }
104
-
105
102
impl < T : Debug > Debug for Definition < T > {
106
103
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
107
- match self . 0 . value . get ( ) {
104
+ match self . value . get ( ) {
108
105
Some ( value) => value. fmt ( f) ,
109
106
None => "..." . fmt ( f) ,
110
107
}
@@ -113,7 +110,7 @@ impl<T: Debug> Debug for Definition<T> {
113
110
114
111
impl < T : PyGcTraverse > PyGcTraverse for DefinitionRef < T > {
115
112
fn py_gc_traverse ( & self , visit : & PyVisit < ' _ > ) -> Result < ( ) , PyTraverseError > {
116
- if let Some ( value) = self . value . 0 . value . get ( ) {
113
+ if let Some ( value) = self . value . upgrade ( ) . as_ref ( ) . and_then ( |v| v . get ( ) ) {
117
114
value. py_gc_traverse ( visit) ?;
118
115
}
119
116
Ok ( ( ) )
@@ -123,15 +120,15 @@ impl<T: PyGcTraverse> PyGcTraverse for DefinitionRef<T> {
123
120
impl < T : PyGcTraverse > PyGcTraverse for Definitions < T > {
124
121
fn py_gc_traverse ( & self , visit : & PyVisit < ' _ > ) -> Result < ( ) , PyTraverseError > {
125
122
for value in self . 0 . values ( ) {
126
- if let Some ( value) = value. 0 . value . get ( ) {
123
+ if let Some ( value) = value. value . get ( ) {
127
124
value. py_gc_traverse ( visit) ?;
128
125
}
129
126
}
130
127
Ok ( ( ) )
131
128
}
132
129
}
133
130
134
- #[ derive( Clone , Debug ) ]
131
+ #[ derive( Debug ) ]
135
132
pub struct DefinitionsBuilder < T > {
136
133
definitions : Definitions < T > ,
137
134
}
@@ -148,45 +145,48 @@ impl<T: std::fmt::Debug> DefinitionsBuilder<T> {
148
145
// We either need a String copy or two hashmap lookups
149
146
// Neither is better than the other
150
147
// We opted for the easier outward facing API
151
- let name = Arc :: new ( reference. to_string ( ) ) ;
152
- let value = match self . definitions . 0 . entry ( name . clone ( ) ) {
148
+ let reference = Arc :: new ( reference. to_string ( ) ) ;
149
+ let value = match self . definitions . 0 . entry ( reference . clone ( ) ) {
153
150
Entry :: Occupied ( entry) => entry. into_mut ( ) ,
154
- Entry :: Vacant ( entry) => entry. insert ( Definition ( Arc :: new ( DefinitionInner {
155
- value : OnceLock :: new ( ) ,
156
- name : LazyName :: new ( ) ,
157
- } ) ) ) ,
151
+ Entry :: Vacant ( entry) => entry. insert ( Definition {
152
+ value : Arc :: new ( OnceLock :: new ( ) ) ,
153
+ name : Arc :: new ( LazyName :: new ( ) ) ,
154
+ } ) ,
158
155
} ;
159
156
DefinitionRef {
160
- name,
161
- value : value. clone ( ) ,
157
+ reference,
158
+ value : Arc :: downgrade ( & value. value ) ,
159
+ name : value. name . clone ( ) ,
162
160
}
163
161
}
164
162
165
163
/// Add a definition, returning the ReferenceId that maps to it
166
164
pub fn add_definition ( & mut self , reference : String , value : T ) -> PyResult < DefinitionRef < T > > {
167
- let name = Arc :: new ( reference) ;
168
- let value = match self . definitions . 0 . entry ( name . clone ( ) ) {
165
+ let reference = Arc :: new ( reference) ;
166
+ let value = match self . definitions . 0 . entry ( reference . clone ( ) ) {
169
167
Entry :: Occupied ( entry) => {
170
168
let definition = entry. into_mut ( ) ;
171
- match definition. 0 . value . set ( value) {
172
- Ok ( ( ) ) => definition. clone ( ) ,
173
- Err ( _) => return py_schema_err ! ( "Duplicate ref: `{}`" , name ) ,
169
+ match definition. value . set ( value) {
170
+ Ok ( ( ) ) => definition,
171
+ Err ( _) => return py_schema_err ! ( "Duplicate ref: `{}`" , reference ) ,
174
172
}
175
173
}
176
- Entry :: Vacant ( entry) => entry
177
- . insert ( Definition ( Arc :: new ( DefinitionInner {
178
- value : OnceLock :: from ( value) ,
179
- name : LazyName :: new ( ) ,
180
- } ) ) )
181
- . clone ( ) ,
174
+ Entry :: Vacant ( entry) => entry. insert ( Definition {
175
+ value : Arc :: new ( OnceLock :: from ( value) ) ,
176
+ name : Arc :: new ( LazyName :: new ( ) ) ,
177
+ } ) ,
182
178
} ;
183
- Ok ( DefinitionRef { name, value } )
179
+ Ok ( DefinitionRef {
180
+ reference,
181
+ value : Arc :: downgrade ( & value. value ) ,
182
+ name : value. name . clone ( ) ,
183
+ } )
184
184
}
185
185
186
186
/// Consume this Definitions into a vector of items, indexed by each items ReferenceId
187
187
pub fn finish ( self ) -> PyResult < Definitions < T > > {
188
188
for ( reference, def) in & self . definitions . 0 {
189
- if def. 0 . value . get ( ) . is_none ( ) {
189
+ if def. value . get ( ) . is_none ( ) {
190
190
return py_schema_err ! ( "Definitions error: definition `{}` was never filled" , reference) ;
191
191
}
192
192
}
0 commit comments