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).
This commit is contained in:
@@ -20,6 +20,7 @@ INC += pv/messageQueue.h
|
||||
INC += pv/destroyable.h
|
||||
INC += pv/status.h
|
||||
INC += pv/sharedPtr.h
|
||||
INC += pv/debugPtr.h
|
||||
INC += pv/localStaticLock.h
|
||||
INC += pv/typeCast.h
|
||||
INC += pv/sharedVector.h
|
||||
@@ -40,4 +41,4 @@ LIBSRCS += messageQueue.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
|
||||
LIBSRCS += debugPtr.cpp
|
||||
|
||||
145
src/misc/debugPtr.cpp
Normal file
145
src/misc/debugPtr.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <pv/debugPtr.h>
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
struct tracker {
|
||||
std::mutex mutex;
|
||||
ptr_base::ref_set_t refs;
|
||||
};
|
||||
|
||||
void shared_ptr_base::track_new()
|
||||
{
|
||||
if(track) {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void shared_ptr_base::track_new(void* ptr)
|
||||
{
|
||||
track_clear();
|
||||
if(ptr){
|
||||
track.reset(new tracker);
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_assign(const shared_ptr_base &o)
|
||||
{
|
||||
if(track!=o.track) {
|
||||
track_clear();
|
||||
track = o.track;
|
||||
if(track) {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_clear()
|
||||
{
|
||||
if(track) {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.reset();
|
||||
m_depth = 0;
|
||||
}
|
||||
|
||||
void shared_ptr_base::swap(shared_ptr_base &o)
|
||||
{
|
||||
// we cheat a bit here to avoid lock order, and to lock only twice
|
||||
if(track) {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
track->refs.insert(&o);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.swap(o.track);
|
||||
if(track) {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
track->refs.erase(&o);
|
||||
}
|
||||
//TODO: keep original somehow???
|
||||
snap_stack();
|
||||
o.snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::snap_stack()
|
||||
{
|
||||
if(!track) {
|
||||
m_depth = 0;
|
||||
return;
|
||||
}
|
||||
#if defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
m_depth=backtrace(m_stack,EXCEPT_DEPTH);
|
||||
}
|
||||
#else
|
||||
{}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void shared_ptr_base::show_stack(std::ostream& strm) const
|
||||
{
|
||||
strm<<"ptr "<<this;
|
||||
if(m_depth<=0) return;
|
||||
#if 0 && defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
|
||||
char **symbols=backtrace_symbols(m_stack, m_depth);
|
||||
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<symbols[i]<<", ";
|
||||
}
|
||||
|
||||
std::free(symbols);
|
||||
}
|
||||
#else
|
||||
{
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<std::hex<<m_stack[i]<<" ";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void ptr_base::show_refs(std::ostream& strm, bool self, bool weak) const
|
||||
{
|
||||
if(!track) {
|
||||
strm<<"# No refs\n";
|
||||
} else {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
for(auto ref : track->refs) {
|
||||
if(!self && ref==this) continue;
|
||||
strm<<'#';
|
||||
ref->show_stack(strm);
|
||||
strm<<'\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ptr_base::spy_refs(ref_set_t &refs) const
|
||||
{
|
||||
if(track) {
|
||||
std::lock_guard<std::mutex> G(track->mutex);
|
||||
refs.insert(track->refs.begin(), track->refs.end());
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
322
src/misc/pv/debugPtr.h
Normal file
322
src/misc/pv/debugPtr.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* Author: Michael Davidsaver */
|
||||
/* wrapper around shared_ptr which tracks backwards references.
|
||||
* Can help to find ref. leaks, loops, and other exciting bugs.
|
||||
* See comments in sharedPtr.h
|
||||
*/
|
||||
#ifndef DEBUGPTR_H
|
||||
#define DEBUGPTR_H
|
||||
|
||||
#if __cplusplus<201103L
|
||||
# error c++11 required
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
//! User code should test this macro
|
||||
//! before calling epics::debug::shared_ptr::show_refs()
|
||||
#define HAVE_SHOW_REFS
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
struct tracker;
|
||||
class shared_ptr_base;
|
||||
|
||||
class ptr_base {
|
||||
friend class shared_ptr_base;
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
protected:
|
||||
typedef std::shared_ptr<tracker> track_t;
|
||||
track_t track;
|
||||
|
||||
ptr_base() noexcept : track() {}
|
||||
ptr_base(const track_t& track) :track(track) {}
|
||||
ptr_base(const ptr_base&) = delete;
|
||||
ptr_base(ptr_base&&) = delete;
|
||||
|
||||
ptr_base& operator=(const ptr_base&) = delete;
|
||||
|
||||
public:
|
||||
typedef std::set<const shared_ptr_base *> ref_set_t;
|
||||
void show_refs(std::ostream&, bool self=true, bool weak=false) const;
|
||||
void spy_refs(ref_set_t&) const;
|
||||
};
|
||||
class weak_ptr_base : public ptr_base {
|
||||
protected:
|
||||
weak_ptr_base() {}
|
||||
weak_ptr_base(const track_t& track) :ptr_base(track) {}
|
||||
};
|
||||
class shared_ptr_base : public ptr_base {
|
||||
protected:
|
||||
shared_ptr_base() noexcept
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
:m_stack(), m_depth(0)
|
||||
#endif
|
||||
{}
|
||||
shared_ptr_base(const track_t& track) :ptr_base(track)
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
,m_stack(), m_depth(0)
|
||||
#endif
|
||||
{}
|
||||
~shared_ptr_base() {track_clear();}
|
||||
|
||||
// add ourselves to tracker
|
||||
void track_new();
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void track_new(void* ptr);
|
||||
// copy tracker and add ourself
|
||||
void track_assign(const shared_ptr_base& o);
|
||||
void track_clear();
|
||||
void swap(shared_ptr_base& o);
|
||||
void snap_stack();
|
||||
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
void *m_stack[EXCEPT_DEPTH];
|
||||
int m_depth; // always <= EXCEPT_DEPTH
|
||||
#endif
|
||||
|
||||
public:
|
||||
void show_stack(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr;
|
||||
template<typename T>
|
||||
class weak_ptr;
|
||||
template<class Base>
|
||||
class enable_shared_from_this;
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<T>&, ...) {}
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr : public shared_ptr_base {
|
||||
typedef ::std::shared_ptr<T> real_type;
|
||||
|
||||
real_type real;
|
||||
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
|
||||
// ctor for casts
|
||||
shared_ptr(const real_type& r, const ptr_base::track_t& t)
|
||||
:shared_ptr_base(t), real(r)
|
||||
{track_new();}
|
||||
public:
|
||||
typedef typename real_type::element_type element_type;
|
||||
typedef weak_ptr<T> weak_type;
|
||||
|
||||
// new NULL
|
||||
shared_ptr() noexcept {}
|
||||
// copy existing same type
|
||||
shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
// copy existing of implicitly castable type
|
||||
template<typename A>
|
||||
shared_ptr(const shared_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
|
||||
// construct around new pointer
|
||||
template<typename A, class ... Args>
|
||||
explicit shared_ptr(A* a, Args ... args) : shared_ptr_base(), real(a, args...) {
|
||||
track_new(a);
|
||||
do_enable_shared_from_this(*this, a);
|
||||
}
|
||||
|
||||
// make strong ref from weak
|
||||
template<typename A>
|
||||
shared_ptr(const weak_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
|
||||
~shared_ptr() {}
|
||||
|
||||
shared_ptr& operator=(const shared_ptr& o) {
|
||||
if(this!=&o) {
|
||||
real = o.real;
|
||||
track_assign(o);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename A>
|
||||
shared_ptr& operator=(const shared_ptr<A>& o) {
|
||||
if(get()!=o.get()) {
|
||||
real = o.real;
|
||||
track_assign(o);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() noexcept { real.reset(); track_clear(); }
|
||||
template<typename A, class ... Args>
|
||||
void reset(A* a, Args ... args)
|
||||
{
|
||||
real.reset(a, args...);
|
||||
track_new(a);
|
||||
do_enable_shared_from_this(*this, a);
|
||||
}
|
||||
void swap(shared_ptr &o) noexcept
|
||||
{
|
||||
if(this!=&o) {
|
||||
real.swap(o.real);
|
||||
shared_ptr_base::swap(o);
|
||||
}
|
||||
}
|
||||
|
||||
// proxy remaining to underlying shared_ptr
|
||||
|
||||
T* get() const noexcept { return real.get(); }
|
||||
typename std::add_lvalue_reference<T>::type operator*() const noexcept { return *real; }
|
||||
T* operator->() const noexcept { return real.get(); }
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
explicit operator bool() const noexcept { return bool(real); }
|
||||
|
||||
bool operator==(const shared_ptr<T>& o) const { return real==o.real; }
|
||||
bool operator!=(const shared_ptr<T>& o) const { return real!=o.real; }
|
||||
bool operator<(const shared_ptr<T>& o) const { return real<o.real; }
|
||||
|
||||
template<typename A>
|
||||
bool owner_before(const shared_ptr<A>& o) { return real.owner_before(o); }
|
||||
template<typename A>
|
||||
bool owner_before(const weak_ptr<A>& o) { return real.owner_before(o); }
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename Store, typename Actual>
|
||||
friend void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
};
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::static_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::const_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::dynamic_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class weak_ptr : public weak_ptr_base {
|
||||
typedef ::std::weak_ptr<T> real_type;
|
||||
|
||||
real_type real;
|
||||
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
|
||||
public:
|
||||
typedef typename real_type::element_type element_type;
|
||||
typedef weak_ptr<T> weak_type;
|
||||
|
||||
// new NULL
|
||||
weak_ptr() noexcept {}
|
||||
// copy existing same type
|
||||
weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
// copy existing of similar type
|
||||
template<typename A>
|
||||
weak_ptr(const weak_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
|
||||
// create week ref from strong ref
|
||||
template<typename A>
|
||||
weak_ptr(const shared_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
|
||||
~weak_ptr() {}
|
||||
|
||||
weak_ptr& operator=(const weak_ptr& o) {
|
||||
if(this!=&o) {
|
||||
real = o.real;
|
||||
track = o.track;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename A>
|
||||
weak_ptr& operator=(const shared_ptr<A>& o) {
|
||||
real = o.real;
|
||||
track = o.track;
|
||||
return *this;
|
||||
}
|
||||
|
||||
shared_ptr<T> lock() const noexcept { return shared_ptr<T>(real.lock(), track); }
|
||||
void reset() noexcept { track.reset(); real.reset(); }
|
||||
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
};
|
||||
|
||||
template<class Base>
|
||||
class enable_shared_from_this {
|
||||
mutable weak_ptr<Base> xxInternalSelf;
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
friend
|
||||
void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
public:
|
||||
shared_ptr<Base> shared_from_this() const {
|
||||
return shared_ptr<Base>(xxInternalSelf);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
)
|
||||
{
|
||||
shared_ptr<Actual> actual(dynamic_pointer_cast<Actual>(dest));
|
||||
if(!actual)
|
||||
throw std::logic_error("epics::debug::enabled_shared_from_this fails");
|
||||
self->xxInternalSelf = actual;
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
template<typename T>
|
||||
std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
|
||||
{
|
||||
strm<<ptr.get();
|
||||
return strm;
|
||||
}
|
||||
|
||||
#endif // DEBUGPTR_H
|
||||
@@ -31,6 +31,32 @@
|
||||
* # boost version of tr1/memory
|
||||
*/
|
||||
|
||||
/* Debugging shared_ptr with debugPtr.h requires >= c++11
|
||||
*
|
||||
* Define DEBUG_SHARED_PTR globally to cause epics::debug::shared_ptr
|
||||
* to be injected as std::tr1::shared_ptr and the macro
|
||||
* HAVE_SHOW_REFS will be defined.
|
||||
*
|
||||
* epics::debug::shared_ptr wraps std::shared_ptr with additional
|
||||
* tracking of backwards references.
|
||||
* std::shared_ptr::use_count() gives the number of shared_ptr
|
||||
* (strong refs) to the referenced object.
|
||||
*
|
||||
* If use_count()==5 then epics::debug::shared_ptr::show_refs() will print
|
||||
* 5 lines of the format
|
||||
*
|
||||
* # <addr>: <IP0> <IP1> ...
|
||||
*
|
||||
* Given the numberic address of each shared_ptr as well as the call stack
|
||||
* at the point where it was initialized.
|
||||
* Use the 'addr2line' utility to interpret the stack addresses.
|
||||
*
|
||||
* On linux w/ ASLR it is necessary to turn on static linking to meaningfully
|
||||
* interpret call stack addresses.
|
||||
* Append "STATIC_BUILD=YES" to configure/CONFIG_SITE
|
||||
*/
|
||||
//#define DEBUG_SHARED_PTR
|
||||
|
||||
#if defined(SHARED_FROM_MANUAL)
|
||||
// define SHARED_FROM_MANUAL if from some reason it is desirable to manually select
|
||||
// which shared_ptr implementation to use
|
||||
@@ -59,18 +85,39 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifndef DEBUG_SHARED_PTR
|
||||
|
||||
namespace std {
|
||||
namespace tr1 {
|
||||
using std::shared_ptr;
|
||||
using std::weak_ptr;
|
||||
using std::static_pointer_cast;
|
||||
using std::dynamic_pointer_cast;
|
||||
using std::const_pointer_cast;
|
||||
using std::enable_shared_from_this;
|
||||
using std::bad_weak_ptr;
|
||||
using ::std::shared_ptr;
|
||||
using ::std::weak_ptr;
|
||||
using ::std::static_pointer_cast;
|
||||
using ::std::dynamic_pointer_cast;
|
||||
using ::std::const_pointer_cast;
|
||||
using ::std::enable_shared_from_this;
|
||||
using ::std::bad_weak_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
#else // DEBUG_SHARED_PTR
|
||||
|
||||
#include "debugPtr.h"
|
||||
|
||||
namespace std {
|
||||
namespace tr1 {
|
||||
using ::epics::debug::shared_ptr;
|
||||
using ::epics::debug::weak_ptr;
|
||||
using ::epics::debug::static_pointer_cast;
|
||||
using ::epics::debug::dynamic_pointer_cast;
|
||||
using ::epics::debug::const_pointer_cast;
|
||||
using ::epics::debug::enable_shared_from_this;
|
||||
using ::std::bad_weak_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // DEBUG_SHARED_PTR
|
||||
|
||||
#elif defined(SHARED_FROM_TR1)
|
||||
# include <tr1/memory>
|
||||
|
||||
@@ -101,6 +148,42 @@ namespace std {
|
||||
# undef SHARED_FROM_BOOST
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
struct ref_shower {
|
||||
const std::tr1::shared_ptr<T>& ptr;
|
||||
bool self, weak;
|
||||
ref_shower(const std::tr1::shared_ptr<T>& ptr, bool self, bool weak) :ptr(ptr),self(self),weak(weak) {}
|
||||
};
|
||||
}
|
||||
|
||||
/** Print a list (one per line) of shared_ptr which refer to the same object
|
||||
*
|
||||
* @param ptr Use the object pointed to by this shared_ptr
|
||||
* @param self include or omit a line for this shared_ptr
|
||||
* @param weak include a line for each weak_ptr (not implemented)
|
||||
@code
|
||||
shared_ptr<int> x;
|
||||
std::cout << show_referrers(x);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline detail::ref_shower<T> show_referrers(const std::tr1::shared_ptr<T>& ptr, bool self=true, bool weak=false)
|
||||
{
|
||||
return detail::ref_shower<T>(ptr, self, weak);
|
||||
}
|
||||
|
||||
namespace std{
|
||||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T>& refs)
|
||||
{
|
||||
#ifdef HAVE_SHOW_REFS
|
||||
refs.ptr.show_refs(strm, refs.self, refs.weak);
|
||||
#endif // HAVE_SHOW_REFS
|
||||
return strm;
|
||||
}
|
||||
}//namespace std
|
||||
|
||||
#define POINTER_DEFINITIONS(clazz) \
|
||||
typedef std::tr1::shared_ptr<clazz> shared_pointer; \
|
||||
typedef std::tr1::shared_ptr<const clazz> const_shared_pointer; \
|
||||
|
||||
@@ -45,6 +45,10 @@ testSharedVector_SRCS += testSharedVector.cpp
|
||||
testHarness_SRCS += testSharedVector.cpp
|
||||
TESTS += testSharedVector
|
||||
|
||||
TESTPROD_HOST += testDebugPtr
|
||||
testDebugPtr_SRCS += testDebugPtr.cpp
|
||||
TESTS += testDebugPtr
|
||||
|
||||
TESTPROD_HOST += testSerialization
|
||||
testSerialization_SRCS += testSerialization.cpp
|
||||
testHarness_SRCS += testSerialization.cpp
|
||||
|
||||
280
testApp/misc/testDebugPtr.cpp
Normal file
280
testApp/misc/testDebugPtr.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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
|
||||
Reference in New Issue
Block a user