在Rust中终止Lua脚本/线程的正确方法是什么

ogq8wdun  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(274)

我正在使用mlua和Rust构建一个脚本引擎,一个脚本一次只能运行一个。我认为最好的方法是生成一个线程来运行每个脚本。线程不会返回任何数据,并且可以运行无限长的时间,直到用户停止它。
我的问题是如何在执行中停止线程?
例如,我的Lua脚本是无限的:

while true do
    log("I'm stuck in an infinite loop!")
end

我当前的Rust代码如下所示:

pub struct Script {
    name: String,
    path: PathBuf,
    lua: Arc<Mutex<Lua>>,
    thread: ThreadManager,
    should_stop: Arc<AtomicBool>,
}

impl Script {
    pub fn new(name: &str, path: PathBuf) -> Result<Self> {
        Ok(Self {
            name: name.to_string(),
            path,
            lua: Arc::new(Mutex::new(Lua::new())),
            thread: ThreadManager::new(),
            should_stop: Arc::new(AtomicBool::new(false)),
        })
    }

    pub fn load(&mut self) -> Result<()> {
        let lua = self.lua.lock().unwrap();
        let globals = lua.globals();

        // register API functions here
        globals.set(
            "log",
            lua.create_function(|_, msg: String| {
                info!("{}", msg);
                Ok(())
            })?,
        )?;

        info!("Loaded script: {}", self.name);
        Ok(())
    }

    pub fn start(&mut self) -> Result<()> {
        let script_content = std::fs::read_to_string(&self.path);
        let script_content = match script_content {
            Ok(content) => content,
            Err(_) => {
                return Err(Error::RuntimeError(
                    "failed to read script file".to_string(),
                ));
            }
        };
        let lua_arc_clone = Arc::clone(&self.lua);
        let thread_state = self.thread.get_state(&self.name);
        if thread_state == ThreadState::Stopped {
            let is_started = self.thread.start(&self.name, move || {
                let lua = lua_arc_clone.lock().unwrap();
                lua.load(&script_content).exec().unwrap();
            });
            if !is_started {
                warn!("failed to start thread for: {}", self.name);
            }
        }
        Ok(())
    }

    pub fn stop(&mut self) -> Result<()> {
        let thread_state = self.thread.get_state(&self.name);
        if thread_state == ThreadState::Running {
            self.thread.stop(&self.name);
            info!("script stopped: {}", self.name);
        }
        Ok(())
    }
}

要启动线程:

pub fn start<T>(&mut self, name: &str, t: T) -> bool
where
    T: Fn() + Send + 'static,
{
    let state = Arc::new(Mutex::new(ThreadState::Running));
    let state_clone = Arc::clone(&state);

    thread::spawn(move || loop {
        match *state_clone.lock().unwrap() {
            ThreadState::Running => {
                info!("Running thread");
                t();
            }
            ThreadState::Paused => {
                info!("Paused thread");
                thread::sleep(Duration::from_millis(10));
            }
            ThreadState::Stopped => {
                info!("Stopped thread");
                break;
            }
        }
    });

    self.threads.insert(name.clone().to_string(), state);

    true
}

我试过使用信号,但问题是Lua执行阻塞了ofc的检查。

e37o9pze

e37o9pze1#

您可以调用Thread::reset来终止它。
或者,如果您切换到异步,您可以dropAsyncThread以获得类似的效果。
另一种选择是使用debug.sethook强制Lua VM中断并将控制权交还给Rust代码,但是我认为这会减慢Lua代码的速度,因为它会不断中断自己并等待Rust程序恢复它。

相关问题