summaryrefslogtreecommitdiff
path: root/server/utils.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'server/utils.hpp')
-rw-r--r--server/utils.hpp497
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"