1use core::ptr::NonNull;
2
3use crate::VmErrorResult;
4use crate::call::ThreadStack;
5use crate::function::FunctionRuntime;
6use crate::gc::{GcBarrier, GcRuntime};
7use crate::handle::RawHandle;
8use crate::state::{
9 BASIC_CI_SIZE, BASIC_STACK_SIZE, GlobalState, INITIAL_STACK_SIZE, LuaCallbacks, RawLuaState,
10 THREAD_STATUS_BREAK, THREAD_STATUS_OK, THREAD_STATUS_YIELD, ThreadLifecycle, ThreadState,
11};
12use crate::value::nil_object;
13
14#[allow(
15 clippy::missing_safety_doc,
16 reason = "Thread's shared unsafe auxiliary API contract is documented on Thread"
17)]
18mod auxiliary;
19#[allow(
20 clippy::missing_safety_doc,
21 reason = "Thread's shared unsafe call API contract is documented on Thread"
22)]
23mod call;
24#[allow(
25 clippy::missing_safety_doc,
26 reason = "Thread's shared unsafe debug API contract is documented on Thread"
27)]
28mod debug;
29#[allow(
30 clippy::missing_safety_doc,
31 reason = "Thread's shared unsafe GC API contract is documented on Thread"
32)]
33mod gc;
34#[allow(
35 clippy::missing_safety_doc,
36 reason = "Thread's shared unsafe stack API contract is documented on Thread"
37)]
38pub(crate) mod stack;
39#[allow(
40 clippy::missing_safety_doc,
41 reason = "Thread's shared unsafe string builder contract is documented on Thread"
42)]
43mod string_builder;
44#[allow(
45 clippy::missing_safety_doc,
46 reason = "Thread's shared unsafe table API contract is documented on Thread"
47)]
48mod table;
49#[allow(
50 clippy::missing_safety_doc,
51 reason = "Thread's shared unsafe userdata API contract is documented on Thread"
52)]
53mod userdata;
54
55pub use crate::types::{
56 LUA_TBOOLEAN, LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TINTEGER, LUA_TLIGHTUSERDATA,
57 LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
58 LUA_TVECTOR,
59};
60pub use string_builder::{LuaStringBuilder, LuaStringBuilderStorage};
61
62#[derive(PartialEq, Eq)]
63#[repr(transparent)]
64pub struct Thread {
82 pub(crate) raw: NonNull<RawLuaState>,
83}
84
85impl crate::handle::sealed::Sealed for Thread {}
86
87impl RawHandle for Thread {
88 type Raw = RawLuaState;
89
90 fn as_ptr(&self) -> *mut RawLuaState {
91 self.raw.as_ptr()
92 }
93}
94
95impl AsRef<Thread> for Thread {
96 fn as_ref(&self) -> &Thread {
97 self
98 }
99}
100
101impl Thread {
102 pub unsafe fn global(&self) -> GlobalState {
110 unsafe {
111 GlobalState::from_raw(NonNull::new_unchecked(
112 self.as_ptr().as_ref().unwrap_unchecked().global,
113 ))
114 }
115 }
116}
117
118#[must_use = "dropping the guard restores the captured stack height"]
124pub struct StackGuard<'thread> {
125 thread: &'thread Thread,
126 top: i32,
127 restore_on_drop: bool,
128}
129
130impl<'thread> StackGuard<'thread> {
131 pub unsafe fn new(thread: &'thread Thread) -> Self {
139 Self {
140 thread,
141 top: unsafe { thread.get_top() },
142 restore_on_drop: true,
143 }
144 }
145
146 pub const fn top(&self) -> i32 {
147 self.top
148 }
149
150 pub fn keep(&mut self, count: i32) {
151 debug_assert!(count >= 0);
152 self.top += count;
153 }
154
155 pub fn into_top(mut self) -> i32 {
156 self.restore_on_drop = false;
157 self.top
158 }
159
160 pub fn dismiss(mut self) {
161 self.restore_on_drop = false;
162 }
163}
164
165impl Drop for StackGuard<'_> {
166 fn drop(&mut self) {
167 if self.restore_on_drop {
168 unsafe { self.thread.restore_top(self.top) };
169 }
170 }
171}
172
173pub const LUA_MIN_STACK: usize = 20;
175
176pub const LUAI_MAX_C_STACK: i32 = 8000;
178
179pub const LUAI_MAX_CALLS: usize = 20_000;
181
182pub const LUAI_MAX_NATIVE_CALLS: u16 = 200;
184
185pub const LUA_BUFFER_SIZE: usize = 512;
187
188pub const LUA_MULTRET: i32 = -1;
189pub const LUA_TNONE: i32 = -1;
190
191pub const LUA_REGISTRY_INDEX: i32 = -LUAI_MAX_C_STACK - 2000;
192pub const LUA_ENVIRON_INDEX: i32 = -LUAI_MAX_C_STACK - 2001;
193pub const LUA_GLOBALS_INDEX: i32 = -LUAI_MAX_C_STACK - 2002;
194
195pub const LUA_NOREF: i32 = -1;
196pub const LUA_REFNIL: i32 = 0;
197
198pub const LUA_CORUN: i32 = 0;
199pub const LUA_COSUS: i32 = 1;
200pub const LUA_CONOR: i32 = 2;
201pub const LUA_COFIN: i32 = 3;
202pub const LUA_COERR: i32 = 4;
203
204pub const fn upvalue_index(index: i32) -> i32 {
206 LUA_GLOBALS_INDEX - index
207}
208
209pub const fn is_pseudo(index: i32) -> bool {
211 index <= LUA_REGISTRY_INDEX
212}
213
214#[allow(
216 clippy::missing_safety_doc,
217 reason = "Thread's shared unsafe lifecycle API contract is documented on Thread"
218)]
219impl Thread {
220 pub unsafe fn same_vm(&self, other: impl AsRef<Thread>) -> bool {
226 unsafe { self.global() == other.as_ref().global() }
227 }
228
229 pub unsafe fn encode_pointer(&self, pointer: usize) -> usize {
231 unsafe { self.global().encode_pointer(pointer) }
232 }
233
234 pub unsafe fn set_pointer_encode_key(&self, a: u64, b: u64, c: u64, d: u64) {
236 unsafe {
237 self.global()
238 .as_ptr()
239 .as_mut()
240 .unwrap_unchecked()
241 .ptr_enc_key = [a & !1, b | 1, c, d];
242 }
243 }
244
245 pub unsafe fn reset(&self) -> VmErrorResult {
247 unsafe {
248 debug_assert!(!self.as_ptr().as_ref().unwrap_unchecked().is_active);
249 debug_assert!(
250 self.as_ptr().as_ref().unwrap_unchecked().status != THREAD_STATUS_OK
251 || self.as_ptr().as_ref().unwrap_unchecked().ci
252 == self.as_ptr().as_ref().unwrap_unchecked().base_ci
253 );
254
255 self.close(self.base_call_info().function().value_unchecked());
256
257 let function = self.stack();
258 let ci = self.base_call_info();
259 ci.function().value_unchecked().set_nil();
260 ci.init_call(function, function.add(1 + LUA_MIN_STACK), 0, None);
261
262 let raw = self.as_ptr().as_mut().unwrap_unchecked();
263 raw.status = THREAD_STATUS_OK;
264 raw.native_call_depth = 0;
265 raw.base_native_call_depth = 0;
266 self.set_current_call_info(self.base_call_info_cursor());
267 self.set_stack_base(ci.base());
268 self.set_stack_top(ci.base());
269
270 if self.as_ptr().as_ref().unwrap_unchecked().size_ci as usize != BASIC_CI_SIZE {
271 self.realloc_ci(BASIC_CI_SIZE as i32)?;
272 }
273
274 let target_stack_size = INITIAL_STACK_SIZE as i32;
275 if self.as_ptr().as_ref().unwrap_unchecked().stack_size != target_stack_size {
276 self.realloc_stack(BASIC_STACK_SIZE as i32, false)?;
277 }
278
279 let stack = self.stack();
280 let stack_size = self.as_ptr().as_ref().unwrap_unchecked().stack_size as usize;
281 for index in 0..stack_size {
282 stack.add(index).value_unchecked().set_nil();
283 }
284 }
285 Ok(())
286 }
287
288 pub unsafe fn is_reset(&self) -> bool {
290 unsafe {
291 self.current_call_info() == self.base_call_info()
292 && self.as_ptr().as_ref().unwrap_unchecked().base
293 == self.as_ptr().as_ref().unwrap_unchecked().top
294 && self.as_ptr().as_ref().unwrap_unchecked().status == THREAD_STATUS_OK
295 }
296 }
297
298 pub unsafe fn status(&self) -> i32 {
300 unsafe { self.as_ptr().as_ref().unwrap_unchecked().status as i32 }
301 }
302
303 pub unsafe fn is_yieldable(&self) -> i32 {
305 i32::from(unsafe {
306 self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
307 <= self
308 .as_ptr()
309 .as_ref()
310 .unwrap_unchecked()
311 .base_native_call_depth
312 })
313 }
314
315 pub unsafe fn thread_data(&self) -> *mut () {
317 unsafe { self.as_ptr().as_ref().unwrap_unchecked().userdata }
318 }
319
320 pub unsafe fn set_thread_data(&self, data: *mut ()) {
322 unsafe {
323 self.as_ptr().as_mut().unwrap_unchecked().userdata = data;
324 }
325 }
326
327 pub unsafe fn callbacks(&self) -> *mut LuaCallbacks {
340 unsafe { self.global().callbacks() }
341 }
342}
343
344#[allow(
346 clippy::missing_safety_doc,
347 reason = "Thread's shared unsafe thread-value API contract is documented on Thread"
348)]
349impl Thread {
350 pub unsafe fn push_thread(&self) -> VmErrorResult<i32> {
352 unsafe {
353 self.thread_barrier();
354 self.ensure_stack(self, 1)?;
355
356 let top = self.stack_top();
357 top.value_unchecked().set_thread_value(self);
358 debug_assert!(top < self.current_call_info().top());
359 self.set_stack_top(top.add(1));
360 }
361
362 Ok(i32::from(unsafe { self.global().main_thread() == *self }))
363 }
364
365 pub unsafe fn to_thread(&self, index: i32) -> Option<Thread> {
367 let object = unsafe { self.index_to_addr(index) };
368 if object == nil_object() || !object.is_thread() {
369 None
370 } else {
371 Some(object.thread_value())
372 }
373 }
374
375 pub unsafe fn new_thread(&self) -> VmErrorResult<Thread> {
377 unsafe {
378 self.check_gc()?;
379 self.thread_barrier();
380 self.ensure_stack(self, 1)?;
381 let thread = self.new_thread_internal()?;
382
383 let top = self.stack_top();
384 top.value_unchecked().set_thread_value(&thread);
385 debug_assert!(top < self.current_call_info().top());
386 self.set_stack_top(top.add(1));
387
388 if let Some(user_thread) = self.global().user_thread_callback() {
389 user_thread(Some(self), &thread);
390 }
391
392 Ok(thread)
393 }
394 }
395
396 pub unsafe fn co_status(&self, thread: &Thread) -> i32 {
398 unsafe {
399 debug_assert!(self.global() == thread.global());
400
401 if *thread == *self {
402 LUA_CORUN
403 } else if thread.as_ptr().as_ref().unwrap_unchecked().status == THREAD_STATUS_YIELD {
404 LUA_COSUS
405 } else if thread.as_ptr().as_ref().unwrap_unchecked().status == THREAD_STATUS_BREAK {
406 LUA_CONOR
407 } else if thread.as_ptr().as_ref().unwrap_unchecked().status != THREAD_STATUS_OK {
408 LUA_COERR
409 } else if thread.current_call_info() != thread.base_call_info() {
410 LUA_CONOR
411 } else if thread.stack_top() == thread.stack_base() {
412 LUA_COFIN
413 } else {
414 LUA_COSUS
415 }
416 }
417 }
418}