Merge d7eb4c94bb1dbacd879b9ae56ce8365f55a04aca into 60ba92928e8af3441f2a4eea5f18fd2563d98ad0

This commit is contained in:
julien-hecht-netatmo 2026-10-15 18:45:27 +02:00 committed by GitHub
commit f74871fef2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 967 additions and 29 deletions

View File

@ -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).

View File

@ -114,4 +114,5 @@ SOFTWARE.
#define ETL_INTRUSIVE_AVL_TREE_FILE_ID "81"
#define ETL_FUTURE_FILE_ID "82"
#define ETL_CYCLIC_VALUE_FILE_ID "83"
#define ETL_SEMAPHORE_FILE_ID "84"
#endif

View File

@ -110,6 +110,15 @@ SOFTWARE.
#endif
#define ETL_NOT_USING_STD_MUTEX (1 - ETL_USING_STD_MUTEX)
//*************************************
// Helper macros for ETL_NO_STD_SEMAPHORE.
#if defined(ETL_NO_STD_SEMAPHORE)
#define ETL_USING_STD_SEMAPHORE 0
#else
#define ETL_USING_STD_SEMAPHORE 1
#endif
#define ETL_NOT_USING_STD_SEMAPHORE (1 - ETL_USING_STD_SEMAPHORE)
//*************************************
// Helper macros for ETL_STLPORT.
#if defined(ETL_STLPORT)

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" // IWYU pragma: export
#define ETL_HAS_SEMAPHORE 1
#elif ETL_USING_STL && ETL_USING_CPP20 && ETL_USING_STD_SEMAPHORE
#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" // IWYU pragma: export
#define ETL_HAS_SEMAPHORE 1
#elif defined(ETL_COMPILER_CLANG) && ETL_USING_CPP11
#include "semaphore/semaphore_clang_sync.h" // IWYU pragma: export
#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,131 @@
/******************************************************************************
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 "../rounded_integral_division.h"
#include "../type_traits.h"
#include "semaphore_exceptions.h"
#include "FreeRTOS.h"
#include <semphr.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(chrono::seconds(0));
}
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 ETL_CONSTEXPR ptrdiff_t max() ETL_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,127 @@
/******************************************************************************
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 "../chrono.h"
#include <chrono>
#include <cstddef>
#include <semaphore>
namespace etl
{
//***************************************************************************
///\ingroup semaphore
///\brief These semaphore classes are implemented using std::counting_semaphore and std::binary_semaphore.
//***************************************************************************
namespace private_semaphore_std
{
template <class Clock>
struct compat_clock
{
using rep = typename Clock::rep;
using period = std::ratio<Clock::period::num, Clock::period::den>;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<compat_clock>;
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 <ptrdiff_t LeastMaxValue = std::counting_semaphore<>::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)
: sem(desired)
{
}
void acquire()
{
return sem.acquire();
}
bool try_acquire()
{
return sem.try_acquire();
}
template <class Rep, class Period>
bool try_acquire_for(const etl::chrono::duration<Rep, Period>& rel_time)
{
std::chrono::duration<Rep, std::ratio<Period::num, Period::den>> duration(rel_time.count());
return sem.try_acquire_for(duration);
}
template <class Clock, class Duration>
bool try_acquire_until(const etl::chrono::time_point<Clock, Duration>& abs_time)
{
using CompatDuration = std::chrono::duration<typename Duration::rep, std::ratio<Duration::period::num, Duration::period::den>>;
std::chrono::time_point<private_semaphore_std::compat_clock<Clock>, 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:
// Non-copyable
counting_semaphore(const counting_semaphore&) ETL_DELETE;
counting_semaphore& operator=(const counting_semaphore&) ETL_DELETE;
std::counting_semaphore<LeastMaxValue> sem;
};
using binary_semaphore = counting_semaphore<1>;
} // namespace etl
#endif

View File

@ -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
@ -298,6 +299,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

View File

@ -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);
}
}

View File

@ -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<int>
#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

View File

@ -136,6 +136,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"

View File

@ -1,5 +1,6 @@
etl_test_sources = files(
'main.cpp',
'clocks_for_unit_tests.cpp',
'murmurhash3.cpp',
'test_algorithm.cpp',
'test_algorithm_ranges.cpp',
@ -286,6 +287,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',

View File

@ -343,6 +343,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

View File

@ -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 <etl/semaphore.h>

View File

@ -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 <type_traits>
//*****************************************************************************
// 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
{

View File

@ -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
@ -107,6 +108,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);

193
test/test_semaphore.cpp Normal file
View File

@ -0,0 +1,193 @@
/******************************************************************************
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 <thread>
#include <etl/semaphore.h>
using namespace test::clocks;
#if ETL_USING_ATOMIC
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
#endif

View File

@ -3696,6 +3696,12 @@
<ClInclude Include="..\..\include\etl\mutex\mutex_arm.h" />
<ClInclude Include="..\..\include\etl\mutex\mutex_gcc_sync.h" />
<ClInclude Include="..\..\include\etl\mutex\mutex_std.h" />
<ClInclude Include="..\..\include\etl\semaphore.h" />
<ClInclude Include="..\..\include\etl\semaphore\semaphore_exceptions.h" />
<ClInclude Include="..\..\include\etl\semaphore\semaphore_freertos.h" />
<ClInclude Include="..\..\include\etl\semaphore\semaphore_gcc_sync.h" />
<ClInclude Include="..\..\include\etl\semaphore\semaphore_clang_sync.h" />
<ClInclude Include="..\..\include\etl\semaphore\semaphore_std.h" />
<ClInclude Include="..\..\include\etl\packet.h" />
<ClInclude Include="..\..\include\etl\permutations.h" />
<ClInclude Include="..\..\include\etl\private\minmax_pop.h" />
@ -4040,6 +4046,7 @@
<ClInclude Include="..\etl_profile.h" />
<ClInclude Include="..\murmurhash3.h" />
<ClInclude Include="..\iterators_for_unit_tests.h" />
<ClInclude Include="..\clocks_for_unit_tests.h" />
<ClInclude Include="..\unittest++\AssertException.h" />
<ClInclude Include="..\unittest++\CheckMacros.h" />
<ClInclude Include="..\unittest++\Checks.h" />
@ -4076,6 +4083,7 @@
<ClInclude Include="..\unit_test_framework.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\clocks_for_unit_tests.cpp" />
<ClCompile Include="..\etl_error_handler\assert_function\assert_function.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug MSVC C++20|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Clang C++20|Win32'">true</ExcludedFromBuild>
@ -11405,6 +11413,7 @@
<ClCompile Include="..\test_reference_flat_multiset.cpp" />
<ClCompile Include="..\test_reference_flat_set.cpp" />
<ClCompile Include="..\test_scaled_rounding.cpp" />
<ClCompile Include="..\test_semaphore.cpp" />
<ClCompile Include="..\test_set.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@ -11825,4 +11834,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -226,6 +226,9 @@
<Filter Include="Documentation">
<UniqueIdentifier>{5b809d95-b37c-4011-af5e-b5ea6dcb354e}</UniqueIdentifier>
</Filter>
<Filter Include="ETL\Utilities\Semaphore">
<UniqueIdentifier>{304d989b-df11-403b-8ce9-ea594a66e759}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\etl\enum_type.h">
@ -558,6 +561,9 @@
<ClInclude Include="..\..\include\etl\mutex.h">
<Filter>ETL\Utilities</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\semaphore.h">
<Filter>ETL\Utilities</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\combinations.h">
<Filter>ETL\Maths</Filter>
</ClInclude>
@ -1098,6 +1104,12 @@
<ClInclude Include="..\iterators_for_unit_tests.h">
<Filter>Tests\Test Support</Filter>
</ClInclude>
<ClInclude Include="..\clocks_for_unit_tests.h">
<Filter>Tests\Test Support</Filter>
</ClInclude>
<ClInclude Include="..\clocks_for_unit_tests.cpp">
<Filter>Tests\Test Support</Filter>
</ClInclude>
<ClInclude Include="..\etl_profile.h">
<Filter>Tests\Test Support</Filter>
</ClInclude>
@ -1566,6 +1578,21 @@
<ClInclude Include="..\..\include\etl\format.h">
<Filter>UnitTest++\Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\semaphore\semaphore_std.h">
<Filter>ETL\Utilities\Semaphore</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\semaphore\semaphore_clang_sync.h">
<Filter>ETL\Utilities\Semaphore</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\semaphore\semaphore_exceptions.h">
<Filter>ETL\Utilities\Semaphore</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\semaphore\semaphore_freertos.h">
<Filter>ETL\Utilities\Semaphore</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\semaphore\semaphore_gcc_sync.h">
<Filter>ETL\Utilities\Semaphore</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\test_string_char.cpp">
@ -2021,6 +2048,9 @@
<ClCompile Include="..\test_format_spec.cpp">
<Filter>Tests\Strings</Filter>
</ClCompile>
<ClCompile Include="..\test_semaphore.cpp">
<Filter>Tests</Filter>
</ClCompile>
<ClCompile Include="..\test_set.cpp">
<Filter>Tests\Containers</Filter>
</ClCompile>
@ -4121,4 +4151,4 @@
<Filter>Resource Files\Images</Filter>
</Image>
</ItemGroup>
</Project>
</Project>