@@ -2,16 +2,19 @@ mod flags;
22pub use self :: flags:: * ;
33
44mod alloc;
5+ pub use self :: alloc:: { AllocInProcess , StackPrimitives } ;
6+
57mod gc;
68
79use core:: alloc:: { AllocErr , Layout } ;
810use core:: mem;
9- use core:: ptr:: NonNull ;
11+ use core:: ptr:: { self , NonNull } ;
1012use core:: sync:: atomic:: { AtomicUsize , Ordering } ;
1113
1214use intrusive_collections:: { LinkedList , UnsafeRef } ;
13-
1415use hashbrown:: HashMap ;
16+ use liblumen_core:: locks:: SpinLock ;
17+
1518
1619use self :: gc:: * ;
1720use super :: * ;
@@ -38,10 +41,8 @@ pub struct ProcessControlBlock {
3841 young : YoungHeap ,
3942 // old generation heap
4043 old : OldHeap ,
41- // virtual binary heap
42- vheap : VirtualBinaryHeap ,
4344 // off-heap allocations
44- off_heap : LinkedList < HeapFragmentAdapter > ,
45+ off_heap : SpinLock < LinkedList < HeapFragmentAdapter > > ,
4546 off_heap_size : AtomicUsize ,
4647 // process dictionary
4748 dictionary : HashMap < Term , Term > ,
@@ -60,8 +61,7 @@ impl ProcessControlBlock {
6061 pub fn new ( heap : * mut Term , heap_size : usize ) -> Self {
6162 let young = YoungHeap :: new ( heap, heap_size) ;
6263 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 ( ) ) ) ;
6565 let dictionary = HashMap :: new ( ) ;
6666 Self {
6767 flags : AtomicProcessFlag :: new ( ProcessFlag :: Default ) ,
@@ -73,7 +73,6 @@ impl ProcessControlBlock {
7373 max_gen_gcs : 65535 ,
7474 young,
7575 old,
76- vheap,
7776 off_heap,
7877 off_heap_size : AtomicUsize :: new ( 0 ) ,
7978 dictionary,
@@ -92,26 +91,6 @@ impl ProcessControlBlock {
9291 self . flags . clear ( flags) ;
9392 }
9493
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-
11594 /// Perform a heap allocation, but do not fall back to allocating a heap fragment
11695 /// if the process heap is not able to fulfill the allocation request
11796 #[ inline]
@@ -149,101 +128,47 @@ impl ProcessControlBlock {
149128 let frag_ref = frag. as_ref ( ) ;
150129 let size = frag_ref. size ( ) ;
151130 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) ;
153134 self . off_heap_size . fetch_add ( size, Ordering :: AcqRel ) ;
154135 Ok ( data)
155136 }
156137
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-
179138 /// Frees stack space occupied by the last term on the stack,
180139 /// adjusting the stack pointer accordingly.
181140 ///
182141 /// Use `stack_popn` to pop multiple terms from the stack at once
183142 #[ 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+ }
186151 }
187152
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
189156 #[ 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 ( ( ) )
193164 }
194165
195166 /// Returns the term at the top of the stack
196167 #[ inline]
197- pub unsafe fn stack_top ( & mut self ) -> Option < Term > {
168+ pub fn stack_top ( & mut self ) -> Option < Term > {
198169 self . stack_slot ( 1 )
199170 }
200171
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-
247172 /// Puts a new value under the given key in the process dictionary
248173 #[ inline]
249174 pub fn put ( & mut self , key : Term , value : Term ) -> Term {
@@ -326,10 +251,15 @@ impl ProcessControlBlock {
326251 return true ;
327252 }
328253 // 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+ }
333263 }
334264
335265 #[ inline( always) ]
@@ -371,7 +301,7 @@ impl ProcessControlBlock {
371301 // we are able to pick up from the current process context
372302 let mut rootset = RootSet :: new ( roots) ;
373303 // 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 ( ) ) ;
375305 // The process dictionary is also used for roots
376306 for ( k, v) in & self . dictionary {
377307 rootset. push ( k as * const _ as * mut _ ) ;
@@ -393,17 +323,97 @@ impl Drop for ProcessControlBlock {
393323 // the dictionary is dropped. This leaves only the young and old heap
394324
395325 // Free young heap
396- let young_heap_start = self . young . start ;
326+ let young_heap_start = self . young . heap_start ( ) ;
397327 let young_heap_size = self . young . size ( ) ;
398328 unsafe { alloc:: free ( young_heap_start, young_heap_size) } ;
399329 // Free old heap, if active
400330 if self . old . active ( ) {
401- let old_heap_start = self . old . start ;
331+ let old_heap_start = self . old . heap_start ( ) ;
402332 let old_heap_size = self . old . size ( ) ;
403333 unsafe { alloc:: free ( old_heap_start, old_heap_size) } ;
404334 }
405335 }
406336}
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+ }
407417
408418#[ cfg( test) ]
409419mod test;
0 commit comments