@@ -2,16 +2,19 @@ mod flags;
2
2
pub use self :: flags:: * ;
3
3
4
4
mod alloc;
5
+ pub use self :: alloc:: { AllocInProcess , StackPrimitives } ;
6
+
5
7
mod gc;
6
8
7
9
use core:: alloc:: { AllocErr , Layout } ;
8
10
use core:: mem;
9
- use core:: ptr:: NonNull ;
11
+ use core:: ptr:: { self , NonNull } ;
10
12
use core:: sync:: atomic:: { AtomicUsize , Ordering } ;
11
13
12
14
use intrusive_collections:: { LinkedList , UnsafeRef } ;
13
-
14
15
use hashbrown:: HashMap ;
16
+ use liblumen_core:: locks:: SpinLock ;
17
+
15
18
16
19
use self :: gc:: * ;
17
20
use super :: * ;
@@ -38,10 +41,8 @@ pub struct ProcessControlBlock {
38
41
young : YoungHeap ,
39
42
// old generation heap
40
43
old : OldHeap ,
41
- // virtual binary heap
42
- vheap : VirtualBinaryHeap ,
43
44
// off-heap allocations
44
- off_heap : LinkedList < HeapFragmentAdapter > ,
45
+ off_heap : SpinLock < LinkedList < HeapFragmentAdapter > > ,
45
46
off_heap_size : AtomicUsize ,
46
47
// process dictionary
47
48
dictionary : HashMap < Term , Term > ,
@@ -60,8 +61,7 @@ impl ProcessControlBlock {
60
61
pub fn new ( heap : * mut Term , heap_size : usize ) -> Self {
61
62
let young = YoungHeap :: new ( heap, heap_size) ;
62
63
let old = OldHeap :: default ( ) ;
63
- let vheap = VirtualBinaryHeap :: new ( heap_size) ;
64
- let off_heap = LinkedList :: new ( HeapFragmentAdapter :: new ( ) ) ;
64
+ let off_heap = SpinLock :: new ( LinkedList :: new ( HeapFragmentAdapter :: new ( ) ) ) ;
65
65
let dictionary = HashMap :: new ( ) ;
66
66
Self {
67
67
flags : AtomicProcessFlag :: new ( ProcessFlag :: Default ) ,
@@ -73,7 +73,6 @@ impl ProcessControlBlock {
73
73
max_gen_gcs : 65535 ,
74
74
young,
75
75
old,
76
- vheap,
77
76
off_heap,
78
77
off_heap_size : AtomicUsize :: new ( 0 ) ,
79
78
dictionary,
@@ -92,26 +91,6 @@ impl ProcessControlBlock {
92
91
self . flags . clear ( flags) ;
93
92
}
94
93
95
- /// Perform a heap allocation.
96
- ///
97
- /// If space on the process heap is not immediately available, then the allocation
98
- /// will be pushed into a heap fragment which will then be later moved on to the
99
- /// process heap during garbage collection
100
- #[ inline]
101
- pub unsafe fn alloc ( & mut self , need : usize ) -> Result < NonNull < Term > , AllocErr > {
102
- match self . young . alloc ( need) {
103
- ok @ Ok ( _) => ok,
104
- Err ( _) => self . alloc_fragment ( need) ,
105
- }
106
- }
107
-
108
- /// Same as `alloc`, but takes a `Layout` rather than the size in words
109
- #[ inline]
110
- pub unsafe fn alloc_layout ( & mut self , layout : Layout ) -> Result < NonNull < Term > , AllocErr > {
111
- let words = Self :: layout_to_words ( layout) ;
112
- self . alloc ( words)
113
- }
114
-
115
94
/// Perform a heap allocation, but do not fall back to allocating a heap fragment
116
95
/// if the process heap is not able to fulfill the allocation request
117
96
#[ inline]
@@ -149,101 +128,47 @@ impl ProcessControlBlock {
149
128
let frag_ref = frag. as_ref ( ) ;
150
129
let size = frag_ref. size ( ) ;
151
130
let data = frag_ref. data ( ) . cast :: < Term > ( ) ;
152
- self . off_heap . push_front ( UnsafeRef :: from_raw ( frag. as_ptr ( ) ) ) ;
131
+ let mut off_heap = self . off_heap . lock ( ) ;
132
+ off_heap. push_front ( UnsafeRef :: from_raw ( frag. as_ptr ( ) ) ) ;
133
+ drop ( off_heap) ;
153
134
self . off_heap_size . fetch_add ( size, Ordering :: AcqRel ) ;
154
135
Ok ( data)
155
136
}
156
137
157
- fn layout_to_words ( layout : Layout ) -> usize {
158
- let size = layout. size ( ) ;
159
- let mut words = size / mem:: size_of :: < Term > ( ) ;
160
- if size % mem:: size_of :: < Term > ( ) != 0 {
161
- words += 1 ;
162
- }
163
- words
164
- }
165
-
166
- /// Perform a stack allocation
167
- #[ inline]
168
- pub unsafe fn alloca ( & mut self , need : usize ) -> Result < NonNull < Term > , AllocErr > {
169
- self . young . stack_alloc ( need)
170
- }
171
-
172
- /// Perform a stack allocation, but with a `Layout`
173
- #[ inline]
174
- pub unsafe fn alloca_layout ( & mut self , layout : Layout ) -> Result < NonNull < Term > , AllocErr > {
175
- let need = to_word_size ( layout. size ( ) ) ;
176
- self . young . stack_alloc ( need)
177
- }
178
-
179
138
/// Frees stack space occupied by the last term on the stack,
180
139
/// adjusting the stack pointer accordingly.
181
140
///
182
141
/// Use `stack_popn` to pop multiple terms from the stack at once
183
142
#[ inline]
184
- pub unsafe fn stack_pop ( & mut self ) {
185
- self . stack_popn ( 1 )
143
+ pub fn stack_pop ( & mut self ) -> Option < Term > {
144
+ match self . stack_top ( ) {
145
+ None => None ,
146
+ ok @ Some ( _) => {
147
+ self . stack_popn ( 1 ) ;
148
+ ok
149
+ }
150
+ }
186
151
}
187
152
188
- /// Like `stack_pop`, but frees `n` terms from the stack
153
+ /// Pushes an immediate term or reference to term/list on top of the stack
154
+ ///
155
+ /// Returns `Err(AllocErr)` if the process is out of stack space
189
156
#[ inline]
190
- pub unsafe fn stack_popn ( & mut self , n : usize ) {
191
- assert ! ( n > 0 ) ;
192
- self . young . stack_popn ( n) ;
157
+ pub fn stack_push ( & mut self , term : Term ) -> Result < ( ) , AllocErr > {
158
+ assert ! ( term. is_immediate( ) || term. is_boxed( ) || term. is_list( ) ) ;
159
+ unsafe {
160
+ let stack0 = self . alloca ( 1 ) ?. as_ptr ( ) ;
161
+ ptr:: write ( stack0, term) ;
162
+ }
163
+ Ok ( ( ) )
193
164
}
194
165
195
166
/// Returns the term at the top of the stack
196
167
#[ inline]
197
- pub unsafe fn stack_top ( & mut self ) -> Option < Term > {
168
+ pub fn stack_top ( & mut self ) -> Option < Term > {
198
169
self . stack_slot ( 1 )
199
170
}
200
171
201
- /// Returns the term located in the given stack slot
202
- ///
203
- /// The stack slot index counts upwards from 1, so 1
204
- /// is equivalent to calling `stack_top`, 2 is the
205
- /// term immediately preceding `stack_top` in the stack,
206
- /// and so on
207
- #[ inline]
208
- pub unsafe fn stack_slot ( & mut self , slot : usize ) -> Option < Term > {
209
- assert ! ( slot > 0 ) ;
210
- self . young . stack_slot ( slot)
211
- }
212
-
213
- /// Returns the number of terms allocated on the stack
214
- #[ inline]
215
- pub fn stack_size ( & mut self ) -> usize {
216
- self . young . stack_size ( )
217
- }
218
-
219
- /// Returns the size of the stack space availabe in units of `Term`
220
- #[ inline]
221
- pub fn stack_available ( & mut self ) -> usize {
222
- self . young . stack_available ( )
223
- }
224
-
225
- /// Pushes a reference-counted binary on to this processes virtual heap
226
- ///
227
- /// NOTE: It is expected that the binary reference (the actual `ProcBin` struct)
228
- /// has already been allocated on the process heap, and that this function is
229
- /// being called simply to add the reference to the virtual heap
230
- #[ inline]
231
- pub fn vheap_push ( & mut self , bin : & ProcBin ) -> Term {
232
- self . vheap . push ( bin)
233
- }
234
-
235
- /// Returns a boolean for if the given pointer is owned by memory allocated to this process
236
- #[ inline]
237
- pub fn is_owner ( & mut self , ptr : * const Term ) -> bool {
238
- if self . young . contains ( ptr) || self . old . contains ( ptr) {
239
- return true ;
240
- }
241
- if self . off_heap . iter ( ) . any ( |frag| frag. contains ( ptr) ) {
242
- return true ;
243
- }
244
- self . vheap . contains ( ptr)
245
- }
246
-
247
172
/// Puts a new value under the given key in the process dictionary
248
173
#[ inline]
249
174
pub fn put ( & mut self , key : Term , value : Term ) -> Term {
@@ -326,10 +251,15 @@ impl ProcessControlBlock {
326
251
return true ;
327
252
}
328
253
// Check if virtual heap size indicates we should do a collection
329
- let used = self . vheap . heap_used ( ) ;
330
- let unused = self . vheap . unused ( ) ;
331
- let threshold = ( ( used + unused) as f64 * self . gc_threshold ) . ceil ( ) as usize ;
332
- used >= threshold
254
+ let used = self . young . virtual_heap_used ( ) ;
255
+ let unused = self . young . virtual_heap_unused ( ) ;
256
+ if unused > 0 {
257
+ let threshold = ( ( used + unused) as f64 * self . gc_threshold ) . ceil ( ) as usize ;
258
+ used >= threshold
259
+ } else {
260
+ // We've exceeded the virtual heap size
261
+ true
262
+ }
333
263
}
334
264
335
265
#[ inline( always) ]
@@ -371,7 +301,7 @@ impl ProcessControlBlock {
371
301
// we are able to pick up from the current process context
372
302
let mut rootset = RootSet :: new ( roots) ;
373
303
// The primary source of roots we add is the process stack
374
- rootset. push_range ( self . young . stack_start , self . young . stack_used ( ) ) ;
304
+ rootset. push_range ( self . young . stack_pointer ( ) , self . young . stack_size ( ) ) ;
375
305
// The process dictionary is also used for roots
376
306
for ( k, v) in & self . dictionary {
377
307
rootset. push ( k as * const _ as * mut _ ) ;
@@ -393,17 +323,97 @@ impl Drop for ProcessControlBlock {
393
323
// the dictionary is dropped. This leaves only the young and old heap
394
324
395
325
// Free young heap
396
- let young_heap_start = self . young . start ;
326
+ let young_heap_start = self . young . heap_start ( ) ;
397
327
let young_heap_size = self . young . size ( ) ;
398
328
unsafe { alloc:: free ( young_heap_start, young_heap_size) } ;
399
329
// Free old heap, if active
400
330
if self . old . active ( ) {
401
- let old_heap_start = self . old . start ;
331
+ let old_heap_start = self . old . heap_start ( ) ;
402
332
let old_heap_size = self . old . size ( ) ;
403
333
unsafe { alloc:: free ( old_heap_start, old_heap_size) } ;
404
334
}
405
335
}
406
336
}
337
+ impl AllocInProcess for ProcessControlBlock {
338
+ #[ inline]
339
+ unsafe fn alloc ( & mut self , need : usize ) -> Result < NonNull < Term > , AllocErr > {
340
+ match self . young . alloc ( need) {
341
+ ok @ Ok ( _) => ok,
342
+ Err ( _) => self . alloc_fragment ( need) ,
343
+ }
344
+ }
345
+
346
+ #[ inline]
347
+ unsafe fn alloca ( & mut self , need : usize ) -> Result < NonNull < Term > , AllocErr > {
348
+ self . young . alloca ( need)
349
+ }
350
+
351
+ #[ inline]
352
+ unsafe fn alloca_unchecked ( & mut self , need : usize ) -> NonNull < Term > {
353
+ self . young . alloca_unchecked ( need)
354
+ }
355
+
356
+ #[ inline]
357
+ fn virtual_alloc ( & mut self , bin : & ProcBin ) -> Term {
358
+ self . young . virtual_alloc ( bin)
359
+ }
360
+
361
+ #[ inline]
362
+ fn is_owner < T > ( & mut self , ptr : * const T ) -> bool {
363
+ if self . young . contains ( ptr) || self . old . contains ( ptr) {
364
+ return true ;
365
+ }
366
+ if self . young . virtual_heap_contains ( ptr) || self . old . virtual_heap_contains ( ptr) {
367
+ return true ;
368
+ }
369
+ let off_heap = self . off_heap . lock ( ) ;
370
+ if off_heap. iter ( ) . any ( |frag| frag. contains ( ptr) ) {
371
+ return true ;
372
+ }
373
+ false
374
+ }
375
+ }
376
+ impl StackPrimitives for ProcessControlBlock {
377
+ #[ inline]
378
+ fn stack_size ( & self ) -> usize {
379
+ self . young . stack_size ( )
380
+ }
381
+
382
+ #[ inline]
383
+ unsafe fn set_stack_size ( & mut self , size : usize ) {
384
+ self . young . set_stack_size ( size) ;
385
+ }
386
+
387
+ #[ inline]
388
+ fn stack_pointer ( & mut self ) -> * mut Term {
389
+ self . young . stack_pointer ( )
390
+ }
391
+
392
+ #[ inline]
393
+ unsafe fn set_stack_pointer ( & mut self , sp : * mut Term ) {
394
+ self . young . set_stack_pointer ( sp) ;
395
+ }
396
+
397
+ #[ inline]
398
+ fn stack_used ( & self ) -> usize {
399
+ self . young . stack_used ( )
400
+ }
401
+
402
+ #[ inline]
403
+ fn stack_available ( & self ) -> usize {
404
+ self . young . stack_available ( )
405
+ }
406
+
407
+ #[ inline]
408
+ fn stack_slot ( & mut self , n : usize ) -> Option < Term > {
409
+ self . young . stack_slot ( n)
410
+ }
411
+
412
+ #[ inline]
413
+ fn stack_popn ( & mut self , n : usize ) {
414
+ self . young . stack_popn ( n) ;
415
+ }
416
+ }
407
417
408
418
#[ cfg( test) ]
409
419
mod test;
0 commit comments