Skip to content

Commit

Permalink
iox-eclipse-iceoryx#2301 Introduce SpinSemaphore
Browse files Browse the repository at this point in the history
  • Loading branch information
elBoberido committed Sep 19, 2024
1 parent 04c9938 commit a867c13
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 53 deletions.
3 changes: 2 additions & 1 deletion iceoryx_hoofs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ iox_add_library(
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 Expand Up @@ -135,7 +136,7 @@ iox_add_library(
posix/sync/source/named_semaphore.cpp
posix/sync/source/signal_handler.cpp
posix/sync/source/signal_watcher.cpp
posix/sync/source/semaphore_interface.cpp
posix/sync/source/semaphore_helper.cpp
posix/sync/source/thread.cpp
posix/sync/source/unnamed_semaphore.cpp
posix/time/source/adaptive_wait.cpp
Expand Down
86 changes: 86 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,86 @@

// 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/optional.hpp"
#include "iox/semaphore_interface.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;

private:
friend class optional<SpinSemaphore>;
friend class detail::SemaphoreInterface<SpinSemaphore>;

explicit SpinSemaphore(int32_t initial_value) noexcept;

expected<void, SemaphoreError> post_impl() noexcept;

expected<void, SemaphoreError> wait_impl() noexcept;

expected<bool, SemaphoreError> try_wait_impl() noexcept;

expected<SemaphoreWaitState, SemaphoreError> timed_wait_impl(const units::Duration& timeout) 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
112 changes: 112 additions & 0 deletions iceoryx_hoofs/concurrent/sync/source/spin_semaphore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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_semaphore.hpp"
#include "iox/detail/adaptive_wait.hpp"

namespace iox
{
namespace concurrent
{
expected<void, SemaphoreError>
SpinSemaphoreBuilder::create(optional<SpinSemaphore>& uninitializedSemaphore) const noexcept
{
if (m_initialValue > IOX_SEM_VALUE_MAX)
{
IOX_LOG(ERROR,
"The spin semaphore initial value of " << m_initialValue << " exceeds the maximum semaphore value "
<< IOX_SEM_VALUE_MAX);
return err(SemaphoreError::SEMAPHORE_OVERFLOW);
}

uninitializedSemaphore.emplace(static_cast<int32_t>(m_initialValue));
return ok();
}

SpinSemaphore::SpinSemaphore(int32_t initial_value) noexcept
: m_count(initial_value)
{
LockBuilder()
.is_inter_process_capable(true)
.lock_type(LockType::NORMAL)
.create(m_spinlock)
.expect("Failed to create Lock");
}

SpinSemaphore::~SpinSemaphore() noexcept
{
m_to_be_destroyed = true;
}

expected<void, SemaphoreError> SpinSemaphore::post_impl() noexcept
{
std::lock_guard<concurrent::SpinLock> lock(*m_spinlock);

if (m_count.load(std::memory_order_relaxed) == IOX_SEM_VALUE_MAX)
{
return err(SemaphoreError::SEMAPHORE_OVERFLOW);
}

++m_count;
return ok();
}

expected<void, SemaphoreError> SpinSemaphore::wait_impl() noexcept
{
detail::adaptive_wait spinner;
spinner.wait_loop([this] {
auto wait_result = this->tryWait();
return wait_result.has_value() && !wait_result.value();
});
return ok();
}

expected<bool, SemaphoreError> SpinSemaphore::try_wait_impl() noexcept
{
std::lock_guard<concurrent::SpinLock> lock(*m_spinlock);
if (m_to_be_destroyed.load(std::memory_order_relaxed))
{
return ok(true);
}
if (m_count.load(std::memory_order_relaxed) > 0)
{
--m_count;
return ok(true);
}
return ok(false);
}

expected<SemaphoreWaitState, SemaphoreError> SpinSemaphore::timed_wait_impl(const units::Duration& timeout) noexcept
{
iox::deadline_timer deadline_timer(timeout);
detail::adaptive_wait spinner;

auto ret_val = SemaphoreWaitState::TIMEOUT;
spinner.wait_loop([this, &deadline_timer, &ret_val] {
auto wait_result = this->tryWait();

if (wait_result.has_value() && wait_result.value())
{
ret_val = SemaphoreWaitState::NO_TIMEOUT;
return false;
}
return !deadline_timer.hasExpired();
});

return ok(ret_val);
}
} // namespace concurrent
} // namespace iox
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
// 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.
Expand All @@ -14,8 +15,8 @@
//
// SPDX-License-Identifier: Apache-2.0

#ifndef IOX_HOOFS_POSIX_SYNC_SEMAPHORE_INTERFACE_HPP
#define IOX_HOOFS_POSIX_SYNC_SEMAPHORE_INTERFACE_HPP
#ifndef IOX_HOOFS_DESIGN_SEMAPHORE_INTERFACE_HPP
#define IOX_HOOFS_DESIGN_SEMAPHORE_INTERFACE_HPP

#include "iceoryx_platform/semaphore.hpp"
#include "iox/duration.hpp"
Expand Down Expand Up @@ -59,32 +60,41 @@ class SemaphoreInterface
/// @brief Increments the semaphore by one
/// @return Fails when the value of the semaphore overflows or when the
/// semaphore was removed from outside the process
expected<void, SemaphoreError> post() noexcept;
expected<void, SemaphoreError> post() noexcept
{
return static_cast<SemaphoreChild*>(this)->post_impl();
}

/// @brief Decrements the semaphore by one. When the semaphore value is zero
/// it blocks until the semaphore value is greater zero
/// @return Fails when semaphore was removed from outside the process
expected<void, SemaphoreError> wait() noexcept;
expected<void, SemaphoreError> wait() noexcept
{
return static_cast<SemaphoreChild*>(this)->wait_impl();
}

/// @brief Tries to decrement the semaphore by one. When the semaphore value is zero
/// it returns false otherwise it returns true and decrement the value by one.
/// @return Fails when semaphore was removed from outside the process
expected<bool, SemaphoreError> tryWait() noexcept;
expected<bool, SemaphoreError> tryWait() noexcept
{
return static_cast<SemaphoreChild*>(this)->try_wait_impl();
}

/// @brief Tries to decrement the semaphore by one. When the semaphore value is zero
/// it waits until the timeout has passed.
/// @return If during the timeout time the semaphore value increases to non zero
/// it returns SemaphoreWaitState::NO_TIMEOUT and decreases the semaphore by one
/// otherwise returns SemaphoreWaitState::TIMEOUT
expected<SemaphoreWaitState, SemaphoreError> timedWait(const units::Duration& timeout) noexcept;
expected<SemaphoreWaitState, SemaphoreError> timedWait(const units::Duration& timeout) noexcept
{
return static_cast<SemaphoreChild*>(this)->timed_wait_impl(timeout);
}

protected:
SemaphoreInterface() noexcept = default;

private:
iox_sem_t* getHandle() noexcept;
};
} // namespace detail
} // namespace iox

#endif // IOX_HOOFS_POSIX_SYNC_SEMAPHORE_INTERFACE_HPP
#endif // IOX_HOOFS_DESIGN_SEMAPHORE_INTERFACE_HPP
54 changes: 54 additions & 0 deletions iceoryx_hoofs/posix/sync/include/iox/detail/semaphore_helper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2022 by Apex.AI Inc. 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_POSIX_SYNC_SEMAPHORE_HELPER_HPP
#define IOX_HOOFS_POSIX_SYNC_SEMAPHORE_HELPER_HPP

#include "iceoryx_platform/semaphore.hpp"
#include "iox/duration.hpp"
#include "iox/expected.hpp"
#include "iox/semaphore_interface.hpp"

namespace iox
{
namespace detail
{
/// @brief Increments the semaphore by one
/// @return Fails when the value of the semaphore overflows or when the
/// semaphore was removed from outside the process
expected<void, SemaphoreError> sem_post(iox_sem_t* handle) noexcept;

/// @brief Decrements the semaphore by one. When the semaphore value is zero
/// it blocks until the semaphore value is greater zero
/// @return Fails when semaphore was removed from outside the process
expected<void, SemaphoreError> sem_wait(iox_sem_t* handle) noexcept;

/// @brief Tries to decrement the semaphore by one. When the semaphore value is zero
/// it returns false otherwise it returns true and decrement the value by one.
/// @return Fails when semaphore was removed from outside the process
expected<bool, SemaphoreError> sem_try_wait(iox_sem_t* handle) noexcept;

/// @brief Tries to decrement the semaphore by one. When the semaphore value is zero
/// it waits until the timeout has passed.
/// @return If during the timeout time the semaphore value increases to non zero
/// it returns SemaphoreWaitState::NO_TIMEOUT and decreases the semaphore by one
/// otherwise returns SemaphoreWaitState::TIMEOUT
expected<SemaphoreWaitState, SemaphoreError> sem_timed_wait(iox_sem_t* handle, const units::Duration& timeout) noexcept;

} // namespace detail
} // namespace iox

#endif // IOX_HOOFS_POSIX_SYNC_SEMAPHORE_HELPER_HPP
12 changes: 10 additions & 2 deletions iceoryx_hoofs/posix/sync/include/iox/named_semaphore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,22 @@

#include "iceoryx_platform/platform_settings.hpp"
#include "iox/builder.hpp"
#include "iox/detail/semaphore_interface.hpp"
#include "iox/expected.hpp"
#include "iox/filesystem.hpp"
#include "iox/optional.hpp"
#include "iox/semaphore_interface.hpp"
#include "iox/string.hpp"

namespace iox
{
class NamedSemaphoreBuilder;

/// @brief A named posix semaphore.
class NamedSemaphore final : public detail::SemaphoreInterface<NamedSemaphore>
{
public:
using Builder = NamedSemaphoreBuilder;

static constexpr uint64_t LENGTH_OF_SEMAPHORE_SLASH_PREFIX = 1U;
using Name_t = string<platform::IOX_MAX_SEMAPHORE_NAME_LENGTH - LENGTH_OF_SEMAPHORE_SLASH_PREFIX>;

Expand All @@ -46,7 +50,11 @@ class NamedSemaphore final : public detail::SemaphoreInterface<NamedSemaphore>
friend class detail::SemaphoreInterface<NamedSemaphore>;

NamedSemaphore(iox_sem_t* handle, const Name_t& name, const bool hasOwnership) noexcept;
iox_sem_t* getHandle() noexcept;

expected<void, SemaphoreError> post_impl() noexcept;
expected<void, SemaphoreError> wait_impl() noexcept;
expected<bool, SemaphoreError> try_wait_impl() noexcept;
expected<SemaphoreWaitState, SemaphoreError> timed_wait_impl(const units::Duration& timeout) noexcept;

iox_sem_t* m_handle = nullptr;
Name_t m_name;
Expand Down
Loading

0 comments on commit a867c13

Please sign in to comment.