Skip to content

Commit

Permalink
iox-eclipse-iceoryx#2301 Introduce SpinLock
Browse files Browse the repository at this point in the history
  • Loading branch information
elBoberido committed Sep 19, 2024
1 parent 5ea8482 commit ca63dd8
Show file tree
Hide file tree
Showing 16 changed files with 681 additions and 191 deletions.
4 changes: 2 additions & 2 deletions doc/website/release-notes/iceoryx-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -725,8 +725,8 @@

// after
iox::optional<iox::mutex> myMutex;
iox::MutexBuilder()
.mutexType(iox::MutexType::RECURSIVE)
iox::LockBuilder()
.lock_type(iox::LockType::RECURSIVE)
.create(myMutex);
myMutex->lock();
```
Expand Down
2 changes: 2 additions & 0 deletions iceoryx_hoofs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ 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
concurrent/sync/source/spin_semaphore.cpp
filesystem/source/file_reader.cpp
filesystem/source/filesystem.cpp
memory/source/bump_allocator.cpp
Expand Down
67 changes: 67 additions & 0 deletions iceoryx_hoofs/concurrent/sync/include/iox/spin_lock.hpp
Original file line number Diff line number Diff line change
@@ -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<SpinLock>
{
public:
SpinLock(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock& operator=(SpinLock&&) = delete;

~SpinLock() noexcept = default;

private:
friend class optional<SpinLock>;
friend class LockInterface<SpinLock>;

explicit SpinLock(const LockType lock_type) noexcept;

expected<void, LockError> lock_impl() noexcept;

expected<void, UnlockError> unlock_impl() noexcept;

expected<TryLock, TryLockError> 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<LockInfo> m_lock_info{LockInfo{0, 0}};
const concurrent::Atomic<bool> m_recursive{false};
};

} // namespace concurrent
} // namespace iox

#endif // IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP
85 changes: 85 additions & 0 deletions iceoryx_hoofs/concurrent/sync/include/iox/spin_semaphore.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

// 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_SEMAPHORE_HPP
#define IOX_HOOFS_CONCURRENT_SYNC_SPIN_SEMAPHORE_HPP

#include "iox/atomic.hpp"
#include "iox/deadline_timer.hpp"
#include "iox/detail/adaptive_wait.hpp"
#include "iox/detail/semaphore_interface.hpp"
#include "iox/optional.hpp"
#include "iox/spin_lock.hpp"

namespace iox
{
namespace concurrent
{
class SpinSemaphoreBuilder;

class SpinSemaphore : public detail::SemaphoreInterface<SpinSemaphore>
{
public:
using Builder = SpinSemaphoreBuilder;

SpinSemaphore(const SpinSemaphore&) = delete;
SpinSemaphore(SpinSemaphore&&) = delete;
SpinSemaphore& operator=(const SpinSemaphore&) = delete;
SpinSemaphore& operator=(SpinSemaphore&&) = delete;

~SpinSemaphore() noexcept;

expected<void, SemaphoreError> post() noexcept;

expected<void, SemaphoreError> wait() noexcept;

expected<bool, SemaphoreError> tryWait() noexcept;

expected<SemaphoreWaitState, SemaphoreError> timedWait(const units::Duration& timeout) noexcept;

private:
friend class optional<SpinSemaphore>;

explicit SpinSemaphore(int32_t initial_value) noexcept;

private:
concurrent::Atomic<int32_t> m_count{0};
concurrent::Atomic<bool> m_to_be_destroyed{false};
optional<concurrent::SpinLock> m_spinlock;
};

class SpinSemaphoreBuilder
{
/// @brief Set the initial value of the spin semaphore
IOX_BUILDER_PARAMETER(uint32_t, initialValue, 0U)

/// @brief Set if the spin semaphore can be stored in the shared memory
/// for inter process usage
IOX_BUILDER_PARAMETER(bool, isInterProcessCapable, true)

public:
/// @brief Create a spin semaphore
/// @param[in] uninitializedSemaphore since the semaphore is not movable the user has to provide
/// memory to store the semaphore into - packed in an optional
/// @return an error describing the failure or success
expected<void, SemaphoreError> create(optional<SpinSemaphore>& uninitializedSemaphore) const noexcept;
};

} // namespace concurrent
} // namespace iox

#endif // IOX_HOOFS_CONCURRENT_SYNC_SPIN_LOCK_HPP
138 changes: 138 additions & 0 deletions iceoryx_hoofs/concurrent/sync/source/spin_lock.cpp
Original file line number Diff line number Diff line change
@@ -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<void, LockError> 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<void, UnlockError> 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<TryLock, TryLockError> 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<void, LockCreationError>
LockBuilder::create<concurrent::SpinLock>(optional<concurrent::SpinLock>& 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
Loading

0 comments on commit ca63dd8

Please sign in to comment.