A question about lua_toclose()

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

A question about lua_toclose()

云风 Cloud Wu
I want to use lua_toclose() to close the state of C structure in the C
stack. For example,

static int
close_foo(lua_State *L) {
  lua_getfield(L, 1, "_ptr");
  struct mystruct *foo = (struct mystruct *)lua_touserdata(L, -1);
  mystruct_exit(foo);
  return 0;
}

int
foobar(lua_State *L) {
  struct mystruct foo;
  mystruct_init(&foo);
  lua_settop(L, 0);

  lua_newtable(L);   // index 1
  lua_pushlightuserdata(L, &foo);
  lua_setfield(L, 1, "_ptr");

  lua_newtable(L);
  lua_pushcfunction(L, close_foo);
  lua_setfield(L, -2, "__close");
  lua_setmetatable(L, 1);

  lua_toclose(L, 1);   // index 1 is a to-be-closed "variable"

  do_something(L, &foo);   // may raise error in this function

  lua_pushnil(L);
  lua_setvalue(L, 1);  // ignore to-be-closed variable 1.

  mystruct_exit(&foo);

   return 0;
}

Is safe to call mystruct_exit(foo) in close_foo ?

From the source code of lua 5.4, I think it's safe, because calling
foobar is before raising error, the stack of the C function is valid.

 But I'm not sure from the document :

"Here, in the context of a C function, to go out of scope means that
the running function returns to Lua, there is an error, or the index
is removed from the stack through lua_settop or lua_pop."

There is no precise definition of when it will happen (before or after
out of scope).

--
http://blog.codingnow.com
Reply | Threaded
Open this post in threaded view
|

Re: A question about lua_toclose()

Roberto Ierusalimschy
> I want to use lua_toclose() to close the state of C structure in the C
> stack. For example,
>
> static int
> close_foo(lua_State *L) {
>   lua_getfield(L, 1, "_ptr");
>   struct mystruct *foo = (struct mystruct *)lua_touserdata(L, -1);
>   mystruct_exit(foo);
>   return 0;
> }
>
> int
> foobar(lua_State *L) {
>   struct mystruct foo;
>   mystruct_init(&foo);
>   lua_settop(L, 0);
>
>   lua_newtable(L);   // index 1
>   lua_pushlightuserdata(L, &foo);
>   lua_setfield(L, 1, "_ptr");
>
>   lua_newtable(L);
>   lua_pushcfunction(L, close_foo);
>   lua_setfield(L, -2, "__close");
>   lua_setmetatable(L, 1);
>
>   lua_toclose(L, 1);   // index 1 is a to-be-closed "variable"
>
>   do_something(L, &foo);   // may raise error in this function
>
>   lua_pushnil(L);
>   lua_setvalue(L, 1);  // ignore to-be-closed variable 1.
>
>   mystruct_exit(&foo);
>
>    return 0;
> }
>
> Is safe to call mystruct_exit(foo) in close_foo ?

What do you mean by "safe"? What is exactly your concerns?

(A detail in your code: you do not need the last three calls [pushnil,
setvalue, mystruct_exit]. If you do not set nil, when you return
Lua will call close_foo and then mystruct_exit for you.)

-- Roberto
Reply | Threaded
Open this post in threaded view
|

Re: A question about lua_toclose()

云风 Cloud Wu
Roberto Ierusalimschy <[hidden email]> 于2020年9月2日周三 下午11:52写道:
>
> What do you mean by "safe"? What is exactly your concerns?

I mean, if the C stack unwind before calling __close when raising an
error, the pointer is unsafe. because it's on the C stack.

> (A detail in your code: you do not need the last three calls [pushnil,
> setvalue, mystruct_exit]. If you do not set nil, when you return
> Lua will call close_foo and then mystruct_exit for you.)

I think `close_foo` would be called after `foobar(lua_State *L)`
returns, and the `&foo` is invalid because of C stack unwinding.
(`struct mystruct foo` is on the C stack)
Maybe I can call `lua_settop(L, 0)` before returns to trigger __close,
but calling `mystruct_exit` manually  would be better performance
(IMO).

--
http://blog.codingnow.com
Reply | Threaded
Open this post in threaded view
|

Re: A question about lua_toclose()

Roberto Ierusalimschy
> Roberto Ierusalimschy <[hidden email]> 于2020年9月2日周三 下午11:52写道:
> >
> > What do you mean by "safe"? What is exactly your concerns?
>
> I mean, if the C stack unwind before calling __close when raising an
> error, the pointer is unsafe. because it's on the C stack.

You are thinking in the wrong abstraction level. When Lua calls __close,
the table containing the pointer is given as a parameter. So, it is
in slot #1, as valid as it was before the call. Lua never collects
an object and uses it afterwards.

-- Roberto
Reply | Threaded
Open this post in threaded view
|

Re: A question about lua_toclose()

云风 Cloud Wu


Roberto Ierusalimschy <[hidden email]> 于2020年9月2日周三 下午11:52写道:

What do you mean by "safe"? What is exactly your concerns?

I mean, if the C stack unwind before calling __close when raising an
error, the pointer is unsafe. because it's on the C stack.

You are thinking in the wrong abstraction level. When Lua calls __close,
the table containing the pointer is given as a parameter. So, it is
in slot #1, as valid as it was before the call. Lua never collects
an object and uses it afterwards.


int
foobar(lua_State *L) {
  struct mystruct foo;
  lua_settop(L, 0);

  lua_newtable(L);   // index 1 <—— CLOSED
  lua_pushlightuserdata(L, &foo);
  lua_setfield(L, 1, "_ptr"); // CLOSED._ptr = &foo

  lua_newtable(L);
  lua_pushcfunction(L, close_foo);
  lua_setfield(L, -2, "__close");
  lua_setmetatable(L, 1);

  lua_toclose(L, 1);   // index 1 is a to-be-closed "variable"

  ...

  return 0;
}

Lua calls CLOSED.__close() after foobar() returns, but CLOSED._ptr is the address on the C stackframe of the foobar(). It fells like:

struct mystruct *
foobar() {
    struct mystruct foo;
    mystruct_init(&foo);
    return &foo;
}

struct mystruct *ptr = foobar();
mystruct_exit(ptr);

It’s incorrect because the object foo is on the stack and we can’t use &foo after foobar() returns.

I think the point is that the lifespan of lua function foobar’s stackframe (slot #1) is longer than C stackframe of foobar.





Reply | Threaded
Open this post in threaded view
|

Re: A question about lua_toclose()

Francisco Olarte
On Thu, Sep 3, 2020 at 7:18 PM 云风 <[hidden email]> wrote:

> It’s incorrect because the object foo is on the stack and we can’t use &foo after foobar() returns.
> I think the point is that the lifespan of lua function foobar’s stackframe (slot #1) is longer than C stackframe of foobar.

Your analysis is right, but the problem is caused by putting a pointer
to a stack allocated object ( & foo ) inside what is equivalent to a
heap allocated one ( the table ), and not managing your objects right.
You may need to do it, in which case your solution is probably the
correct one, but the sample smells fishy. Normally when you need to do
this things you pcall() ( or try catch or whatever ) as soon as you
put the pointer inside the heap object, clear it as soon as pcall
returns and make the "destructor" check for pointer nullness or a
similar thing.

It's like doing:
struct foo;
struc x {
   foo * f;
   x(foo * _f): f(_f) {}
   ~x() { do_something(f); }
}
x* a() {
  foo f;
  return new x(&f);
}
x * z = a();
You may need to do that, but it does not look right, x needs to get
foo from someone which ignore proper lifetimes, and you are not
insuring it.

The classic way for this would be to allocate the foo with malloc,
store its lightuserdata in the table ( so it controls the ownership )
and make the close method test, call the exit func and free.

Or more luaesque, allocate foo inside a full userdata, store that in
the table and let lua free it after close automatically.

Francisco Olarte.
Reply | Threaded
Open this post in threaded view
|

Re: A question about lua_toclose()

Roberto Ierusalimschy
In reply to this post by 云风 Cloud Wu
> >>> What do you mean by "safe"? What is exactly your concerns?
> >>
> >> I mean, if the C stack unwind before calling __close when raising an
> >> error, the pointer is unsafe. because it's on the C stack.
> >
> [...]
>
> int
> foobar(lua_State *L) {
>   struct mystruct foo;
>   lua_settop(L, 0);
>
>   lua_newtable(L);   // index 1 <—— CLOSED
>   lua_pushlightuserdata(L, &foo);
>   [...]
>
> Lua calls CLOSED.__close() after foobar() returns, but CLOSED._ptr is the address on the C stackframe of the foobar(). It fells like:
>
> [...]

That is right! Sorry about my wrong answer. I did not realize that 'foo'
was a stack-allocated variable, not a pointer.

As Francisco pointed out, that code "smells fishy" :-) It is
always slippery to put pointers to stack-allocated variables into
heap-allocated ones. It may be worth adding a note in the manual about
this interaction between lua_toclose and stack-allocated variables.

-- Roberto