lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Nov 30, 2007 12:17 AM, John Dunn <John_Dunn@qscaudio.com> wrote:
> [...]
>> Timer.addEventHandler(wrap(function()
>>   while true do
>>     for count = 1, 10 do  -- indeed "count to 10 over and over again"
>>       -- ...
>>       yield()
>>     end
>>   end
>> end))
> [...]
> After thinking about this a little more I have some questions. I assume
> when one yields(), at some point it's assumed it will be resumed?

Yes indeed, see my explanation below.

> It seems like there's ( at least ) 2 ways to approach the handler-
>
> 1. The handler is called via lua_pcall(). The handler function is
> expected to return to give control back to the app. If the function
> doesn't return at some point it's likely bad things will happen.

This is what happens in my example. The handler is first passed to
coroutine.wrap, which creates a coroutine with the handler as its
body, and returns a wrapper function. When called by the timer, the
wrapper resumes the coroutine; and when the coroutine yields, the
wrapper returns. Thus we're hiding the fact that the handler is
implemented as a coroutine.

> 2. The handler is initially called with lua_newthread() but subsequent
> calls are via lua_resume. The handler is expected to return control to
> the app via a call to yield(). If the function doesn't call yield at
> some point it's likely bad things will happen.

Ok, except you need to push the handler function the first time you lua_resume.

> Is one approach more lua-y ( lua-ish? )? Coming from a C++ background
> (1) seems more straightforward but it does seem like (2) is more
> powerful.

You are correct. Quoting Roberto from http://www.lua.org/pil/9.3.html
: "Usually, coroutine.wrap is simpler to use than coroutine.create. It
gives us exactly what we need from a coroutine: a function to resume
it. However, it is also less flexible. There is no way to check the
status of a coroutine created with wrap. Moreover, we cannot check for
errors."

To make that last point clear, let's try throwing errors from
coroutines in the Lua shell.

When using create, you can check the return status of resume:

> c = coroutine.create(function() error "test" end)
> ok, msg = coroutine.resume(c)
> print(ok, msg)
false   stdin:1: test

But when using wrap, the error is propagated:

> f = coroutine.wrap(function() error "test" end)
> f()
stdin:1: stdin:1: test
stack traceback:
        [C]: in function 'f'
        stdin:1: in main chunk
        [C]: ?

So, we need to use pcall to catch errors:

> f = coroutine.wrap(function() error "test" end)
> ok, msg = pcall(f)
> print(ok, msg)
false   stdin:1: test

Bottom line: use explicit coroutines if you care about their status,
otherwise wrapping is probably more handy.

-- 
-- Thomas