33use std:: {
44 collections:: HashMap ,
55 marker:: PhantomData ,
6- mem:: MaybeUninit ,
7- sync:: atomic:: { AtomicBool , AtomicPtr , Ordering } ,
6+ sync:: atomic:: { AtomicPtr , Ordering } ,
87} ;
98
9+ use once_cell:: sync:: OnceCell ;
10+
1011use crate :: {
1112 builders:: FunctionBuilder ,
1213 exception:: PhpException ,
@@ -37,6 +38,10 @@ pub trait RegisteredClass: Sized + 'static {
3738 /// The key should be the name of the property and the value should be a
3839 /// reference to the property with reference to `self`. The value is a
3940 /// [`Property`].
41+ ///
42+ /// Instead of using this method directly, you should access the properties
43+ /// through the [`ClassMetadata::get_properties`] function, which builds the
44+ /// hashmap one and stores it in memory.
4045 fn get_properties < ' a > ( ) -> HashMap < & ' static str , Property < ' a , Self > > ;
4146}
4247
@@ -81,8 +86,8 @@ impl<T> From<T> for ConstructorResult<T> {
8186/// Stores the class entry and handlers for a Rust type which has been exported
8287/// to PHP. Usually allocated statically.
8388pub struct ClassMetadata < T > {
84- handlers_init : AtomicBool ,
85- handlers : MaybeUninit < ZendObjectHandlers > ,
89+ handlers : OnceCell < ZendObjectHandlers > ,
90+ properties : OnceCell < HashMap < & ' static str , Property < ' static , T > > > ,
8691 ce : AtomicPtr < ClassEntry > ,
8792
8893 // `AtomicPtr` is used here because it is `Send + Sync`.
@@ -95,8 +100,8 @@ impl<T> ClassMetadata<T> {
95100 /// Creates a new class metadata instance.
96101 pub const fn new ( ) -> Self {
97102 Self {
98- handlers_init : AtomicBool :: new ( false ) ,
99- handlers : MaybeUninit :: uninit ( ) ,
103+ handlers : OnceCell :: new ( ) ,
104+ properties : OnceCell :: new ( ) ,
100105 ce : AtomicPtr :: new ( std:: ptr:: null_mut ( ) ) ,
101106 phantom : PhantomData ,
102107 }
@@ -107,10 +112,7 @@ impl<T: RegisteredClass> ClassMetadata<T> {
107112 /// Returns an immutable reference to the object handlers contained inside
108113 /// the class metadata.
109114 pub fn handlers ( & self ) -> & ZendObjectHandlers {
110- self . check_handlers ( ) ;
111-
112- // SAFETY: `check_handlers` guarantees that `handlers` has been initialized.
113- unsafe { & * self . handlers . as_ptr ( ) }
115+ self . handlers . get_or_init ( ZendObjectHandlers :: new :: < T > )
114116 }
115117
116118 /// Checks if the class entry has been stored, returning a boolean.
@@ -142,20 +144,23 @@ impl<T: RegisteredClass> ClassMetadata<T> {
142144 /// Panics if the class entry has already been set in the class metadata.
143145 /// This function should only be called once.
144146 pub fn set_ce ( & self , ce : & ' static mut ClassEntry ) {
145- if !self . ce . load ( Ordering :: SeqCst ) . is_null ( ) {
146- panic ! ( "Class entry has already been set." ) ;
147- }
148-
149- self . ce . store ( ce, Ordering :: SeqCst ) ;
147+ self . ce
148+ . compare_exchange (
149+ std:: ptr:: null_mut ( ) ,
150+ ce,
151+ Ordering :: SeqCst ,
152+ Ordering :: Relaxed ,
153+ )
154+ . expect ( "Class entry has already been set" ) ;
150155 }
151156
152- /// Checks if the handlers have been initialized, and initializes them if
153- /// they are not .
154- fn check_handlers ( & self ) {
155- if ! self . handlers_init . load ( Ordering :: SeqCst ) {
156- // SAFETY: `MaybeUninit` has the same size as the handlers.
157- unsafe { ZendObjectHandlers :: init :: < T > ( self . handlers . as_ptr ( ) as * mut _ ) } ;
158- self . handlers_init . store ( true , Ordering :: SeqCst ) ;
159- }
157+ /// Retrieves a reference to the hashmap storing the classes property
158+ /// accessors .
159+ ///
160+ /// # Returns
161+ ///
162+ /// Immutable reference to the properties hashmap.
163+ pub fn get_properties ( & self ) -> & HashMap < & ' static str , Property < ' static , T > > {
164+ self . properties . get_or_init ( T :: get_properties )
160165 }
161166}
0 commit comments