scope(exit) & stack => double free or corruption (fasttop) ... help?

Ben Davis entheh at cantab.net
Tue Feb 26 16:24:36 PST 2013


On 26/02/2013 06:21, Charles Hixson wrote:
> On 02/24/2013 05:39 PM, H. S. Teoh wrote:
>> On Sun, Feb 24, 2013 at 03:14:01PM -0800, Charles Hixson wrote:
>>> Given a struct with:
>>>
>>> ~this()
>>> {  close();    }
>>>
>>> void    close()
>>> {  if    (currentKey !is null)    currentKey    =    null;
>>>     if    (cursor is null)    return;
>>>     tcbdbcurdel(cursor);
>>> }
>>>
>>> and:
>>>
>>>    scope (exit)    if (bdb !is null)  tcbdbclose(bdb);
>>>    //scope(exit)    cur.close;       //<<- cur is the struct noted above
>> [...]
>>
>> The struct dtor is automatically called upon exiting the scope, so your
>> scope(exit) here is redundant, and is the cause of the double free.
>>
>>
>> T
>>
>
> Sorry, but that wasn't the answer...which was trivial, when I saw it.  I
> needed to change the close() routine to:
> void    close()
> {  if    (currentKey !is null)    currentKey    =    null;
>     if    (cursor is null)    return;
>     tcbdbcurdel(cursor);
>     cursor  = null;
> }
>
> Apparently the library didn't null the cursor after it closed (deleted) it.

You're both right.

Your struct is presumably declared as a simple local variable, like this:

someFunction() {
   YOUR_STRUCT cur;
   ...
}

What we're saying is that cur's destructor is called automatically as 
soon as execution reaches the }, even if an exception is thrown or a 
break/continue/return/goto jumps out.

So when you write "scope (exit) cur.close();", you're queueing the 
close() to happen under the same circumstances. This doesn't stop the 
destructor call happening. Your destructor calls close() too, so it gets 
called twice.

You should also know that the library you're using *can't* set 'cursor' 
to null for you, because you're in control of the memory location where 
'cursor' is stored (it's inside your struct), and when you pass it to 
tcbdbcurdel(), you're only passing a copy of the value. In order for it 
to set it to null for you, you would have pass a pointer to the value, 
by writing '&cursor'. (This is not strictly true for D or C++ functions 
since they can take 'references' which are implicit pointers, but you 
said it's a C library - and in any case, 'ref' parameters will usually 
be much more obviously 'ref' parameters than this one, if the API is 
well designed.)

So - setting cursor to null is a good safe fix, as it makes it safe to 
call close() more than once - but you also don't need the 'scope 
(exit)', as you can rely on the destructor being called.

(But further to that - you can only rely on the destructor being called 
if it's a struct (and not a pointer to one), or a scope class (a class 
with the 'scope' attribute). Normal classes will be destroyed eventually 
by the GC, but not at a well-defined time, and there's (probably?) no 
guarantee it'll be called before the program exits.)

I guess D isn't as simple as it wanted to be! But it is powerful :)


More information about the Digitalmars-d-learn mailing list