@@ -64,12 +64,13 @@ use zerogc::{GcSafe, Trace, GcVisitor};
64
64
use zerogc_context:: utils:: { ThreadId , MemorySize } ;
65
65
66
66
use crate :: alloc:: { SmallArenaList , SmallArena } ;
67
- use crate :: layout:: { StaticGcType , GcType , SimpleVecRepr , DynamicObj , StaticVecType , SimpleMarkData , SimpleMarkDataSnapshot , GcHeader , BigGcObject , HeaderLayout , GcArrayHeader , GcVecHeader } ;
67
+ use crate :: layout:: { StaticGcType , GcType , SimpleVecRepr , DynamicObj , StaticVecType , SimpleMarkData , SimpleMarkDataSnapshot , GcHeader , BigGcObject , HeaderLayout , GcArrayHeader , GcVecHeader , GcTypeLayout } ;
68
68
69
69
use zerogc_context:: collector:: { RawSimpleAlloc , RawCollectorImpl } ;
70
70
use zerogc_context:: handle:: { GcHandleList , RawHandleImpl } ;
71
71
use zerogc_context:: { CollectionManager as AbstractCollectionManager , RawContext as AbstractRawContext , CollectorContext } ;
72
72
use zerogc:: vec:: { GcRawVec } ;
73
+ use std:: cell:: Cell ;
73
74
74
75
#[ cfg( feature = "small-object-arenas" ) ]
75
76
mod alloc;
@@ -116,6 +117,9 @@ impl Default for GcConfig {
116
117
}
117
118
}
118
119
120
+ /// The alignment of the singleton empty vector
121
+ const EMPTY_VEC_ALIGNMENT : usize = std:: mem:: align_of :: < usize > ( ) ;
122
+
119
123
#[ cfg( feature = "sync" ) ]
120
124
type RawContext < C > = zerogc_context:: state:: sync:: RawContext < C > ;
121
125
#[ cfg( feature = "sync" ) ]
@@ -166,20 +170,40 @@ unsafe impl RawSimpleAlloc for RawSimpleCollector {
166
170
( context. collector ( ) . id ( ) , ptr. cast ( ) )
167
171
}
168
172
173
+ #[ inline]
174
+ fn alloc_vec < ' gc , T > ( context : & ' gc CollectorContext < Self > ) -> GcVec < ' gc , T > where T : GcSafe + ' gc {
175
+ if std:: mem:: align_of :: < T > ( ) > EMPTY_VEC_ALIGNMENT {
176
+ // We have to do an actual allocation because we want higher alignment :(
177
+ return Self :: alloc_vec_with_capacity ( context, 0 )
178
+ }
179
+ let header = context. collector ( ) . heap . empty_vec ( ) ;
180
+ // NOTE: Assuming header is already initialized
181
+ unsafe {
182
+ debug_assert_eq ! ( ( * header) . len. get( ) , 0 ) ;
183
+ debug_assert_eq ! ( ( * header) . capacity, 0 ) ;
184
+ }
185
+ let id = context. collector ( ) . id ( ) ;
186
+ let ptr = unsafe { NonNull :: new_unchecked ( ( header as * mut u8 )
187
+ . add ( GcVecHeader :: LAYOUT . value_offset ( EMPTY_VEC_ALIGNMENT ) ) ) } ;
188
+ GcVec {
189
+ raw : unsafe { GcRawVec :: from_repr ( Gc :: from_raw ( id, ptr. cast ( ) ) ) } ,
190
+ context
191
+ }
192
+ }
193
+
169
194
fn alloc_vec_with_capacity < ' gc , T > ( context : & ' gc CollectorContext < Self > , capacity : usize ) -> GcVec < ' gc , T > where T : GcSafe + ' gc {
170
- let ptr = if capacity == 0 {
171
- NonNull :: dangling ( )
172
- } else {
173
- let ( header, value_ptr) = context. collector ( ) . heap . allocator . alloc_layout (
174
- GcVecHeader :: LAYOUT ,
175
- SimpleVecRepr :: < T > :: layout ( capacity) ,
176
- <T as StaticVecType >:: STATIC_VEC_TYPE
177
- ) ;
178
- unsafe {
179
- ( * header) . capacity = capacity;
180
- ( * header) . len . set ( 0 ) ;
181
- NonNull :: new_unchecked ( value_ptr as * mut SimpleVecRepr < T > )
182
- }
195
+ if capacity == 0 && std:: mem:: align_of :: < T > ( ) <= EMPTY_VEC_ALIGNMENT {
196
+ return Self :: alloc_vec ( context)
197
+ }
198
+ let ( header, value_ptr) = context. collector ( ) . heap . allocator . alloc_layout (
199
+ GcVecHeader :: LAYOUT ,
200
+ SimpleVecRepr :: < T > :: layout ( capacity) ,
201
+ <T as StaticVecType >:: STATIC_VEC_TYPE
202
+ ) ;
203
+ let ptr = unsafe {
204
+ ( * header) . capacity = capacity;
205
+ ( * header) . len . set ( 0 ) ;
206
+ NonNull :: new_unchecked ( value_ptr as * mut SimpleVecRepr < T > )
183
207
} ;
184
208
let id = context. collector ( ) . id ( ) ;
185
209
GcVec {
@@ -241,7 +265,8 @@ unsafe impl DynTrace for GcHandleListWrapper {
241
265
struct GcHeap {
242
266
config : Arc < GcConfig > ,
243
267
threshold : AtomicUsize ,
244
- allocator : SimpleAlloc
268
+ allocator : SimpleAlloc ,
269
+ cached_empty_vec : Cell < Option < * mut GcVecHeader > >
245
270
}
246
271
impl GcHeap {
247
272
fn new ( config : Arc < GcConfig > ) -> GcHeap {
@@ -252,7 +277,43 @@ impl GcHeap {
252
277
config. initial_threshold
253
278
} ) ,
254
279
allocator : SimpleAlloc :: new ( ) ,
255
- config
280
+ config, cached_empty_vec : Cell :: new ( None )
281
+ }
282
+ }
283
+ #[ inline]
284
+ pub fn empty_vec ( & self ) -> * mut GcVecHeader {
285
+ match self . cached_empty_vec . get ( ) {
286
+ Some ( cached) => cached,
287
+ None => {
288
+ let res = self . create_empty_vec ( ) ;
289
+ self . cached_empty_vec . set ( Some ( self . create_empty_vec ( ) ) ) ;
290
+ res
291
+ }
292
+ }
293
+ }
294
+ #[ cold]
295
+ fn create_empty_vec < ' gc > ( & self ) -> * mut GcVecHeader {
296
+ const DUMMY_LAYOUT : Layout = unsafe { Layout :: from_size_align_unchecked (
297
+ 0 , EMPTY_VEC_ALIGNMENT
298
+ ) } ;
299
+ const DUMMY_TYPE : GcType = GcType {
300
+ layout : GcTypeLayout :: Vec {
301
+ element_layout : DUMMY_LAYOUT ,
302
+ } ,
303
+ value_offset_from_common_header : GcVecHeader :: LAYOUT
304
+ . value_offset_from_common_header ( EMPTY_VEC_ALIGNMENT ) ,
305
+ drop_func : None ,
306
+ trace_func : None
307
+ } ;
308
+ let ( header, _) = self . allocator . alloc_layout (
309
+ GcVecHeader :: LAYOUT ,
310
+ DUMMY_LAYOUT ,
311
+ & DUMMY_TYPE
312
+ ) ;
313
+ unsafe {
314
+ ( * header) . capacity = 0 ;
315
+ ( * header) . len . set ( 0 ) ;
316
+ header
256
317
}
257
318
}
258
319
#[ inline]
0 commit comments