Commit c9079be5 authored by Davis King's avatar Davis King

Added a thread safe shared pointer object

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402530
parent 6fbcf3ac
......@@ -39,6 +39,9 @@ namespace dlib
This object is not thread safe. Especially so since it is
reference counted. So you should take care to not have two shared_ptr
objects in different threads that point to the same object.
If you want a thread safe version of this object you should use the
dlib::shared_ptr_thread_safe object instead.
!*/
public:
......
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_SHARED_THREAD_SAFE_PTr_
#define DLIB_SHARED_THREAD_SAFE_PTr_
#include <algorithm>
#include <memory>
#include <typeinfo>
#include <string> // for the exceptions
#include "../algs.h"
#include "shared_ptr_thread_safe_abstract.h"
#include "../threads.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
struct shared_ptr_thread_safe_deleter
{
virtual void del(const void* p) = 0;
virtual ~shared_ptr_thread_safe_deleter() {}
virtual void* get_deleter_void(const std::type_info& t) const = 0;
/*!
ensures
- if (the deleter in this object has typeid() == t) then
- returns a pointer to the deleter
- else
- return 0
!*/
};
struct shared_ptr_thread_safe_node
{
shared_ptr_thread_safe_node(
) :
ref_count(1),
del(0)
{}
dlib::mutex m;
long ref_count;
shared_ptr_thread_safe_deleter* del;
};
struct shared_ptr_ts_static_cast {};
struct shared_ptr_ts_const_cast {};
struct shared_ptr_ts_dynamic_cast {};
// ----------------------------------------------------------------------------------------
template<typename T>
class shared_ptr_thread_safe
{
/*!
CONVENTION
- get() == data
- unique() == (shared_node != 0) && (shared_node->ref_count == 1)
- if (shared_node != 0) then
- use_count() == shared_node->ref_count
- get() == a valid pointer
- if (we are supposed to use the deleter) then
- shared_node->del == the deleter to use
- else
- shared_node->del == 0
- else
- use_count() == 0
- get() == 0
!*/
template <typename D>
struct deleter_template : public shared_ptr_thread_safe_deleter
{
deleter_template(const D& d_) : d(d_) {}
void del(const void* p) { d((T*)p); }
D d;
void* get_deleter_void(const std::type_info& t) const
{
if (typeid(D) == t)
return (void*)&d;
else
return 0;
}
};
public:
typedef T element_type;
shared_ptr_thread_safe(
) : data(0), shared_node(0) {}
template<typename Y>
explicit shared_ptr_thread_safe(
Y* p
) : data(p)
{
DLIB_ASSERT(p != 0,
"\tshared_ptr::shared_ptr_thread_safe(p)"
<< "\n\tp can't be null"
<< "\n\tthis: " << this
);
try
{
shared_node = new shared_ptr_thread_safe_node;
}
catch (...)
{
delete p;
throw;
}
}
template<typename Y, typename D>
shared_ptr_thread_safe(
Y* p,
const D& d
) :
data(p)
{
DLIB_ASSERT(p != 0,
"\tshared_ptr::shared_ptr_thread_safe(p,d)"
<< "\n\tp can't be null"
<< "\n\tthis: " << this
);
try
{
shared_node = 0;
shared_node = new shared_ptr_thread_safe_node;
shared_node->del = new deleter_template<D>(d);
}
catch (...)
{
if (shared_node) delete shared_node;
d(p);
throw;
}
}
~shared_ptr_thread_safe()
{
if ( shared_node != 0)
{
shared_node->m.lock();
if (shared_node->ref_count == 1)
{
// delete the data in the appropriate way
if (shared_node->del)
{
shared_node->del->del(data);
delete shared_node->del;
}
else
{
delete data;
}
shared_node->m.unlock();
// finally delete the shared_node
delete shared_node;
}
else
{
shared_node->ref_count -= 1;
shared_node->m.unlock();
}
}
}
shared_ptr_thread_safe(
const shared_ptr_thread_safe& r
)
{
data = r.data;
shared_node = r.shared_node;
if (shared_node)
{
auto_mutex M(shared_node->m);
shared_node->ref_count += 1;
}
}
template<typename Y>
shared_ptr_thread_safe(
const shared_ptr_thread_safe<Y>& r,
const shared_ptr_ts_static_cast&
)
{
data = static_cast<T*>(r.data);
if (data != 0)
{
shared_node = r.shared_node;
auto_mutex M(shared_node->m);
shared_node->ref_count += 1;
}
else
{
shared_node = 0;
}
}
template<typename Y>
shared_ptr_thread_safe(
const shared_ptr_thread_safe<Y>& r,
const shared_ptr_ts_const_cast&
)
{
data = const_cast<T*>(r.data);
if (data != 0)
{
shared_node = r.shared_node;
auto_mutex M(shared_node->m);
shared_node->ref_count += 1;
}
else
{
shared_node = 0;
}
}
template<typename Y>
shared_ptr_thread_safe(
const shared_ptr_thread_safe<Y>& r,
const shared_ptr_ts_dynamic_cast&
)
{
data = dynamic_cast<T*>(r.data);
if (data != 0)
{
shared_node = r.shared_node;
auto_mutex M(shared_node->m);
shared_node->ref_count += 1;
}
else
{
shared_node = 0;
}
}
template<typename Y>
shared_ptr_thread_safe(
const shared_ptr_thread_safe<Y>& r
)
{
data = r.data;
shared_node = r.shared_node;
if (shared_node)
{
auto_mutex M(shared_node->m);
shared_node->ref_count += 1;
}
}
template<typename Y>
explicit shared_ptr_thread_safe(
std::auto_ptr<Y>& r
)
{
DLIB_ASSERT(r.get() != 0,
"\tshared_ptr::shared_ptr_thread_safe(auto_ptr r)"
<< "\n\tr.get() can't be null"
<< "\n\tthis: " << this
);
shared_node = new shared_ptr_thread_safe_node;
data = r.release();
}
shared_ptr_thread_safe& operator= (
const shared_ptr_thread_safe& r
)
{
shared_ptr_thread_safe(r).swap(*this);
return *this;
}
template<typename Y>
shared_ptr_thread_safe& operator= (
const shared_ptr_thread_safe<Y>& r
)
{
shared_ptr_thread_safe(r).swap(*this);
return *this;
}
template<typename Y>
shared_ptr_thread_safe& operator= (
std::auto_ptr<Y>& r
)
{
DLIB_ASSERT(r.get() != 0,
"\tshared_ptr::operator=(auto_ptr r)"
<< "\n\tr.get() can't be null"
<< "\n\tthis: " << this
);
reset();
shared_node = new shared_ptr_thread_safe_node;
data = r.release();
return *this;
}
void reset()
{
shared_ptr_thread_safe().swap(*this);
}
template<typename Y>
void reset(Y* p)
{
DLIB_ASSERT(p != 0,
"\tshared_ptr::reset(p)"
<< "\n\tp can't be null"
<< "\n\tthis: " << this
);
shared_ptr_thread_safe(p).swap(*this);
}
template<typename Y, typename D>
void reset(
Y* p,
const D& d
)
{
DLIB_ASSERT(p != 0,
"\tshared_ptr::reset(p,d)"
<< "\n\tp can't be null"
<< "\n\tthis: " << this
);
shared_ptr_thread_safe(p,d).swap(*this);
}
T& operator*(
) const
{
DLIB_ASSERT(get() != 0,
"\tshared_ptr::operator*()"
<< "\n\tget() can't be null if you are going to dereference it"
<< "\n\tthis: " << this
);
return *data;
}
T* operator->(
) const
{
DLIB_ASSERT(get() != 0,
"\tshared_ptr::operator->()"
<< "\n\tget() can't be null"
<< "\n\tthis: " << this
);
return data;
}
T* get() const { return data; }
bool unique() const
{
return use_count() == 1;
}
long use_count() const
{
if (shared_node != 0)
{
auto_mutex M(shared_node->m);
return shared_node->ref_count;
}
else
{
return 0;
}
}
operator bool(
) const { return get() != 0; }
void swap(shared_ptr_thread_safe& b)
{
std::swap(data, b.data);
std::swap(shared_node, b.shared_node);
}
template <typename D>
D* _get_deleter(
) const
{
if (shared_node)
{
auto_mutex M(shared_node->m);
if (shared_node->del)
return static_cast<D*>(shared_node->del->get_deleter_void(typeid(D)));
}
else
{
return 0;
}
}
template <typename Y>
bool _private_less (
const shared_ptr_thread_safe<Y>& rhs
) const
{
return shared_node < rhs.shared_node;
}
private:
template <typename Y> friend class shared_ptr_thread_safe;
T* data;
shared_ptr_thread_safe_node* shared_node;
};
// ----------------------------------------------------------------------------------------
template<typename T, typename U>
bool operator== (
const shared_ptr_thread_safe<T>& a,
const shared_ptr_thread_safe<U>& b
) { return a.get() == b.get(); }
template<typename T, typename U>
bool operator!= (
const shared_ptr_thread_safe<T>& a,
const shared_ptr_thread_safe<U>& b
) { return a.get() != b.get(); }
template<typename T, typename U>
bool operator< (
const shared_ptr_thread_safe<T>& a,
const shared_ptr_thread_safe<U>& b
)
{
return a._private_less(b);
}
template<typename T>
void swap(
shared_ptr_thread_safe<T>& a,
shared_ptr_thread_safe<T>& b
) { a.swap(b); }
template<typename T, typename U>
shared_ptr_thread_safe<T> static_pointer_cast(
const shared_ptr_thread_safe<U>& r
)
{
return shared_ptr_thread_safe<T>(r, shared_ptr_ts_static_cast());
}
template<typename T, typename U>
shared_ptr_thread_safe<T> const_pointer_cast(
shared_ptr_thread_safe<U> const & r
)
{
return shared_ptr_thread_safe<T>(r, shared_ptr_ts_const_cast());
}
template<typename T, typename U>
shared_ptr_thread_safe<T> dynamic_pointer_cast(
const shared_ptr_thread_safe<U>& r
)
{
return shared_ptr_thread_safe<T>(r, shared_ptr_ts_dynamic_cast());
}
template<typename E, typename T, typename Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr_thread_safe<Y> const & p)
{
os << p.get();
return os;
}
template<typename D, typename T>
D* get_deleter(const shared_ptr_thread_safe<T>& p)
{
return p.template _get_deleter<D>();
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_SHARED_THREAD_SAFE_PTr_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_SHARED_PTr_THREAD_SAFE_ABSTRACT_
#ifdef DLIB_SHARED_PTr_THREAD_SAFE_ABSTRACT_
#include <exception>
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T
>
class shared_ptr_thread_safe
{
/*!
INITIAL VALUE
defined by constructors
WHAT THIS OBJECT REPRESENTS
This object represents a reference counted smart pointer. Each shared_ptr_thread_safe
contains a pointer to some object and when the last shared_ptr_thread_safe that points
to the object is destructed or reset() then the object is guaranteed to be
deleted.
This is an implementation of the std::tr1::shared_ptr template from the
document ISO/IEC PDTR 19768, Proposed Draft Technical Report on C++
Library Extensions. The only deviation from that document is that this
shared_ptr_thread_safe is declared inside the dlib namespace rather than std::tr1,
this one is explicitly thread safe, and there isn't a corresponding weak_ptr.
THREAD SAFETY
This is a version of the shared_ptr object that can be used to share pointers
across more than one thread. Note however, that individual instances of this object
must still have access to them serialized by a mutex lock if they are to be modified
by more than one thread. But if you have two different shared_ptr_thread_safe objects
that both point to the same thing from different threads then you are safe.
!*/
public:
typedef T element_type;
shared_ptr_thread_safe(
);
/*!
ensures
- #get() == 0
- #use_count() == 0
!*/
template<typename Y>
explicit shared_ptr_thread_safe(
Y* p
);
/*!
requires
- p is convertible to a T* type pointer
- p can be deleted by calling "delete p;" and doing so will not throw exceptions
- p != 0
ensures
- #get() == p
- #use_count() == 1
- #*this object owns the pointer p
throws
- std::bad_alloc
if this exception is thrown then "delete p;" is called
!*/
template<typename Y, typename D>
shared_ptr_thread_safe(
Y* p,
const D& d
);
/*!
requires
- p is convertible to a T* type pointer
- D is copy constructable (and the copy constructor of D doesn't throw)
- p can be deleted by calling "d(p);" and doing so will not throw exceptions
- p != 0
ensures
- #get() == p
- #use_count() == 1
- #*this object owns the pointer p
throws
- std::bad_alloc
if this exception is thrown then "d(p);" is called
!*/
shared_ptr_thread_safe(
const shared_ptr_thread_safe& r
);
/*!
ensures
- #get() == #r.get()
- #use_count() == #r.use_count()
- If r is empty, constructs an empty shared_ptr_thread_safe object; otherwise, constructs
a shared_ptr_thread_safe object that shares ownership with r.
!*/
template<typename Y>
shared_ptr_thread_safe(
const shared_ptr_thread_safe<Y>& r
);
/*!
requires
- Y* is convertible to T*
ensures
- #get() == #r.get()
- #use_count() == #r.use_count()
- If r is empty, constructs an empty shared_ptr_thread_safe object; otherwise, constructs
a shared_ptr_thread_safe object that shares ownership with r.
!*/
template<typename Y>
explicit shared_ptr_thread_safe(
std::auto_ptr<Y>& r
);
/*!
requires
- p.get() != 0
- p.release() is convertible to a T* type pointer
- p.release() can be deleted by calling "delete p.release();" and doing so will not throw exceptions
ensures
- #get() == p.release()
- #use_count() == 1
- #r.get() == 0
- #*this object owns the pointer p.release()
throws
- std::bad_alloc
!*/
~shared_ptr_thread_safe(
);
/*!
ensures
- if (use_count() > 1)
- this object destroys itself but otherwise has no effect (i.e.
the pointer get() is still valid and shared between the remaining
shared_ptr_thread_safe objects)
- else if (use_count() == 1)
- deletes the pointer get() by calling delete (or using the deleter passed
to the constructor if one was passed in)
- else
- in this case get() == 0 so there is nothing to do so nothing occurs
!*/
shared_ptr_thread_safe& operator= (
const shared_ptr_thread_safe& r
);
/*!
ensures
- equivalent to shared_ptr_thread_safe(r).swap(*this).
- returns #*this
!*/
template<typename Y>
shared_ptr_thread_safe& operator= (
const shared_ptr_thread_safe<Y>& r
);
/*!
requires
- Y* is convertible to T*
ensures
- equivalent to shared_ptr_thread_safe(r).swap(*this).
- returns #*this
!*/
template<typename Y>
shared_ptr_thread_safe& operator= (
std::auto_ptr<Y>& r
);
/*!
requires
- p.get() != 0
- p.release() is convertible to a T* type pointer
- p.release() can be deleted by calling "delete p.release();" and doing so will not throw exceptions
ensures
- equivalent to shared_ptr_thread_safe(r).swap(*this).
- returns #*this
!*/
void reset(
);
/*!
ensures
- equivalent to shared_ptr_thread_safe().swap(*this)
!*/
template<typename Y>
void reset(
Y* p
);
/*!
requires
- p is convertible to a T* type pointer
- p can be deleted by calling "delete p;" and doing so will not throw exceptions
- p != 0
ensures
- equivalent to shared_ptr_thread_safe(p).swap(*this)
!*/
template<typename Y, typename D>
void reset(
Y* p,
const D& d
);
/*!
requires
- p is convertible to a T* type pointer
- D is copy constructable (and the copy constructor of D doesn't throw)
- p can be deleted by calling "d(p);" and doing so will not throw exceptions
- p != 0
ensures
- equivalent to shared_ptr_thread_safe(p,d).swap(*this)
!*/
T* get(
) const;
/*!
ensures
- returns the stored pointer
!*/
T& operator*(
) const;
/*!
requires
- get() != 0
ensures
- returns a reference to *get()
!*/
T* operator->(
) const;
/*!
requires
- get() != 0
ensures
- returns get()
!*/
bool unique(
) const;
/*!
ensures
- returns (use_count() == 1)
!*/
long use_count(
) const;
/*!
ensures
- The number of shared_ptr_thread_safe objects, *this included, that share ownership with *this, or 0 when *this
is empty.
!*/
operator bool(
) const;
/*!
ensures
- returns (get() != 0)
!*/
void swap(
shared_ptr_thread_safe& b
);
/*!
ensures
- swaps *this and item
!*/
};
// ----------------------------------------------------------------------------------------
template<typename T, typename U>
bool operator== (
const shared_ptr_thread_safe<T>& a,
const shared_ptr_thread_safe<U>& b
);
/*!
ensures
- returns a.get() == b.get()
!*/
template<typename T, typename U>
bool operator!= (
const shared_ptr_thread_safe<T>& a,
const shared_ptr_thread_safe<U>& b
) { return a.get() != b.get(); }
/*!
ensures
- returns a.get() != b.get()
!*/
template<typename T, typename U>
bool operator< (
const shared_ptr_thread_safe<T>& a,
const shared_ptr_thread_safe<U>& b
);
/*!
ensures
- Defines an operator< on shared_ptr_thread_safe types appropriate for use in the associative
containers.
!*/
template<typename T>
void swap(
shared_ptr_thread_safe<T>& a,
shared_ptr_thread_safe<T>& b
) { a.swap(b); }
/*!
provides a global swap function
!*/
template<typename T, typename U>
shared_ptr_thread_safe<T> static_pointer_cast(
const shared_ptr_thread_safe<U>& r
);
/*!
- if (r.get() == 0) then
- returns shared_ptr_thread_safe<T>()
- else
- returns a shared_ptr_thread_safe<T> object that stores static_cast<T*>(r.get()) and shares
ownership with r.
!*/
template<typename T, typename U>
shared_ptr_thread_safe<T> const_pointer_cast(
const shared_ptr_thread_safe<U>& r
);
/*!
- if (r.get() == 0) then
- returns shared_ptr_thread_safe<T>()
- else
- returns a shared_ptr_thread_safe<T> object that stores const_cast<T*>(r.get()) and shares
ownership with r.
!*/
template<typename T, typename U>
shared_ptr_thread_safe<T> dynamic_pointer_cast(
const shared_ptr_thread_safe<U>& r
);
/*!
ensures
- if (dynamic_cast<T*>(r.get()) returns a nonzero value) then
- returns a shared_ptr_thread_safe<T> object that stores a copy of
dynamic_cast<T*>(r.get()) and shares ownership with r
- else
- returns an empty shared_ptr_thread_safe<T> object.
!*/
template<typename E, typename T, typename Y>
std::basic_ostream<E, T> & operator<< (
std::basic_ostream<E, T> & os,
const shared_ptr_thread_safe<Y>& p
);
/*!
ensures
- performs os << p.get()
- returns os
!*/
template<typename D, typename T>
D* get_deleter(
const shared_ptr_thread_safe<T>& p
);
/*!
ensures
- if (*this owns a deleter d of type cv-unqualified D) then
- returns &d
- else
- returns 0
!*/
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_SHARED_PTr_THREAD_SAFE_ABSTRACT_
// Copyright (C) 2008 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_SMART_POINTERs_THREAD_SAFE_H_
#define DLIB_SMART_POINTERs_THREAD_SAFE_H_
#include "smart_pointers/shared_ptr_thread_safe.h"
#endif // DLIB_SMART_POINTERs_THREAD_SAFE_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment