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