luau_vm/state/
lifecycle.rs1use core::mem::size_of;
2use core::ptr::{self, NonNull};
3
4use luau_common::{ByteSlice, flags};
5
6use crate::gc::GcObject;
7use crate::handle::RawHandle;
8use crate::handle::sealed::Sealed;
9use crate::memory::{LuaPage, MemoryRuntime};
10use crate::metamethod::{MetamethodRuntime, TmEvent};
11use crate::string::{LUA_MIN_STRING_TABLE_SIZE, StringRuntime};
12use crate::table::TableRuntime;
13use crate::thread::{LUA_MIN_STACK, Thread};
14use crate::types::LUA_TTHREAD;
15use crate::value::{RawTValue, TValue, TValueCursor};
16use crate::{VmErrorResult, VmResult};
17
18use super::{
19 BASIC_CI_SIZE, EXTRA_STACK, GlobalState, INITIAL_STACK_SIZE, LUA_ERRERRMSG, LUA_MEMERRMSG,
20 RawCallInfo, RawLuaState, THREAD_STATUS_OK, ThreadState,
21};
22
23#[allow(
31 clippy::missing_safety_doc,
32 reason = "all methods share the capability-level safety contract"
33)]
34pub trait ThreadLifecycle: Sealed {
35 unsafe fn allocation_size(&self) -> usize;
37
38 unsafe fn new_thread_internal(&self) -> VmErrorResult<Thread>;
40
41 unsafe fn free_thread(&self, thread: &Thread, page: LuaPage);
43}
44impl ThreadLifecycle for Thread {
45 unsafe fn allocation_size(&self) -> usize {
46 unsafe {
47 size_of::<RawLuaState>()
48 + size_of::<RawTValue>()
49 * self.as_ptr().as_ref().unwrap_unchecked().stack_size as usize
50 + size_of::<RawCallInfo>()
51 * self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize
52 }
53 }
54
55 unsafe fn new_thread_internal(&self) -> VmErrorResult<Thread> {
57 unsafe {
58 let global = self.global();
59 let memcat = self.as_ptr().as_ref().unwrap_unchecked().active_memcat;
60 let thread = self.new_gco::<Thread>(size_of::<RawLuaState>(), memcat)?;
61 GcObject::from(&thread).init_header(self, LUA_TTHREAD as u8);
62
63 thread.preinit_state(global);
64 thread.as_ptr().as_mut().unwrap_unchecked().active_memcat = memcat;
65 self.init_stack(&thread)?;
66
67 let source = self.as_ptr().as_ref().unwrap_unchecked();
68 thread.set_globals(self.globals());
69 thread.as_ptr().as_mut().unwrap_unchecked().single_step = source.single_step;
70
71 debug_assert!(GcObject::from(&thread).is_white());
72 Ok(thread)
73 }
74 }
75
76 unsafe fn free_thread(&self, thread: &Thread, page: LuaPage) {
78 unsafe {
79 if let Some(user_thread) = self.global().user_thread_callback() {
80 user_thread(None, thread);
81 }
82
83 self.free_stack(thread);
84 self.free_gco(
85 thread.into(),
86 size_of::<RawLuaState>(),
87 thread.as_ptr().as_ref().unwrap_unchecked().memcat,
88 page,
89 );
90 }
91 }
92}
93
94impl Thread {
95 pub(crate) unsafe fn init_stack(&self, thread: &Thread) -> VmErrorResult {
97 unsafe {
98 let memcat = thread.as_ptr().as_ref().unwrap_unchecked().active_memcat;
99 let base_ci = self.new_array::<RawCallInfo>(BASIC_CI_SIZE, memcat)?;
100 {
101 let raw = thread.as_ptr().as_mut().unwrap_unchecked();
102 raw.base_ci = base_ci;
103 raw.size_ci = BASIC_CI_SIZE as i32;
104 raw.end_ci = base_ci.add(BASIC_CI_SIZE - 1);
105 }
106
107 let stack = self.new_array::<RawTValue>(INITIAL_STACK_SIZE, memcat)?;
108 {
109 let raw = thread.as_ptr().as_mut().unwrap_unchecked();
110 raw.stack = stack;
111 raw.stack_size = INITIAL_STACK_SIZE as i32;
112 raw.stack_last = stack.add(INITIAL_STACK_SIZE - EXTRA_STACK);
113 }
114
115 for index in 0..INITIAL_STACK_SIZE {
116 TValue::from_raw(NonNull::new_unchecked(stack.add(index))).set_nil();
117 }
118
119 thread.set_current_call_info(thread.base_call_info_cursor());
120
121 let function = TValueCursor::from_ptr(stack);
122 function.value_unchecked().set_nil();
123 let base = function.add(1);
124 thread
125 .base_call_info()
126 .init_call(function, base.add(LUA_MIN_STACK), 0, None);
127 thread.set_stack_top(base);
128 thread.set_stack_base(base);
129 }
130 Ok(())
131 }
132
133 pub(crate) unsafe fn free_stack(&self, thread: &Thread) {
135 unsafe {
136 let memcat = thread.as_ptr().as_ref().unwrap_unchecked().active_memcat;
137 self.free_array(
138 thread.as_ptr().as_ref().unwrap_unchecked().base_ci,
139 thread.as_ptr().as_ref().unwrap_unchecked().size_ci as usize,
140 memcat,
141 );
142 self.free_array(
143 thread.as_ptr().as_ref().unwrap_unchecked().stack,
144 thread.as_ptr().as_ref().unwrap_unchecked().stack_size as usize,
145 memcat,
146 );
147 }
148 }
149
150 pub(crate) unsafe fn preinit_state(&self, global: GlobalState) {
152 let raw = unsafe { self.as_ptr().as_mut().unwrap_unchecked() };
153 raw.global = global.as_ptr();
154 raw.stack = ptr::null_mut();
155 raw.stack_size = 0;
156 raw.gt = ptr::null_mut();
157 raw.open_upval = ptr::null_mut();
158 raw.size_ci = 0;
159 raw.native_call_depth = 0;
160 raw.base_native_call_depth = 0;
161 raw.status = THREAD_STATUS_OK;
162 raw.base_ci = ptr::null_mut();
163 raw.ci = ptr::null_mut();
164 raw.name_call = ptr::null_mut();
165 raw.cached_slot = 0;
166 raw.single_step = false;
167 raw.is_active = false;
168 raw.active_memcat = 0;
169 raw.userdata = ptr::null_mut();
170 raw.top = ptr::null_mut();
171 raw.base = ptr::null_mut();
172 raw.stack_last = ptr::null_mut();
173 raw.end_ci = ptr::null_mut();
174 raw.gc_list = ptr::null_mut();
175 }
176
177 pub(crate) unsafe fn open_main_state(&self) -> VmErrorResult {
179 unsafe {
180 self.init_stack(self)?;
181
182 let globals = self.new_table_internal(0, 2)?;
183 let registry = self.new_table_internal(0, 2)?;
184
185 self.set_globals(globals);
186 self.global().registry().set_table_value(registry);
187
188 self.resize(LUA_MIN_STRING_TABLE_SIZE as i32)?;
189 self.init()?;
190
191 if flags::LuauGcTraceUdata.get() {
192 let weak_registry = self.new_table_internal(0, 0)?;
193 let metatable = self.new_table_internal(0, 1)?;
194 let mode = self.intern_string(b"v".as_bstr())?;
195 let mode_slot = self
196 .set_str(metatable, self.global().tm_name(TmEvent::Mode as usize))?
197 .node_unchecked()
198 .value_unchecked();
199 mode_slot.set_string_value(mode);
200 weak_registry.set_metatable(Some(metatable));
201 self.global().weak_registry().set_table_value(weak_registry);
202 }
203
204 self.intern_string(LUA_MEMERRMSG.as_bstr())?.fix();
205 self.intern_string(LUA_ERRERRMSG.as_bstr())?.fix();
206
207 let global = self.global();
208 let global_ref = global.as_ptr().as_mut().unwrap_unchecked();
209 global_ref.gc_threshold = 4 * global_ref.total_bytes;
210 }
211 Ok(())
212 }
213}
214
215pub(crate) unsafe fn open_main_state(thread: &Thread, _: &mut ()) -> VmResult {
217 unsafe { thread.open_main_state() }?;
218 Ok(())
219}