Add etl::counting_semaphore and etl::binary_semaphore

This commit is contained in:
Julien Hecht 2026-07-22 15:52:42 +02:00
parent 7f290365a9
commit 048e89f022
7 changed files with 457 additions and 0 deletions

View File

@ -112,4 +112,5 @@ SOFTWARE.
#define ETL_FORMAT_FILE_ID "79"
#define ETL_INPLACE_FUNCTION_FILE_ID "80"
#define ETL_INTRUSIVE_AVL_TREE_FILE_ID "81"
#define ETL_SEMAPHORE_FILE_ID "82"
#endif

60
include/etl/semaphore.h Normal file
View File

@ -0,0 +1,60 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 Julien Hecht
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.
******************************************************************************/
#ifndef ETL_SEMAPHORE_INCLUDED
#define ETL_SEMAPHORE_INCLUDED
#include "platform.h"
#include "semaphore/semaphore_exceptions.h"
#if defined(ETL_TARGET_OS_FREERTOS)
#include "semaphore/semaphore_freertos.h"
#define ETL_HAS_SEMAPHORE 1
#elif ETL_USING_STL && ETL_USING_CPP20 && ETL_USING_STD_SEMAPHORE
#include "semaphore/semaphore_std.h"
#define ETL_HAS_SEMAPHORE 1
#elif defined(ETL_COMPILER_GCC) && ETL_USING_CPP11
#include "semaphore/semaphore_gcc_sync.h"
#define ETL_HAS_SEMAPHORE 1
#elif defined(ETL_COMPILER_CLANG) && ETL_USING_CPP11
#include "semaphore/semaphore_clang_sync.h"
#define ETL_HAS_SEMAPHORE 1
#else
#define ETL_HAS_SEMAPHORE 0
#endif
namespace etl
{
namespace traits
{
static ETL_CONSTANT bool has_semaphore = (ETL_HAS_SEMAPHORE == 1);
}
} // namespace etl
#endif

View File

@ -0,0 +1,34 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 Julien Hecht
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.
******************************************************************************/
#ifndef ETL_SEMAPHORE_CLANG_SYNC_INCLUDED
#define ETL_SEMAPHORE_CLANG_SYNC_INCLUDED
#include "semaphore_gcc_sync.h"
#endif

View File

@ -0,0 +1,71 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 Julien Hecht
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.
******************************************************************************/
#ifndef ETL_SEMAPHORE_EXCEPTIONS_INCLUDED
#define ETL_SEMAPHORE_EXCEPTIONS_INCLUDED
#include "../platform.h"
#include "../error_handler.h"
#include "../exception.h"
namespace etl
{
//***************************************************************************
///\ingroup semaphore
/// Exception base for semaphores
//***************************************************************************
class semaphore_exception : public exception
{
public:
semaphore_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
: exception(reason_, file_name_, line_number_)
{
}
};
//***************************************************************************
///\ingroup semaphore
/// Exception thrown when the semaphore value gets out of bounds
/// For example:
/// - Creating a semaphore with a `desired` value over `max()`
/// - Releasing a semaphore with a negative update value, or value that would bring it over `max()`
//***************************************************************************
class semaphore_out_of_bounds : public semaphore_exception
{
public:
semaphore_out_of_bounds(string_type file_name_, numeric_type line_number_)
: semaphore_exception(ETL_ERROR_TEXT("semaphore: out of bounds", ETL_SEMAPHORE_FILE_ID"A"), file_name_, line_number_)
{
}
};
} // namespace etl
#endif

View File

@ -0,0 +1,126 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 Julien Hecht
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.
******************************************************************************/
#ifndef ETL_SEMAPHORE_FREERTOS_INCLUDED
#define ETL_SEMAPHORE_FREERTOS_INCLUDED
#include "../platform.h"
#include "../chrono.h"
#include "../error_handler.h"
#include "../limits.h"
#include "semaphore_exceptions.h"
namespace etl
{
//***************************************************************************
///\ingroup semaphore
///\brief This counting_semaphore class is implemented using FreeRTOS's semaphores
//***************************************************************************
template <ptrdiff_t LeastMaxValue = numeric_limits<BaseType_t>::max()>
class counting_semaphore
{
// On FreeRTOS, LeastMaxValue = 0 causes an assert. This deviates slightly from the C++ spec, but it should be fine.
ETL_STATIC_ASSERT(LeastMaxValue > 0, "LeastMaxValue should be 1 or more");
ETL_STATIC_ASSERT(LeastMaxValue <= numeric_limits<BaseType_t>::max(), "LeastMaxValue is too high");
public:
explicit counting_semaphore(ptrdiff_t desired)
{
ETL_ASSERT(desired >= 0, ETL_ERROR(semaphore_out_of_bounds));
sem = xSemaphoreCreateCountingStatic(max(), desired, &semaphore_allocation);
}
void acquire()
{
// portMAX_DELAY=block forever
// Looping guarantees waiting forever even if the configuration does not have `INCLUDE_vTaskSuspend == 1`
while (xSemaphoreTake(sem, portMAX_DELAY) != pdTRUE);
}
bool try_acquire()
{
return try_acquire_for(0_s);
}
template <class Rep, class Period>
bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time)
{
using URep_t = make_unsigned_t<Rep>;
TickType_t ticks_to_wait = 0;
chrono::milliseconds ms_to_wait{rel_time};
if (ms_to_wait > chrono::duration<Rep, Period>::zero())
{
ticks_to_wait = divide_round_to_ceiling(static_cast<URep_t>(ms_to_wait.count()), portTICK_PERIOD_MS);
}
return xSemaphoreTake(sem, ticks_to_wait) == pdTRUE;
}
template <class Clock, class Duration>
bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time)
{
return try_acquire_for(abs_time - Clock::now());
}
void release(ptrdiff_t update = 1)
{
if (update <= 0)
{
return;
}
// No need to check if the semaphore goes over `max()` because it's guaranteed by FreeRTOS APIs
while (update--)
{
xSemaphoreGive(sem);
}
}
static constexpr ptrdiff_t max() noexcept
{
return LeastMaxValue;
}
private:
// Non-copyable
counting_semaphore(const counting_semaphore&) ETL_DELETE;
counting_semaphore& operator=(const counting_semaphore&) ETL_DELETE;
// Memory to hold the semaphore
StaticSemaphore_t semaphore_allocation;
// The semaphore handle itself
SemaphoreHandle_t sem;
};
using binary_semaphore = counting_semaphore<1>;
} // namespace etl
#endif

View File

@ -0,0 +1,117 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 Julien Hecht
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.
******************************************************************************/
#ifndef ETL_SEMAPHORE_GCC_SYNC_INCLUDED
#define ETL_SEMAPHORE_GCC_SYNC_INCLUDED
#include "../platform.h"
#include "../chrono.h"
#include "../limits.h"
#include "semaphore_exceptions.h"
namespace etl
{
//***************************************************************************
///\ingroup semaphore
///\brief This counting_semaphore class is implemented using GCC's __atomic functions.
//***************************************************************************
template <ptrdiff_t LeastMaxValue = numeric_limits<ptrdiff_t>::max()>
class counting_semaphore
{
public:
ETL_STATIC_ASSERT(LeastMaxValue >= 0, "LeastMaxValue shall be non-negative");
ETL_STATIC_ASSERT(LeastMaxValue <= numeric_limits<ptrdiff_t>::max(), "LeastMaxValue is too high");
ETL_CONSTEXPR14 explicit counting_semaphore(ptrdiff_t desired)
: counter(desired)
{
ETL_ASSERT(desired >= 0 && desired <= max(), ETL_ERROR(semaphore_out_of_bounds));
}
void acquire()
{
while (!try_acquire())
{
}
}
bool try_acquire()
{
ptrdiff_t old = __atomic_load_n(&counter, __ATOMIC_ACQUIRE);
if (old <= 0)
{
return false;
}
return __atomic_compare_exchange_n(&counter, &old, old - 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
template <class Rep, class Period>
bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time)
{
auto now = chrono::steady_clock::now();
return try_acquire_until(now + rel_time);
}
template <class Clock, class Duration>
bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time)
{
bool ret = false;
while (!(ret = try_acquire()) && Clock::now() < abs_time)
{
}
return ret;
}
void release(ptrdiff_t update = 1)
{
ptrdiff_t old = __atomic_fetch_add(&counter, update, __ATOMIC_RELEASE);
// Preconditions are tested afterwards because otherwise it makes the atomic code needlessly complex.
ETL_ASSERT(update >= 0, ETL_ERROR(semaphore_out_of_bounds));
ETL_ASSERT(update <= max() - old, ETL_ERROR(semaphore_out_of_bounds));
}
static constexpr ptrdiff_t max() noexcept
{
return LeastMaxValue;
}
private:
counting_semaphore(const counting_semaphore&) ETL_DELETE;
counting_semaphore& operator=(const counting_semaphore&) ETL_DELETE;
ptrdiff_t counter;
};
using binary_semaphore = counting_semaphore<1>;
} // namespace etl
#endif

View File

@ -0,0 +1,48 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 Julien Hecht
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.
******************************************************************************/
#ifndef ETL_SEMAPHORE_STD_INCLUDED
#define ETL_SEMAPHORE_STD_INCLUDED
#include "../platform.h"
#include "../limits.h"
#include <semaphore>
namespace etl
{
//***************************************************************************
///\ingroup semaphore
///\brief These semaphore classes are implemented using std::counting_semaphore and std::binary_semaphore.
//***************************************************************************
template <ptrdiff_t LeastMaxValue = etl::numeric_limits<ptrdiff_t>::max()>
using counting_semaphore = std::counting_semaphore<LeastMaxValue>;
using binary_semaphore = std::binary_semaphore;
} // namespace etl
#endif