From 048e89f022b2d7f43bce553b052819f8027af322 Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Wed, 22 Jul 2026 15:52:42 +0200 Subject: [PATCH 01/10] Add etl::counting_semaphore and etl::binary_semaphore --- include/etl/file_error_numbers.h | 1 + include/etl/semaphore.h | 60 +++++++++ include/etl/semaphore/semaphore_clang_sync.h | 34 +++++ include/etl/semaphore/semaphore_exceptions.h | 71 +++++++++++ include/etl/semaphore/semaphore_freertos.h | 126 +++++++++++++++++++ include/etl/semaphore/semaphore_gcc_sync.h | 117 +++++++++++++++++ include/etl/semaphore/semaphore_std.h | 48 +++++++ 7 files changed, 457 insertions(+) create mode 100644 include/etl/semaphore.h create mode 100644 include/etl/semaphore/semaphore_clang_sync.h create mode 100644 include/etl/semaphore/semaphore_exceptions.h create mode 100644 include/etl/semaphore/semaphore_freertos.h create mode 100644 include/etl/semaphore/semaphore_gcc_sync.h create mode 100644 include/etl/semaphore/semaphore_std.h diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 945f1d87..fed24366 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -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 diff --git a/include/etl/semaphore.h b/include/etl/semaphore.h new file mode 100644 index 00000000..dfcb90ae --- /dev/null +++ b/include/etl/semaphore.h @@ -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 diff --git a/include/etl/semaphore/semaphore_clang_sync.h b/include/etl/semaphore/semaphore_clang_sync.h new file mode 100644 index 00000000..0c7e88d0 --- /dev/null +++ b/include/etl/semaphore/semaphore_clang_sync.h @@ -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 diff --git a/include/etl/semaphore/semaphore_exceptions.h b/include/etl/semaphore/semaphore_exceptions.h new file mode 100644 index 00000000..23b7b007 --- /dev/null +++ b/include/etl/semaphore/semaphore_exceptions.h @@ -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 diff --git a/include/etl/semaphore/semaphore_freertos.h b/include/etl/semaphore/semaphore_freertos.h new file mode 100644 index 00000000..9a89e133 --- /dev/null +++ b/include/etl/semaphore/semaphore_freertos.h @@ -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 ::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::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 + bool try_acquire_for(const chrono::duration& rel_time) + { + using URep_t = make_unsigned_t; + + TickType_t ticks_to_wait = 0; + chrono::milliseconds ms_to_wait{rel_time}; + if (ms_to_wait > chrono::duration::zero()) + { + ticks_to_wait = divide_round_to_ceiling(static_cast(ms_to_wait.count()), portTICK_PERIOD_MS); + } + return xSemaphoreTake(sem, ticks_to_wait) == pdTRUE; + } + + template + bool try_acquire_until(const chrono::time_point& 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 diff --git a/include/etl/semaphore/semaphore_gcc_sync.h b/include/etl/semaphore/semaphore_gcc_sync.h new file mode 100644 index 00000000..a9b1e8ae --- /dev/null +++ b/include/etl/semaphore/semaphore_gcc_sync.h @@ -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 ::max()> + class counting_semaphore + { + public: + + ETL_STATIC_ASSERT(LeastMaxValue >= 0, "LeastMaxValue shall be non-negative"); + ETL_STATIC_ASSERT(LeastMaxValue <= numeric_limits::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 + bool try_acquire_for(const chrono::duration& rel_time) + { + auto now = chrono::steady_clock::now(); + return try_acquire_until(now + rel_time); + } + + template + bool try_acquire_until(const chrono::time_point& 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 diff --git a/include/etl/semaphore/semaphore_std.h b/include/etl/semaphore/semaphore_std.h new file mode 100644 index 00000000..74aaf2e5 --- /dev/null +++ b/include/etl/semaphore/semaphore_std.h @@ -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 + +namespace etl +{ + //*************************************************************************** + ///\ingroup semaphore + ///\brief These semaphore classes are implemented using std::counting_semaphore and std::binary_semaphore. + //*************************************************************************** + template ::max()> + using counting_semaphore = std::counting_semaphore; + using binary_semaphore = std::binary_semaphore; +} // namespace etl + +#endif From f1854d852a9771166d9b7279004e4b7b1b92b494 Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Wed, 22 Jul 2026 15:52:55 +0200 Subject: [PATCH 02/10] Add semaphores to tests --- test/maincpp03check.cpp | 1 + test/syntax_check/CMakeLists.txt | 1 + test/syntax_check/semaphore.h.t.cpp | 29 +++++++++++++++++++++++++++++ test/test_etl_traits.cpp | 2 ++ test/vs2022/etl.vcxproj | 8 +++++++- test/vs2022/etl.vcxproj.filters | 21 +++++++++++++++++++++ 6 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 test/syntax_check/semaphore.h.t.cpp diff --git a/test/maincpp03check.cpp b/test/maincpp03check.cpp index a1745c6e..2ca192b2 100644 --- a/test/maincpp03check.cpp +++ b/test/maincpp03check.cpp @@ -135,6 +135,7 @@ SOFTWARE. #include "etl/reference_flat_set.h" #include "etl/scaled_rounding.h" #include "etl/scheduler.h" +#include "etl/semaphore.h" #include "etl/set.h" #include "etl/smallest.h" #include "etl/sqrt.h" diff --git a/test/syntax_check/CMakeLists.txt b/test/syntax_check/CMakeLists.txt index 3546cfa3..d29e8750 100644 --- a/test/syntax_check/CMakeLists.txt +++ b/test/syntax_check/CMakeLists.txt @@ -341,6 +341,7 @@ target_sources(tests PRIVATE rounded_integral_division.h.t.cpp scaled_rounding.h.t.cpp scheduler.h.t.cpp + semaphore.h.t.cpp set.h.t.cpp shared_message.h.t.cpp signal.h.t.cpp diff --git a/test/syntax_check/semaphore.h.t.cpp b/test/syntax_check/semaphore.h.t.cpp new file mode 100644 index 00000000..a3cb50e0 --- /dev/null +++ b/test/syntax_check/semaphore.h.t.cpp @@ -0,0 +1,29 @@ +/****************************************************************************** +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. +******************************************************************************/ + +#include diff --git a/test/test_etl_traits.cpp b/test/test_etl_traits.cpp index a90f073c..be016f54 100644 --- a/test/test_etl_traits.cpp +++ b/test/test_etl_traits.cpp @@ -33,6 +33,7 @@ SOFTWARE. #include "etl/platform.h" #include "etl/char_traits.h" #include "etl/mutex.h" +#include "etl/semaphore.h" #include "etl/version.h" namespace @@ -69,6 +70,7 @@ namespace // Has... CHECK_EQUAL((ETL_HAS_ATOMIC == 1), etl::traits::has_atomic); CHECK_EQUAL((ETL_HAS_MUTEX == 1), etl::traits::has_mutex); + CHECK_EQUAL((ETL_HAS_SEMAPHORE == 1), etl::traits::has_semaphore); CHECK_EQUAL((ETL_HAS_NULLPTR == 1), etl::traits::has_nullptr); CHECK_EQUAL((ETL_HAS_CHAR8_T == 1), etl::traits::has_char8_t); CHECK_EQUAL((ETL_HAS_NATIVE_CHAR8_T == 1), etl::traits::has_native_char8_t); diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 503563eb..4ea07f78 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3698,6 +3698,12 @@ + + + + + + @@ -11804,4 +11810,4 @@ - \ No newline at end of file + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index f20f1a7a..80dc8585 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -226,6 +226,9 @@ {5b809d95-b37c-4011-af5e-b5ea6dcb354e} + + {304d989b-df11-403b-8ce9-ea594a66e759} + @@ -555,6 +558,9 @@ ETL\Utilities + + ETL\Utilities + ETL\Maths @@ -1569,6 +1575,21 @@ UnitTest++\Header Files + + ETL\Utilities\Semaphore + + + ETL\Utilities\Semaphore + + + ETL\Utilities\Semaphore + + + ETL\Utilities\Semaphore + + + ETL\Utilities\Semaphore + From 5de50821605384888972f3a6f1a86456e1e251ad Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Thu, 6 Aug 2026 17:22:04 +0200 Subject: [PATCH 03/10] Implement etl::counting_semaphore as a wrapper of std::counting_semaphore The original "using" statement would cause issues with using `etl::chrono::duration` with the functions as the compiler would fail to convert an `etl::ratio` to an `std::ratio` when calling `try_acquire_until` and `try_acquire_for`. --- include/etl/semaphore/semaphore_std.h | 80 +++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/include/etl/semaphore/semaphore_std.h b/include/etl/semaphore/semaphore_std.h index 74aaf2e5..505bb060 100644 --- a/include/etl/semaphore/semaphore_std.h +++ b/include/etl/semaphore/semaphore_std.h @@ -30,8 +30,10 @@ SOFTWARE. #define ETL_SEMAPHORE_STD_INCLUDED #include "../platform.h" -#include "../limits.h" +#include "../chrono.h" +#include +#include #include namespace etl @@ -40,9 +42,79 @@ namespace etl ///\ingroup semaphore ///\brief These semaphore classes are implemented using std::counting_semaphore and std::binary_semaphore. //*************************************************************************** - template ::max()> - using counting_semaphore = std::counting_semaphore; - using binary_semaphore = std::binary_semaphore; + + namespace private_semaphore_std + { + template + struct CompatClock + { + using rep = Clock::rep; + using period = std::ratio; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + static const bool is_steady = Clock::is_steady; + static time_point now() + { + return time_point{duration{Clock::now().time_since_epoch().count()}}; + } + }; + } // namespace private_semaphore_std + + template ::max()> + class counting_semaphore + { + public: + + explicit counting_semaphore(ptrdiff_t desired) + : sem(desired) + { + } + + void acquire() + { + return sem.acquire(); + } + + bool try_acquire() + { + return sem.try_acquire(); + } + + template + bool try_acquire_for(const etl::chrono::duration& rel_time) + { + std::chrono::duration> duration(rel_time.count()); + + return sem.try_acquire_for(duration); + } + + template + bool try_acquire_until(const etl::chrono::time_point& abs_time) + { + using CompatDuration = std::chrono::duration>; + + std::chrono::time_point, CompatDuration> time_point{ + CompatDuration(abs_time.time_since_epoch().count())}; + + return sem.try_acquire_until(time_point); + } + + void release(ptrdiff_t update = 1) + { + return sem.release(update); + } + + static constexpr ptrdiff_t max() noexcept + { + return LeastMaxValue; + } + + private: + + std::counting_semaphore sem; + }; + + using binary_semaphore = counting_semaphore<1>; } // namespace etl #endif From 573a224292bc6c29b991634f94eb66704b55c83d Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Wed, 2 Sep 2026 15:55:24 +0200 Subject: [PATCH 04/10] Split test clocks from test_chrono_clocks.cpp to clocks_for_unit_tests.cpp This makes clocks available for other tests when needed. --- test/CMakeLists.txt | 1 + test/clocks_for_unit_tests.cpp | 59 +++++++++++++++++++++++++++++++++ test/clocks_for_unit_tests.h | 54 ++++++++++++++++++++++++++++++ test/meson.build | 1 + test/test_chrono_clocks.cpp | 29 ++-------------- test/vs2022/etl.vcxproj | 2 ++ test/vs2022/etl.vcxproj.filters | 6 ++++ 7 files changed, 125 insertions(+), 27 deletions(-) create mode 100644 test/clocks_for_unit_tests.cpp create mode 100644 test/clocks_for_unit_tests.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fbbf091e..84fcfbcd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,7 @@ project(etl_unit_tests LANGUAGES CXX) add_executable(etl_tests main.cpp murmurhash3.cpp + clocks_for_unit_tests.cpp test_algorithm.cpp test_algorithm_ranges.cpp test_alignment.cpp diff --git a/test/clocks_for_unit_tests.cpp b/test/clocks_for_unit_tests.cpp new file mode 100644 index 00000000..157ef3a4 --- /dev/null +++ b/test/clocks_for_unit_tests.cpp @@ -0,0 +1,59 @@ +/****************************************************************************** +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. +******************************************************************************/ + +#include "clocks_for_unit_tests.h" +#include "etl/atomic.h" +#include "etl/chrono.h" + +namespace test +{ + namespace clocks + { + CLOCK_TYPE nanoseconds{0}; + CLOCK_TYPE milliseconds{0}; + CLOCK_TYPE seconds{0}; + } // namespace clocks +} // namespace test + +extern "C" +{ + etl::chrono::system_clock::rep etl_get_system_clock() + { + return etl::chrono::system_clock::rep(test::clocks::milliseconds); + } + + etl::chrono::high_resolution_clock::rep etl_get_high_resolution_clock() + { + return etl::chrono::high_resolution_clock::rep(test::clocks::nanoseconds); + } + + etl::chrono::steady_clock::rep etl_get_steady_clock() + { + return etl::chrono::steady_clock::rep(test::clocks::seconds); + } +} diff --git a/test/clocks_for_unit_tests.h b/test/clocks_for_unit_tests.h new file mode 100644 index 00000000..b2630a4e --- /dev/null +++ b/test/clocks_for_unit_tests.h @@ -0,0 +1,54 @@ +/****************************************************************************** +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_TEST_CLOCKS_INCLUDED +#define ETL_TEST_CLOCKS_INCLUDED + +#include "etl/platform.h" +#include "etl/atomic.h" + +#if defined(ETL_HAS_ATOMIC) && ETL_HAS_ATOMIC == 1 + // In general it is more correct to use atomics to keep this value since it + // may be used and modified across multiple threads (see test_semaphore.cpp). + #define CLOCK_TYPE etl::atomic +#else + // However, in practice, a data race here isn't really an issue so int is fine too. + #define CLOCK_TYPE int +#endif + +namespace test +{ + namespace clocks + { + extern CLOCK_TYPE nanoseconds; + extern CLOCK_TYPE milliseconds; + extern CLOCK_TYPE seconds; + } // namespace clocks +} // namespace test + +#endif diff --git a/test/meson.build b/test/meson.build index add9b29b..7aa82aa7 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,5 +1,6 @@ etl_test_sources = files( 'main.cpp', + 'clocks_for_unit_tests.cpp', 'murmurhash3.cpp', 'test_algorithm.cpp', 'test_algorithm_ranges.cpp', diff --git a/test/test_chrono_clocks.cpp b/test/test_chrono_clocks.cpp index 6fefc751..982332d5 100644 --- a/test/test_chrono_clocks.cpp +++ b/test/test_chrono_clocks.cpp @@ -30,39 +30,14 @@ SOFTWARE. #include "etl/platform.h" +#include "clocks_for_unit_tests.h" #include "unit_test_framework.h" #include "etl/chrono.h" #include -//***************************************************************************** -// Global clock functions. -//***************************************************************************** -namespace -{ - int nanoseconds = 0; - int milliseconds = 0; - int seconds = 0; -} // namespace - -extern "C" -{ - etl::chrono::system_clock::rep etl_get_system_clock() - { - return etl::chrono::system_clock::rep(milliseconds); - } - - etl::chrono::high_resolution_clock::rep etl_get_high_resolution_clock() - { - return etl::chrono::high_resolution_clock::rep(nanoseconds); - } - - etl::chrono::steady_clock::rep etl_get_steady_clock() - { - return etl::chrono::steady_clock::rep(seconds); - } -} +using namespace test::clocks; namespace { diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 4ea07f78..7923fc41 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -4047,6 +4047,7 @@ + @@ -4083,6 +4084,7 @@ + true true diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 80dc8585..b74f6e1c 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1101,6 +1101,12 @@ Tests\Test Support + + Tests\Test Support + + + Tests\Test Support + Tests\Test Support From 84f5c4ac91eda9fabb3536d5b52271318c601d00 Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Wed, 2 Sep 2026 15:56:32 +0200 Subject: [PATCH 05/10] Fix various semaphore issues As mentioned in PR 1520. --- include/etl/platform.h | 10 ++++++++++ include/etl/semaphore/semaphore_freertos.h | 7 ++++++- include/etl/semaphore/semaphore_gcc_sync.h | 2 +- include/etl/semaphore/semaphore_std.h | 15 +++++++++++---- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/etl/platform.h b/include/etl/platform.h index 494b4981..42911a5a 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -112,6 +112,16 @@ SOFTWARE. #define ETL_NOT_USING_STD_MUTEX 0 #endif +//************************************* +// Helper macros for ETL_NO_STD_SEMAPHORE. +#if defined(ETL_NO_STD_SEMAPHORE) + #define ETL_USING_STD_SEMAPHORE 0 + #define ETL_NOT_USING_STD_SEMAPHORE 1 +#else + #define ETL_USING_STD_SEMAPHORE 1 + #define ETL_NOT_USING_STD_SEMAPHORE 0 +#endif + //************************************* // Helper macros for ETL_STLPORT. #if defined(ETL_STLPORT) diff --git a/include/etl/semaphore/semaphore_freertos.h b/include/etl/semaphore/semaphore_freertos.h index 9a89e133..ae4d0e0f 100644 --- a/include/etl/semaphore/semaphore_freertos.h +++ b/include/etl/semaphore/semaphore_freertos.h @@ -33,8 +33,13 @@ SOFTWARE. #include "../chrono.h" #include "../error_handler.h" #include "../limits.h" +#include "../rounded_integral_division.h" +#include "../type_traits.h" #include "semaphore_exceptions.h" +#include "FreeRTOS.h" +#include + namespace etl { //*************************************************************************** @@ -65,7 +70,7 @@ namespace etl bool try_acquire() { - return try_acquire_for(0_s); + return try_acquire_for(chrono::seconds(0)); } template diff --git a/include/etl/semaphore/semaphore_gcc_sync.h b/include/etl/semaphore/semaphore_gcc_sync.h index a9b1e8ae..15d6b110 100644 --- a/include/etl/semaphore/semaphore_gcc_sync.h +++ b/include/etl/semaphore/semaphore_gcc_sync.h @@ -98,7 +98,7 @@ namespace etl ETL_ASSERT(update <= max() - old, ETL_ERROR(semaphore_out_of_bounds)); } - static constexpr ptrdiff_t max() noexcept + static ETL_CONSTEXPR ptrdiff_t max() ETL_NOEXCEPT { return LeastMaxValue; } diff --git a/include/etl/semaphore/semaphore_std.h b/include/etl/semaphore/semaphore_std.h index 505bb060..2daa36d5 100644 --- a/include/etl/semaphore/semaphore_std.h +++ b/include/etl/semaphore/semaphore_std.h @@ -46,12 +46,12 @@ namespace etl namespace private_semaphore_std { template - struct CompatClock + struct compat_clock { - using rep = Clock::rep; + using rep = typename Clock::rep; using period = std::ratio; using duration = std::chrono::duration; - using time_point = std::chrono::time_point; + using time_point = std::chrono::time_point; static const bool is_steady = Clock::is_steady; static time_point now() { @@ -63,6 +63,9 @@ namespace etl template ::max()> class counting_semaphore { + ETL_STATIC_ASSERT(LeastMaxValue > 0, "LeastMaxValue should be 1 or more"); + ETL_STATIC_ASSERT(LeastMaxValue <= std::counting_semaphore<>::max(), "LeastMaxValue is too high"); + public: explicit counting_semaphore(ptrdiff_t desired) @@ -93,7 +96,7 @@ namespace etl { using CompatDuration = std::chrono::duration>; - std::chrono::time_point, CompatDuration> time_point{ + std::chrono::time_point, CompatDuration> time_point{ CompatDuration(abs_time.time_since_epoch().count())}; return sem.try_acquire_until(time_point); @@ -111,6 +114,10 @@ namespace etl private: + // Non-copyable + counting_semaphore(const counting_semaphore&) ETL_DELETE; + counting_semaphore& operator=(const counting_semaphore&) ETL_DELETE; + std::counting_semaphore sem; }; From 193f25224230b048468ab7a444d1a0f51622e88a Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Wed, 2 Sep 2026 15:56:49 +0200 Subject: [PATCH 06/10] Add semaphore unit tests --- test/CMakeLists.txt | 1 + test/meson.build | 1 + test/test_semaphore.cpp | 189 ++++++++++++++++++++++++++++++++ test/vs2022/etl.vcxproj | 1 + test/vs2022/etl.vcxproj.filters | 5 +- 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 test/test_semaphore.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 84fcfbcd..c1c6ccc3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -297,6 +297,7 @@ add_executable(etl_tests test_rms.cpp test_rounded_integral_division.cpp test_scaled_rounding.cpp + test_semaphore.cpp test_set.cpp test_shared_message.cpp test_signal.cpp diff --git a/test/meson.build b/test/meson.build index 7aa82aa7..c5ed323c 100644 --- a/test/meson.build +++ b/test/meson.build @@ -285,6 +285,7 @@ etl_test_sources = files( 'test_rms.cpp', 'test_rounded_integral_division.cpp', 'test_scaled_rounding.cpp', + 'test_semaphore.cpp', 'test_set.cpp', 'test_shared_message.cpp', 'test_signal.cpp', diff --git a/test/test_semaphore.cpp b/test/test_semaphore.cpp new file mode 100644 index 00000000..1f718a5a --- /dev/null +++ b/test/test_semaphore.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** +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. +******************************************************************************/ + +#include "clocks_for_unit_tests.h" +#include "unit_test_framework.h" + +#include + +#include + +using namespace test::clocks; + +namespace +{ + SUITE(test_semaphore) + { + TEST(test_semaphore_counting_nominal) + { + int initial_count = 10; + int release_count = 20; + int acquire_count = initial_count + release_count; + + etl::counting_semaphore<> sem{initial_count}; + + std::thread thread( + [&sem, &release_count]() + { + while (release_count > 0) + { + sem.release(); + release_count--; + } + }); + + while (acquire_count > 0) + { + sem.acquire(); + acquire_count--; + } + + thread.join(); + + CHECK_EQUAL(release_count, 0); + CHECK_EQUAL(acquire_count, 0); + CHECK_FALSE(sem.try_acquire()); + } + + TEST(test_semaphore_counting_try_acquire) + { + etl::counting_semaphore<> sem{1}; + + CHECK_TRUE(sem.try_acquire()); + CHECK_FALSE(sem.try_acquire()); + } + + TEST(test_semaphore_counting_try_for) + { + etl::counting_semaphore<> sem{1}; + + // try_for uses steady_clock in the `gcc_sync` implementation + // (default backend if STL is not available or FreeRTOS is not enabled) + // During testing, this means the clock used for waiting has a granularity of 1s. + seconds = 0; + std::thread thread( + []() + { + // Just sleep a bit, not literally 1s, because that's a wait of time + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + seconds++; + }); + + CHECK_TRUE(sem.try_acquire_for(etl::chrono::milliseconds(10))); + CHECK_FALSE(sem.try_acquire_for(etl::chrono::milliseconds(10))); + + thread.join(); + } + + TEST(test_semaphore_counting_try_until) + { + etl::counting_semaphore<> sem{1}; + + nanoseconds = 0; + std::thread thread( + []() + { + // Count to 20 milliseconds + for (int i = 0; i < 20; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + nanoseconds += 1000000; + } + }); + + auto now = etl::chrono::high_resolution_clock::now(); + CHECK_TRUE(sem.try_acquire_until(now + etl::chrono::milliseconds(10))); + CHECK_FALSE(sem.try_acquire_until(now + etl::chrono::milliseconds(10))); + + thread.join(); + } + + TEST(test_semaphore_binary_nominal) + { + etl::binary_semaphore sem{0}; + + sem.release(); + sem.acquire(); + + CHECK_FALSE(sem.try_acquire()); + } + + TEST(test_semaphore_binary_try_acquire) + { + etl::binary_semaphore sem{1}; + + CHECK_TRUE(sem.try_acquire()); + CHECK_FALSE(sem.try_acquire()); + } + + TEST(test_semaphore_binary_try_for) + { + etl::binary_semaphore sem{1}; + + // try_for uses steady_clock in the `gcc_sync` implementation + // (default backend if STL is not available or FreeRTOS is not enabled) + // During testing, this means the clock used for waiting has a granularity of 1s. + seconds = 0; + std::thread thread( + []() + { + // Just sleep a bit, not literally 1s, because that's a waste of time + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + seconds++; + }); + + CHECK_TRUE(sem.try_acquire_for(etl::chrono::milliseconds(10))); + CHECK_FALSE(sem.try_acquire_for(etl::chrono::milliseconds(10))); + + thread.join(); + } + + TEST(test_semaphore_binary_try_until) + { + etl::binary_semaphore sem{1}; + + nanoseconds = 0; + std::thread thread( + []() + { + // Count to 20 milliseconds + for (int i = 0; i < 20; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + nanoseconds += 1000000; + } + }); + + auto now = etl::chrono::high_resolution_clock::now(); + CHECK_TRUE(sem.try_acquire_until(now + etl::chrono::milliseconds(10))); + CHECK_FALSE(sem.try_acquire_until(now + etl::chrono::milliseconds(10))); + + thread.join(); + } + } +} // namespace diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 7923fc41..e80a0a96 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -11392,6 +11392,7 @@ + true true diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index b74f6e1c..e61fb35d 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -2051,6 +2051,9 @@ Tests\Strings + + Tests + Tests\Containers @@ -4142,4 +4145,4 @@ Resource Files\Images - \ No newline at end of file + From 7f97ff67639b10ceb8964bf82beaa44260672937 Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Tue, 13 Oct 2026 13:27:49 +0200 Subject: [PATCH 07/10] Fix semaphores build on VS2022 Patch provided by @jwellbelove --- test/test_semaphore.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_semaphore.cpp b/test/test_semaphore.cpp index 1f718a5a..10ef781f 100644 --- a/test/test_semaphore.cpp +++ b/test/test_semaphore.cpp @@ -35,6 +35,8 @@ SOFTWARE. using namespace test::clocks; +#if ETL_USING_ATOMIC + namespace { SUITE(test_semaphore) @@ -187,3 +189,5 @@ namespace } } } // namespace + +#endif From 04e5b9b2114b65344aabf0d334bcba5bbe2cee82 Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Wed, 14 Oct 2026 17:06:53 +0200 Subject: [PATCH 08/10] Add semaphore.h documentation --- docs/utilities/semaphore.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/utilities/semaphore.md diff --git a/docs/utilities/semaphore.md b/docs/utilities/semaphore.md new file mode 100644 index 00000000..8a33f308 --- /dev/null +++ b/docs/utilities/semaphore.md @@ -0,0 +1,31 @@ +--- +title: "semaphore" +--- + +{{< callout type="info">}} + Header: `semaphore.h` + Since: TBC + Similar to: [std::counting_semaphore, std::binary_semaphore](https://en.cppreference.com/cpp/thread/counting_semaphore) +{{< /callout >}} + +This header attempts to replicate standard counting and binary semaphores. + +## Supported implementations + +| Implementation | Associated definitions | +|-------------------|---------------------------------------------------------------| +| FreeRTOS | `ETL_TARGET_OS_FREERTOS` | +| STL/STD (C++20) | `ETL_USING_STL && ETL_USING_CPP20 && ETL_USING_STD_SEMAPHORE` | +| GCC/clang atomics | `(ETL_COMPILER_GCC \|\| ETL_COMPILER_CLANG) && ETL_USING_CPP11` | + + +## Implementation quirks and deviations from standard + +### FreeRTOS + +On FreeRTOS, `LeastMaxValue` must be `1` or greater and not `0` or greater as the standard specifies. + +### GCC/clang sync + +The GCC/clang sync implementation uses `etl::chrono::steady_clock`. Make sure to +provide the methods associated with this clock: see [the relevant documentation](/docs/chrono/clocks/#platform-clock-access). From af4d60f5b4003b6d63e738a0f5c68942a300ae7f Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Thu, 15 Oct 2026 18:30:17 +0200 Subject: [PATCH 09/10] Move semaphore to new `ETL_USING_` syntax in platform.h --- include/etl/platform.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/etl/platform.h b/include/etl/platform.h index cac6e0b6..452b0647 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -115,12 +115,11 @@ SOFTWARE. //************************************* // Helper macros for ETL_NO_STD_SEMAPHORE. #if defined(ETL_NO_STD_SEMAPHORE) - #define ETL_USING_STD_SEMAPHORE 0 - #define ETL_NOT_USING_STD_SEMAPHORE 1 + #define ETL_USING_STD_SEMAPHORE 0 #else - #define ETL_USING_STD_SEMAPHORE 1 - #define ETL_NOT_USING_STD_SEMAPHORE 0 + #define ETL_USING_STD_SEMAPHORE 1 #endif +#define ETL_NOT_USING_STD_SEMAPHORE (1 - ETL_USING_STD_SEMAPHORE) //************************************* // Helper macros for ETL_STLPORT. From d7eb4c94bb1dbacd879b9ae56ce8365f55a04aca Mon Sep 17 00:00:00 2001 From: Julien Hecht Date: Thu, 15 Oct 2026 18:45:21 +0200 Subject: [PATCH 10/10] Add `IWYU pragma: export` to private headers in semaphore.h --- include/etl/semaphore.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/etl/semaphore.h b/include/etl/semaphore.h index dfcb90ae..1b2bb8d2 100644 --- a/include/etl/semaphore.h +++ b/include/etl/semaphore.h @@ -34,16 +34,16 @@ SOFTWARE. #include "semaphore/semaphore_exceptions.h" #if defined(ETL_TARGET_OS_FREERTOS) - #include "semaphore/semaphore_freertos.h" + #include "semaphore/semaphore_freertos.h" // IWYU pragma: export #define ETL_HAS_SEMAPHORE 1 #elif ETL_USING_STL && ETL_USING_CPP20 && ETL_USING_STD_SEMAPHORE - #include "semaphore/semaphore_std.h" + #include "semaphore/semaphore_std.h" // IWYU pragma: export #define ETL_HAS_SEMAPHORE 1 #elif defined(ETL_COMPILER_GCC) && ETL_USING_CPP11 - #include "semaphore/semaphore_gcc_sync.h" + #include "semaphore/semaphore_gcc_sync.h" // IWYU pragma: export #define ETL_HAS_SEMAPHORE 1 #elif defined(ETL_COMPILER_CLANG) && ETL_USING_CPP11 - #include "semaphore/semaphore_clang_sync.h" + #include "semaphore/semaphore_clang_sync.h" // IWYU pragma: export #define ETL_HAS_SEMAPHORE 1 #else #define ETL_HAS_SEMAPHORE 0