diff --git a/doc/website/release-notes/iceoryx-unreleased.md b/doc/website/release-notes/iceoryx-unreleased.md index 0c40df1da2..742c7f50f7 100644 --- a/doc/website/release-notes/iceoryx-unreleased.md +++ b/doc/website/release-notes/iceoryx-unreleased.md @@ -725,8 +725,8 @@ // after iox::optional myMutex; - iox::MutexBuilder() - .mutexType(iox::MutexType::RECURSIVE) + iox::LockBuilder() + .lock_type(iox::LockType::RECURSIVE) .create(myMutex); myMutex->lock(); ``` diff --git a/iceoryx_hoofs/CMakeLists.txt b/iceoryx_hoofs/CMakeLists.txt index afdcf8cca3..f35d8705fb 100644 --- a/iceoryx_hoofs/CMakeLists.txt +++ b/iceoryx_hoofs/CMakeLists.txt @@ -104,6 +104,7 @@ iox_add_library( cli/source/option_definition.cpp cli/source/option_manager.cpp concurrent/buffer/source/mpmc_loffli.cpp + concurrent/sync/source/spin_lock.cpp filesystem/source/file_reader.cpp filesystem/source/filesystem.cpp memory/source/bump_allocator.cpp diff --git a/iceoryx_hoofs/concurrent/sync/include/iox/spin_lock.hpp b/iceoryx_hoofs/concurrent/sync/include/iox/spin_lock.hpp new file mode 100644 index 0000000000..b158506f55 --- /dev/null +++ b/iceoryx_hoofs/concurrent/sync/include/iox/spin_lock.hpp @@ -0,0 +1,67 @@ +// 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_CONCURRENT_SYNC_SPIN_LOCK_HPP +#define IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP + +#include "iox/atomic.hpp" +#include "iox/lock_interface.hpp" + +namespace iox +{ +namespace concurrent +{ + +/// @brief A spin lock implementation as drop-in replacement for a mutex +class SpinLock : public LockInterface +{ + public: + SpinLock(const SpinLock&) = delete; + SpinLock(SpinLock&&) = delete; + SpinLock& operator=(const SpinLock&) = delete; + SpinLock& operator=(SpinLock&&) = delete; + + ~SpinLock() noexcept = default; + + private: + friend class optional; + friend class LockInterface; + + explicit SpinLock(const LockType lock_type) noexcept; + + expected lock_impl() noexcept; + + expected unlock_impl() noexcept; + + expected try_lock_impl() noexcept; + + struct LockInfo + { + pid_t tid; + uint32_t recursive_count; + }; + + private: + concurrent::AtomicFlag m_lock_flag = + ATOMIC_FLAG_INIT; // NOTE: only initialization via assignment is guaranteed to work + concurrent::Atomic m_lock_info{LockInfo{0, 0}}; + const concurrent::Atomic m_recursive{false}; +}; + +} // namespace concurrent +} // namespace iox + +#endif // IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP diff --git a/iceoryx_hoofs/concurrent/sync/source/spin_lock.cpp b/iceoryx_hoofs/concurrent/sync/source/spin_lock.cpp new file mode 100644 index 0000000000..3aca835c36 --- /dev/null +++ b/iceoryx_hoofs/concurrent/sync/source/spin_lock.cpp @@ -0,0 +1,138 @@ +// 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 + +#include "iox/spin_lock.hpp" +#include "iox/detail/adaptive_wait.hpp" + +namespace iox +{ +namespace concurrent +{ +SpinLock::SpinLock(const LockType lock_type) noexcept + : m_recursive(lock_type == LockType::RECURSIVE) +{ +} + +expected SpinLock::lock_impl() noexcept +{ + pid_t tid = gettid(); + + auto lock_info = m_lock_info.load(); + + if (lock_info.tid == tid) + { + if (m_recursive.load(std::memory_order_relaxed)) + { + lock_info.recursive_count += 1; + m_lock_info.store(lock_info); + + return ok(); + } + + return err(LockError::DEADLOCK_CONDITION); + } + + detail::adaptive_wait spinner; + spinner.wait_loop([this] { return this->m_lock_flag.test_and_set(std::memory_order_acquire); }); + + m_lock_info.store(LockInfo{tid, 1}); + + return ok(); +} + +expected SpinLock::unlock_impl() noexcept +{ + pid_t tid = gettid(); + + auto lock_info = m_lock_info.load(); + + if (lock_info.tid != tid) + { + return err(UnlockError::NOT_OWNED_BY_THREAD); + } + + if (lock_info.recursive_count == 0) + { + return err(UnlockError::NOT_LOCKED); + } + + lock_info.recursive_count -= 1; + if (lock_info.recursive_count == 0) + { + lock_info.tid = 0; + m_lock_info.store(lock_info); + m_lock_flag.clear(std::memory_order_release); + } + else + { + m_lock_info.store(lock_info); + } + + return ok(); +} + +expected SpinLock::try_lock_impl() noexcept +{ + pid_t tid = gettid(); + + auto lock_info = m_lock_info.load(); + + if (lock_info.tid == tid) + { + if (m_recursive.load(std::memory_order_relaxed)) + { + lock_info.recursive_count += 1; + m_lock_info.store(lock_info); + return ok(TryLock::LOCK_SUCCEEDED); + } + + return ok(TryLock::FAILED_TO_ACQUIRE_LOCK); + } + + if (!m_lock_flag.test_and_set(std::memory_order_acquire)) + { + m_lock_info.store(LockInfo{tid, 1}); + + return ok(TryLock::LOCK_SUCCEEDED); + } + return ok(TryLock::FAILED_TO_ACQUIRE_LOCK); +} + + +} // namespace concurrent + +template <> +expected +LockBuilder::create(optional& uninitializedLock) noexcept +{ + if (m_priority_inheritance != LockPriorityInheritance::NONE) + { + return err(LockCreationError::USED_PRIORITY_UNSUPPORTED_BY_LOCK); + } + if (m_priority_ceiling.has_value()) + { + return err(LockCreationError::PRIORITY_CEILING_NOT_SUPPORTED_BY_LOCK); + } + if (m_thread_termination_behavior != LockThreadTerminationBehavior::STALL_WHEN_LOCKED) + { + return err(LockCreationError::USED_THREAD_TERMINATION_BEHAVIOR_UNSUPPORTED_BY_LOCK); + } + + uninitializedLock.emplace(m_lock_type); + return ok(); +} + +} // namespace iox diff --git a/iceoryx_hoofs/design/include/iox/lock_interface.hpp b/iceoryx_hoofs/design/include/iox/lock_interface.hpp new file mode 100644 index 0000000000..2d7c0620ab --- /dev/null +++ b/iceoryx_hoofs/design/include/iox/lock_interface.hpp @@ -0,0 +1,191 @@ +// 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_DESIGN_LOCK_INTERFACE_HPP +#define IOX_HOOFS_DESIGN_LOCK_INTERFACE_HPP + +#include "iceoryx_platform/pthread.hpp" +#include "iox/builder.hpp" +#include "iox/expected.hpp" +#include "iox/optional.hpp" + +#include + +namespace iox +{ +enum class LockCreationError : uint8_t +{ + LOCK_ALREADY_INITIALIZED, + INSUFFICIENT_MEMORY, + INSUFFICIENT_RESOURCES, + PERMISSION_DENIED, + INTER_PROCESS_LOCK_UNSUPPORTED_BY_PLATFORM, + PRIORITIES_UNSUPPORTED_BY_PLATFORM, + USED_PRIORITY_UNSUPPORTED_BY_PLATFORM, + USED_PRIORITY_UNSUPPORTED_BY_LOCK, + PRIORITY_CEILING_NOT_SUPPORTED_BY_LOCK, + INVALID_PRIORITY_CEILING_VALUE, + USED_THREAD_TERMINATION_BEHAVIOR_UNSUPPORTED_BY_LOCK, + UNKNOWN_ERROR +}; + +enum class LockError : uint8_t +{ + PRIORITY_MISMATCH, + MAXIMUM_NUMBER_OF_RECURSIVE_LOCKS_EXCEEDED, + DEADLOCK_CONDITION, + LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED, + UNKNOWN_ERROR +}; + +enum class UnlockError : uint8_t +{ + NOT_OWNED_BY_THREAD, + NOT_LOCKED, + UNKNOWN_ERROR +}; + +enum class TryLockError : uint8_t +{ + PRIORITY_MISMATCH, + MAXIMUM_NUMBER_OF_RECURSIVE_LOCKS_EXCEEDED, + LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED, + UNKNOWN_ERROR +}; + +enum class TryLock : uint8_t +{ + LOCK_SUCCEEDED, + FAILED_TO_ACQUIRE_LOCK +}; + +template +class LockInterface +{ + public: + /// @brief Engages the lock. + /// @return When it fails it returns an enum describing the error. + expected lock() noexcept + { + return static_cast(this)->lock_impl(); + } + + /// @brief Releases the lock. + /// @return When it fails it returns an enum describing the error. + expected unlock() noexcept + { + return static_cast(this)->unlock_impl(); + } + + /// @brief Tries to engage the lock. + /// @return If the lock was acquired LockInterfaceTryLock::LOCK_SUCCEEDED will be returned otherwise + /// LockInterfaceTryLock::FAILED_TO_ACQUIRE_LOCK. + /// If the lock is a recursive lock, this call will also succeed. + /// On failure it returns an enum describing the failure. + expected try_lock() noexcept + { + return static_cast(this)->try_lock_impl(); + } + + protected: + LockInterface() noexcept = default; +}; + +/// @brief Describes the type of the lock. +// NOLINTNEXTLINE(performance-enum-size) int32_t required for POSIX API +enum class LockType : int32_t +{ + /// @brief Behavior without error detection and multiple locks from within + /// the same thread lead to deadlock + NORMAL = IOX_PTHREAD_MUTEX_NORMAL, + + /// @brief Multiple locks from within the same thread do not lead to deadlock + /// but one requires the same amount of unlocks to make the thread lockable + /// from other threads + RECURSIVE = IOX_PTHREAD_MUTEX_RECURSIVE, + + /// @brief Multiple locks from within the same thread will be detected and + /// reported. It detects also when unlock is called from a different + /// thread. + WITH_DEADLOCK_DETECTION = IOX_PTHREAD_MUTEX_ERRORCHECK, +}; + +/// @brief Describes how the priority of a mutex owning thread changes when another thread +/// with an higher priority would like to acquire the mutex. +// NOLINTNEXTLINE(performance-enum-size) int32_t required for POSIX API +enum class LockPriorityInheritance : int32_t +{ + /// @brief No priority setting. + NONE = IOX_PTHREAD_PRIO_NONE, + + /// @brief The priority of a thread holding the mutex is promoted to the priority of the + /// highest priority thread waiting for the lock. + INHERIT = IOX_PTHREAD_PRIO_INHERIT, + + /// @brief The priority of a thread holding the mutex is always promoted to the priority set up + /// in priority_ceiling. + PROTECT = IOX_PTHREAD_PRIO_PROTECT +}; + +/// @brief Defines the behavior when a mutex owning thread is terminated +// NOLINTNEXTLINE(performance-enum-size) int32_t required for POSIX API +enum class LockThreadTerminationBehavior : int32_t +{ + /// @brief The mutex stays locked, is unlockable and no longer usable. + /// This can also lead to a mutex leak in the destructor. + STALL_WHEN_LOCKED = IOX_PTHREAD_MUTEX_STALLED, + + /// @brief It implies the same behavior as LockType::WITH_DEADLOCK_DETECTION. Additionally, when a mutex owning + /// thread/process dies the mutex is put into an inconsistent state which can be recovered with + /// Mutex::make_consistent(). The inconsistent state is detected by the next instance which calls + /// Mutex::lock() or Mutex::try_lock() by the error value + /// MutexError::LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED + RELEASE_WHEN_LOCKED = IOX_PTHREAD_MUTEX_ROBUST, +}; + +/// @brief Builder which creates a lock +class LockBuilder +{ + /// @brief Defines if the lock should be usable in an inter process context. Default: true + IOX_BUILDER_PARAMETER(bool, is_inter_process_capable, true) + + /// @brief Sets the LockInterfaceLockType, default: LockInterfaceLockType::RECURSIVE + IOX_BUILDER_PARAMETER(LockType, lock_type, LockType::RECURSIVE) + + /// @brief States how thread priority is adjusted when they own the lock, default: + /// LockInterfacePriorityInheritance::NONE + IOX_BUILDER_PARAMETER(LockPriorityInheritance, priority_inheritance, LockPriorityInheritance::NONE) + + /// @brief Defines the maximum priority to which a thread which owns the thread can be promoted + IOX_BUILDER_PARAMETER(optional, priority_ceiling, nullopt) + + /// @brief Defines how an engaged lock behaves when the lock owning thread terminates, + /// default: LockThreadTerminationBehavior::STALL_WHEN_LOCKED + IOX_BUILDER_PARAMETER(LockThreadTerminationBehavior, + thread_termination_behavior, + LockThreadTerminationBehavior::STALL_WHEN_LOCKED) + + public: + /// @brief Initializes a provided uninitialized lock + /// @param[in] uninitializedLock the uninitialized lock which should be initialized + /// @return On failure LockCreationError which explains the error + template + expected create(optional& uninitializedLock) noexcept; +}; + +} // namespace iox + +#endif // IOX_HOOFS_DESIGN_LOCK_INTERFACE_HPP diff --git a/iceoryx_hoofs/posix/sync/include/iox/mutex.hpp b/iceoryx_hoofs/posix/sync/include/iox/mutex.hpp index 367f2adccc..10942881fa 100644 --- a/iceoryx_hoofs/posix/sync/include/iox/mutex.hpp +++ b/iceoryx_hoofs/posix/sync/include/iox/mutex.hpp @@ -19,53 +19,19 @@ #define IOX_HOOFS_POSIX_SYNC_MUTEX_HPP #include "iceoryx_platform/pthread.hpp" -#include "iox/builder.hpp" #include "iox/expected.hpp" +#include "iox/lock_interface.hpp" #include "iox/optional.hpp" namespace iox { -enum class MutexCreationError : uint8_t -{ - MUTEX_ALREADY_INITIALIZED, - INSUFFICIENT_MEMORY, - INSUFFICIENT_RESOURCES, - PERMISSION_DENIED, - INTER_PROCESS_MUTEX_UNSUPPORTED_BY_PLATFORM, - PRIORITIES_UNSUPPORTED_BY_PLATFORM, - USED_PRIORITY_UNSUPPORTED_BY_PLATFORM, - INVALID_PRIORITY_CEILING_VALUE, - UNKNOWN_ERROR -}; +using MutexCreationError = LockCreationError; -enum class MutexLockError : uint8_t -{ - PRIORITY_MISMATCH, - MAXIMUM_NUMBER_OF_RECURSIVE_LOCKS_EXCEEDED, - DEADLOCK_CONDITION, - LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED, - UNKNOWN_ERROR -}; +using MutexLockError = LockError; +using MutexUnlockError = UnlockError; -enum class MutexUnlockError : uint8_t -{ - NOT_OWNED_BY_THREAD, - UNKNOWN_ERROR -}; - -enum class MutexTryLockError : uint8_t -{ - PRIORITY_MISMATCH, - MAXIMUM_NUMBER_OF_RECURSIVE_LOCKS_EXCEEDED, - LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED, - UNKNOWN_ERROR -}; - -enum class MutexTryLock : uint8_t -{ - LOCK_SUCCEEDED, - FAILED_TO_ACQUIRE_LOCK -}; +using MutexTryLockError = TryLockError; +using MutexTryLock = TryLock; /// @brief Wrapper for a inter-process pthread based mutex which does not use /// exceptions! @@ -74,12 +40,12 @@ enum class MutexTryLock : uint8_t /// /// int main() { /// optional myMutex; -/// iox::MutexBuilder().isInterProcessCapable(true) -/// .mutexType(MutexType::RECURSIVE) -/// .priorityInheritance(MutexPriorityInheritance::NONE) -/// .threadTerminationBehavior(MutexThreadTerminationBehavior::RELEASE_WHEN_LOCKED) -/// .create(myMutex) -/// .expect("Failed to create mutex!"); +/// iox::LockBuilder().is_inter_process_capable(true) +/// .lock_type(LockType::RECURSIVE) +/// .priority_inheritance(LockPriorityInheritance::NONE) +/// .thread_termination_behavior(LockThreadTerminationBehavior::RELEASE_WHEN_LOCKED) +/// .create(myMutex) +/// .expect("Failed to create mutex!"); /// /// myMutex->lock().expect("Mutex lock failed. Maybe the system is corrupted."); /// // ... do stuff @@ -92,7 +58,7 @@ enum class MutexTryLock : uint8_t /// /// } /// @endcode -class mutex +class mutex : public LockInterface { public: /// @brief Destroys the mutex. When the mutex is still locked this will fail and the @@ -109,24 +75,9 @@ class mutex mutex& operator=(const mutex&) = delete; mutex& operator=(mutex&&) = delete; - /// @brief Locks the mutex. - /// @return When it fails it returns an enum describing the error. - expected lock() noexcept; - - /// @brief Unlocks the mutex. - /// @return When it fails it returns an enum describing the error. - expected unlock() noexcept; - - /// @brief Tries to lock the mutex. - /// @return If the lock was acquired MutexTryLock::LOCK_SUCCEEDED will be returned otherwise - /// MutexTryLock::FAILED_TO_ACQUIRE_LOCK. - /// If the lock is of MutexType::RECURSIVE the lock will also succeed. - /// On failure it returns an enum describing the failure. - expected try_lock() noexcept; - /// @brief When a mutex owning thread/process with MutexThreadTerminationBehavior::RELEASE_WHEN_LOCKED dies then the /// next instance which would like to acquire the lock will get an - /// Mutex{Try}LockError::LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED error. This method puts + /// {Try}LockError::LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED error. This method puts /// the mutex again into a consistent state. If the mutex is already in a consistent state it will do /// nothing. void make_consistent() noexcept; @@ -134,8 +85,15 @@ class mutex private: mutex() noexcept = default; + expected lock_impl() noexcept; + + expected unlock_impl() noexcept; + + expected try_lock_impl() noexcept; + private: - friend class MutexBuilder; + friend class LockBuilder; + friend class LockInterface; friend class optional; iox_pthread_mutex_t m_handle = IOX_PTHREAD_MUTEX_INITIALIZER; @@ -144,84 +102,17 @@ class mutex }; /// @brief Describes the type of mutex. -// NOLINTNEXTLINE(performance-enum-size) int32_t required for POSIX API -enum class MutexType : int32_t -{ - /// @brief Behavior without error detection and multiple locks from within - /// the same thread lead to deadlock - NORMAL = IOX_PTHREAD_MUTEX_NORMAL, - - /// @brief Multiple locks from within the same thread do not lead to deadlock - /// but one requires the same amount of unlocks to make the thread lockable - /// from other threads - RECURSIVE = IOX_PTHREAD_MUTEX_RECURSIVE, - - /// @brief Multiple locks from within the same thread will be detected and - /// reported. It detects also when unlock is called from a different - /// thread. - WITH_DEADLOCK_DETECTION = IOX_PTHREAD_MUTEX_ERRORCHECK, -}; +using MutexType = LockType; /// @brief Describes how the priority of a mutex owning thread changes when another thread /// with an higher priority would like to acquire the mutex. -// NOLINTNEXTLINE(performance-enum-size) int32_t required for POSIX API -enum class MutexPriorityInheritance : int32_t -{ - /// @brief No priority setting. - NONE = IOX_PTHREAD_PRIO_NONE, - - /// @brief The priority of a thread holding the mutex is promoted to the priority of the - /// highest priority thread waiting for the lock. - INHERIT = IOX_PTHREAD_PRIO_INHERIT, - - /// @brief The priority of a thread holding the mutex is always promoted to the priority set up - /// in priorityCeiling. - PROTECT = IOX_PTHREAD_PRIO_PROTECT -}; +using MutexPriorityInheritance = LockPriorityInheritance; /// @brief Defines the behavior when a mutex owning thread is terminated -// NOLINTNEXTLINE(performance-enum-size) int32_t required for POSIX API -enum class MutexThreadTerminationBehavior : int32_t -{ - /// @brief The mutex stays locked, is unlockable and no longer usable. - /// This can also lead to a mutex leak in the destructor. - STALL_WHEN_LOCKED = IOX_PTHREAD_MUTEX_STALLED, - - /// @brief It implies the same behavior as MutexType::WITH_DEADLOCK_DETECTION. Additionally, when a mutex owning - /// thread/process dies the mutex is put into an inconsistent state which can be recovered with - /// Mutex::make_consistent(). The inconsistent state is detected by the next instance which calls - /// Mutex::lock() or Mutex::try_lock() by the error value - /// MutexError::LOCK_ACQUIRED_BUT_HAS_INCONSISTENT_STATE_SINCE_OWNER_DIED - RELEASE_WHEN_LOCKED = IOX_PTHREAD_MUTEX_ROBUST, -}; +using MutexThreadTerminationBehavior = LockThreadTerminationBehavior; /// @brief Builder which creates a posix mutex -class MutexBuilder -{ - /// @brief Defines if the mutex should be usable in an inter process context. Default: true - IOX_BUILDER_PARAMETER(bool, isInterProcessCapable, true) - - /// @brief Sets the MutexType, default: MutexType::RECURSIVE - IOX_BUILDER_PARAMETER(MutexType, mutexType, MutexType::RECURSIVE) - - /// @brief States how thread priority is adjusted when they own the mutex, default: MutexPriorityInheritance::NONE - IOX_BUILDER_PARAMETER(MutexPriorityInheritance, priorityInheritance, MutexPriorityInheritance::NONE) - - /// @brief Defines the maximum priority to which a thread which owns the thread can be promoted - IOX_BUILDER_PARAMETER(optional, priorityCeiling, nullopt) - - /// @brief Defines how a locked mutex behaves when the mutex owning thread terminates, - /// default: MutexThreadTerminationBehavior::RELEASE_WHEN_LOCKED - IOX_BUILDER_PARAMETER(MutexThreadTerminationBehavior, - threadTerminationBehavior, - MutexThreadTerminationBehavior::RELEASE_WHEN_LOCKED) - - public: - /// @brief Initializes a provided uninitialized mutex - /// @param[in] uninitializedMutex the uninitialized mutex which should be initialized - /// @return On failure MutexError which explains the error - expected create(optional& uninitializedMutex) noexcept; -}; +using MutexBuilder = LockBuilder; } // namespace iox #endif // IOX_HOOFS_POSIX_SYNC_MUTEX_HPP diff --git a/iceoryx_hoofs/posix/sync/source/mutex.cpp b/iceoryx_hoofs/posix/sync/source/mutex.cpp index 17c4f6b294..72b7691aac 100644 --- a/iceoryx_hoofs/posix/sync/source/mutex.cpp +++ b/iceoryx_hoofs/posix/sync/source/mutex.cpp @@ -83,7 +83,7 @@ struct MutexAttributes { case ENOTSUP: IOX_LOG(ERROR, "The platform does not support shared mutex (inter process mutex)"); - return err(MutexCreationError::INTER_PROCESS_MUTEX_UNSUPPORTED_BY_PLATFORM); + return err(MutexCreationError::INTER_PROCESS_LOCK_UNSUPPORTED_BY_PLATFORM); default: IOX_LOG(ERROR, "This should never happen. An unknown error occurred while setting up the inter process " @@ -95,21 +95,21 @@ struct MutexAttributes return ok(); } - expected setType(const MutexType mutexType) noexcept + expected setType(const LockType lock_type) noexcept { - auto result = IOX_POSIX_CALL(iox_pthread_mutexattr_settype)(&*m_attributes, static_cast(mutexType)) + auto result = IOX_POSIX_CALL(iox_pthread_mutexattr_settype)(&*m_attributes, static_cast(lock_type)) .returnValueMatchesErrno() .evaluate(); if (result.has_error()) { IOX_LOG(ERROR, "This should never happen. An unknown error occurred while setting up the mutex type."); - return err(MutexCreationError::UNKNOWN_ERROR); + return err(LockCreationError::UNKNOWN_ERROR); } return ok(); } - expected setProtocol(const MutexPriorityInheritance priorityInheritance) + expected setProtocol(const LockPriorityInheritance priorityInheritance) { auto result = IOX_POSIX_CALL(iox_pthread_mutexattr_setprotocol)(&*m_attributes, static_cast(priorityInheritance)) @@ -121,24 +121,24 @@ struct MutexAttributes { case ENOSYS: IOX_LOG(ERROR, "The system does not support mutex priorities"); - return err(MutexCreationError::PRIORITIES_UNSUPPORTED_BY_PLATFORM); + return err(LockCreationError::PRIORITIES_UNSUPPORTED_BY_PLATFORM); case ENOTSUP: IOX_LOG(ERROR, "The used mutex priority is not supported by the platform"); - return err(MutexCreationError::USED_PRIORITY_UNSUPPORTED_BY_PLATFORM); + return err(LockCreationError::USED_PRIORITY_UNSUPPORTED_BY_PLATFORM); case EPERM: IOX_LOG(ERROR, "Insufficient permissions to set mutex priorities"); - return err(MutexCreationError::PERMISSION_DENIED); + return err(LockCreationError::PERMISSION_DENIED); default: IOX_LOG(ERROR, "This should never happen. An unknown error occurred while setting up the mutex priority."); - return err(MutexCreationError::UNKNOWN_ERROR); + return err(LockCreationError::UNKNOWN_ERROR); } } return ok(); } - expected setPrioCeiling(const int32_t priorityCeiling) noexcept + expected setPrioCeiling(const int32_t priorityCeiling) noexcept { auto result = IOX_POSIX_CALL(iox_pthread_mutexattr_setprioceiling)(&*m_attributes, static_cast(priorityCeiling)) @@ -150,10 +150,10 @@ struct MutexAttributes { case EPERM: IOX_LOG(ERROR, "Insufficient permissions to set the mutex priority ceiling."); - return err(MutexCreationError::PERMISSION_DENIED); + return err(LockCreationError::PERMISSION_DENIED); case ENOSYS: IOX_LOG(ERROR, "The platform does not support mutex priority ceiling."); - return err(MutexCreationError::PRIORITIES_UNSUPPORTED_BY_PLATFORM); + return err(LockCreationError::PRIORITIES_UNSUPPORTED_BY_PLATFORM); case EINVAL: { auto minimumPriority = detail::getSchedulerPriorityMinimum(detail::Scheduler::FIFO); @@ -163,20 +163,20 @@ struct MutexAttributes "The priority ceiling \"" << priorityCeiling << "\" is not in the valid priority range [ " << minimumPriority << ", " << maximumPriority << "] of the Scheduler::FIFO."); - return err(MutexCreationError::INVALID_PRIORITY_CEILING_VALUE); + return err(LockCreationError::INVALID_PRIORITY_CEILING_VALUE); } default: IOX_LOG( ERROR, "This should never happen. An unknown error occurred while setting up the mutex priority ceiling."); - return err(MutexCreationError::UNKNOWN_ERROR); + return err(LockCreationError::UNKNOWN_ERROR); } } return ok(); } - expected + expected setThreadTerminationBehavior(const MutexThreadTerminationBehavior behavior) noexcept { auto result = IOX_POSIX_CALL(iox_pthread_mutexattr_setrobust)(&*m_attributes, static_cast(behavior)) @@ -187,7 +187,7 @@ struct MutexAttributes IOX_LOG(ERROR, "This should never happen. An unknown error occurred while setting up the mutex thread " "termination behavior."); - return err(MutexCreationError::UNKNOWN_ERROR); + return err(LockCreationError::UNKNOWN_ERROR); } return ok(); @@ -196,8 +196,8 @@ struct MutexAttributes optional m_attributes; }; -expected initializeMutex(iox_pthread_mutex_t* const handle, - const iox_pthread_mutexattr_t* const attributes) noexcept +expected initializeMutex(iox_pthread_mutex_t* const handle, + const iox_pthread_mutexattr_t* const attributes) noexcept { auto initResult = IOX_POSIX_CALL(iox_pthread_mutex_init)(handle, attributes).returnValueMatchesErrno().evaluate(); if (initResult.has_error()) @@ -224,12 +224,13 @@ expected initializeMutex(iox_pthread_mutex_t* const ha return ok(); } -expected MutexBuilder::create(optional& uninitializedMutex) noexcept +template <> +expected MutexBuilder::create(optional& uninitializedLock) noexcept { - if (uninitializedMutex.has_value()) + if (uninitializedLock.has_value()) { IOX_LOG(ERROR, "Unable to override an already initialized mutex with a new mutex"); - return err(MutexCreationError::MUTEX_ALREADY_INITIALIZED); + return err(MutexCreationError::LOCK_ALREADY_INITIALIZED); } MutexAttributes mutexAttributes; @@ -240,50 +241,50 @@ expected MutexBuilder::create(optional& uniniti return result; } - result = mutexAttributes.enableIpcSupport(m_isInterProcessCapable); + result = mutexAttributes.enableIpcSupport(m_is_inter_process_capable); if (result.has_error()) { return result; } - result = mutexAttributes.setType(m_mutexType); + result = mutexAttributes.setType(m_lock_type); if (result.has_error()) { return result; } - result = mutexAttributes.setProtocol(m_priorityInheritance); + result = mutexAttributes.setProtocol(m_priority_inheritance); if (result.has_error()) { return result; } - if (m_priorityInheritance == MutexPriorityInheritance::PROTECT && m_priorityCeiling.has_value()) + if (m_priority_inheritance == MutexPriorityInheritance::PROTECT && m_priority_ceiling.has_value()) { - result = mutexAttributes.setPrioCeiling(*m_priorityCeiling); + result = mutexAttributes.setPrioCeiling(*m_priority_ceiling); if (result.has_error()) { return result; } } - result = mutexAttributes.setThreadTerminationBehavior(m_threadTerminationBehavior); + result = mutexAttributes.setThreadTerminationBehavior(m_thread_termination_behavior); if (result.has_error()) { return result; } - uninitializedMutex.emplace(); - uninitializedMutex->m_isDestructable = false; + uninitializedLock.emplace(); + uninitializedLock->m_isDestructable = false; - result = initializeMutex(&uninitializedMutex->m_handle, &*mutexAttributes.m_attributes); + result = initializeMutex(&uninitializedLock->m_handle, &*mutexAttributes.m_attributes); if (result.has_error()) { - uninitializedMutex.reset(); + uninitializedLock.reset(); return result; } - uninitializedMutex->m_isDestructable = true; + uninitializedLock->m_isDestructable = true; return ok(); } @@ -325,7 +326,7 @@ void mutex::make_consistent() noexcept } } -expected mutex::lock() noexcept +expected mutex::lock_impl() noexcept { auto result = IOX_POSIX_CALL(iox_pthread_mutex_lock)(&m_handle).returnValueMatchesErrno().evaluate(); if (result.has_error()) @@ -359,7 +360,7 @@ expected mutex::lock() noexcept return ok(); } -expected mutex::unlock() noexcept +expected mutex::unlock_impl() noexcept { auto result = IOX_POSIX_CALL(iox_pthread_mutex_unlock)(&m_handle).returnValueMatchesErrno().evaluate(); if (result.has_error()) @@ -382,7 +383,7 @@ expected mutex::unlock() noexcept return ok(); } -expected mutex::try_lock() noexcept +expected mutex::try_lock_impl() noexcept { auto result = IOX_POSIX_CALL(iox_pthread_mutex_trylock)(&m_handle).returnValueMatchesErrno().ignoreErrnos(EBUSY).evaluate(); diff --git a/iceoryx_hoofs/test/moduletests/test_posix_mutex.cpp b/iceoryx_hoofs/test/moduletests/test_posix_mutex.cpp index 5bc695d3e5..63b184f78d 100644 --- a/iceoryx_hoofs/test/moduletests/test_posix_mutex.cpp +++ b/iceoryx_hoofs/test/moduletests/test_posix_mutex.cpp @@ -36,8 +36,8 @@ class Mutex_test : public Test { deadlockWatchdog.watchAndActOnFailure([] { std::terminate(); }); - ASSERT_FALSE(MutexBuilder().mutexType(MutexType::RECURSIVE).create(sutRecursive).has_error()); - ASSERT_FALSE(MutexBuilder().mutexType(MutexType::NORMAL).create(sutNonRecursive).has_error()); + ASSERT_FALSE(LockBuilder().lock_type(LockType::RECURSIVE).create(sutRecursive).has_error()); + ASSERT_FALSE(LockBuilder().lock_type(LockType::NORMAL).create(sutNonRecursive).has_error()); } void TearDown() override @@ -182,7 +182,7 @@ TEST_F(Mutex_test, MutexWithDeadlockDetectionsFailsOnDeadlock) { ::testing::Test::RecordProperty("TEST_ID", "feb07935-674d-4ebc-abaa-66664751719a"); iox::optional sut; - ASSERT_FALSE(MutexBuilder().mutexType(MutexType::WITH_DEADLOCK_DETECTION).create(sut).has_error()); + ASSERT_FALSE(MutexBuilder().lock_type(MutexType::WITH_DEADLOCK_DETECTION).create(sut).has_error()); EXPECT_FALSE(sut->lock().has_error()); auto result = sut->lock(); ASSERT_TRUE(result.has_error()); @@ -196,7 +196,7 @@ TEST_F(Mutex_test, MutexWithDeadlockDetectionsFailsWhenSameThreadTriesToUnlockIt { ::testing::Test::RecordProperty("TEST_ID", "062e411e-a5d3-4759-9faf-db6f4129d395"); iox::optional sut; - ASSERT_FALSE(MutexBuilder().mutexType(MutexType::WITH_DEADLOCK_DETECTION).create(sut).has_error()); + ASSERT_FALSE(MutexBuilder().lock_type(MutexType::WITH_DEADLOCK_DETECTION).create(sut).has_error()); EXPECT_FALSE(sut->lock().has_error()); EXPECT_FALSE(sut->unlock().has_error()); @@ -209,7 +209,7 @@ TEST_F(Mutex_test, MutexWithDeadlockDetectionsFailsWhenAnotherThreadTriesToUnloc { ::testing::Test::RecordProperty("TEST_ID", "4dcea981-2259-48c6-bf27-7839ad9013b4"); iox::optional sut; - ASSERT_FALSE(MutexBuilder().mutexType(MutexType::WITH_DEADLOCK_DETECTION).create(sut).has_error()); + ASSERT_FALSE(MutexBuilder().lock_type(MutexType::WITH_DEADLOCK_DETECTION).create(sut).has_error()); EXPECT_FALSE(sut->lock().has_error()); std::thread t([&] { @@ -231,7 +231,7 @@ TEST_F(Mutex_test, #endif iox::optional sut; ASSERT_FALSE(MutexBuilder() - .threadTerminationBehavior(MutexThreadTerminationBehavior::RELEASE_WHEN_LOCKED) + .thread_termination_behavior(MutexThreadTerminationBehavior::RELEASE_WHEN_LOCKED) .create(sut) .has_error()); @@ -254,7 +254,7 @@ TEST_F(Mutex_test, MutexWithStallWhenLockedBehaviorDoesntUnlockMutexWhenThreadTe #endif iox::optional sut; ASSERT_FALSE(MutexBuilder() - .threadTerminationBehavior(MutexThreadTerminationBehavior::STALL_WHEN_LOCKED) + .thread_termination_behavior(MutexThreadTerminationBehavior::STALL_WHEN_LOCKED) .create(sut) .has_error()); @@ -274,6 +274,6 @@ TEST_F(Mutex_test, InitializingMutexTwiceResultsInError) auto result = MutexBuilder().create(sutRecursive); ASSERT_THAT(result.has_error(), Eq(true)); - EXPECT_THAT(result.error(), Eq(MutexCreationError::MUTEX_ALREADY_INITIALIZED)); + EXPECT_THAT(result.error(), Eq(MutexCreationError::LOCK_ALREADY_INITIALIZED)); } } // namespace 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..7a56be3290 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 @@ -17,11 +17,18 @@ #define IOX_POSH_POPO_BUILDING_BLOCKS_LOCKING_POLICY_HPP #include "iox/mutex.hpp" +#include "iox/spin_lock.hpp" namespace iox { namespace popo { +#ifdef IOX_EXPERIMENTAL_32_64_BIT_MIX_MODE +using InterProcessLock = concurrent::SpinLock; +#else +using InterProcessLock = mutex; +#endif + class ThreadSafePolicy { public: @@ -33,7 +40,7 @@ class ThreadSafePolicy bool tryLock() const noexcept; private: - mutable optional m_mutex; + mutable optional m_lock; }; class SingleThreadedPolicy diff --git a/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp b/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp index db1796b422..0611adb6f5 100644 --- a/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp +++ b/iceoryx_posh/source/popo/building_blocks/locking_policy.cpp @@ -25,16 +25,16 @@ namespace popo { ThreadSafePolicy::ThreadSafePolicy() noexcept { - MutexBuilder() - .isInterProcessCapable(true) - .mutexType(MutexType::RECURSIVE) - .create(m_mutex) + LockBuilder() + .is_inter_process_capable(true) + .lock_type(LockType::RECURSIVE) + .create(m_lock) .expect("Failed to create Mutex"); } void ThreadSafePolicy::lock() const noexcept { - if (!m_mutex->lock()) + if (!m_lock->lock()) { IOX_LOG(FATAL, "Locking of an inter-process mutex failed! This indicates that the application holding the lock " @@ -45,7 +45,7 @@ void ThreadSafePolicy::lock() const noexcept void ThreadSafePolicy::unlock() const noexcept { - if (!m_mutex->unlock()) + if (!m_lock->unlock()) { IOX_LOG(FATAL, "Unlocking of an inter-process mutex failed! This indicates that the resources were cleaned up " @@ -56,12 +56,12 @@ void ThreadSafePolicy::unlock() const noexcept bool ThreadSafePolicy::tryLock() const noexcept { - auto tryLockResult = m_mutex->try_lock(); + auto tryLockResult = m_lock->try_lock(); if (tryLockResult.has_error()) { IOX_REPORT_FATAL(PoshError::POPO__CHUNK_TRY_LOCK_ERROR); } - return *tryLockResult == MutexTryLock::LOCK_SUCCEEDED; + return *tryLockResult == TryLock::LOCK_SUCCEEDED; } void SingleThreadedPolicy::lock() const noexcept