[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: C++, pcall, and yield
- From: Mike Pall <mikelu-0604@...>
- Date: Tue, 18 Apr 2006 15:18:39 +0200
Hi,
sorry for the late reply. Just catching up with messages after
vacation. Long reply follows:
Adam D. Moss wrote:
> I think, however, that LuaJIT will always require 'real'
> C-coroutine support for yielding from JIT'd code, hence an
> unbreakable Coco dependence there (unless you want yielding
> to be even pickier than regular Lua - I live with this but
> it's a bit fiddly); I don't think the RVM approach can cut
> it as-is. I guess Mike would know for sure.
LuaJIT is pretty much chained to Coco. Even if a future Lua core
would be changed to include RVM, I'd have to rip it out and
replace it with Coco.
The reason behind this is a bit difficult to explain. Let's just
say that the Lua interpreter has only a few execution paths where
C stack unwinding/rewinding is required. These can be accounted
for with a limited amount of code. And RVM doesn't really rewind
the C stack -- it uses a sawtooth call pattern because this is
the only portable approach. This is why you need to write all of
your resumable C functions in a particular way.
OTOH LuaJIT adds a plethora of new code paths (specialization,
inlining) and directly encodes them into machine code. Manually
rewinding the C stack for all of these cases would be awfully
complicated. And it would need lots of adjunct data structures to
capture all of the complexity.
I guess RVM would seriously inhibit the evolution of LuaJIT. Many
advanced optimizations (like shadow slots on the C stack) are
almost impossible to handle.
Comparing this to Coco, yielding from C (or from LuaJIT generated
machine code) is dead simple. Just call lua_yield() and you're
done.
About Coco's portability: yes, it's not ANSI C, but it's still
very portable. It's hard to find a platform where it doesn't
work. If inline assembler or setjmp buffer patching makes you
uneasy: it works fine -- that's all that counts. Often enough
it's easier to add some OS or machine specific code than relying
on a portable solution which has too many side effects (see the
DirectX vs. lua_number2int() discussion).
Mark Hamburg wrote:
> Will LuaJIT work when Lua's exception mechanism is redefined to use C++? Or
> does the stack switching confuse the C++ exception mechanism?
AFAIK yes. LuaJIT only runs on x86 right now. The Windows
per-thread exception pointer is taken care of with Fibers. Most
x86 platforms I know of use either frame pointer unwinding (and
LuaJIT leaves the frame-pointer alone) or in the absence of it
unwind records (e.g. DWARF2 with GCC). Both should work (but see
below).
I have no good answer about the compatibility of C++ and stack
switching in general (i.e. Coco and not just LuaJIT). One has to
check the docs or just try it on each platform one needs to
support. :-/
> I know this is a likely issue with Apple's Objective-C/Cocoa runtime unless
> one hacks in to swap private globals appropriately. Most C++ runtimes don't
> even expose that information.
Since I've discussed this privately with Mark before, I should
note that the outcome was that RVM does _not_ solve the ObjC
issue and is generally incompatible with it. But there was a
relatively simple (untested) solution for Coco.
Greg Falcon wrote:
> I really like Lua's approach to C/C++. The benefit of using of
> try/catch for errors in C++ is easily misunderstood. Its benefit is
> not that it will catch unhandled exceptions, and its use is more than
> just a "let's use this because it's there" measure. Because try/catch
> property unwinds the stack (where setjmp/longjmp doesn't), you can use
> C++ objects inside your Lua C++ functions, and they will be property
> destructed if an error is thrown (or, in this patch, if a yield occurs
> in your resumable Lua C++ function). That's something I'm not at all
> ready to give up. :)
But there's a caveat which has been missed in the previous
discussion: there is no guarantee that it's safe to unwind across
the Lua core. Even if you redefine the Lua exception mechanism to
use try/except.
The reason can be had by turning your argument around: Lua is
written in C and does not hook into the C++ exception mechanism.
So it cannot restore its state in all places it may need to.
E.g. throwing a C++ exception and catching it with pcall() may
create an invalid Lua stack slot (L->status is -1,
luaD_seterrorobj doesn't handle this case, but still increments
the stack). This may be considered a bug (RVM solves this by
defining LUA_ERREXC).
Just assuming you can throw an exception through the Lua core
without affecting its internal state is bound to break in special
circumstances or in future Lua versions.
The only safe way to handle C++ exceptions is to catch them on
the C++ side and convert them to Lua errors or appropriate return
values (nil + error string convention).
Roberto Ierusalimschy wrote:
> Something like Coco solves the problem in a much simpler and a little
> more generic way. But, yes, it is not portable, and so we do not intend
> to incorporate it to the Lua core. But we still feel uneasy to add that
> complexity in the core to solve something that can be solved in a much
> simpler way by external means. (Maybe time may cure our uneasiness...)
Considering that I wrote both Coco and RVM, this may be a
surprising opinion -- I've changed my mind several times on this
issue: IMHO neither should be added to the Lua core right now.
Both solutions have their benefits and drawbacks. This issue
cannot be completely solved while at the same time keeping your
primary goals for the Lua core (simplicity and portability).
The question is whether a partial solution is desirable and/or
acceptable (see below).
Adam D. Moss wrote:
> Maybe digesting both approaches will magically inspire a third
> approach which is both simple and portable. :)
The "yield across pcall" issue could be solved in a different way
by changing the C API (not relying on an exception handler on the
C stack for it). Basically pcall() would need to tag the current
frame as exception catching, set up the new frame and return to
the Lua core with a special return value signalling a call to the
new frame and a handler which catches any exceptions or untags
the current frame.
The "yield from C" issue can only be worked around. RVM has
lua_vyield(), but this becomes cumbersome when you need to
yield from inside several C call levels. Still much better than
not having the option.
The "yield from everywhere" issue (metamethods, iterators and so
on) may be partly solved by restructuring the Lua core (using the
pcall mechanism outlined above). But this is not a solution for C
callbacks into the Lua core (e.g. lua_gettable).
Bye,
Mike