【问题标题】:How to handle errors when resuming a Lua thread (coroutine)恢复 Lua 线程(协程)时如何处理错误
【发布时间】:2011-07-08 14:12:41
【问题描述】:

背景:我正在使用 Lua 线程(协程)来处理来自标准输入的用户输入(以允许程序在等待来自另一个 FD 的数据时暂停)。因为它是用户输入,所以即使不可能,也可能出现错误,例如调用一个不存在的函数。

问题:能否恢复 Lua 线程,以便我可以继续处理来自 stdin 的更多数据,或者我是否必须在每次错误后取消线程并创建一个新线程?

这是我现在正在做的一些粗略的示例/伪代码:

while (1) {
  select((max, &read_fds, &write_fds, NULL, NULL);
  for each file descriptor {
    if (read fd is set) {
      read data into a buffer
      if (current fd is stdin)
        process_stdin()
      else if (current fd is from server connection)
        process_remote()
    }
    if (write fd is set) {
      write data on non-blocking fd
    }
  }
}

process_stdin() {
  status=luaL_loadbuffer(L, stdin_buf, len, "stdin");
  if (status == LUA_ERRSYNTAX) {
    /* handle EOF which means more user input needed
     * or print error message for user, this works fine */
  }
  else if (status == 0) {
    status=lua_resume(L, 0);
    if (status != 0 && status != LUA_YIELD) {
      /* Do I nuke the thread or is there another way to recover at this point??? */
    }
  }
}

通常,我会使用pcall 来捕获错误并恢复,但pcall 不支持 5.1 中的产量(尽管 5.2 在这里可能是一个很好的解决方案)。通过lua_resume 通话,我在会话中遇到了以下问题:

> no_such_func()
Error: attempt to call global 'no_such_func' (a nil value)
> print("hello world")
Error: cannot resume non-suspended coroutine

在第一个命令之后,线程状态设置为 2 (LUA_ERRRUN)。

编辑:我收到的错误消息似乎不是因为展开的堆栈。我从 ldo.c 看到这条消息,这表明问题是因为线程状态设置为 2。

  if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))
      return resume_error(L, "cannot resume non-suspended coroutine");

所以我要么需要一种方法来重置状态,要么首先避免改变状态。我的猜测是,我可能会坚持将线程从主堆栈中弹出并重新创建一个新的,或者升级到 5.2 以便我可以从 pcall 中获得收益。

【问题讨论】:

  • 这实际上是异步的,还是 Lua 协程?
  • 协程,这个应用程序是单线程的,使用 Lua 协程来实现 yield 功能。
  • 我继续升级到 Lua 5.2。解决方案是将pcall 压入堆栈,然后运行lua_loadbuffer,当lua_resume 状态为0 时,您可以检查pcall 结果以查看它是真还是假。比我看到的其他选项干净得多。

标签: c lua


【解决方案1】:

据我所知,抛出错误会展开协程内的堆栈,这意味着没有函数可以跳回。 (参考手册对此只字未提。)

看来您必须创建一个新线程。

【讨论】:

  • 展开堆栈没问题。示例中的打印函数在下一次从标准输入读取时被压入堆栈。但由于线程状态为 2,Lua 不会运行它。
  • @BMitch:不,展开堆栈 OK:这就是协程无法恢复的原因。它被认为已完成,因为堆栈未展开到协程的基础。这样协程就结束了。
  • @Nicol Bolas:协程每次处理完来自标准输入的一行时都会结束,然后在您传递另一行时重新启动(通过luaL_loadbuffer)。没有错误,lua_resume 调用提供了与 lua.c 中使用的lua_pcall 类似的功能。我的堆栈不为空的唯一一次是例程是否产生,此时我不会再加载任何行,直到 lua_resume 停止产生。
【解决方案2】:

对于 Lua 5.1,您可以使用 coxpcall

【讨论】:

  • 不理想,但我认为这可以作为 5.1 的答案。看起来他们在父协程中包装了一个协程,以便可以来回传递挂起/恢复,并且错误被捕获而不是冒泡。看到这有多不理想,我想我是时候尝试一下 5.2 了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
相关资源
最近更新 更多