Skip to main content

luau_vm/thread/
gc.rs

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    /// `lua_gc`
15    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    /// `lua_memorydump`
106    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    /// `lua_allocationrate`
115    pub unsafe fn allocation_rate(&self) -> i64 {
116        unsafe { <Self as GcRuntime>::allocation_rate(self) }
117    }
118
119    /// `lua_setmemcat`
120    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    /// `lua_totalbytes`
128    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}