463 lines
12 KiB
C++
463 lines
12 KiB
C++
// 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_
|
|
|