Re: Garbage Collection - The Trash Begins To Pile Up
Peter Dimov wrote:
Andreas Huber wrote:
Moreover, Hans Boehm has shown that under certain circumstances
shared_ptr is unable to release resources correctly even if there
are no cycles ...
<http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html> (see
Appendix B)
.... due to the fact that the release takes place in the thread that
held the last reference to the resource. IIUC, then certain
circumstances *require* non-deterministic release. In other words,
RAII coupled with reference-counting is no silver bullet either.
The problem with this code:
void C::update(boost::shared_ptr<has_foo> other) {
boost::mutex::scoped_lock scoped_lock(mutex);
int count = impl_use_count[my_index];
long new_val = combine(impls[my_index] -> get_data(),
X::messy_fn(other));
if (count > 1) {
// Clone my C_impl.
int new_index = first_available();
impl_use_count[new_index] = 1;
impls[new_index] = new C_impl(new_val);
--impl_use_count[my_index];
my_index = new_index;
} else {
impls[my_index]->set_data(new_val);
}
}
is that it calls a "messy function" while holding a lock.
Right, but under the given circumstances I can see why a programmer
would go the path of least resistance and just lock before the call to
combine. From Hans's description the programmer is not aware that
messy_fn might acquire a lock (for what we know that could not have been
known by messy_fn's programmer himself). The programmer might also not
know that messy_fn is expensive.
It's true
that calling the destructor asynchronously (or not calling it at all
in this case since GC will take care of the impls) can prevent some
of the problems with this approach. But certainly not all. A simple
illustration is
long X::messy_fn( x )
{
C c;
return 0;
}
Correct, but given its semantics, messy_fn isn't supposed to do that.
It's only supposed to take a given C object and compute a value.
In addition, holding the class level C lock while waiting for
X::messy_fn is a performance problem as well.
Definitely, but the programmer implementing C::update() might not know
that messy_fn is expensive. The cache inside messy_fn might conceal
that.
[snip]
Why not just
void C::update(boost::shared_ptr<has_foo> other)
{
long new_val = combine(pimpl_ -> get_data(),
X::messy_fn(other));
pimpl_ = create_C_impl( new_val );
}
where pimpl_ is a shared_ptr<C_impl> and create_C_impl is something
like
shared_ptr<C_impl> create_C_impl( long value ) synchronized
{
if( cache contains value )
{
return cached impl;
}
else
{
shared_ptr<C_impl> pci( new C_impl( value ) );
update_cache_with( pci ); // depending on cache policy
return pci;
}
}
As the author acknowledges, there are ways around the problem, no doubt
about that. But these only become apparent when you have a look at
messy_fn's implementation. That is, it seems you need to break the
abstraction created by messy_fn, although messy_fn does not do anything
unreasonable.
--
Andreas Huber
When replying by private email, please remove the words spam and trap
from the address shown in the header.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]