added missing files
Build on RHEL9 docker image / build (push) Successful in 4m25s
Build on RHEL8 docker image / build (push) Successful in 5m6s
Run Simulator Tests on local RHEL9 / build (push) Successful in 20m40s
Run Simulator Tests on local RHEL8 / build (push) Successful in 24m32s

This commit is contained in:
Erik Fröjdh committed 2026-10-01 11:19:29 +02:00
1 parent de3dcbc194
commit d49d62723c
2 files changed
+227

No files matched your search

@@ -0,0 +1,80 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
/**
* @file counting_semaphore.h
* @short C++17 backport of std::counting_semaphore and std::binary_semaphore
*
* Mirrors the C++20 <semaphore> types so that the project can switch by
* replacing this include with <semaphore> and sls:: with std::. Unnamed POSIX
* semaphores (sem_init) cannot be used instead as they are not implemented on
* macOS.
*
* Differences to the std types:
* - try_acquire_for and try_acquire_until are not implemented
* - the constructor is not constexpr
* - try_acquire never fails spuriously (std allows it to)
* - the preconditions (0 <= desired <= max() and
* 0 <= update <= max() - counter) are not checked, as in std violating
* them is undefined behaviour
*
* Built on std::mutex and std::condition_variable, therefore NOT
* async-signal-safe. Do not call release() from a signal handler.
*/
#include <condition_variable>
#include <cstddef>
#include <limits>
#include <mutex>
namespace sls {
template <std::ptrdiff_t LeastMaxValue =
std::numeric_limits<std::ptrdiff_t>::max()>
class counting_semaphore {
static_assert(LeastMaxValue >= 0, "LeastMaxValue must be non-negative");
public:
static constexpr std::ptrdiff_t max() noexcept { return LeastMaxValue; }
explicit counting_semaphore(std::ptrdiff_t desired) : count_(desired) {}
counting_semaphore(const counting_semaphore &) = delete;
counting_semaphore &operator=(const counting_semaphore &) = delete;
void acquire() {
std::unique_lock<std::mutex> lk(mtx_);
cv_.wait(lk, [this] { return count_ > 0; });
--count_;
}
bool try_acquire() noexcept {
std::lock_guard<std::mutex> lk(mtx_);
if (count_ == 0)
return false;
--count_;
return true;
}
void release(std::ptrdiff_t update = 1) {
// notify while holding the lock: a thread returning from acquire()
// may destroy the semaphore, which must not happen before we are done
// with the condition variable
std::lock_guard<std::mutex> lk(mtx_);
count_ += update;
if (update == 1)
cv_.notify_one();
else
cv_.notify_all();
}
private:
std::mutex mtx_;
std::condition_variable cv_;
std::ptrdiff_t count_;
};
using binary_semaphore = counting_semaphore<1>;
} // namespace sls
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "catch.hpp"
#include "sls/counting_semaphore.h"
#include <atomic>
#include <chrono>
#include <cstddef>
#include <thread>
#include <type_traits>
#include <vector>
namespace sls {
// Only what std::counting_semaphore guarantees is tested, so that the tests
// also pass with the std types once the project moves to C++20.
static_assert(!std::is_copy_constructible<counting_semaphore<>>::value,
"semaphore must not be copyable");
static_assert(!std::is_copy_assignable<counting_semaphore<>>::value,
"semaphore must not be copyable");
static_assert(!std::is_move_constructible<counting_semaphore<>>::value,
"semaphore must not be movable");
static_assert(
std::is_same<binary_semaphore, counting_semaphore<1>>::value,
"binary_semaphore is a counting_semaphore with a least max value of 1");
TEST_CASE("max is at least the least max value", "[support][semaphore]") {
CHECK(binary_semaphore::max() >= 1);
CHECK(counting_semaphore<5>::max() >= 5);
CHECK(counting_semaphore<>::max() >= 1);
}
TEST_CASE("try_acquire takes what the semaphore was constructed with",
"[support][semaphore]") {
counting_semaphore<> s(2);
CHECK(s.try_acquire());
CHECK(s.try_acquire());
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("try_acquire fails on an empty semaphore", "[support][semaphore]") {
binary_semaphore s{0};
CHECK_FALSE(s.try_acquire());
s.release();
CHECK(s.try_acquire());
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("release with an update count adds that many",
"[support][semaphore]") {
counting_semaphore<> s(0);
s.release(3);
s.release();
for (int i = 0; i != 4; ++i) {
CHECK(s.try_acquire());
}
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("acquire does not block when the count is positive",
"[support][semaphore]") {
counting_semaphore<> s(1);
s.acquire();
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("acquire blocks until release", "[support][semaphore]") {
binary_semaphore s{0};
std::atomic<bool> acquired{false};
std::thread t([&] {
s.acquire();
acquired = true;
});
// cannot have passed acquire, no matter how the thread is scheduled
std::this_thread::sleep_for(std::chrono::milliseconds(50));
CHECK_FALSE(acquired);
s.release();
t.join();
CHECK(acquired);
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("release with an update count wakes up that many waiting threads",
"[support][semaphore]") {
const int nThreads = 4;
counting_semaphore<> s(0);
std::atomic<int> acquired{0};
std::vector<std::thread> threads;
for (int i = 0; i != nThreads; ++i) {
threads.emplace_back([&] {
s.acquire();
++acquired;
});
}
s.release(nThreads);
for (auto &t : threads) {
t.join();
}
CHECK(acquired == nThreads);
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("every release is acquired exactly once", "[support][semaphore]") {
const int nProducers = 3;
const int nConsumers = 3;
const int nPerThread = 2000;
counting_semaphore<> s(0);
std::atomic<int> acquired{0};
std::vector<std::thread> threads;
for (int i = 0; i != nConsumers; ++i) {
threads.emplace_back([&] {
for (int j = 0; j != nPerThread; ++j) {
s.acquire();
++acquired;
}
});
}
for (int i = 0; i != nProducers; ++i) {
threads.emplace_back([&] {
for (int j = 0; j != nPerThread; ++j) {
s.release();
}
});
}
for (auto &t : threads) {
t.join();
}
CHECK(acquired == nConsumers * nPerThread);
CHECK_FALSE(s.try_acquire());
}
TEST_CASE("a waiter can destroy the semaphore as soon as it has acquired",
"[support][semaphore]") {
for (int i = 0; i != 200; ++i) {
auto *done = new binary_semaphore(0);
std::thread worker([done] { done->release(); });
done->acquire();
delete done;
worker.join();
}
SUCCEED();
}
} // namespace sls