Files
pvData/testApp/misc/testDebugPtr.cpp
Michael Davidsaver 568ee1fa85 add debugPtr.h to troubleshoot shared_ptr problems
A wrapper around shared_ptr which tracks backwards references
to help untangle complicated ownership situations (aka. ref loop
waiting to happen).
2017-06-26 15:59:45 +02:00

281 lines
6.3 KiB
C++

/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* Author: Michael Davidsaver */
#include <iostream>
#include <sstream>
#include <exception>
#include <ostream>
#include <iterator>
#include <epicsUnitTest.h>
#include <testMain.h>
#if __cplusplus>=201103L
#include <pv/debugPtr.h>
namespace {
void show(const epics::debug::shared_ptr_base& ref)
{
epics::debug::ptr_base::ref_set_t refs;
ref.spy_refs(refs);
std::ostringstream strm;
std::copy(refs.begin(), refs.end(), std::ostream_iterator<const void*>(strm, ", "));
testDiag("refs: %s", strm.str().c_str());
ref.show_refs(std::cout);
}
void testEmpty()
{
testDiag("testEmpty()");
epics::debug::shared_ptr<int> empty;
testOk1(!empty);
epics::debug::ptr_base::ref_set_t refs;
empty.spy_refs(refs);
testOk1(refs.empty());
}
void testSimple()
{
testDiag("testSimple()");
testDiag("ctor");
epics::debug::shared_ptr<int> one(new int(42)),
two;
testDiag("one = %p two = %p", &one, &two);
show(one);
show(two);
{
epics::debug::ptr_base::ref_set_t refs;
one.spy_refs(refs);
testOk1(refs.size()==1);
testOk1(refs.find(&one)!=refs.end());
testOk1(refs.find(&two)==refs.end());
}
testDiag("assign non-NULL");
two = one;
show(one);
show(two);
{
epics::debug::ptr_base::ref_set_t refs;
one.spy_refs(refs);
testOk1(refs.size()==2);
testOk1(refs.find(&one)!=refs.end());
testOk1(refs.find(&two)!=refs.end());
}
testDiag("reset");
one.reset();
show(one);
show(two);
{
epics::debug::ptr_base::ref_set_t refs;
one.spy_refs(refs);
testOk1(refs.size()==0);
}
{
epics::debug::ptr_base::ref_set_t refs;
two.spy_refs(refs);
testOk1(refs.size()==1);
testOk1(refs.find(&one)==refs.end());
testOk1(refs.find(&two)!=refs.end());
}
testDiag("copy ctor");
epics::debug::shared_ptr<int> three(two),
empty(one);
show(three);
show(empty);
testDiag("three = %p empty = %p", &three, &empty);
{
epics::debug::ptr_base::ref_set_t refs;
empty.spy_refs(refs);
testOk1(refs.size()==0);
}
{
epics::debug::ptr_base::ref_set_t refs;
three.spy_refs(refs);
testOk1(refs.size()==2);
testOk1(refs.find(&one)==refs.end());
testOk1(refs.find(&two)!=refs.end());
testOk1(refs.find(&three)!=refs.end());
testOk1(refs.find(&empty)==refs.end());
}
}
struct Base {virtual ~Base(){}};
struct Derv : public Base {virtual ~Derv(){}};
void testCast()
{
testDiag("testCast()");
epics::debug::shared_ptr<int> one(new int(42));
testOk1(!!one);
epics::debug::shared_ptr<void> two(one);
testOk1(one.get()==two.get());
epics::debug::shared_ptr<const int> three(one);
testOk1(one.get()==three.get());
epics::debug::shared_ptr<int> four(epics::debug::const_pointer_cast<int>(three));
testOk1(one.get()==four.get());
epics::debug::shared_ptr<Base> X(new Derv);
epics::debug::shared_ptr<Derv> Y(epics::debug::static_pointer_cast<Derv>(X));
testOk1(X.get()==Y.get());
}
void testWeak()
{
testDiag("testWeak()");
epics::debug::shared_ptr<int> one(new int(42));
{
epics::debug::weak_ptr<int> two(one);
epics::debug::shared_ptr<int> three(two);
testOk1(three.get()==one.get());
}
{
epics::debug::weak_ptr<int> two(one);
epics::debug::shared_ptr<int> three(two.lock());
testOk1(three.get()==one.get());
}
}
struct MySelf : public epics::debug::enable_shared_from_this<MySelf>
{};
void testEnable()
{
testDiag("testEnable()");
epics::debug::shared_ptr<MySelf> self(new MySelf),
other(self->shared_from_this());
testOk1(!!self);
testOk1(self.get()==other.get());
}
template<typename T>
struct set_flag_dtor {
bool *pflag;
set_flag_dtor(bool *pflag) :pflag(pflag) {}
void operator()(T* x) {
delete x;
*pflag = true;
pflag = 0; // paranoia
}
};
void testDtor()
{
testDiag("testDtor()");
bool flag = false;
{
epics::debug::shared_ptr<Derv> x(new Derv, set_flag_dtor<Derv>(&flag));
testOk1(!!x);
testOk1(!flag);
}
testOk1(flag);
flag = false;
bool flag2 = false;
{
epics::debug::shared_ptr<Derv> x(new Derv, set_flag_dtor<Derv>(&flag));
testOk1(!!x);
Derv *old = x.get();
testOk1(!flag);
testOk1(!flag2);
x.reset(new Derv, set_flag_dtor<Derv>(&flag2));
testOk1(!!x);
testOk1(old!=x.get());
testOk1(flag);
testOk1(!flag2);
}
testOk1(flag2);
testDiag("destroy Derv as Base");
flag = false;
{
epics::debug::shared_ptr<Base> x(new Derv, set_flag_dtor<Base>(&flag));
testOk1(!!x);
testOk1(!flag);
}
testOk1(flag);
}
struct BaseEnable {
virtual ~BaseEnable() {}
virtual epics::debug::shared_ptr<BaseEnable> self() =0;
};
struct DervEnable : public BaseEnable,
public epics::debug::enable_shared_from_this<DervEnable>
{
virtual ~DervEnable() {}
virtual epics::debug::shared_ptr<BaseEnable> self() {
return shared_from_this();
}
};
void testEnableDerv()
{
testDiag("testEnableDerv()");
epics::debug::shared_ptr<BaseEnable> x(new DervEnable),
y(x->self());
testOk1(!!x);
testOk1(x.get()==y.get());
BaseEnable *old = x.get();
x.reset();
testOk1(!x);
testOk1(y.unique());
x.reset(new DervEnable);
y = x->self();
testOk1(!!x);
testOk1(x.get()!=old);
testOk1(x.get()==y.get());
}
} // namespace
MAIN(testDebugPtr)
{
testPlan(48);
try {
testEmpty();
testSimple();
testCast();
testWeak();
testEnable();
testDtor();
testEnableDerv();
}catch(std::exception& e){
testAbort("Unhandled exception: %s", e.what());
}
return testDone();
}
#else // __cplusplus>=201103L
MAIN(testDebugPtr)
{
testPlan(1);
testSkip(1, "Not c++11");
return testDone();
}
#endif // __cplusplus>=201103L