3
3
use std:: {
4
4
collections:: HashMap ,
5
5
marker:: PhantomData ,
6
- mem:: MaybeUninit ,
7
- sync:: atomic:: { AtomicBool , AtomicPtr , Ordering } ,
6
+ sync:: atomic:: { AtomicPtr , Ordering } ,
8
7
} ;
9
8
9
+ use once_cell:: sync:: OnceCell ;
10
+
10
11
use crate :: {
11
12
builders:: FunctionBuilder ,
12
13
exception:: PhpException ,
@@ -37,6 +38,10 @@ pub trait RegisteredClass: Sized + 'static {
37
38
/// The key should be the name of the property and the value should be a
38
39
/// reference to the property with reference to `self`. The value is a
39
40
/// [`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.
40
45
fn get_properties < ' a > ( ) -> HashMap < & ' static str , Property < ' a , Self > > ;
41
46
}
42
47
@@ -81,8 +86,8 @@ impl<T> From<T> for ConstructorResult<T> {
81
86
/// Stores the class entry and handlers for a Rust type which has been exported
82
87
/// to PHP. Usually allocated statically.
83
88
pub struct ClassMetadata < T > {
84
- handlers_init : AtomicBool ,
85
- handlers : MaybeUninit < ZendObjectHandlers > ,
89
+ handlers : OnceCell < ZendObjectHandlers > ,
90
+ properties : OnceCell < HashMap < & ' static str , Property < ' static , T > > > ,
86
91
ce : AtomicPtr < ClassEntry > ,
87
92
88
93
// `AtomicPtr` is used here because it is `Send + Sync`.
@@ -95,8 +100,8 @@ impl<T> ClassMetadata<T> {
95
100
/// Creates a new class metadata instance.
96
101
pub const fn new ( ) -> Self {
97
102
Self {
98
- handlers_init : AtomicBool :: new ( false ) ,
99
- handlers : MaybeUninit :: uninit ( ) ,
103
+ handlers : OnceCell :: new ( ) ,
104
+ properties : OnceCell :: new ( ) ,
100
105
ce : AtomicPtr :: new ( std:: ptr:: null_mut ( ) ) ,
101
106
phantom : PhantomData ,
102
107
}
@@ -107,10 +112,7 @@ impl<T: RegisteredClass> ClassMetadata<T> {
107
112
/// Returns an immutable reference to the object handlers contained inside
108
113
/// the class metadata.
109
114
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 > )
114
116
}
115
117
116
118
/// Checks if the class entry has been stored, returning a boolean.
@@ -142,20 +144,23 @@ impl<T: RegisteredClass> ClassMetadata<T> {
142
144
/// Panics if the class entry has already been set in the class metadata.
143
145
/// This function should only be called once.
144
146
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" ) ;
150
155
}
151
156
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 )
160
165
}
161
166
}
0 commit comments