[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: block-scope finalization
- From: "Soni L." <fakedme@...>
- Date: Fri, 13 Nov 2015 14:16:38 -0200
On 13/11/15 08:19 AM, Viacheslav Usov wrote:
This may have been discussed earlier; if so, kindly point me to the
previous discussions.
Lua uses garbage collection for everything. User-defined objects can
use metatables to perform appropriate finalization. For example, Lua's
built-in io library use the __gc field in its files' metatable to
close the underlying OS file, even if the user does not call the
file:close function. That works very well, except that finalization is
non-deterministic, i.e., it is impossible to predict when it will happen.
Using the example of files, this may be problematic, because the file
remains open unpredictably long, which may interfere with the other
uses of the file. It could be said that if determinism is important,
the user must ensure that file:close is called. Unfortunately, taking
into account that there can be some very complicated logic between
io.open and file:close, which may also raise Lua errors, this could
lead to extremely unwieldy code.
This problem is not specific to Lua and probably exists in every
GC-collected environment, so there are some established ways of
dealing with it. In C#, for example, this is done via the keyword
'using', which establishes a scope, upon exiting which (including
exiting via an exception), the object is finalized. Example from
https://msdn.microsoft.com/en-us/library/yh598w02.aspx
using (Font font1 =new Font("Arial", 10.0f))
{
byte charset = font1.GdiCharSet;
}
using(Font.new("Arial", 10.0), function(font1)
local charset = font1.GdiCharSet
end)
Where using() runs the function in a coroutine and hooks errors in order
to finalize the font.
local function cleanup(ret)
collectgarbage()
collectgarbage() -- twice to make sure it's collected
return table.unpack(ret, 1, ret.n)
end
function using(...)
local f = select(-1, ...)
local co = coroutine.create(f)
local ret = table.pack(co.resume(...)) -- or something
local errmsg
local yielded = -- process coroutine.yield() or something
-- etc
while co.status() ~= "dead" do
ret = table.pack(co.resume(table.unpack(yielded)))
local status = table.remove(ret, 1) -- remove ret[1], which
contains the status
if status then
-- process coroutine.yield() or something
else
errmsg = table.remove(ret, 1) -- remove ret[1] again, which now
contains the error message
end
end
if co.status() == "dead" and errmsg then
return cleanup(ret) -- pop `...` from the call stack
end
end
Syntactically, this could be transferred to Lua using a new keyword,
such as block, instead of local, to define a local variable that must
be finalized when exiting the block (including error exits). The
finalization will use the __gc methamethod, if present, and subsequent
garbage collection proceeds in the usual manner. For example:
block file = io.open('file')
The use of a new keyword seems necessary so that the user could
control whether this block-scope finalization is to happen, because it
will be unsuitable when the object is supposed to outlive its block.
This could probably be also controlled by some clever metatable
manipulation; but that will make it an 'advanced' feature, limiting
its adoption.
Comments?
Cheers,
V.
--
Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.