1use luau_common::BString;
2
3use crate::VmErrorResult;
4use crate::gc::{GCS_PAUSE, GcCategoryNamer, GcRuntime};
5use crate::gc::{
6 LUA_GC_COLLECT, LUA_GC_COUNT, LUA_GC_COUNT_B, LUA_GC_IS_PAUSED, LUA_GC_IS_RUNNING,
7 LUA_GC_RESTART, LUA_GC_SET_GOAL, LUA_GC_SET_STEP_MUL, LUA_GC_SET_STEP_SIZE, LUA_GC_STEP,
8 LUA_GC_STOP,
9};
10use crate::handle::RawHandle;
11use crate::thread::Thread;
12
13impl Thread {
14 pub unsafe fn gc(&self, what: i32, data: i32) -> VmErrorResult<i32> {
16 let global = unsafe { self.global() };
17 let mut result = 0;
18
19 unsafe {
20 match what {
21 LUA_GC_STOP => {
22 global.as_ptr().as_mut().unwrap_unchecked().gc_threshold = usize::MAX;
23 }
24 LUA_GC_RESTART => {
25 let global_mut = global.as_ptr().as_mut().unwrap_unchecked();
26 global_mut.gc_threshold = global_mut.total_bytes;
27 }
28 LUA_GC_COLLECT => {
29 self.full_gc();
30 }
31 LUA_GC_COUNT => {
32 result = (global.as_ptr().as_ref().unwrap_unchecked().total_bytes >> 10) as i32;
33 }
34 LUA_GC_COUNT_B => {
35 result =
36 (global.as_ptr().as_ref().unwrap_unchecked().total_bytes & 1023) as i32;
37 }
38 LUA_GC_IS_RUNNING => {
39 result = i32::from(
40 global.as_ptr().as_ref().unwrap_unchecked().gc_threshold != usize::MAX,
41 );
42 }
43 LUA_GC_STEP => {
44 let amount = (data as usize) << 10;
45 let old_credit = if global.gc_state() == GCS_PAUSE {
46 0
47 } else {
48 global.as_ptr().as_ref().unwrap_unchecked().gc_threshold as isize
49 - global.as_ptr().as_ref().unwrap_unchecked().total_bytes as isize
50 };
51
52 if amount <= global.as_ptr().as_ref().unwrap_unchecked().total_bytes {
53 let global_mut = global.as_ptr().as_mut().unwrap_unchecked();
54 global_mut.gc_threshold = global_mut.total_bytes - amount;
55 } else {
56 global.as_ptr().as_mut().unwrap_unchecked().gc_threshold = 0;
57 }
58
59 let mut actual_work = 0usize;
60 while global.as_ptr().as_ref().unwrap_unchecked().gc_threshold
61 <= global.as_ptr().as_ref().unwrap_unchecked().total_bytes
62 {
63 actual_work += self.step(false)?;
64
65 if global.gc_state() == GCS_PAUSE {
66 result = 1;
67 break;
68 }
69 }
70
71 if global.gc_state() != GCS_PAUSE {
72 let total_bytes = global.as_ptr().as_ref().unwrap_unchecked().total_bytes;
73 let new_threshold =
74 total_bytes as isize + actual_work as isize + old_credit;
75 global.as_ptr().as_mut().unwrap_unchecked().gc_threshold =
76 if new_threshold < 0 {
77 0
78 } else {
79 new_threshold as usize
80 };
81 }
82 }
83 LUA_GC_SET_GOAL => {
84 result = global.as_ptr().as_ref().unwrap_unchecked().gc_goal;
85 global.as_ptr().as_mut().unwrap_unchecked().gc_goal = data;
86 }
87 LUA_GC_SET_STEP_MUL => {
88 result = global.as_ptr().as_ref().unwrap_unchecked().gc_step_mul;
89 global.as_ptr().as_mut().unwrap_unchecked().gc_step_mul = data;
90 }
91 LUA_GC_SET_STEP_SIZE => {
92 result = global.as_ptr().as_ref().unwrap_unchecked().gc_step_size >> 10;
93 global.as_ptr().as_mut().unwrap_unchecked().gc_step_size = data << 10;
94 }
95 LUA_GC_IS_PAUSED => {
96 result = i32::from(global.gc_state() == GCS_PAUSE);
97 }
98 _ => result = -1,
99 }
100 }
101
102 Ok(result)
103 }
104
105 pub unsafe fn memory_dump(
107 &self,
108 output: &mut BString,
109 category_name: Option<&mut dyn GcCategoryNamer>,
110 ) {
111 unsafe { <Self as GcRuntime>::dump(self, (output as *mut BString).cast(), category_name) };
112 }
113
114 pub unsafe fn allocation_rate(&self) -> i64 {
116 unsafe { <Self as GcRuntime>::allocation_rate(self) }
117 }
118
119 pub unsafe fn set_mem_cat(&self, category: i32) {
121 debug_assert!((category as u32) < crate::memory::LUA_MEMORY_CATEGORIES as u32);
122 unsafe {
123 self.as_ptr().as_mut().unwrap_unchecked().active_memcat = category as u8;
124 }
125 }
126
127 pub unsafe fn total_bytes(&self, category: i32) -> usize {
129 debug_assert!(category < crate::memory::LUA_MEMORY_CATEGORIES as i32);
130
131 let global_handle = unsafe { self.global() };
132 let global = unsafe { global_handle.as_ptr().as_ref().unwrap_unchecked() };
133 if category < 0 {
134 global.total_bytes
135 } else {
136 global.memcat_bytes[category as usize]
137 }
138 }
139}