[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Re[2]: state of the Lua nation on resource cleanup
- From: Cosmin Apreutesei <cosmin.apreutesei@...>
- Date: Mon, 1 Jun 2009 02:16:07 +0300
On Mon, Jun 1, 2009 at 01:50, Peter Cawley <lua@corsix.org> wrote:
> On Sun, May 31, 2009 at 11:22 PM, Cosmin Apreutesei
> <cosmin.apreutesei@gmail.com> wrote:
>> On Mon, Jun 1, 2009 at 00:58, Peter Cawley <lua@corsix.org> wrote:
>>> On Sun, May 31, 2009 at 10:42 PM, Cosmin Apreutesei
>>> <cosmin.apreutesei@gmail.com> wrote:
>>>> With immediately-collected resources, the functionality of f:close()
>>>> remains, as replaced by f = nil. I don't think any control is taken
>>>> away.
>>>>
>>>
>>> Expect "f = nil" would not provide the solid guarantee that the
>>> resource was collected, whereas "f:close()" does. Consider the example
>>> of a database API which only allows one (non-closed) cursor to exist
>>> at any one time. After running a query and using a cursor to iterate
>>> the results, you want to be absolutely sure that the cursor is
>>> collected, as otherwise no other database calls will work. If you
>>> accidently make a copy of "f" to some place, then "f = nil" would not
>>> collect the cursor, and your application would start failing all over
>>> the place. Using "f:close()" would guarantee that the cursor is
>>> closed, and any further operations on this one cursor object would
>>> result in a Lua error, which is (IMO) a much better result than having
>>> the rest of the application's future DB interactions failing.
>>
>> But that's a feature any library could provide if so designed. That
>> database API would surely have a close() method. And if it doesn't,
>> you could encapsulate the handler with a few lines of lua code and
>> provide the close() method yourself.
>>
> Consider the simple implementation of a hypothetical DB handling function:
>
> function DoStuff()
> local query = DB.execute"select * from blah"
> for row in query do
> -- do something with row
> end
> query:close()
>
> query = DB.execute"select * from meh"
> -- etc.
> query:close()
> end
>
> This is fine, unless the "do something with row" code causes an error
> to be thrown, or includes a "return" statement, at which point, the
> execution of DoStuff is terminated, and "query:close()" is never run.
> The next iteration might then look something like the following:
>
> function DoStuff()
> local query = DB.execute"select * from blah"
> local status, err = pcall(function()
> for row in query do
> -- do something with row
> -- NB: return statements no longer cause DoStuff to return
> end
> end)
> query:close()
> if err then error(err) end -- NB: location of error moved, stack
> trace will be affected
>
> query = DB.execute"select * from meh"
> -- etc.
> query:close()
> end
>
> This implementation would cause "query:close()" to run even if "do
> something with row" causes an error, or if it contains a return
> statement. It still has a number of shortcomings though:
> * return statements within the for loop no longer do the same thing
> as they did before
> * the location at which the error happens has changed, which will
> affect any debug stack traces
> * the resource clean-up code is still separated from resource
> acquisition (image the "do something with row" code being very long)
> * the code complexity has increased a fair bit
> * the "do something with row" code cannot call coroutine.yield, as
> it would be yielding across a C call (the pcall)
>
> You could consider another Lua solution which would preserve the
> behaviour of return statements, but this would increase the complexity
> significantly (a robust soluation would have to save a vararg list of
> the return values and all sorts of other stuff). On the other hand,
> consider a hypothetical language-level "with" construct:
>
> function DoStuff()
> local query = DB.execute"select * from blah"
> with query do
> for row in query do
> -- do something with row
> -- NB: return statements work as intended, and error location
> information is preserved
> end
> end
>
> query = DB.execute"select * from meh"
> -- etc.
> query:close()
> end
>
> This achieves the initial goal of ensuring that query:close() is
> called, regardless of how execution leaves the for loop. It does so in
> a short and elegant manner, addressing all of the shortcomings of the
> previous method.
>
The 'with' construct has been reviewed already, and also got critiqued.
- References:
- Re: state of the Lua nation on resource cleanup, Cosmin Apreutesei
- Re: state of the Lua nation on resource cleanup, Cosmin Apreutesei
- Re[2]: state of the Lua nation on resource cleanup, Bulat Ziganshin
- Re: Re[2]: state of the Lua nation on resource cleanup, David Manura
- Re: Re[2]: state of the Lua nation on resource cleanup, Cosmin Apreutesei
- Re: Re[2]: state of the Lua nation on resource cleanup, Matthew Wild
- Re: Re[2]: state of the Lua nation on resource cleanup, Cosmin Apreutesei
- Re: Re[2]: state of the Lua nation on resource cleanup, Peter Cawley
- Re: Re[2]: state of the Lua nation on resource cleanup, Cosmin Apreutesei
- Re: Re[2]: state of the Lua nation on resource cleanup, Peter Cawley