From 25556ee8b7b7b5bf0310cb7fd5e21aad23b7d264 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sun, 15 Sep 2024 20:29:51 +0200 Subject: [PATCH] iox-#2301 TODO --- .../sync/include/iox/spin_lock_mutex.hpp | 120 ++++++++++++++++++ .../condition_variable_data.hpp | 71 ++++++++++- .../popo/building_blocks/locking_policy.hpp | 96 +++++++++++++- .../condition_variable_data.cpp | 8 +- .../popo/building_blocks/locking_policy.cpp | 12 +- 5 files changed, 297 insertions(+), 10 deletions(-) create mode 100644 iceoryx_hoofs/posix/sync/include/iox/spin_lock_mutex.hpp diff --git a/iceoryx_hoofs/posix/sync/include/iox/spin_lock_mutex.hpp b/iceoryx_hoofs/posix/sync/include/iox/spin_lock_mutex.hpp new file mode 100644 index 0000000000..516110304a --- /dev/null +++ b/iceoryx_hoofs/posix/sync/include/iox/spin_lock_mutex.hpp @@ -0,0 +1,120 @@ +// Copyright (c) 2024 by ekxide IO GmbH. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef IOX_HOOFS_POXIX_SYNC_SPIN_LOCK_MUTEX_HPP +#define IOX_HOOFS_POXIX_SYNC_SPIN_LOCK_MUTEX_HPP + +#include "iox/atomic.hpp" +#include "iox/detail/adaptive_wait.hpp" +#include "iox/mutex.hpp" + +namespace iox +{ +class SpinlockMutex +{ + public: + SpinlockMutex() + : m_flag(ATOMIC_FLAG_INIT) + , m_recursive{Recursive{0, 0}} + { + } + + expected lock() noexcept + { + pid_t tid = gettid(); + + auto recursive = m_recursive.load(); + + if (recursive.tid == tid) + { + recursive.count += 1; + m_recursive.store(recursive); + + return ok(); + } + + detail::adaptive_wait spinner; + spinner.wait_loop([this] { return this->m_flag.test_and_set(std::memory_order_acquire); }); + + m_recursive.store(Recursive{tid, 1}); + + return ok(); + } + + expected unlock() noexcept + { + pid_t tid = gettid(); + + auto recursive = m_recursive.load(); + + if (recursive.tid == tid) + { + recursive.count -= 1; + + if (recursive.count == 0) + { + recursive.tid = 0; + m_recursive.store(recursive); + m_flag.clear(std::memory_order_release); + } + else + { + m_recursive.store(recursive); + } + + return ok(); + } + + + return err(MutexUnlockError::UNKNOWN_ERROR); + } + + expected try_lock() noexcept + { + pid_t tid = gettid(); + + auto recursive = m_recursive.load(); + + if (recursive.tid == tid) + { + recursive.count += 1; + m_recursive.store(recursive); + return ok(MutexTryLock::LOCK_SUCCEEDED); + } + + if (!m_flag.test_and_set(std::memory_order_acquire)) + { + m_recursive.store(Recursive{tid, 1}); + + return ok(MutexTryLock::LOCK_SUCCEEDED); + } + return ok(MutexTryLock::FAILED_TO_ACQUIRE_LOCK); + } + + struct Recursive + { + pid_t tid; + uint32_t count; + }; + + private: + concurrent::AtomicFlag m_flag; + concurrent::Atomic m_recursive; +}; + +} // namespace iox + +#endif // IOX_HOOFS_POXIX_SYNC_SPIN_LOCK_MUTEX_HPP diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp index 532539c19a..d258d625a6 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp @@ -18,14 +18,83 @@ #define IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_DATA_HPP #include "iceoryx_posh/iceoryx_posh_types.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/locking_policy.hpp" #include "iceoryx_posh/internal/posh_error_reporting.hpp" #include "iox/atomic.hpp" +#include "iox/deadline_timer.hpp" +#include "iox/detail/adaptive_wait.hpp" #include "iox/unnamed_semaphore.hpp" namespace iox { namespace popo { +class SpinLockSemaphore : public detail::SemaphoreInterface +{ + public: + SpinLockSemaphore() + : m_count(0) + { + } + + ~SpinLockSemaphore() + { + m_to_be_destroyed = true; + } + + expected post() + { + std::lock_guard lock(spinlock); + ++m_count; + return ok(); + } + + expected wait() + { + detail::adaptive_wait spinner; + spinner.wait_loop([this] { return !this->tryWait(); }); + return ok(); + } + + expected tryWait() + { + std::lock_guard lock(spinlock); + if (m_to_be_destroyed == true) + { + return ok(true); + } + if (m_count > 0) + { + --m_count; + return ok(true); + } + return ok(false); + } + + expected timedWait(const units::Duration& timeout) + { + iox::deadline_timer deadline_timer(timeout); + detail::adaptive_wait spinner; + + auto ret_val = SemaphoreWaitState::TIMEOUT; + spinner.wait_loop([this, &deadline_timer, &ret_val] { + if (this->tryWait()) + { + ret_val = SemaphoreWaitState::NO_TIMEOUT; + return false; + } + return !deadline_timer.hasExpired(); + }); + + return ok(ret_val); + } + + private: + concurrent::Atomic m_count{0}; + concurrent::Atomic m_to_be_destroyed{false}; + SpinlockMutex spinlock; +}; + struct ConditionVariableData { ConditionVariableData() noexcept; @@ -37,7 +106,7 @@ struct ConditionVariableData ConditionVariableData& operator=(ConditionVariableData&& rhs) = delete; ~ConditionVariableData() noexcept = default; - optional m_semaphore; + optional m_semaphore; RuntimeName_t m_runtimeName; concurrent::Atomic m_toBeDestroyed{false}; concurrent::Atomic m_activeNotifications[MAX_NUMBER_OF_NOTIFIERS]; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/locking_policy.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/locking_policy.hpp index 973662da1a..c078507db4 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/locking_policy.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/locking_policy.hpp @@ -16,12 +16,106 @@ #ifndef IOX_POSH_POPO_BUILDING_BLOCKS_LOCKING_POLICY_HPP #define IOX_POSH_POPO_BUILDING_BLOCKS_LOCKING_POLICY_HPP +#include "iox/detail/adaptive_wait.hpp" #include "iox/mutex.hpp" +#include "iox/spin_lock_mutex.hpp" namespace iox { namespace popo { +class SpinlockMutex +{ + public: + SpinlockMutex() + : m_flag(ATOMIC_FLAG_INIT) + , m_recursive{Recursive{0, 0}} + { + } + + expected lock() noexcept + { + pid_t tid = gettid(); + + auto recursive = m_recursive.load(); + + if (recursive.tid == tid) + { + recursive.count += 1; + m_recursive.store(recursive); + + return ok(); + } + + detail::adaptive_wait spinner; + spinner.wait_loop([this] { return this->m_flag.test_and_set(std::memory_order_acquire); }); + + m_recursive.store(Recursive{tid, 1}); + + return ok(); + } + + expected unlock() noexcept + { + pid_t tid = gettid(); + + auto recursive = m_recursive.load(); + + if (recursive.tid == tid) + { + recursive.count -= 1; + + if (recursive.count == 0) + { + recursive.tid = 0; + m_recursive.store(recursive); + m_flag.clear(std::memory_order_release); + } + else + { + m_recursive.store(recursive); + } + + return ok(); + } + + + return err(MutexUnlockError::UNKNOWN_ERROR); + } + + expected try_lock() noexcept + { + pid_t tid = gettid(); + + auto recursive = m_recursive.load(); + + if (recursive.tid == tid) + { + recursive.count += 1; + m_recursive.store(recursive); + return ok(MutexTryLock::LOCK_SUCCEEDED); + } + + if (!m_flag.test_and_set(std::memory_order_acquire)) + { + m_recursive.store(Recursive{tid, 1}); + + return ok(MutexTryLock::LOCK_SUCCEEDED); + } + return ok(MutexTryLock::FAILED_TO_ACQUIRE_LOCK); + } + + struct Recursive + { + pid_t tid; + uint32_t count; + }; + + private: + concurrent::AtomicFlag m_flag; + concurrent::Atomic m_recursive; +}; + class ThreadSafePolicy { public: @@ -33,7 +127,7 @@ class ThreadSafePolicy bool tryLock() const noexcept; private: - mutable optional m_mutex; + mutable optional m_mutex; }; class SingleThreadedPolicy diff --git a/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp b/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp index 9b786c5399..2941fe3c6c 100644 --- a/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp +++ b/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp @@ -30,9 +30,11 @@ ConditionVariableData::ConditionVariableData() noexcept ConditionVariableData::ConditionVariableData(const RuntimeName_t& runtimeName) noexcept : m_runtimeName(runtimeName) { - UnnamedSemaphoreBuilder().initialValue(0U).isInterProcessCapable(true).create(m_semaphore).or_else([](auto) { - IOX_REPORT_FATAL(PoshError::POPO__CONDITION_VARIABLE_DATA_FAILED_TO_CREATE_SEMAPHORE); - }); + /// @todo iox-#2301 fix this with a proper spin-lock implementation + // UnnamedSemaphoreBuilder().initialValue(0U).isInterProcessCapable(true).create(m_semaphore).or_else([](auto) { + // IOX_REPORT_FATAL(PoshError::POPO__CONDITION_VARIABLE_DATA_FAILED_TO_CREATE_SEMAPHORE); + // }); + m_semaphore.emplace(); for (auto& id : m_activeNotifications) { diff --git a/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp b/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp index db1796b422..84e0fb612f 100644 --- a/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp +++ b/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp @@ -25,11 +25,13 @@ namespace popo { ThreadSafePolicy::ThreadSafePolicy() noexcept { - MutexBuilder() - .isInterProcessCapable(true) - .mutexType(MutexType::RECURSIVE) - .create(m_mutex) - .expect("Failed to create Mutex"); + /// @todo iox-#2301 fix this with a proper spin-lock implementation + // MutexBuilder() + // .isInterProcessCapable(true) + // .mutexType(MutexType::RECURSIVE) + // .create(m_mutex) + // .expect("Failed to create Mutex"); + m_mutex.emplace(); } void ThreadSafePolicy::lock() const noexcept