spice/server/utils.hpp
Frediano Ziglio be5bda4d4f utils: Add red::weak_ptr and red::shared_ptr_counted_weak
Implements weak pointers and helper to implement them.
They will be used for RedCharDevice.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
2020-05-01 06:58:09 +01:00

418 lines
9.3 KiB
C++

/*
Copyright (C) 2019 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 library; if not, see <http://www.gnu.org/licenses/>.
*/
/* Generic utilities for C++
*/
#pragma once
#include <memory>
#include <atomic>
#include "push-visibility.h"
namespace red {
template <typename T>
inline T* add_ref(T* p)
{
if (p) {
p->ref();
}
return p;
}
/* Smart pointer allocated once
*
* It just keep the pointer passed to constructor and delete
* the object in the destructor. No copy or move allowed.
* Very easy but make sure we don't change it and that's
* initialized.
*/
template <typename T>
class unique_link
{
public:
unique_link(): p(new T())
{
}
unique_link(T* p): p(p)
{
}
~unique_link()
{
delete p;
}
T* operator->() noexcept
{
return p;
}
const T* operator->() const noexcept
{
return p;
}
private:
T *const p;
unique_link(const unique_link&)=delete;
void operator=(const unique_link&)=delete;
};
template <typename T>
struct GLibDeleter {
void operator()(T* p)
{
g_free(p);
}
};
template <typename T>
using glib_unique_ptr = std::unique_ptr<T, GLibDeleter<T>>;
/* Returns the size of an array.
* Introduced in C++17 but lacking in C++11
*/
template <class T, size_t N>
constexpr size_t size(const T (&array)[N]) noexcept
{
return N;
}
template <typename T>
class weak_ptr;
/* Basic shared pointer for internal reference
*
* Similar to STL version but using intrusive. This to allow creating multiple
* shared pointers from the raw pointer without problems having the object
* freed when one single set of shared pointer is removed. The code used
* to increment the reference to make sure the object was not deleted in
* some cases of self destruction.
*
* This class is inspired to boost::intrusive_ptr.
*
* To allow to reference and unrefered any object the object should
* define shared_ptr_add_ref and shared_ptr_unref, both taking a pointer and incrementing and
* decrementing respectively. You should not call these function yourselves.
*
* It's recommended that you create the object with make_shared provided below. This to make sure
* that there is at least one strong reference to the object.
*/
template <typename T>
class shared_ptr
{
friend class weak_ptr<T>;
public:
explicit shared_ptr(T *p=nullptr): p(p)
{
if (p) {
shared_ptr_add_ref(p);
}
}
template <class Q>
explicit shared_ptr(Q *p): shared_ptr(static_cast<T*>(p))
{
}
shared_ptr(const shared_ptr& rhs): p(rhs.p)
{
if (p) {
shared_ptr_add_ref(p);
}
}
template <class Q>
shared_ptr(const shared_ptr<Q>& rhs): shared_ptr(static_cast<T*>(rhs.get()))
{
}
shared_ptr& operator=(const shared_ptr& rhs)
{
if (rhs.p != p) {
reset(rhs.p);
}
return *this;
}
template <class Q>
shared_ptr& operator=(const shared_ptr<Q>& rhs)
{
reset(rhs.get());
return *this;
}
shared_ptr(shared_ptr&& rhs): p(rhs.p)
{
rhs.p = nullptr;
}
shared_ptr& operator=(shared_ptr&& rhs)
{
if (p) {
shared_ptr_unref(p);
}
p = rhs.p;
rhs.p = nullptr;
return *this;
}
~shared_ptr()
{
if (p) {
shared_ptr_unref(p);
}
}
void reset(T *ptr=nullptr)
{
if (ptr) {
shared_ptr_add_ref(ptr);
}
if (p) {
shared_ptr_unref(p);
}
p = ptr;
}
T *release()
{
T *ptr = p;
if (p) {
shared_ptr_unref(p);
p = nullptr;
}
return ptr;
}
operator bool() const
{
return p;
}
T& operator*() const noexcept
{
return *p;
}
T* operator->() const noexcept
{
return p;
}
T *get() const noexcept
{
return p;
}
private:
T* p;
// for weak_ptr
explicit shared_ptr(T *p, bool dummy): p(p)
{
}
};
template <class T, class O>
inline bool operator==(const shared_ptr<T>& a, const shared_ptr<O>& b)
{
return a.get() == b.get();
}
template <class T, class O>
inline bool operator!=(const shared_ptr<T>& a, const shared_ptr<O>& b)
{
return a.get() != b.get();
}
template<typename T, typename... Args>
inline shared_ptr<T> make_shared(Args&&... args)
{
return shared_ptr<T>(new T(args...));
}
/* Utility to help implementing shared_ptr requirement
*
* You should inherit publicly this class in order to have base internal reference counting
* implementation.
*
* This class uses aromic operations and virtual destructor so it's not really light.
*/
class shared_ptr_counted
{
public:
SPICE_CXX_GLIB_ALLOCATOR
shared_ptr_counted(): ref_count(0)
{
}
protected:
virtual ~shared_ptr_counted() {}
private:
std::atomic_int ref_count;
shared_ptr_counted(const shared_ptr_counted& rhs)=delete;
void operator=(const shared_ptr_counted& rhs)=delete;
friend inline void shared_ptr_add_ref(shared_ptr_counted*);
friend inline void shared_ptr_unref(shared_ptr_counted*);
};
// implements requirements for shared_ptr
inline void shared_ptr_add_ref(shared_ptr_counted* p)
{
++p->ref_count;
}
inline void shared_ptr_unref(shared_ptr_counted* p)
{
if (--p->ref_count == 0) {
delete p;
}
}
/* Basic weak pointer for internal reference
*
* Similar to STL version like shared_ptr here.
*
* In order to support weak_ptr for an object weak_ptr_add_ref,
* weak_ptr_unref and weak_ptr_lock should be implemented. See below.
*/
template <typename T>
class weak_ptr
{
public:
explicit weak_ptr(T *p=nullptr): p(p)
{
if (p) {
weak_ptr_add_ref(p);
}
}
weak_ptr(const weak_ptr& rhs): p(rhs.p)
{
if (p) {
weak_ptr_add_ref(p);
}
}
weak_ptr& operator=(const weak_ptr& rhs)
{
if (rhs.p != p) {
reset(rhs.p);
}
return *this;
}
weak_ptr(weak_ptr&& rhs): p(rhs.p)
{
rhs.p = nullptr;
}
weak_ptr& operator=(weak_ptr&& rhs)
{
if (p) {
weak_ptr_unref(p);
}
p = rhs.p;
rhs.p = nullptr;
return *this;
}
~weak_ptr()
{
if (p) {
weak_ptr_unref(p);
}
}
// get a strong reference
shared_ptr<T> lock()
{
return shared_ptr<T>(p && weak_ptr_lock(p) ? p : nullptr, false);
}
void reset(T *ptr=nullptr)
{
if (ptr) {
weak_ptr_add_ref(ptr);
}
if (p) {
weak_ptr_unref(p);
}
p = ptr;
}
// NOTE do not add operator bool using p, we need to check if still valid
private:
T* p;
};
/* Utility to help implementing shared ptr with weak semantic too
*
* Similar to shared_ptr_counted but you can use weak pointers too.
*/
class shared_ptr_counted_weak
{
public:
SPICE_CXX_GLIB_ALLOCATOR
shared_ptr_counted_weak(): ref_count(0), weak_count(1)
{
}
protected:
virtual ~shared_ptr_counted_weak() {}
private:
std::atomic_int ref_count;
std::atomic_int weak_count;
shared_ptr_counted_weak(const shared_ptr_counted_weak& rhs)=delete;
void operator=(const shared_ptr_counted_weak& rhs)=delete;
// this is used in order to use operator delete defined in this class, not global one
void free_helper(void *p) { operator delete(p); }
friend inline void shared_ptr_add_ref(shared_ptr_counted_weak*);
friend inline void shared_ptr_unref(shared_ptr_counted_weak*);
friend inline void weak_ptr_add_ref(shared_ptr_counted_weak*);
friend inline void weak_ptr_unref(shared_ptr_counted_weak*);
friend inline bool weak_ptr_lock(shared_ptr_counted_weak*);
};
// implements requirements for shared_ptr
inline void shared_ptr_add_ref(shared_ptr_counted_weak* p)
{
++p->ref_count;
}
inline void shared_ptr_unref(shared_ptr_counted_weak* p)
{
if (--p->ref_count == 0) {
p->~shared_ptr_counted_weak();
std::atomic_thread_fence(std::memory_order_release);
if (--p->weak_count == 0) {
p->free_helper(p);
}
}
}
// implements requirements for weak_ptr
inline void weak_ptr_add_ref(shared_ptr_counted_weak* p)
{
p->weak_count++;
}
inline void weak_ptr_unref(shared_ptr_counted_weak* p)
{
if (--p->weak_count == 0) {
std::atomic_thread_fence(std::memory_order_acquire);
p->free_helper(p);
}
}
inline bool weak_ptr_lock(shared_ptr_counted_weak* p)
{
int count = (int) p->ref_count;
do {
if (count == 0) {
return false;
}
} while (!p->ref_count.compare_exchange_weak(count, count + 1));
return true;
}
} // namespace red
#include "pop-visibility.h"