Skip to main content

rsgc/heap/
thread.rs

1use std::{
2    any::Any,
3    cell::UnsafeCell,
4    collections::HashMap,
5    intrinsics::{likely, unlikely},
6    mem::{size_of, MaybeUninit},
7    panic::{AssertUnwindSafe, UnwindSafe},
8    ptr::{null, null_mut},
9    sync::atomic::{AtomicI8, AtomicU64, AtomicUsize, Ordering},
10    thread::{JoinHandle, ThreadId},
11};
12
13use crate::{heap::tlab::ThreadLocalAllocBuffer, system::finalizer::register_for_finalization};
14use crate::{
15    formatted_size,
16    heap::{align_down, mark::MarkTask, stack::approximate_stack_pointer},
17    offsetof,
18    sync::{
19        self,
20        mutex::{Condvar, Mutex, MutexGuard},
21    },
22    system::object::{
23        Allocation, ConstVal, Handle, HeapObjectHeader, SizeTag, VTable, VtableTag, VT,
24    },
25    system::traits::Object,
26    utils::{
27        deque::LocalSSB,
28        machine_context::{registers_from_ucontext, PlatformRegisters},
29    },
30};
31
32use super::{
33    align_usize,
34    bitmap::HeapBitmap,
35    card_table::CardTable,
36    heap::{heap, Heap},
37    marking_context::MarkingContext,
38    region::HeapArguments,
39    safepoint,
40    shared_vars::SharedFlag,
41    stack::StackBounds,
42    AllocRequest,
43};
44
45// gc_state = 1 means the thread is doing GC or is waiting for the GC to
46//              finish.
47pub const GC_STATE_WAITING: i8 = 1;
48// gc_state = 2 means the thread is running unmanaged code that can be
49//              execute at the same time with the GC.
50pub const GC_STATE_SAFE: i8 = 2;
51
52/// GC Mutator.
53///
54/// By using this structure you can allocate, synchronize with GC and insert write barriers.
55pub struct Thread {
56    pub(crate) id: u64,
57    pub(crate) tlab: ThreadLocalAllocBuffer,
58    pub(crate) biased_begin: usize,
59    satb_mark_queue: LocalSSB,
60    cm_in_progress: bool,
61    mark_ctx: *mut MarkingContext,
62    mark_bitmap: *const HeapBitmap<16>,
63    stack: StackBounds,
64    max_tlab_size: usize,
65    safepoint: *mut u8,
66    last_sp: *mut u8,
67    gc_state: i8,
68    pub(crate) platform_registers: *mut PlatformRegisters,
69}
70
71static THREAD_ID: AtomicU64 = AtomicU64::new(0);
72
73impl Thread {
74    pub fn safepoint_offset() -> usize {
75        offsetof!(Thread, safepoint)
76    }
77
78    pub fn mark_queue_offset() -> usize {
79        offsetof!(Thread, satb_mark_queue)
80    }
81
82    pub fn satb_buffer_offset() -> usize {
83        offsetof!(Thread, satb_mark_queue.buf)
84    }
85
86    pub fn satb_index_offset() -> usize {
87        offsetof!(Thread, satb_mark_queue.index)
88    }
89
90    pub fn cm_in_progress_offset() -> usize {
91        offsetof!(Thread, cm_in_progress)
92    }
93
94    pub fn mark_ctx_offset() -> usize {
95        offsetof!(Thread, mark_ctx)
96    }
97
98    pub fn mark_bitmap_offset() -> usize {
99        offsetof!(Thread, mark_bitmap)
100    }
101
102    pub fn tlab_start_offset() -> usize {
103        offsetof!(Thread, tlab.start)
104    }
105
106    pub fn tlab_top_offset() -> usize {
107        offsetof!(Thread, tlab.top)
108    }
109
110    pub fn tlab_end_offset() -> usize {
111        offsetof!(Thread, tlab.end)
112    }
113
114    pub fn tlab_bitmap_offset() -> usize {
115        offsetof!(Thread, tlab.bitmap)
116    }
117
118    pub unsafe fn satb_mark_queue(&self) -> &LocalSSB {
119        &self.satb_mark_queue
120    }
121
122    pub unsafe fn satb_mark_queue_mut(&mut self) -> &mut LocalSSB {
123        &mut self.satb_mark_queue
124    }
125
126    /// Allocates fixed sized object on the heap.
127    pub fn allocate<T: 'static + Allocation>(&mut self, value: T) -> Handle<T> {
128        unsafe {
129            let size = align_usize(T::SIZE + size_of::<HeapObjectHeader>(), 16);
130            let mem = self.allocate_raw(size);
131            let obj = mem as *mut HeapObjectHeader;
132            (*obj).word = 0;
133            (*obj).set_vtable(VT::<T>::VAL as *const VTable as _);
134            (*obj).set_heap_size(size);
135            if T::NO_HEAP_PTRS {
136                (*obj).set_no_heap_ptrs();
137            }
138            obj.add(1).cast::<T>().write(value);
139
140            // We implement black mutator technique and SATB for concurrent marking.
141            //
142            // This means we collect garbage that was allocated *before* the cycle started
143            // and thus all new objects should implicitly be marked as black. This has problem
144            // of floating garbage but it significantly simplifies write barrier implementation.
145
146            #[cfg(feature = "gc-satb")]
147            {
148                // Need to maintain black-mutator invariant with SATB
149                (*self.mark_bitmap).set_bit(obj as _);
150            }   
151
152            let handle = Handle::from_raw(obj.add(1).cast());
153
154            if T::FINALIZE {
155                register_for_finalization(handle);
156            }
157
158            handle
159
160            
161        }
162    }
163
164    /// Allocates variably sized object on the heap.
165    ///
166    /// Note that `length` field is automatically written at [Allocation::VARSIZE_OFFSETOF_CAPACITY].
167    pub fn allocate_varsize<T: 'static + Allocation>(
168        &mut self,
169        length: usize,
170    ) -> Handle<MaybeUninit<T>> {
171        unsafe {
172            let size = align_usize(
173                T::SIZE + size_of::<HeapObjectHeader>() + T::VARSIZE_ITEM_SIZE * length,
174                16,
175            );
176
177            let mem = self.allocate_raw(size);
178            let obj = mem as *mut HeapObjectHeader;
179            (*obj).word = 0;
180            (*obj).set_vtable(VT::<T>::VAL as *const VTable as _);
181            (*obj).set_heap_size(size);
182            if T::NO_HEAP_PTRS {
183                (*obj).set_no_heap_ptrs();
184            }
185            obj.add(1)
186                .cast::<u8>()
187                .add(T::VARSIZE_OFFSETOF_CAPACITY)
188                .cast::<usize>()
189                .write(length);
190
191            #[cfg(feature = "gc-satb")]
192            {
193                // Need to maintain black-mutator invariant with SATB
194                (*self.mark_bitmap).set_bit(obj as _);
195            }
196            
197            let handle = Handle::from_raw(obj.add(1).cast());
198
199            if T::FINALIZE {
200                register_for_finalization(handle);
201            }
202
203            handle
204        }
205    }
206
207    /// Allocates raw memory, unsafe to use outside of RSGC impl itself.
208    ///
209    /// ## TODO
210    ///
211    /// I should really update it to include all code to properly tell GC that we allocated something, right now it is done in `allocate` and `allocate_varsize` but it should be done here.
212    #[inline]
213    pub unsafe fn allocate_raw(&mut self, size: usize) -> *mut u8 {
214        let mem = self.alloc_inside_tlab_fast(size);
215        if likely(!mem.is_null()) {
216            return mem;
217        }
218
219        self.allocate_slow(size)
220    }
221
222    #[cold]
223    #[inline(never)]
224    unsafe fn allocate_slow(&mut self, size: usize) -> *mut u8 {
225        assert!(
226            self.is_registered(),
227            "trying to perform allocation in unregistered thread with id: {:?}",
228            self.id
229        );
230        if size > self.max_tlab_size {
231            self.allocate_outside_tlab(size)
232        } else {
233            let mem = self.alloc_inside_tlab_slow(size);
234            if mem.is_null() {
235                self.allocate_outside_tlab(size)
236            } else {
237                mem
238            }
239        }
240    }
241
242    unsafe fn allocate_outside_tlab(&mut self, size: usize) -> *mut u8 {
243        let mut req = AllocRequest::new(super::AllocType::Shared, size, size);
244
245        let mem = heap().allocate_memory(&mut req);
246
247        if mem.is_null() {
248            std::panic::panic_any(OOM(size));
249        }
250        heap().mark_live(mem);
251
252        mem
253    }
254
255    #[inline]
256    unsafe fn alloc_inside_tlab_fast(&mut self, size: usize) -> *mut u8 {
257        self.tlab.allocate(size)
258    }
259
260    unsafe fn alloc_inside_tlab_slow(&mut self, size: usize) -> *mut u8 {
261        self.tlab.retire(self.id);
262
263        let tlab_size = self.max_tlab_size;
264        let mut req = AllocRequest::new(super::AllocType::ForLAB, heap().options().min_tlab_size, tlab_size);
265        let mem = heap().allocate_memory(&mut req);
266
267        if mem.is_null() {
268            return null_mut();
269        }
270
271        std::ptr::write_bytes(mem, 0, req.actual_size());
272        self.tlab.initialize_(
273            mem as _,
274            mem.add(size) as _,
275            mem.add(req.actual_size()) as _,
276        );
277        (*self.tlab.bitmap).set_atomic(mem as _);
278        mem
279    }
280
281    #[cold]
282    pub(crate) fn register(&mut self) {
283        self.safepoint = safepoint::SAFEPOINT_PAGE.address();
284        assert_ne!(self.safepoint, null_mut());
285        self.stack = StackBounds::current_thread_stack_bounds();
286        let sp = approximate_stack_pointer();
287        self.last_sp = align_down(sp as _, 8) as _;
288        self.mark_ctx = heap().marking_context_mut() as *mut MarkingContext;
289        let th = threads();
290        th.add_thread(Thread::current());
291
292        for _ in 0..3 {
293            self.safepoint();
294        }
295
296        let heap = heap();
297
298        self.max_tlab_size = if heap.options().tlab_size > 0 {
299            heap.options().tlab_size // TLAB size set by user
300        } else {
301            heap.options().max_tlab_size // TLAB size set automatically based on heap options
302        };
303
304        /*self.queue = vec![null_mut(); heap.options().max_satb_buffer_size].into_boxed_slice();
305        self.queue_index = 0;
306        self.queue_size = self.queue.len();*/
307        let buffer =
308            unsafe { libc::malloc(size_of::<usize>() * heap.options().max_satb_buffer_size) };
309        self.satb_mark_queue.set_buffer(buffer.cast());
310        self.satb_mark_queue
311            .set_index(heap.options().max_satb_buffer_size);
312        self.biased_begin = heap.card_table().get_biased_begin();
313        self.mark_bitmap = heap.marking_context().mark_bitmap();
314        self.cm_in_progress = heap.is_concurrent_mark_in_progress(); // Thread migth be attached while marking is running.
315    }
316
317    /// SATB write barrier. Ensures that object processes all references correctly.
318    /// Must be inserted before write to `handle`.
319    #[inline]
320    pub fn write_barrier<T: Object + ?Sized>(&mut self, handle: Handle<T>) {
321        unsafe {
322            self.raw_write_barrier::<true>(handle.as_ptr().sub(size_of::<HeapObjectHeader>()).cast());
323        }
324    }   
325
326    /// Same as `write_barrier` but does not filter marked objects, instead they are filtered
327    /// when flushing SATB buffer to collector.
328    #[inline]
329    pub fn write_barrier_no_filter<T: Object + ?Sized>(&mut self, handle: Handle<T>) {
330        unsafe {
331            self.raw_write_barrier::<false>(handle.as_ptr().sub(size_of::<HeapObjectHeader>()).cast());
332        }
333    }
334
335    pub(crate) fn toggle_write_barrier(&mut self, value: bool) {
336        self.cm_in_progress = value;
337    }
338
339    /// Raw implementation of write-barrier.
340    ///
341    /// If SATB mode is used this code will do Yuasa deletion write barrier that captures
342    /// writes to white objects. Note that this barrier is very conservative: it does not
343    /// check colors of new values that are being written.
344    ///
345    /// In Incremental Update mode uses Steele's write barrier that captures black<-white writes.
346    /// This barrier is very conservative as well. Note that with IU mode concurrent mark termination
347    /// might take longer.
348    ///
349    /// In passive mode does nothing.
350    #[inline]
351    pub unsafe fn raw_write_barrier<const FILTER_SATB: bool>(&mut self, obj: *mut HeapObjectHeader) {
352        // Yuasa deletion barrier implementation
353        // Capture white<-black writes
354        #[cfg(feature = "gc-satb")]
355        if self.cm_in_progress {
356            // Filter marked objects before hitting the SATB queues.
357            if FILTER_SATB && !(*self.mark_bitmap).check_bit(obj as _) {
358                if !self.satb_mark_queue.try_enqueue(obj as _) {
359                    self.slow_write_barrier(obj);
360                }
361            }
362        }
363
364        // Capture black<-white writes
365        #[cfg(feature = "gc-incremental-update")]
366        if self.cm_in_progress {
367            if (*self.mark_bitmap).check_bit(obj as _) {
368                // unmark object before putting to queue.
369                (*self.mark_bitmap).clear_bit(obj as _);
370                if !self.satb_mark_queue.try_enqueue(obj as _) {
371                    self.slow_write_barrier(obj);
372                }
373            }
374        }
375    }
376
377    #[inline(never)]
378    #[cold]
379    unsafe fn slow_write_barrier(&mut self, obj: *mut HeapObjectHeader) {
380        self.flush_ssb();
381        self.raw_write_barrier::<true>(obj);
382    }
383
384    /// Flush SSB queue. This function is called when SSB queue is full.
385    /// 
386    /// Writes are pushed to global SATB queue. If object is already marked it is not pushed to SATB queue.
387    pub fn flush_ssb(&mut self) {
388        let heap = heap();
389        unsafe {
390            for i in self.satb_mark_queue.index..heap.options().max_satb_buffer_size {
391                let obj = self.satb_mark_queue.buf.add(i).read();
392                assert!(heap.is_in(obj), "not in heap: {:p} (cm_in_progress?={})", obj, self.cm_in_progress);
393                // GC can do some progress in background and already mark object that was in SSB.
394                if !(*self.mark_ctx).is_marked(obj as _) || cfg!(feature = "gc-incremental-update")
395                {
396                    (*self.mark_ctx)
397                        .mark_queues()
398                        .injector()
399                        .push(MarkTask::new(obj as _, false, false));
400                }
401            }
402        }
403        self.satb_mark_queue
404            .set_index(heap.options().max_satb_buffer_size);
405    }
406
407    pub fn atomic_gc_state(&self) -> &AtomicI8 {
408        unsafe { std::mem::transmute(&self.gc_state) }
409    }
410
411    #[inline]
412    pub unsafe fn gc_state_set(&mut self, state: i8, old_state: i8) -> i8 {
413        self.atomic_gc_state().store(state, Ordering::Release);
414        if old_state != 0 && state == 0 && !self.safepoint.is_null() {
415            self.safepoint();
416        }
417
418        old_state
419    }
420
421    #[inline]
422    pub unsafe fn set_last_sp(&mut self, sp: *mut u8) {
423        self.last_sp = sp;
424    }
425
426    pub unsafe fn state_save_and_set(&mut self, state: i8) -> i8 {
427        self.gc_state_set(state, self.gc_state)
428    }
429
430    #[inline]
431    pub fn stack_start(&self) -> *mut u8 {
432        self.stack.origin
433    }
434
435    #[inline]
436    pub fn last_sp(&self) -> *mut u8 {
437        self.last_sp
438    }
439
440    /// Returns pointer to safepoint page. When JITing your code you can directly
441    /// inline safepoint poll into your code.
442    pub unsafe fn safepoint_page(&self) -> *mut u8 {
443        self.safepoint
444    }
445
446    /// Returns true if safepoints are conditional in this build of RSGC.
447    pub const fn is_conditional_safepoint() -> bool {
448        cfg!(feature = "conditional-safepoint")
449    }
450
451    /// Reads from polling page. If safepoint is disabled nothing happens
452    /// but when safepoint is enabled this triggers page fault (SIGSEGV/SIGBUS on Linux/macOS/BSD)
453    /// and goes into signal to suspend thread.
454    ///
455    /// # Note
456    ///
457    /// Enable `conditional-safepoint` feature when running in LLDB/GDB, otherwise safepoint events
458    /// will be treatened as segfault by debuggers.
459    #[inline(always)]
460    pub fn safepoint(&mut self) {
461        std::sync::atomic::compiler_fence(Ordering::SeqCst);
462        let safepoint = self.safepoint;
463
464        // Two paths here if conditional safepoints disabled:
465        //
466        // 1) Safepoint page is not armed, so read does not cause anythin
467        // 2) Safepoint page is armed, read causes SIGBUG/SIGSEGV and we go into signal handler
468        //    where we would wait for safepoint to be disabled using condvar + mutex.
469        //
470        let val = unsafe { safepoint.read_volatile() };
471        let _ = val;
472        #[cfg(feature = "conditional-safepoint")]
473        {
474            // In case of conditional safepoint armed safepoint value is just set to non-zero.
475            // If it is non-zero we go to condvar + mutex wait loop
476            if val != 0 {
477                self.enter_conditional();
478            }
479        }
480        std::sync::atomic::compiler_fence(Ordering::SeqCst);
481    }
482
483    #[inline(never)]
484    #[cold]
485    fn enter_conditional(&mut self) {
486        #[cfg(not(windows))]
487        {
488            extern "C" {
489                #[allow(improper_ctypes)]
490                fn getcontext(ctx: *mut libc::ucontext_t) -> i32;
491            }
492
493            unsafe {
494                let mut ctx = MaybeUninit::<libc::ucontext_t>::zeroed().assume_init();
495                getcontext(&mut ctx as *mut _);
496
497                self.platform_registers = registers_from_ucontext(&mut ctx);
498                self.enter_safepoint(approximate_stack_pointer() as _);
499                self.platform_registers = null_mut();
500                let _ = ctx;
501            }
502        }
503    }
504
505    pub(crate) fn save_registers(&mut self) {
506        #[cfg(not(windows))]
507        {
508            extern "C" {
509                #[allow(improper_ctypes)]
510                fn getcontext(ctx: *mut libc::ucontext_t) -> i32;
511            }
512
513            unsafe {
514                let mut ctx = MaybeUninit::<libc::ucontext_t>::zeroed().assume_init();
515                getcontext(&mut ctx as *mut _);
516
517                self.platform_registers = registers_from_ucontext(&mut ctx);
518                let _ = ctx;
519            }
520        }
521    }
522
523    /// Sets last stack pointer for a thread, waits for safepoint to be disabled and executes
524    /// tasks that are needed to execute at safepoint for this thread.
525    pub(crate) fn enter_safepoint(&mut self, sp: *mut u8) {
526        let mut start = self.stack.origin;
527        let mut end = self.stack.bound;
528        if start > end {
529            std::mem::swap(&mut start, &mut end);
530        }
531
532        assert!(
533            sp >= start && sp < end,
534            "stack-pointer at safepoint is not in thread stack bounds"
535        );
536        self.last_sp = sp;
537
538        self.set_gc_and_wait();
539    }
540
541    pub(crate) fn set_gc_and_wait(&mut self) {
542        let state = self.gc_state;
543        self.atomic_gc_state()
544            .store(GC_STATE_WAITING, Ordering::Release);
545        unsafe {
546            super::safepoint::wait_gc();
547        }
548        self.atomic_gc_state().store(state, Ordering::Release);
549    }
550
551    /// Returns true if thread is registered in a GC.
552    pub fn is_registered(&self) -> bool {
553        !self.safepoint.is_null() && unsafe { self.safepoint != &mut SINK }
554    }
555
556    /// Returns current thread.
557    pub fn current() -> &'static mut Thread {
558        unsafe { &mut THREAD }
559    }
560
561    /// Returns registers for current thread. This is used by GC to trace current registers.
562    ///
563    /// # Safety
564    ///
565    /// Can return invalid pointer or null. Must be used only by GC and only when roots are traced.
566    ///
567    /// Should not be used outside of RSGC.
568    pub unsafe fn get_registers(&self) -> (*mut u8, usize) {
569        (
570            self.platform_registers.cast(),
571            size_of::<PlatformRegisters>() / size_of::<usize>(),
572        )
573    }
574}
575
576pub struct UnsafeScope {
577    state: i8,
578    thread: &'static mut Thread,
579}
580
581impl UnsafeScope {
582    /// Enter unsafe GC state. This means current thread runs "managed by GC code" and GC *must* stop this thread
583    /// at GC cycle.
584    ///
585    /// Returns current state to restore later.
586    pub fn new(thread: &'static mut Thread) -> Self {
587        Self {
588            state: unsafe { thread.state_save_and_set(0) },
589            thread,
590        }
591    }
592}
593
594impl Drop for UnsafeScope {
595    fn drop(&mut self) {
596        unsafe {
597            self.thread.gc_state_set(self.state, 0);
598        }
599    }
600}
601
602/// Enters safepoint scope. This means that current thread is in "safe" state and GC can run.
603///
604/// Note that `cb` MUST not invoke any GC code or access GC objects otherwise UB will happen.
605pub fn safepoint_scope_conditional<R>(enter: bool, cb: impl FnOnce() -> R) -> R {
606    let thread = Thread::current();
607    #[cfg(not(windows))]
608    unsafe {
609        
610        extern "C" {
611            #[allow(improper_ctypes)]
612            fn getcontext(ctx: *mut libc::ucontext_t) -> i32;
613        }
614        let mut ucontext = MaybeUninit::<libc::ucontext_t>::zeroed().assume_init();
615
616        getcontext(&mut ucontext);
617        thread.platform_registers = registers_from_ucontext(&mut ucontext);
618        let state = thread.state_save_and_set(if enter { GC_STATE_SAFE } else { 0 });
619
620        thread.set_last_sp(approximate_stack_pointer() as _);
621        let cb = AssertUnwindSafe(cb);
622        let result = match std::panic::catch_unwind(move || cb()) {
623            Ok(result) => result,
624            Err(err) => {
625                thread.gc_state_set(state, GC_STATE_SAFE);
626                thread.platform_registers = null_mut();
627                std::panic::resume_unwind(err);
628            }
629        };
630
631        thread.gc_state_set(state, if enter { GC_STATE_SAFE } else { 0 });
632        thread.platform_registers = null_mut();
633        result
634    }
635
636    #[cfg(windows)]
637    unsafe {
638        let mut context = MaybeUninit::<winapi::um::winnt::CONTEXT>::zeroed().assume_init();
639
640        winapi::um::processthreadsapi::GetThreadContext(
641            winapi::um::processthreadsapi::GetCurrentThread(),
642            &mut context,
643        );
644
645        thread.platform_registers = &mut context;
646
647        let state = thread.state_save_and_set(if enter { GC_STATE_SAFE } else { 0 });
648
649        thread.set_last_sp(approximate_stack_pointer() as _);
650        let cb = AssertUnwindSafe(cb);
651        let result = match std::panic::catch_unwind(move || cb()) {
652            Ok(result) => result,
653            Err(err) => {
654                thread.gc_state_set(state, GC_STATE_SAFE);
655                thread.platform_registers = null_mut();
656                std::panic::resume_unwind(err);
657            }
658        };
659
660        thread.gc_state_set(state, if enter { GC_STATE_SAFE } else { 0 });
661        thread.platform_registers = null_mut();
662        result
663    }
664}
665
666/// Enters safepoint scope. This means that current thread is in "safe" state and GC can run.
667///
668/// Note that `cb` MUST not invoke any GC code or access GC objects otherwise UB will happen.
669pub fn safepoint_scope<R>(cb: impl FnOnce() -> R) -> R {
670    safepoint_scope_conditional(true, cb)
671}
672
673static mut SINK: u8 = 0;
674
675
676#[thread_local]
677static mut THREAD: Thread = Thread {
678    biased_begin: 0,
679    id: u64::MAX,
680    mark_ctx: null_mut(),
681    mark_bitmap: null_mut(),
682    tlab: ThreadLocalAllocBuffer::new(),
683    stack: StackBounds::none(),
684    safepoint: null_mut(),
685    last_sp: null_mut(),
686    max_tlab_size: 0,
687    gc_state: 0,
688    satb_mark_queue: LocalSSB::new(),
689    cm_in_progress: false,
690    platform_registers: null_mut(),
691};
692
693use parking_lot::lock_api::RawMutex;
694use sync::mutex::RawMutex as Lock;
695
696pub struct Threads {
697    pub threads: Mutex<Vec<*mut Thread>>,
698    pub cv_join: Condvar,
699}
700
701impl Threads {
702    pub fn new() -> Self {
703        Self {
704            threads: Mutex::new(vec![]),
705            cv_join: Condvar::new(),
706        }
707    }
708
709    pub fn add_thread(&self, thread: *mut Thread) {
710        let mut threads = self.threads.lock(false);
711        threads.push(thread);
712    }
713
714    pub fn remove_current_thread(&self) {
715        unsafe {
716            let thread = Thread::current();
717            (*thread).tlab.retire(thread.id);
718            (*thread).flush_ssb();
719            let raw = thread as *mut Thread;
720
721            safepoint_scope(|| {
722                let mut threads = self.threads.lock(true);
723                threads.retain(|th| {
724                    let th = *th;
725                    if th == raw {
726                        false
727                    } else {
728                        true
729                    }
730                });
731            });
732
733            thread.safepoint = &mut SINK;
734            self.cv_join.notify_all();
735        }
736    }
737
738    pub fn join_all(&self) {
739        let mut threads = self.threads.lock(true);
740
741        while threads.len() > 0 {
742            self.cv_join.wait(&mut threads);
743        }
744    }
745
746    pub fn get(&self) -> MutexGuard<'_, Vec<*mut Thread>> {
747        let threads = self.threads.lock(false);
748        threads
749    }
750}
751
752unsafe impl Sync for Threads {}
753unsafe impl Send for Threads {}
754
755static THREADS: once_cell::sync::Lazy<Threads> = once_cell::sync::Lazy::new(Threads::new);
756
757pub(crate) fn threads() -> &'static Threads {
758    &THREADS
759}
760
761pub struct OOM(pub usize);
762
763/// Marks current thread as 'main' thread. Initializes GC with given arguments and calls `callback`.
764///
765/// Note that this function does not terminate until all mutator threads are terminated and GC is stopped.
766pub fn main_thread<R>(
767    args: HeapArguments,
768    callback: impl FnOnce(&mut Heap) -> Result<R, Box<dyn std::error::Error>> + UnwindSafe,
769) -> Result<R, Box<dyn std::error::Error>> {
770    let heap = Heap::new(args);
771    Thread::current().register();
772    let res = std::panic::catch_unwind(|| callback(super::heap::heap()));
773
774    safepoint_scope(|| {
775        threads().remove_current_thread();
776        threads().join_all();
777    });
778
779    unsafe {
780        heap.stop();
781    }
782
783    match res {
784        Ok(res) => res,
785        Err(err) => {
786            std::panic::resume_unwind(err);
787        }
788    }
789}
790
791/// Spawns new thread and registers it with GC.
792pub fn spawn_thread<F, R>(cb: F) -> GCAwareJoinHandle<R>
793where
794    F: 'static + FnOnce() -> R + Send + UnwindSafe,
795    R: 'static + Send,
796{
797    let join = safepoint_scope(|| {
798        let join = std::thread::spawn(move || {
799            Thread::current().register();
800            let res = std::panic::catch_unwind(|| cb());
801
802            threads().remove_current_thread();
803
804            match res {
805                Ok(val) => val,
806                Err(err) => std::panic::resume_unwind(err),
807            }
808        });
809        join
810    });
811    GCAwareJoinHandle { join }
812}
813
814
815pub struct GCAwareJoinHandle<R> {
816    join: JoinHandle<R>,
817}
818
819impl<R> GCAwareJoinHandle<R> {
820    pub fn join(self) -> Result<R, Box<dyn Any + Send>> {
821        let res = safepoint_scope(move || {
822            let res = self.join.join();
823            res
824        });
825
826        res
827    }
828}
829
830pub mod scoped {
831    //! Scoped mutator threads.
832    use std::{
833        marker::PhantomData,
834        panic::{AssertUnwindSafe, UnwindSafe},
835        sync::{
836            atomic::{AtomicBool, AtomicUsize},
837            Arc,
838        },
839        thread::Thread,
840    };
841
842    use atomic::Ordering;
843
844    use crate::heap::stack::approximate_stack_pointer;
845
846    use super::GC_STATE_SAFE;
847
848    pub struct Scope<'a, 'b> {
849        scope: &'a std::thread::Scope<'a, 'b>,
850    }
851
852    impl<'a, 'b> Scope<'a, 'b> {
853        pub fn spawn<F>(&mut self, cb: F)
854        where
855            F: FnOnce() + Send + 'b,
856        {
857            self.scope.spawn(move || {
858                super::Thread::current().register();
859                let wrapper = AssertUnwindSafe(cb);
860                let res = std::panic::catch_unwind(move || {
861                    wrapper();
862                });
863
864                super::threads().remove_current_thread();
865
866                match res {
867                    Ok(val) => val,
868                    Err(err) => std::panic::resume_unwind(err),
869                }
870            });
871        }
872    }
873
874    /// GC-aware [std::thread::scope].
875    pub fn scoped(cb: impl FnOnce(&mut Scope)) {
876        let thread = super::Thread::current();
877        let mut state = 0;
878        std::thread::scope(|scope| unsafe {
879            let mut scope = Scope { scope };
880
881            cb(&mut scope);
882
883            // enter safepoint here since scoepd threads might not be finished there
884            // and when `scope` is dropped, the threads are joined and our thread waits
885            // for them to join
886            thread.last_sp = approximate_stack_pointer() as _;
887            state = thread.state_save_and_set(GC_STATE_SAFE);
888        });
889        unsafe {
890            thread.gc_state_set(state, GC_STATE_SAFE);
891        }
892    }
893}