lua-users home
lua-l archive

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


It was thus said that the Great Lorenzo Donati once stated:
> On 29/05/2020 22:57, Sean Conner wrote:
> >It was thus said that the Great Phil Leblanc once stated:
> >>When os.exit() is called, by default the finalizer for <close>
> >>variables is not executed. If the second (optional) argument to
> >>os.exit() is true, then the Lua state is closed, and finalizers are
> >>executed.
> >>
> 
> I didn't realize that!

  In Lua 5.1, os.exit() calls exit().  In fact, the function is quite short
and reproduced here:

	static int os_exit (lua_State *L) {
	  exit(luaL_optint(L, 1, EXIT_SUCCESS));
	}

  The problem is that the Lua state isn't cleanly closed.  If one wants to
cleanly close the Lua state upon os.exit() in Lua 5.1, one would have to
register a cleanup function via atexit().  Something like this:

	lua_State *gL;	/* global Lua state */

	static void cleanup(void)
	{
	  if (gL) /* to prevent a double call to lua_close(); */
	    lua_close(gL);
	}

	int main(void)
	{
	  gL = luaL_newstate();
	  atexit(cleanup);
	  /* ... rest of code */
	  lua_close(gL);
	  return 0; /* functions registered with atexit() will run */
	}

  This may have been an oversight on the Lua team, but as they have always
stated, the lua exectuable has *always* been an example of how to embed Lua. 
And on most modern systems (POSIX definitely, possibly Windows these days),
the operating system will reclaim any resources used by a process upon
shutdown *anyway*.

  This was rectified with Lua 5.2, but to remain compatible with Lua 5.1
code, they decided to add a parameter to os.exit() to indicate a clean
closing of the Lua state, so the function now looks like:

	static int os_exit (lua_State *L) {
	  int status;
	  if (lua_isboolean(L, 1))
	    status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE);
	  else
	    status = luaL_optint(L, 1, EXIT_SUCCESS);
	  if (lua_toboolean(L, 2))
	    lua_close(L);
	  if (L) exit(status);  /* 'if' to avoid warnings for unreachable 'return' */
	  return 0;
	}

  Why they decided to keep this level of backwards compatability when they
drastically changed how modules and global variables work between 5.1 and
5.2 is beyond me, especially when a workaround for Lua 5.1 (as illustrated
above) was available.

  But we have what we have.

> I agree (reluctantly) on maintaining the backwards compatibility, but 
> <close> finalizers are different things. I would find rather unexpected 
> for them non being called on exit.

  Technically speaking, they don't leave the scope.  The Lua VM doesn't know
that os.exit() actually leaves the scope (or rather, that it leaves the VM
entirely).  To the Lua VM, this:

	do
	  local x <close> = io.open("foobar")
	  os.clock(0)
	end -- now we leave scope, x is closed

and this:

	do
	  local x <close> = io.open("foobar")
	  os.exit(0)
	end -- now we leave scope, x is closed

are the "same"---a to-be-closed variable is created, a function (from the
'os' table) with one parameter is called, then the scope ends, so call
x:__close().

  At least, I *think* this is the case---I haven't actually tested this, but
I think my reasoning is sound on this.

> If one really wants NOT to call __close on exit, it would be better to 
> add another argument to exit (I know, ugly), or maybe define a new 
> behavior for exit when the second arg is a string, so that the string 
> specifies what to call or what not, e.g.:
> 
> os.exit( exit_code, 'noclose, nogc')

  I'm not sold on that.

> Moreover, imagine the confusion when integrating different codebases, 
> which is not uncommon in Lua ecosystems, with all the different small 
> libraries out there.
> 
> If everyone had their own safeexit, with different signature and name, a 
> real hell!

  Is this a real problem, or a hypothetical problem?

  -spc