// itlib-data-mutex v1.00 // // A lockable data type. Merging mutex with data // // SPDX-License-Identifier: MIT // MIT License: // Copyright(c) 2023 Borislav Stanimirov // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files(the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and / or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions : // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // // VERSION HISTORY // // 1.00 (2023-02-07) Initial release // // // DOCUMENTATION // // Simply include this file wherever you need. // The library defines the template type data_mutex which is a // pair of an object and an associated mutex used to synchronize access to it. // // To access the object inside one must use one of the following methods: // * unique_lock (mutex lock/unlock) // * try_unique_lock (mutex try_lock/unlock) // * shared_lock (mutex lock_shared/unlock_shared) // * try_shared_lock (mutex try_lock_shared/unlock_shared) // // They all return thin pointer-like wrappers of T. Which have operators // -> and *. In the case of the try_* functions, they also have a bool // interface // // // TESTS // // You can find unit tests for qalgorithm in its official repo: // https://github.com/iboB/itlib/blob/master/test/ // #pragma once #include namespace itlib { template class data_mutex { mutable Mutex m_mutex; T m_data; public: template data_mutex(Args&&... args) : m_data(std::forward(args)...) {} data_mutex(const data_mutex&) = delete; data_mutex& operator=(const data_mutex&) = delete; data_mutex(data_mutex&&) = delete; data_mutex& operator=(data_mutex&&) = delete; template class lock_base_t { protected: Mutex& m_mutex; CT* m_data; lock_base_t(Mutex& mutex, CT& data) : m_mutex(mutex), m_data(&data) {} lock_base_t(Mutex& mutex) : m_mutex(mutex) {} ~lock_base_t() = default; lock_base_t(const lock_base_t&) = delete; lock_base_t& operator=(const lock_base_t&) = delete; lock_base_t(lock_base_t&& o) noexcept : m_mutex(o.m_mutex), m_data(o.m_data) { o.m_data = nullptr; } lock_base_t& operator=(lock_base_t&& o) noexcept = delete; public: CT& operator*() { return *m_data; } CT* operator->() { return m_data; } const CT& operator*() const { return *m_data; } const CT* operator->() const { return m_data; } }; template class unique_lock_base_t : public lock_base_t { protected: using lock_base_t::lock_base_t; unique_lock_base_t(unique_lock_base_t&&) = default; ~unique_lock_base_t() { if (this->m_data) this->m_mutex.unlock(); } }; struct unique_lock_t : public unique_lock_base_t { explicit unique_lock_t(data_mutex& dm) : unique_lock_base_t(dm.m_mutex, dm.m_data) { this->m_mutex.lock(); } }; struct try_unique_lock_t : public unique_lock_base_t { explicit try_unique_lock_t(data_mutex& dm) : unique_lock_base_t(dm.m_mutex) { if (this->m_mutex.try_lock()) this->m_data = &dm.m_data; else this->m_data = nullptr; } explicit operator bool() const noexcept { return !!this->m_data; } }; struct const_unique_lock_t : public unique_lock_base_t { explicit const_unique_lock_t(const data_mutex& dm) : unique_lock_base_t(dm.m_mutex, dm.m_data) { this->m_mutex.lock(); } }; struct try_const_unique_lock_t : public unique_lock_base_t { explicit try_const_unique_lock_t(const data_mutex& dm) : unique_lock_base_t(dm.m_mutex) { if (this->m_mutex.try_lock()) this->m_data = &dm.m_data; else this->m_data = nullptr; } explicit operator bool() const noexcept { return !!this->m_data; } }; class shared_lock_base_t : public lock_base_t { protected: using lock_base_t::lock_base_t; shared_lock_base_t(shared_lock_base_t&&) = default; ~shared_lock_base_t() { if (this->m_data) this->m_mutex.unlock_shared(); } }; struct shared_lock_t : public shared_lock_base_t { explicit shared_lock_t(const data_mutex& dm) : shared_lock_base_t(dm.m_mutex, dm.m_data) { this->m_mutex.lock_shared(); } }; struct try_shared_lock_t : public shared_lock_base_t { explicit try_shared_lock_t(const data_mutex& dm) : shared_lock_base_t(dm.m_mutex) { if (this->m_mutex.try_lock_shared()) this->m_data = &dm.m_data; else this->m_data = nullptr; } explicit operator bool() const noexcept { return !!this->m_data; } }; unique_lock_t unique_lock() { unique_lock_t x(*this); return x; } const_unique_lock_t unique_lock() const { return const_unique_lock_t(*this); } try_unique_lock_t try_unique_lock() { return try_unique_lock_t(*this); } try_const_unique_lock_t try_unique_lock() const { return try_const_unique_lock_t(*this); } shared_lock_t shared_lock() const { return shared_lock_t(*this); } try_shared_lock_t try_shared_lock() const { return try_shared_lock_t(*this); } }; }