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
45pub const GC_STATE_WAITING: i8 = 1;
48pub const GC_STATE_SAFE: i8 = 2;
51
52pub 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 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 #[cfg(feature = "gc-satb")]
147 {
148 (*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 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 (*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 #[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 } else {
301 heap.options().max_tlab_size };
303
304 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(); }
316
317 #[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 #[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 #[inline]
351 pub unsafe fn raw_write_barrier<const FILTER_SATB: bool>(&mut self, obj: *mut HeapObjectHeader) {
352 #[cfg(feature = "gc-satb")]
355 if self.cm_in_progress {
356 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 #[cfg(feature = "gc-incremental-update")]
366 if self.cm_in_progress {
367 if (*self.mark_bitmap).check_bit(obj as _) {
368 (*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 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 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 pub unsafe fn safepoint_page(&self) -> *mut u8 {
443 self.safepoint
444 }
445
446 pub const fn is_conditional_safepoint() -> bool {
448 cfg!(feature = "conditional-safepoint")
449 }
450
451 #[inline(always)]
460 pub fn safepoint(&mut self) {
461 std::sync::atomic::compiler_fence(Ordering::SeqCst);
462 let safepoint = self.safepoint;
463
464 let val = unsafe { safepoint.read_volatile() };
471 let _ = val;
472 #[cfg(feature = "conditional-safepoint")]
473 {
474 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 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 pub fn is_registered(&self) -> bool {
553 !self.safepoint.is_null() && unsafe { self.safepoint != &mut SINK }
554 }
555
556 pub fn current() -> &'static mut Thread {
558 unsafe { &mut THREAD }
559 }
560
561 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 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
602pub 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
666pub 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
763pub 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
791pub 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 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 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 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}