diff options
Diffstat (limited to 'server/utils.hpp')
-rw-r--r-- | server/utils.hpp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/server/utils.hpp b/server/utils.hpp new file mode 100644 index 00000000..e25e1c5e --- /dev/null +++ b/server/utils.hpp @@ -0,0 +1,497 @@ +/* + 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/>. +*/ + +/** + * @file utils.hpp + * 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* ptr): p(ptr) + { + } + ~unique_link() + { + delete p; + } + T* operator->() noexcept + { + return p; + } + const T* operator->() const noexcept + { + return p; + } + T *get() 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); + } +}; + +/** + * Allows to define a variable holding objects allocated with + * g_malloc(). + * + * @code{.cpp} + * red::glib_unique_ptr<char> s = g_strdup("hello"); + * @endcode + */ +template <typename T> +using glib_unique_ptr = std::unique_ptr<T, GLibDeleter<T>>; + + +/** + * @brief 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 red::make_shared() provided below. This to makes + * 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 *ptr=nullptr): p(ptr) + { + if (p) { + shared_ptr_add_ref(p); + } + } + template <class Q> + explicit shared_ptr(Q *ptr): shared_ptr(static_cast<T*>(ptr)) + { + } + 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; + } + 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 *ptr, bool dummy): p(ptr) + { + } +}; + +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(); +} + +/** + * Allows to create and object and wrap into a smart pointer at the same + * time. + * You should try to allocated any shared pointer managed object with this + * function. + */ +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 requirements. + * + * You should inherit publicly this class in order to have base internal reference counting + * implementation. + * + * This class uses atomic operations and virtual destructor so it's not really light. + * @see simple_ptr_counted + */ +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 *ptr=nullptr): p(ptr) + { + 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; +} + + +/** + * Utility to help implementing shared_ptr requirements. + * + * You should inherit publicly this class in order to have base internal reference counting + * implementation. + * + * This class does not use atomic operations and virtual destructor so it's more light than + * shared_ptr_counted. + * + * To avoid issues with inheritance not calling proper destructor the derived objects should + * follow this pattern (note the final): + * + * @code{.cpp} + * class Name final: public simple_ptr_counted<Name> { + * ... + * } + * @endcode + * + * or + * + * @code{.cpp} + * class Name: public simple_ptr_counted<Name> { + * ... + * virtual ~Name(); + * ... + * } + * @endcode + */ +template <typename T> +class simple_ptr_counted +{ +public: + SPICE_CXX_GLIB_ALLOCATOR + + simple_ptr_counted(): ref_count(0) + { + } +private: + mutable int ref_count; + simple_ptr_counted(const simple_ptr_counted<T>& rhs)=delete; + void operator=(const simple_ptr_counted<T>& rhs)=delete; + template <typename Q> + friend inline void shared_ptr_add_ref(const simple_ptr_counted<Q>*); + template <typename Q> + friend inline void shared_ptr_unref(const simple_ptr_counted<Q>*); +}; + +template <typename T> +inline void shared_ptr_add_ref(const simple_ptr_counted<T>* p) +{ + ++p->ref_count; +} + +template <typename T> +inline void shared_ptr_unref(const simple_ptr_counted<T>* p) +{ + if (--p->ref_count == 0) { + delete const_cast<T*>(static_cast<const T*>(p)); + } +} + + +} // namespace red + +#include "pop-visibility.h" |