1use std::cell::Cell;
27use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
28use std::sync::OnceLock;
29
30use crate::state_stub::{lua_CFunction, upvalue_index, LuaState, LuaStateStubExt as _};
31use lua_types::{error::LuaError, gc::GcRef, value::LuaValue, LuaStatus, LuaThreadClose, LuaType};
32
33thread_local! {
34 static THREAD_CLOSE_SUPPRESS: Cell<u32> = const { Cell::new(0) };
52}
53
54static CHAINING_HOOK_INSTALLED: OnceLock<()> = OnceLock::new();
56
57struct SuppressGuard;
65
66impl SuppressGuard {
67 fn new() -> Self {
68 THREAD_CLOSE_SUPPRESS.with(|c| c.set(c.get() + 1));
69 SuppressGuard
70 }
71}
72
73impl Drop for SuppressGuard {
74 fn drop(&mut self) {
75 THREAD_CLOSE_SUPPRESS.with(|c| c.set(c.get().saturating_sub(1)));
76 }
77}
78
79fn ensure_chaining_panic_hook() {
106 CHAINING_HOOK_INSTALLED.get_or_init(|| {
107 let previous = std::panic::take_hook();
108 std::panic::set_hook(Box::new(move |info| {
109 let suppress = info.payload().downcast_ref::<LuaThreadClose>().is_some()
110 && THREAD_CLOSE_SUPPRESS.with(|c| c.get()) > 0;
111 if !suppress {
112 previous(info);
113 }
114 }));
115 });
116}
117
118const COS_RUN: i32 = 0;
122
123const COS_DEAD: i32 = 1;
125
126const COS_YIELD: i32 = 2;
128
129const COS_NORM: i32 = 3;
131
132const STAT_NAMES: [&[u8]; 4] = [b"running", b"dead", b"suspended", b"normal"];
135
136pub const CO_FUNCS: &[(&[u8], lua_CFunction)] = &[
143 (b"create", co_create),
144 (b"resume", co_resume),
145 (b"running", co_running),
146 (b"status", co_status),
147 (b"wrap", co_wrap),
148 (b"yield", co_yield),
149 (b"isyieldable", co_isyieldable),
150 (b"close", co_close),
151];
152
153fn get_co(state: &mut LuaState) -> Result<GcRef<lua_types::value::LuaThread>, LuaError> {
164 let co = state.to_thread(1);
165 if let Some(co) = co {
166 return Ok(co);
167 }
168 Err(thread_arg_error(state, 1))
169}
170
171fn thread_arg_error(state: &mut LuaState, arg: i32) -> LuaError {
176 use lua_types::LuaVersion;
177 let version = state.global().lua_version;
178 if matches!(version, LuaVersion::V51 | LuaVersion::V52) {
179 return lua_vm::debug::arg_error_impl(state, arg, b"coroutine expected");
180 }
181 if matches!(version, LuaVersion::V53) {
182 return lua_vm::debug::arg_error_impl(state, arg, b"thread expected");
183 }
184 let got = state.value_at(arg);
185 let got_name = match state.full_type_name(&got) {
186 Ok(n) => n,
187 Err(e) => return e,
188 };
189 let mut extramsg = b"thread expected, got ".to_vec();
190 extramsg.extend_from_slice(&got_name);
191 lua_vm::debug::arg_error_impl(state, arg, &extramsg)
192}
193
194fn get_opt_co(state: &mut LuaState) -> Result<GcRef<lua_types::value::LuaThread>, LuaError> {
195 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
196 && state.type_at(1) == LuaType::None
197 {
198 let id = state.global().current_thread_id;
199 return state
200 .global()
201 .thread_value_for(id)
202 .ok_or_else(|| LuaError::runtime(format_args!("current thread is not registered")));
203 }
204 get_co(state)
205}
206
207fn aux_status(state: &mut LuaState, co: &GcRef<lua_types::value::LuaThread>) -> i32 {
223 let co_id = co.id;
224 let entry_rc = {
225 let g = state.global();
226 if co_id == g.current_thread_id {
227 return COS_RUN;
228 }
229 if co_id == g.main_thread_id {
230 return COS_NORM;
231 }
232 match g.threads.get(&co_id) {
233 Some(e) => e.state.clone(),
234 None => return COS_DEAD,
235 }
236 };
237 let co_state = match entry_rc.try_borrow() {
238 Ok(state) => state,
239 Err(_) => {
240 return COS_NORM;
244 }
245 };
246 let raw_status = co_state.status;
247 if raw_status == LuaStatus::Yield as u8 {
248 return COS_YIELD;
249 }
250 if raw_status != LuaStatus::Ok as u8 {
251 return COS_DEAD;
252 }
253 let has_frames = co_state.ci.as_usize() > 0;
254 if has_frames {
255 return COS_NORM;
256 }
257 let ci_func = co_state.call_info[0].func.0;
258 let top = co_state.top.0;
259 let lua_gettop = top as i64 - ci_func as i64 - 1;
260 if lua_gettop == 0 {
261 COS_DEAD
262 } else {
263 COS_YIELD
264 }
265}
266
267fn aux_resume(state: &mut LuaState, co: GcRef<lua_types::value::LuaThread>, narg: i32) -> i32 {
292 let co_id = co.id;
293 let entry_rc = {
294 let g = state.global();
295 match g.threads.get(&co_id) {
296 Some(e) => e.state.clone(),
297 None => {
298 let is_main = co_id == g.main_thread_id;
299 drop(g);
300 if is_main {
301 let msg = non_suspended_resume_message(state);
302 push_lit_or_nil(state, msg);
303 } else {
304 push_lit_or_nil(state, b"cannot resume dead coroutine");
305 }
306 return -1;
307 }
308 }
309 };
310 let parent_thread_id = state.global().current_thread_id;
311 let top_before = state.get_top();
312 if top_before < narg {
313 push_lit_or_nil(state, b"not enough arguments to resume");
314 return -1;
315 }
316 let first_arg_idx = top_before - narg + 1;
317 let mut args = pop_resume_value_buf(state);
318 args.extend((first_arg_idx..=top_before).map(|i| state.value_at(i)));
319 lua_vm::api::set_top(state, (top_before - narg) as i32).ok();
320
321 let mut parent_open_upval_slots = pop_resume_slot_buf(state);
322 parent_open_upval_slots
323 .extend(state.openupval.iter().filter_map(|uv| uv.try_open_payload()));
324 {
325 let mut g = state.global_mut();
326 for (tid, idx) in &parent_open_upval_slots {
327 let val = state.get_at(*idx);
328 g.cross_thread_upvals.insert((*tid, *idx), val);
329 }
330 }
331
332 push_parent_gc_snapshot(state);
333
334 let (status, results_or_err): (LuaStatus, Vec<LuaValue>) = {
335 let mut co_state = match entry_rc.try_borrow_mut() {
336 Ok(b) => b,
337 Err(_) => {
338 pop_parent_gc_snapshot(state);
339 let mut g = state.global_mut();
340 for (tid, idx) in &parent_open_upval_slots {
341 g.cross_thread_upvals.remove(&(*tid, *idx));
342 }
343 drop(g);
344 return_resume_slot_buf(state, parent_open_upval_slots);
345 return_resume_value_buf(state, args);
346 let msg = non_suspended_resume_message(state);
347 push_lit_or_nil(state, msg);
348 return -1;
349 }
350 };
351 if co_state.check_stack(narg + 1).is_err() {
352 drop(co_state);
353 pop_parent_gc_snapshot(state);
354 let mut g = state.global_mut();
355 for (tid, idx) in &parent_open_upval_slots {
356 g.cross_thread_upvals.remove(&(*tid, *idx));
357 }
358 drop(g);
359 return_resume_slot_buf(state, parent_open_upval_slots);
360 return_resume_value_buf(state, args);
361 push_lit_or_nil(state, b"too many arguments to resume");
362 return -1;
363 }
364 for v in args.drain(..) {
365 co_state.push(v);
366 }
367 return_resume_value_buf(state, args);
368 co_state.global_mut().current_thread_id = co_id;
369 let mut nres: i32 = 0;
370 ensure_chaining_panic_hook();
371 let resume_result = {
372 let _suppress = SuppressGuard::new();
373 catch_unwind(AssertUnwindSafe(|| {
374 lua_vm::do_::lua_resume(&mut *co_state, Some(state), narg, &mut nres)
375 }))
376 };
377 co_state.global_mut().current_thread_id = parent_thread_id;
378 let status = match resume_result {
379 Ok(status) => status,
380 Err(payload) => {
381 if let Some(close) = payload.downcast_ref::<LuaThreadClose>() {
382 close.0
383 } else {
384 resume_unwind(payload);
385 }
386 }
387 };
388 let co_top = co_state.top_idx().0 as i32;
389 let ci_func = co_state.current_call_info().func.0 as i32;
390 let count = if status == LuaStatus::Ok || status == LuaStatus::Yield {
391 nres
392 } else {
393 1
394 };
395 let start = co_top - count;
396 let mut vals = pop_resume_value_buf(state);
397 vals.extend((start..co_top).map(|i| co_state.get_at(lua_vm::state::StackIdx(i as u32))));
398 let new_co_top = if status == LuaStatus::Ok || status == LuaStatus::Yield {
399 (co_top - count).max(ci_func + 1)
400 } else {
401 co_top - count
402 };
403 co_state.set_top(lua_vm::state::StackIdx(new_co_top.max(0) as u32));
404 (status, vals)
405 };
406
407 pop_parent_gc_snapshot(state);
409
410 {
411 let mut flush = pop_resume_flush_buf(state);
412 let mut g = state.global_mut();
413 for (tid, idx) in &parent_open_upval_slots {
414 if let Some(v) = g.cross_thread_upvals.remove(&(*tid, *idx)) {
415 flush.push((*idx, v));
416 }
417 }
418 drop(g);
419 for (idx, v) in flush.drain(..) {
420 state.set_at(idx, v);
421 }
422 return_resume_flush_buf(state, flush);
423 }
424 return_resume_slot_buf(state, parent_open_upval_slots);
425
426 let mut results_or_err = results_or_err;
427 match status {
428 LuaStatus::Ok | LuaStatus::Yield => {
429 if state.check_stack(results_or_err.len() as i32 + 1).is_err() {
430 return_resume_value_buf(state, results_or_err);
431 push_lit_or_nil(state, b"too many results to resume");
432 return -1;
433 }
434 let n = results_or_err.len();
435 for v in results_or_err.drain(..) {
436 state.push(v);
437 }
438 return_resume_value_buf(state, results_or_err);
439 n as i32
440 }
441 _ => {
442 for v in results_or_err.drain(..) {
443 state.push(v);
444 }
445 return_resume_value_buf(state, results_or_err);
446 -1
447 }
448 }
449}
450
451fn push_parent_gc_snapshot(state: &mut LuaState) {
452 let top = (state.top_idx().0 as usize).min(state.stack.len());
453 let (mut stack_snapshot, mut upval_snapshot) = {
454 let mut g = state.global_mut();
455 (
456 g.snapshot_stack_pool.pop().unwrap_or_default(),
457 g.snapshot_upval_pool.pop().unwrap_or_default(),
458 )
459 };
460 stack_snapshot.extend(state.stack[..top].iter().map(|sv| sv.val));
461 upval_snapshot.extend(state.openupval.iter().cloned());
462 let mut g = state.global_mut();
463 g.suspended_parent_stacks.push(stack_snapshot);
464 g.suspended_parent_open_upvals.push(upval_snapshot);
465}
466
467fn pop_parent_gc_snapshot(state: &mut LuaState) {
468 let mut g = state.global_mut();
469 if let Some(mut v) = g.suspended_parent_open_upvals.pop() {
470 v.clear();
471 g.snapshot_upval_pool.push(v);
472 }
473 if let Some(mut v) = g.suspended_parent_stacks.pop() {
474 v.clear();
475 g.snapshot_stack_pool.push(v);
476 }
477}
478
479fn pop_resume_slot_buf(state: &mut LuaState) -> Vec<(u64, lua_vm::state::StackIdx)> {
484 state.global_mut().resume_upval_slot_pool.pop().unwrap_or_default()
485}
486
487fn return_resume_slot_buf(state: &mut LuaState, mut buf: Vec<(u64, lua_vm::state::StackIdx)>) {
490 buf.clear();
491 state.global_mut().resume_upval_slot_pool.push(buf);
492}
493
494fn pop_resume_value_buf(state: &mut LuaState) -> Vec<LuaValue> {
498 state.global_mut().resume_value_pool.pop().unwrap_or_default()
499}
500
501fn return_resume_value_buf(state: &mut LuaState, mut buf: Vec<LuaValue>) {
504 buf.clear();
505 state.global_mut().resume_value_pool.push(buf);
506}
507
508fn pop_resume_flush_buf(state: &mut LuaState) -> Vec<(lua_vm::state::StackIdx, LuaValue)> {
512 state.global_mut().resume_flush_pool.pop().unwrap_or_default()
513}
514
515fn return_resume_flush_buf(state: &mut LuaState, mut buf: Vec<(lua_vm::state::StackIdx, LuaValue)>) {
518 buf.clear();
519 state.global_mut().resume_flush_pool.push(buf);
520}
521
522#[cfg(feature = "debug")]
540pub(crate) struct RootedThreadBorrow<'a> {
541 inner: std::cell::RefMut<'a, LuaState>,
542}
543
544#[cfg(feature = "debug")]
545impl RootedThreadBorrow<'_> {
546 pub(crate) fn resnapshot(&mut self) {
550 let top = (self.inner.top_idx().0 as usize).min(self.inner.stack.len());
551 let stack_copy: Vec<LuaValue> = self.inner.stack[..top].iter().map(|sv| sv.val).collect();
552 let upval_copy: Vec<GcRef<lua_types::UpVal>> = self.inner.openupval.to_vec();
553 let mut g = self.inner.global_mut();
554 if let Some(slot) = g.suspended_parent_stacks.last_mut() {
555 slot.clear();
556 slot.extend(stack_copy);
557 }
558 if let Some(slot) = g.suspended_parent_open_upvals.last_mut() {
559 slot.clear();
560 slot.extend(upval_copy);
561 }
562 }
563}
564
565#[cfg(feature = "debug")]
566impl std::ops::Deref for RootedThreadBorrow<'_> {
567 type Target = LuaState;
568 fn deref(&self) -> &LuaState {
569 &self.inner
570 }
571}
572
573#[cfg(feature = "debug")]
574impl std::ops::DerefMut for RootedThreadBorrow<'_> {
575 fn deref_mut(&mut self) -> &mut LuaState {
576 &mut self.inner
577 }
578}
579
580#[cfg(feature = "debug")]
581impl Drop for RootedThreadBorrow<'_> {
582 fn drop(&mut self) {
583 let mut g = self.inner.global_mut();
584 if let Some(mut v) = g.suspended_parent_open_upvals.pop() {
585 v.clear();
586 g.snapshot_upval_pool.push(v);
587 }
588 if let Some(mut v) = g.suspended_parent_stacks.pop() {
589 v.clear();
590 g.snapshot_stack_pool.push(v);
591 }
592 }
593}
594
595#[cfg(feature = "debug")]
599pub(crate) fn borrow_thread_rooted<'a>(
600 state: &mut LuaState,
601 cell: &'a std::cell::RefCell<LuaState>,
602) -> RootedThreadBorrow<'a> {
603 let inner = cell.borrow_mut();
604 let top = (inner.top_idx().0 as usize).min(inner.stack.len());
605 let (mut stack_snapshot, mut upval_snapshot) = {
606 let mut g = state.global_mut();
607 (
608 g.snapshot_stack_pool.pop().unwrap_or_default(),
609 g.snapshot_upval_pool.pop().unwrap_or_default(),
610 )
611 };
612 stack_snapshot.extend(inner.stack[..top].iter().map(|sv| sv.val));
613 upval_snapshot.extend(inner.openupval.iter().cloned());
614 let mut g = state.global_mut();
615 g.suspended_parent_stacks.push(stack_snapshot);
616 g.suspended_parent_open_upvals.push(upval_snapshot);
617 drop(g);
618 RootedThreadBorrow { inner }
619}
620
621fn non_suspended_resume_message(state: &LuaState) -> &'static [u8] {
628 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
629 b"cannot resume running coroutine"
630 } else {
631 b"cannot resume non-suspended coroutine"
632 }
633}
634
635fn push_lit_or_nil(state: &mut LuaState, bytes: &[u8]) {
637 match state.intern_str(bytes) {
638 Ok(s) => state.push(LuaValue::Str(s)),
639 Err(_) => state.push(LuaValue::Nil),
640 }
641}
642
643pub fn co_resume(state: &mut LuaState) -> Result<usize, LuaError> {
657 let co = get_co(state)?;
658 let narg = state.get_top() - 1;
659 let r = aux_resume(state, co, narg);
660 if r < 0 {
661 if state.sandbox_aborting() {
662 let top = state.get_top();
663 let err_val = state.value_at(top);
664 return Err(LuaError::from_value(err_val));
665 }
666 state.push(LuaValue::Bool(false));
667 state.insert(-2)?;
668 Ok(2)
669 } else {
670 state.push(LuaValue::Bool(true));
671 state.insert(-(r + 1))?;
672 Ok((r + 1) as usize)
673 }
674}
675
676fn aux_wrap(state: &mut LuaState) -> Result<usize, LuaError> {
684 let up = state.value_at(upvalue_index(1));
685 let co = match up {
686 LuaValue::Thread(t) => t,
687 _ => {
688 return Err(LuaError::runtime(format_args!(
689 "coroutine.wrap: upvalue is not a thread"
690 )))
691 }
692 };
693 let narg = state.get_top();
694 let r = aux_resume(state, co.clone(), narg);
695 if r < 0 {
696 let top = state.get_top();
697 let mut err_val = state.value_at(top);
698 if aux_status(state, &co) == COS_DEAD {
699 let old_err = state.pop();
700 let nclose = close_suspended_or_dead(state, co)?;
701 err_val = if nclose >= 2 {
702 let top = state.get_top();
703 state.value_at(top)
704 } else {
705 old_err
706 };
707 state.pop_n(nclose);
708 }
709 Err(LuaError::from_value(err_val))
710 } else {
711 Ok(r as usize)
712 }
713}
714
715pub fn co_create(state: &mut LuaState) -> Result<usize, LuaError> {
721 state.check_arg_type(1, LuaType::Function)?;
722 if matches!(state.global().lua_version, lua_types::LuaVersion::V51)
728 && state.is_c_function_at(1)
729 {
730 return Err(lua_vm::debug::arg_error_impl(
731 state,
732 1,
733 b"Lua function expected",
734 ));
735 }
736 let body = state.value_at(1);
737 let _nl = state.new_thread(Some(body))?;
738 Ok(1)
739}
740
741pub fn co_wrap(state: &mut LuaState) -> Result<usize, LuaError> {
749 co_create(state)?;
750 state.push_cclosure(aux_wrap, 1)?;
751 Ok(1)
752}
753
754pub fn co_yield(state: &mut LuaState) -> Result<usize, LuaError> {
761 let n = state.get_top();
762 let r = lua_vm::do_::lua_yieldk(state, n, 0, None)?;
763 Ok(r as usize)
764}
765
766pub fn co_status(state: &mut LuaState) -> Result<usize, LuaError> {
771 let co = get_co(state)?;
772 let idx = aux_status(state, &co) as usize;
773 let name: &[u8] = STAT_NAMES[idx];
774 let interned = state.intern_str(name)?;
775 state.push(LuaValue::Str(interned));
776 Ok(1)
777}
778
779pub fn co_isyieldable(state: &mut LuaState) -> Result<usize, LuaError> {
783 let is_yieldable = if matches!(state.type_at(1), LuaType::None) {
784 state.is_yieldable()
785 } else {
786 let co = get_co(state)?;
787 let co_id = co.id;
788 let (is_main, is_current) = {
789 let g = state.global();
790 (co_id == g.main_thread_id, co_id == g.current_thread_id)
791 };
792 if is_main {
793 false
794 } else if is_current {
795 state.is_yieldable()
796 } else {
797 let entry_rc = {
798 let g = state.global();
799 g.threads
800 .get(&co_id)
801 .expect("thread value carries an id that must resolve in GlobalState::threads")
802 .state
803 .clone()
804 };
805 let target_is_yieldable = match entry_rc.try_borrow() {
806 Ok(b) => b.is_yieldable(),
807 Err(_) => false,
808 };
809 target_is_yieldable
810 }
811 };
812 state.push(LuaValue::Bool(is_yieldable));
813 Ok(1)
814}
815
816pub fn co_running(state: &mut LuaState) -> Result<usize, LuaError> {
828 let is_main = state.push_thread()?;
829 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
830 if is_main {
831 state.pop_n(1);
832 state.push(LuaValue::Nil);
833 }
834 return Ok(1);
835 }
836 state.push(LuaValue::Bool(is_main));
837 Ok(2)
838}
839
840pub fn co_close(state: &mut LuaState) -> Result<usize, LuaError> {
848 lua_vm::state::inc_c_stack(state)?;
849 let result = (|| {
850 let co = get_opt_co(state)?;
851 let status = aux_status(state, &co);
852 match status {
853 COS_DEAD | COS_YIELD => close_suspended_or_dead(state, co),
854 _ => {
855 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
856 && status == COS_RUN
857 && state.global().closing_thread_id == Some(co.id)
858 {
859 state.push(LuaValue::Bool(true));
860 return Ok(1);
861 }
862 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
863 && status == COS_RUN
864 && co.id == state.global().main_thread_id
865 {
866 return Err(LuaError::runtime(format_args!("cannot close main thread")));
867 }
868 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
869 && status == COS_RUN
870 && co.id == state.global().current_thread_id
871 {
872 state.global_mut().closing_thread_id = Some(co.id);
873 let in_status = state.status as i32;
874 let s = lua_vm::state::reset_thread(state, in_status);
875 state.global_mut().closing_thread_id = None;
876 state.n_ccalls = state.n_ccalls.saturating_sub(1);
877 std::panic::panic_any(LuaThreadClose(LuaStatus::from_raw(s)));
878 }
879 let name = if status == COS_RUN {
880 "running"
881 } else {
882 "normal"
883 };
884 Err(LuaError::runtime(format_args!(
885 "cannot close a {} coroutine",
886 name
887 )))
888 }
889 }
890 })();
891 state.n_ccalls -= 1;
892 result
893}
894
895fn close_suspended_or_dead(
897 state: &mut LuaState,
898 co: GcRef<lua_types::value::LuaThread>,
899) -> Result<usize, LuaError> {
900 let co_id = co.id;
901 let entry_rc_opt = {
902 let g = state.global();
903 g.threads.get(&co_id).map(|e| e.state.clone())
904 };
905 let entry_rc = match entry_rc_opt {
906 Some(rc) => rc,
907 None => {
908 state.push(LuaValue::Bool(true));
909 return Ok(1);
910 }
911 };
912 let parent_thread_id = state.global().current_thread_id;
913 let caller_c_calls = state.c_calls();
914
915 let mut parent_open_upval_slots = pop_resume_slot_buf(state);
916 parent_open_upval_slots
917 .extend(state.openupval.iter().filter_map(|uv| uv.try_open_payload()));
918 {
919 let mut g = state.global_mut();
920 for (tid, idx) in &parent_open_upval_slots {
921 let val = state.get_at(*idx);
922 g.cross_thread_upvals.insert((*tid, *idx), val);
923 }
924 }
925
926 push_parent_gc_snapshot(state);
927
928 let (status, err_value): (i32, Option<LuaValue>) = {
929 let mut co_state = entry_rc.borrow_mut();
930 co_state.global_mut().current_thread_id = co_id;
931 co_state.global_mut().closing_thread_id = Some(co_id);
932 co_state.n_ccalls = caller_c_calls;
933 let in_status = co_state.status as i32;
934 let s = lua_vm::state::reset_thread(&mut *co_state, in_status);
935 co_state.global_mut().closing_thread_id = None;
936 co_state.global_mut().current_thread_id = parent_thread_id;
937 if s == LuaStatus::Ok as i32 {
938 (s, None)
939 } else {
940 let top = co_state.top_idx().0;
941 if top > 0 {
942 let err = co_state.get_at(lua_vm::state::StackIdx(top - 1));
943 co_state.set_top(lua_vm::state::StackIdx(top - 1));
944 (s, Some(err))
945 } else {
946 (s, Some(LuaValue::Nil))
947 }
948 }
949 };
950
951 pop_parent_gc_snapshot(state);
952
953 {
954 let mut flush = pop_resume_flush_buf(state);
955 let mut g = state.global_mut();
956 for (tid, idx) in &parent_open_upval_slots {
957 if let Some(v) = g.cross_thread_upvals.remove(&(*tid, *idx)) {
958 flush.push((*idx, v));
959 }
960 }
961 drop(g);
962 for (idx, v) in flush.drain(..) {
963 state.set_at(idx, v);
964 }
965 return_resume_flush_buf(state, flush);
966 }
967 return_resume_slot_buf(state, parent_open_upval_slots);
968
969 if status == LuaStatus::Ok as i32 {
970 state.push(LuaValue::Bool(true));
971 Ok(1)
972 } else {
973 state.push(LuaValue::Bool(false));
974 if let Some(v) = err_value {
975 state.push(v);
976 } else {
977 state.push(LuaValue::Nil);
978 }
979 Ok(2)
980 }
981}
982
983pub fn open_coroutine(state: &mut LuaState) -> Result<usize, LuaError> {
989 use lua_types::LuaVersion;
993 let version = state.global().lua_version;
994 let has_close = !matches!(version, LuaVersion::V51 | LuaVersion::V52 | LuaVersion::V53);
995 let has_isyieldable = !matches!(version, LuaVersion::V51 | LuaVersion::V52);
999 if has_close && has_isyieldable {
1000 state.new_lib(CO_FUNCS)?;
1001 } else {
1002 let filtered: Vec<(&[u8], lua_CFunction)> = CO_FUNCS
1003 .iter()
1004 .filter(|(name, _)| {
1005 (has_close || *name != b"close".as_slice())
1006 && (has_isyieldable || *name != b"isyieldable".as_slice())
1007 })
1008 .copied()
1009 .collect();
1010 state.new_lib(&filtered)?;
1011 }
1012 Ok(1)
1013}