@@ -8,7 +8,7 @@ use std::{
88 fmt:: Debug ,
99 sync:: {
1010 atomic:: { AtomicBool , Ordering } ,
11- Arc , OnceLock ,
11+ Arc , OnceLock , Weak ,
1212 } ,
1313} ;
1414
@@ -28,47 +28,50 @@ use crate::{build_tools::py_schema_err, py_gc::PyGcTraverse};
2828/// They get indexed by a ReferenceId, which are integer identifiers
2929/// that are handed out and managed by DefinitionsBuilder when the Schema{Validator,Serializer}
3030/// gets build.
31- #[ derive( Clone ) ]
3231pub struct Definitions < T > ( AHashMap < Arc < String > , Definition < T > > ) ;
3332
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 > ,
4036}
4137
4238/// Reference to a definition.
4339pub 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 > ,
4645}
4746
4847// DefinitionRef can always be cloned (#[derive(Clone)] would require T: Clone)
4948impl < T > Clone for DefinitionRef < T > {
5049 fn clone ( & self ) -> Self {
5150 Self {
52- name : self . name . clone ( ) ,
51+ reference : self . reference . clone ( ) ,
5352 value : self . value . clone ( ) ,
53+ name : self . name . clone ( ) ,
5454 }
5555 }
5656}
5757
5858impl < T > DefinitionRef < T > {
5959 pub fn id ( & self ) -> usize {
60- Arc :: as_ptr ( & self . value . 0 ) as usize
60+ Weak :: as_ptr ( & self . value ) as usize
6161 }
6262
6363 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) ) ,
6669 None => "..." ,
6770 }
6871 }
6972
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 ( ) ) )
7275 }
7376}
7477
@@ -96,15 +99,9 @@ impl<T: Debug> Debug for Definitions<T> {
9699 }
97100}
98101
99- impl < T > Clone for Definition < T > {
100- fn clone ( & self ) -> Self {
101- Self ( self . 0 . clone ( ) )
102- }
103- }
104-
105102impl < T : Debug > Debug for Definition < T > {
106103 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
107- match self . 0 . value . get ( ) {
104+ match self . value . get ( ) {
108105 Some ( value) => value. fmt ( f) ,
109106 None => "..." . fmt ( f) ,
110107 }
@@ -113,7 +110,7 @@ impl<T: Debug> Debug for Definition<T> {
113110
114111impl < T : PyGcTraverse > PyGcTraverse for DefinitionRef < T > {
115112 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 ( ) ) {
117114 value. py_gc_traverse ( visit) ?;
118115 }
119116 Ok ( ( ) )
@@ -123,15 +120,15 @@ impl<T: PyGcTraverse> PyGcTraverse for DefinitionRef<T> {
123120impl < T : PyGcTraverse > PyGcTraverse for Definitions < T > {
124121 fn py_gc_traverse ( & self , visit : & PyVisit < ' _ > ) -> Result < ( ) , PyTraverseError > {
125122 for value in self . 0 . values ( ) {
126- if let Some ( value) = value. 0 . value . get ( ) {
123+ if let Some ( value) = value. value . get ( ) {
127124 value. py_gc_traverse ( visit) ?;
128125 }
129126 }
130127 Ok ( ( ) )
131128 }
132129}
133130
134- #[ derive( Clone , Debug ) ]
131+ #[ derive( Debug ) ]
135132pub struct DefinitionsBuilder < T > {
136133 definitions : Definitions < T > ,
137134}
@@ -148,45 +145,48 @@ impl<T: std::fmt::Debug> DefinitionsBuilder<T> {
148145 // We either need a String copy or two hashmap lookups
149146 // Neither is better than the other
150147 // 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 ( ) ) {
153150 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+ } ) ,
158155 } ;
159156 DefinitionRef {
160- name,
161- value : value. clone ( ) ,
157+ reference,
158+ value : Arc :: downgrade ( & value. value ) ,
159+ name : value. name . clone ( ) ,
162160 }
163161 }
164162
165163 /// Add a definition, returning the ReferenceId that maps to it
166164 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 ( ) ) {
169167 Entry :: Occupied ( entry) => {
170168 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 ) ,
174172 }
175173 }
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+ } ) ,
182178 } ;
183- Ok ( DefinitionRef { name, value } )
179+ Ok ( DefinitionRef {
180+ reference,
181+ value : Arc :: downgrade ( & value. value ) ,
182+ name : value. name . clone ( ) ,
183+ } )
184184 }
185185
186186 /// Consume this Definitions into a vector of items, indexed by each items ReferenceId
187187 pub fn finish ( self ) -> PyResult < Definitions < T > > {
188188 for ( reference, def) in & self . definitions . 0 {
189- if def. 0 . value . get ( ) . is_none ( ) {
189+ if def. value . get ( ) . is_none ( ) {
190190 return py_schema_err ! ( "Definitions error: definition `{}` was never filled" , reference) ;
191191 }
192192 }
0 commit comments