[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Question regarding handling lua strings inside C API
- From: Alexey Melnichuk <alexeymelnichuck@...>
- Date: Sun, 5 Apr 2015 10:54:34 +0400
Здравствуйте, William.
Вы писали 5 апреля 2015 г., 7:34:01:
> On Sat, Apr 04, 2015 at 04:55:25PM -0700, raksoras lua wrote:
>> I understand that C code generally should not store the pointer to a
>> string returned by lua_tostring beyond the lifetime of the C function
>> call.(PIL says "The lua_tostring function returns a pointer to an
>> internal copy of the string. Lua ensures that this pointer is valid as
>> long as the corresponding value is in the stack. When a C function
>> returns, Lua clears its stack; therefore, as a rule, you should never
>> store pointers to Lua strings outside the function that got them.")
>>
>> However, is this restriction strictly true if I can guarantee - from
>> my code flow - that the original string in lua will continue to be in
>> reachable (that is, it is not garbage collected) when my C function
>> call returns?
> AFAIK, yes, as long as you can guarantee that the string is anchored.
>
>> Here is my actual scenario: Luaw HTTP server uses Lua coroutines to
>> handle multiple connections simultaneously. While generating response
>> a Lua coroutine calls a C function with Lua string that the C function
>> is eventually supposed to write to socket. But since the socket write
>> may block, the C function actually uses async write API (libuv to be
>> specific) to write to the socket. The function then returns
>> immediately and the Lua coroutine that called the C function yields to
>> suspend itself. When the socket is ready for the actual write, libuv's
>> async API invokes a C callback that writes the string on the socket
>> and then resumes the suspended coroutine.
>
>> Now in classical model I should make a copy of the string passed into
>> the first C function as the function will return before the callback
>> has a chance to write the string onto the socket. However I want to
>> avoid the data copy (memcpy) if I can get away with it for performance
>> reasons. My rationale is even though the original C function has
>> returned the Lua coroutine that called the C function gets suspended
>> as soon as C function call returns. So the original string that was
>> passed in the C call is still reference-able from the Lua coroutine
>> and hence should not be garbage collected.
> This is only true if the string were stored in or through a local variable.
> But what if I do something like this:
> socket:write(myobject:createstring())
> The string isn't stored in any intermediate variable. The stack frame is
> cleared when you yield (see luaD_poscall in Lua 5.3). The GC can and very
> well might collect the string because it's not anchored anywhere.
>> So I should be able to just store the pointer to the internal Lua string
>> returned by lua_tostring() without making a copy of it. When the write
>> callback is called it first calls a C function - kind of a continuation of
>> first C call - that writes the string onto the socket and then resumes Lua
>> coroutine. This way I can guarantee that the original Lua string's
>> lifetime is more than the two C calls - original call and then the
>> callback invoked by libuv - involved.
>>
>> Is it safe to not make a copy of a lua string in C function in this case?
> In order to make your optimization work, you need to be sure that the string
> is stored in a local for the duration of the yield. The only way to
> guarantee that is to add an extra call with a function written by yourself
> that you know stores the string in a local variable. For example:
> function lib:write(str)
> return self:uvwrite(str)
> end
> The problem is that Lua will turn this into a tail call and optimize away
> our str local. So we have to do something like:
> function lib:write(str)
> local retval = self:uvwrite(str)
> return retval
> end
> It's possible Lua might optimize away the str local here, too. Certainly
> LuaJIT is likely to optimize it away. You might need to add some function
> call or other crazy call after :uvwrite and before the return statement.
> If you're using Lua 5.2 or 5.3 you can use an intermediate C function to
> preserve the stack frame. C function code and its stack can't be optimized
> away. Using the 5.3 API you can do roughly something like the following:
> static int lib_write(lua_State *L) {
> int nargs = lua_gettop(L);
> int i;
> /*
> * push our real libuv write binding. this would be quicker
> * if it were cached as an upvalue.
> */
> lua_pushcfunction(L, &lib_uvwrite);
> /*
> * copy arguments so originals remain alive on our stack
> * frame for the duration of the call.
> */
> for (i = 1; i <= nargs; i++)
> lua_pushvalue(L, i);
> return lua_callk(L, nargs, NRET, 0, &lib_poswrite);
> }
> /* return from our call to lib_uvwrite */
> static int lib_poswrite(lua_State *L, int status, lua_KContext ctx) {
> assert(status == LUA_YIELD);
> assert(lua_gettop(L) >= NRET);
> return NRET;
> }
> static int lib_uvwrite(lua_State *L) {
> /*
> * here's your original binding that called into libuv.
> */
> }
--
С уважением,
Alexey mailto:alexeymelnichuck@gmail.com
---
Это сообщение проверено на вирусы антивирусом Avast.
http://www.avast.com