From bd2e23fb850f603ea6297cdd63a859965decdab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Thu, 30 Aug 2018 02:29:31 +0300 Subject: [PATCH] Add mxs::rworker_local datatype The mxs::rworker_local is a convenience type that provides fast read access with thread-safe updates. It is intended to be used with data that is read often but updated rarely e.g. configuration data for routers. --- server/core/internal/routingworker.hh | 104 ++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/server/core/internal/routingworker.hh b/server/core/internal/routingworker.hh index 9c22ff537..5dfeb1f2b 100644 --- a/server/core/internal/routingworker.hh +++ b/server/core/internal/routingworker.hh @@ -433,6 +433,110 @@ private: uint32_t handle_epoll_events(uint32_t events); }; +// Data local to a routing worker +template +class rworker_local +{ +public: + + rworker_local(const rworker_local&) = delete; + rworker_local& operator=(const rworker_local&) = delete; + + // Default initialized + rworker_local(): + m_handle(mxs_rworker_create_key()) + { + } + + // Copy-constructed + rworker_local(const T& t): + m_handle(mxs_rworker_create_key()), + m_value(t) + { + } + + ~rworker_local() + { + mxs_rworker_delete_data(m_handle); + } + + // Converts to a const T reference + operator const T&() const + { + return *get_local_value(); + } + + // Arrow operator + const T* operator->() const + { + return get_local_value(); + } + + /** + * Assign a value + * + * Sets the master value and triggers an update on all workers. The value is updated instantly + * if the calling thread is a worker thread. + * + * @param t The new value to assign + */ + void assign(const T& t) + { + std::unique_lock guard(m_lock); + m_value = t; + guard.unlock(); + + // Update the value on all workers + mxs_rworker_broadcast(update_value, this); + } + +private: + + uint64_t m_handle; + mutable std::mutex m_lock; + T m_value; // The "master" value, never used directly + +private: + + T* get_local_value() const + { + T* my_value = static_cast(mxs_rworker_get_data(m_handle)); + + if (my_value == nullptr) + { + // First time we get the local value, allocate it from the master value + std::unique_lock guard(m_lock); + my_value = new T(m_value); + guard.unlock(); + + mxs_rworker_set_data(m_handle, my_value, destroy_value); + } + + mxb_assert(my_value); + return my_value; + } + + void update_local_value() + { + // As get_local_value can cause a lock to be taken, we need the pointer to our value before + // we lock the master value for the updating of our value. + T* my_value = get_local_value(); + + std::lock_guard guard(m_lock); + *my_value = m_value; + } + + static void update_value(void* data) + { + static_cast*>(data)->update_local_value(); + } + + static void destroy_value(void* data) + { + delete static_cast(data); + } +}; + } /**