Loading...
Searching...
No Matches
ResourcePtr.h
/*
* Copyright (C) 2013 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Michi Henning <michi.henning@canonical.com>
*/
#ifndef LOMIRI_UTIL_RESOURCEPTR_H
#define LOMIRI_UTIL_RESOURCEPTR_H
#include <mutex>
#include <type_traits>
#include <stdexcept>
namespace lomiri
{
namespace util
{
namespace
{
// Simple helper class so we can adopt a lock without inconvenient syntax.
template<typename T>
class LockAdopter
{
public:
LockAdopter(T& mutex) noexcept
: m_(mutex, std::adopt_lock)
{
assert(!mutex.try_lock()); // Mutex must be locked to be adoptable.
}
private:
std::unique_lock<T> m_;
};
} // namespace
// TODO: Discuss throwing deleters and requirements (copy constructible, etc.) on deleter.
template<typename R, typename D>
class ResourcePtr final
{
public:
ResourcePtr(ResourcePtr const &) = delete;
ResourcePtr& operator=(ResourcePtr const&) = delete;
typedef R element_type;
typedef D deleter_type;
ResourcePtr();
explicit ResourcePtr(D d);
ResourcePtr(R r, D d);
ResourcePtr(ResourcePtr&& r);
ResourcePtr& operator=(ResourcePtr&& r);
~ResourcePtr() noexcept;
void swap(ResourcePtr& other);
void reset(R r);
R release();
void dealloc();
R get() const;
bool has_resource() const noexcept;
explicit operator bool() const noexcept;
D& get_deleter() noexcept;
D const& get_deleter() const noexcept;
bool operator==(ResourcePtr const& rhs) const;
bool operator!=(ResourcePtr const& rhs) const;
bool operator<(ResourcePtr const& rhs) const;
bool operator<=(ResourcePtr const& rhs) const;
bool operator>(ResourcePtr const& rhs) const;
bool operator>=(ResourcePtr const& rhs) const;
private:
R resource_; // The managed resource.
D delete_; // The deleter to call.
bool initialized_; // True while we have a resource assigned.
mutable std::mutex m_; // Protects this instance.
typedef std::lock_guard<decltype(m_)> AutoLock;
typedef LockAdopter<decltype(m_)> AdoptLock;
};
template<typename R, typename D>
ResourcePtr<R, D>::ResourcePtr()
: initialized_(false)
{
static_assert(!std::is_pointer<deleter_type>::value,
"constructed with null function pointer deleter");
}
template<typename R, typename D>
ResourcePtr<R, D>::ResourcePtr(D d)
: delete_(d), initialized_(false)
{
}
template<typename R, typename D>
ResourcePtr<R, D>::ResourcePtr(R r, D d)
: resource_(r), delete_(d), initialized_(true)
{
}
// TODO: Mark as nothrow if the resource has a nothrow move constructor or nothrow copy constructor
template<typename R, typename D>
ResourcePtr<R, D>::ResourcePtr(ResourcePtr<R, D>&& r)
: resource_(std::move(r.resource_)), delete_(r.delete_), initialized_(r.initialized_)
{
r.initialized_ = false; // Stop r from deleting its resource, if it held any. No need to lock: r is a temporary.
}
// TODO: document exception safety behavior
template<typename R, typename D>
ResourcePtr<R, D>& ResourcePtr<R, D>::operator=(ResourcePtr&& r)
{
AutoLock lock(m_);
if (initialized_) // If we hold a resource, deallocate it first.
{
initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
delete_(resource_); // Delete our own resource.
}
// r is a temporary, so we don't need to lock it.
resource_ = std::move(r.resource_);
initialized_ = r.initialized_;
r.initialized_ = false; // Stop r from deleting its resource, if it held any.
delete_ = r.delete_;
return *this;
}
template<typename R, typename D>
ResourcePtr<R, D>::~ResourcePtr() noexcept
{
try
{
dealloc();
}
catch (...)
{
}
}
// TODO Split this into throw and no-throw versions depending on the underlying swap?
template<typename R, typename D>
void ResourcePtr<R, D>::swap(ResourcePtr& other)
{
if (this == &other) // This is necessary to avoid deadlock for self-swap
{
return;
}
std::lock(m_, other.m_);
AdoptLock left(m_);
AdoptLock right(other.m_);
using std::swap; // Enable ADL
swap(resource_, other.resource_);
swap(delete_, other.delete_);
swap(initialized_, other.initialized_);
}
// The non-member swap() must be in the same namespace as ResourcePtr, so it will work with ADL. And, once it is
// defined here, there is no point in adding a specialization to namespace std any longer, because ADL
// will find it here anyway.
// TODO Split this into throw and no-throw versions depending on the underlying swap?
template<typename R, typename D>
void swap(lomiri::util::ResourcePtr<R, D>& lhs, lomiri::util::ResourcePtr<R, D>& rhs)
{
lhs.swap(rhs);
}
template<typename R, typename D>
void ResourcePtr<R, D>::reset(R r)
{
AutoLock lock(m_);
bool has_old = initialized_;
R old_resource;
if (has_old)
{
old_resource = resource_;
}
resource_ = r;
initialized_ = true; // If the deleter throws, we still satisfy the postcondition: resource_ == r.
if (has_old)
{
delete_(old_resource);
}
}
template<typename R, typename D>
inline
R ResourcePtr<R, D>::release()
{
AutoLock lock(m_);
if (!initialized_)
{
throw std::logic_error("release() called on ResourcePtr without resource");
}
initialized_ = false;
return resource_;
}
template<typename R, typename D>
void ResourcePtr<R, D>::dealloc()
{
AutoLock lock(m_);
if (!initialized_)
{
return;
}
initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
delete_(resource_);
}
template<typename R, typename D>
inline
R ResourcePtr<R, D>::get() const
{
AutoLock lock(m_);
if (!initialized_)
{
throw std::logic_error("get() called on ResourcePtr without resource");
}
return resource_;
}
template<typename R, typename D>
inline
bool ResourcePtr<R, D>::has_resource() const noexcept
{
AutoLock lock(m_);
return initialized_;
}
template<typename R, typename D>
inline
ResourcePtr<R, D>::operator bool() const noexcept
{
return has_resource();
}
template<typename R, typename D>
inline
D& ResourcePtr<R, D>::get_deleter() noexcept
{
AutoLock lock(m_);
return delete_;
}
template<typename R, typename D>
inline
D const& ResourcePtr<R, D>::get_deleter() const noexcept
{
AutoLock lock(m_);
return delete_;
}
template<typename R, typename D>
bool ResourcePtr<R, D>::operator==(ResourcePtr<R, D> const& rhs) const
{
if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
{
return true;
}
std::lock(m_, rhs.m_);
AdoptLock left(m_);
AdoptLock right(rhs.m_);
if (!initialized_)
{
return !rhs.initialized_; // Equal if both are not initialized
}
else if (!rhs.initialized_)
{
return false; // Not equal if lhs initialized, but rhs not initialized
}
else
{
return resource_ == rhs.resource_;
}
}
template<typename R, typename D>
inline
bool ResourcePtr<R, D>::operator!=(ResourcePtr<R, D> const& rhs) const
{
return !(*this == rhs);
}
template<typename R, typename D>
bool ResourcePtr<R, D>::operator<(ResourcePtr<R, D> const& rhs) const
{
if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
{
return false;
}
std::lock(m_, rhs.m_);
AdoptLock left(m_);
AdoptLock right(rhs.m_);
if (!initialized_)
{
return rhs.initialized_; // Not initialized is less than initialized
}
else if (!rhs.initialized_) // Initialized is not less than not initialized
{
return false;
}
else
{
return resource_ < rhs.resource_;
}
}
template<typename R, typename D>
bool ResourcePtr<R, D>::operator<=(ResourcePtr<R, D> const& rhs) const
{
if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
{
return true;
}
// We can't just write:
//
// return *this < rhs || *this == rhs;
//
// because that creates a race condition: the locks would be released and
// re-aquired in between the two comparisons.
std::lock(m_, rhs.m_);
AdoptLock left(m_);
AdoptLock right(rhs.m_);
return resource_ < rhs.resource_ || resource_ == rhs.resource_;
}
template<typename R, typename D>
inline
bool ResourcePtr<R, D>::operator>(ResourcePtr<R, D> const& rhs) const
{
return !(*this <= rhs);
}
template<typename R, typename D>
inline
bool ResourcePtr<R, D>::operator>=(ResourcePtr<R, D> const& rhs) const
{
return !(*this < rhs);
}
} // namespace util
} // namespace lomiri
// Specializations in namespace std, so we play nicely with STL and metaprogramming.
namespace std
{
template<typename R, typename D>
struct equal_to<lomiri::util::ResourcePtr<R, D>>
{
bool operator()(lomiri::util::ResourcePtr<R, D> const& lhs, lomiri::util::ResourcePtr<R, D> const& rhs) const
{
return lhs == rhs;
}
};
template<typename R, typename D>
struct not_equal_to<lomiri::util::ResourcePtr<R, D>>
{
bool operator()(lomiri::util::ResourcePtr<R, D> const& lhs, lomiri::util::ResourcePtr<R, D> const& rhs) const
{
return lhs != rhs;
}
};
template<typename R, typename D>
struct less<lomiri::util::ResourcePtr<R, D>>
{
bool operator()(lomiri::util::ResourcePtr<R, D> const& lhs, lomiri::util::ResourcePtr<R, D> const& rhs) const
{
return lhs < rhs;
}
};
template<typename R, typename D>
struct less_equal<lomiri::util::ResourcePtr<R, D>>
{
bool operator()(lomiri::util::ResourcePtr<R, D> const& lhs, lomiri::util::ResourcePtr<R, D> const& rhs) const
{
return lhs <= rhs;
}
};
template<typename R, typename D>
struct greater<lomiri::util::ResourcePtr<R, D>>
{
bool operator()(lomiri::util::ResourcePtr<R, D> const& lhs, lomiri::util::ResourcePtr<R, D> const& rhs) const
{
return lhs > rhs;
}
};
template<typename R, typename D>
struct greater_equal<lomiri::util::ResourcePtr<R, D>>
{
bool operator()(lomiri::util::ResourcePtr<R, D> const& lhs, lomiri::util::ResourcePtr<R, D> const& rhs) const
{
return lhs >= rhs;
}
};
// TODO: provide hash if std::hash<R> exists.
} // namespace std
#endif
lomiri::util::ResourcePtr
Class to guarantee deallocation of arbitrary resources.
Definition ResourcePtr.h:121
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::deleter_type
internal::GDBusSignalUnsubscriber deleter_type
Definition ResourcePtr.h:138
lomiri::util::ResourcePtr::operator>=
bool operator>=(ResourcePtr const &rhs) const
Returns true if this is greater than or equal to rhs.
Definition ResourcePtr.h:641
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::element_type
guint element_type
Definition ResourcePtr.h:131
lomiri::util::ResourcePtr::operator<=
bool operator<=(ResourcePtr const &rhs) const
Returns true if this is less than or equal to rhs.
Definition ResourcePtr.h:588
lomiri::util::ResourcePtr::operator=
ResourcePtr & operator=(ResourcePtr &&r)
Definition ResourcePtr.h:266
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::reset
void reset(guint r)
lomiri::util::ResourcePtr::ResourcePtr
ResourcePtr(ResourcePtr &&r)
Definition ResourcePtr.h:252
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::get_deleter
internal::GDBusSignalUnsubscriber & get_deleter() noexcept
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::release
guint release()
lomiri::util::ResourcePtr::operator>
bool operator>(ResourcePtr const &rhs) const
Returns true if this is greater than rhs.
Definition ResourcePtr.h:623
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::swap
void swap(ResourcePtr &other)
lomiri::util::ResourcePtr::ResourcePtr
ResourcePtr(ResourcePtr const &)=delete
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::dealloc
void dealloc()
lomiri::util::ResourcePtr::operator=
ResourcePtr & operator=(ResourcePtr const &)=delete
lomiri::util::ResourcePtr::operator==
bool operator==(ResourcePtr const &rhs) const
Compares two instances for equality.
Definition ResourcePtr.h:498
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::has_resource
bool has_resource() const noexcept
lomiri::util::ResourcePtr::~ResourcePtr
~ResourcePtr() noexcept
Definition ResourcePtr.h:291
lomiri::util::ResourcePtr< guint, internal::GDBusSignalUnsubscriber >::get
guint get() const
lomiri::util::ResourcePtr::operator!=
bool operator!=(ResourcePtr const &rhs) const
Compares two instances for inequality.
Definition ResourcePtr.h:533
lomiri::util::ResourcePtr::ResourcePtr
ResourcePtr(R r, D d)
Definition ResourcePtr.h:238
lomiri::util::ResourcePtr::ResourcePtr
ResourcePtr(D d)
Definition ResourcePtr.h:195
lomiri::util::ResourcePtr::operator<
bool operator<(ResourcePtr const &rhs) const
Returns true if this is less than rhs.
Definition ResourcePtr.h:549
lomiri
Top-level namespace for all things Lomiri-related.
Definition Version.h:38
std::equal_to< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition ResourcePtr.h:665
std::greater< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition ResourcePtr.h:729
std::greater_equal< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition ResourcePtr.h:745
std::less< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition ResourcePtr.h:697
std::less_equal< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition ResourcePtr.h:713
std::not_equal_to< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition ResourcePtr.h:681