// Copyright (C) 2007 Davis E. King (davis@dlib.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/threads_kernel.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);
shared_node->m.unlock();
delete shared_node->del;
}
else
{
shared_node->m.unlock();
delete data;
}
// 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;
}
}
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;
}
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)));
}
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_