Nobody said we write tests to catch every possible bug.
Next, much of C++ is simply exposed plumbing. Things like smart pointers. I
can think of a test-first for a pointer, and one to make it smart, but one
generally doesn't bother with such things. The point is to make tests that
reduce debugging and help designing.
That is the problem. Usually code that no one test, make those
problems.
For example I have yet to see thread safe reference counted object
destruction. I have to admit I don't know how to write one, therefore
addref/release when sharing object across threads is controlled
and black boxed in my code.
This post ignited me to inspect boost::shared_ptr implementation
for ref count destruction thread safety.
Code uses add_ref_lock to increase reference usage and
release to decrease. It has internal reference count that when
it reaches 0 results in call to "delete this".
Function add_ref_lock has safety check for use_count_ if 0,
but it makes assumption that between acquisition of mutex
and check for 0 object will always exist.
Function "release", decreases use_count_ , releases lock ,
if 0, calls dispose of object, then decreases reference count
of object itself by calling weak_release.
Function weak_release locks same mutex again and if 0,
does destruct, which is delete this.
Problem: From this code I can't see anything that stops
object being destructed in one thread, while other
thread is blocked in add_ref_lock waiting to acquire
on same mutex( which is part of object).
Only thing that I can hope for is that mutex is some
special kind that *assures* that other thread will
acquire mutex between two refcount releases,
but IMO without looking, I guess this is not so, as
code to use mutex looks pretty generic.
So if I am right (could be wrong though, just looked the code
and I am really tired), and your company for example,
uses this ptr in mt code, then there is bug hanging around
which is neither tested nor inspected?
I think that none of the test cases would hit this bug in foreseeable
future, so inspection by eye is most efficient in this case?
code follows (boost 1-32):
void add_ref_lock()
{
#if defined(BOOST_HAS_THREADS)
mutex_type::scoped_lock lock(mtx_);
#endif
if(use_count_ == 0)
boost::throw_exception(boost::bad_weak_ptr());
++use_count_;
}
void release() // nothrow
{
{
#if defined(BOOST_HAS_THREADS)
mutex_type::scoped_lock lock(mtx_);
#endif
long new_use_count = --use_count_;
if(new_use_count != 0) return;
}
dispose();
weak_release();
}
void weak_add_ref() // nothrow
{
#if defined(BOOST_HAS_THREADS)
mutex_type::scoped_lock lock(mtx_);
#endif
++weak_count_;
}
void weak_release() // nothrow
{
long new_weak_count;
{
#if defined(BOOST_HAS_THREADS)
mutex_type::scoped_lock lock(mtx_);
#endif
new_weak_count = --weak_count_;
}
if(new_weak_count == 0)
{
destruct();
}
}
When you make a test fail, you carefully inspect that it is failing for the
right reason. The workflow here is to run the test and announce out loud to
your pair what you expect the test to do. It should "fail because the target
method doesn't exist", or "fail on that assertion because the method returns
the old value", or "pass", etc.
This tests the tests. If they fail for the wrong reason, you stop and
re-evaluate things.
Problem with this is when code imposes undefined behavior, you cannot
say neither that test will pass or not for any reason. So before doing
any testing, code should be inspected by eye to check for possible ub,
then test.
Greetings, Branimir.