2
votes

Background: I'm using a Lua thread (coroutine) to process user input from stdin (to allow the program to pause while waiting on data from another FD). Because it's user input, errors are possible if not probable, e.g. calling a non-existent function.

Question: Can the Lua thread be recovered so that I can continue to process more data from stdin, or do I have to nuke the thread and create a new one after every error?

Here's some rough sample/pseudo code of what I'm doing now:

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??? */
    }
  }
}

Typically, I would use pcall to catch the error and recover, but pcall doesn't support the yield in 5.1 (though 5.2 may be a good solution here). With the lua_resume call, I'm running into the following in my session:

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

After the first command, the thread status is set to 2 (LUA_ERRRUN).

Edit: the error message I'm getting doesn't appear to be because of the unwound stack. I'm seeing this message from ldo.c which indicates the issue is because the thread state is getting set to 2.

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

So I either need a way to reset the state or avoid having the state change in the first place. My guess is that I may be stuck with popping the thread off my main stack and recreating a new one, or upgrading to 5.2 so I can yield from a pcall.

2
Is this actually asynchronous, or the Lua co-routines?Puppy
co-routines, this application is single threaded using Lua coroutines for the yield functionality.BMitch
I went ahead and upgraded to Lua 5.2. The solution there is to push a pcall on the stack, then run lua_loadbuffer, and when the lua_resume status is 0, you have the check the pcall result to see if it was true or false. Much cleaner than the other options I was looking at.BMitch

2 Answers

1
votes

As far as I know, throwing an error unwinds the stack inside the coroutine, which means there is no function to jump back into. (The reference manual says nothing regarding this.)

Seems like you'll have to create a new thread.

0
votes