Add RTSemaphore and tests
This commit is contained in:
parent
be766ae80f
commit
cbc037e055
5 changed files with 374 additions and 0 deletions
|
|
@ -19,6 +19,7 @@ set (SFIZZ_SOURCES
|
||||||
sfizz/SfzFilter.cpp
|
sfizz/SfzFilter.cpp
|
||||||
sfizz/Curve.cpp
|
sfizz/Curve.cpp
|
||||||
sfizz/Wavetables.cpp
|
sfizz/Wavetables.cpp
|
||||||
|
sfizz/RTSemaphore.cpp
|
||||||
sfizz/Effects.cpp
|
sfizz/Effects.cpp
|
||||||
sfizz/effects/Nothing.cpp
|
sfizz/effects/Nothing.cpp
|
||||||
sfizz/effects/Filter.cpp
|
sfizz/effects/Filter.cpp
|
||||||
|
|
|
||||||
254
src/sfizz/RTSemaphore.cpp
Normal file
254
src/sfizz/RTSemaphore.cpp
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "RTSemaphore.h"
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
RTSemaphore::RTSemaphore(unsigned value)
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
init(ec, value);
|
||||||
|
if (ec)
|
||||||
|
throw std::system_error(ec);
|
||||||
|
good_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTSemaphore::RTSemaphore(std::error_code &ec, unsigned value) noexcept
|
||||||
|
{
|
||||||
|
init(ec, value);
|
||||||
|
good_ = ec ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTSemaphore::~RTSemaphore() noexcept
|
||||||
|
{
|
||||||
|
if (good_) {
|
||||||
|
std::error_code ec;
|
||||||
|
destroy(ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::post()
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
post(ec);
|
||||||
|
if (ec)
|
||||||
|
throw std::system_error(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::wait()
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
wait(ec);
|
||||||
|
if (ec)
|
||||||
|
throw std::system_error(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTSemaphore::try_wait()
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
bool b = try_wait(ec);
|
||||||
|
if (ec)
|
||||||
|
throw std::system_error(ec);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
void RTSemaphore::init(std::error_code &ec, unsigned value)
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
kern_return_t ret = semaphore_create(mach_task_self(), &sem_, SYNC_POLICY_FIFO, value);
|
||||||
|
if (ret != KERN_SUCCESS)
|
||||||
|
ec = std::error_code(ret, mach_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::destroy(std::error_code &ec)
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
kern_return_t ret = semaphore_destroy(mach_task_self(), sem_);
|
||||||
|
if (ret != KERN_SUCCESS)
|
||||||
|
ec = std::error_code(ret, mach_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::post(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
kern_return_t ret = semaphore_signal(sem_);
|
||||||
|
if (ret != KERN_SUCCESS)
|
||||||
|
ec = std::error_code(ret, mach_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::wait(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
do {
|
||||||
|
kern_return_t ret = semaphore_wait(sem_);
|
||||||
|
switch (ret) {
|
||||||
|
case KERN_SUCCESS:
|
||||||
|
return;
|
||||||
|
case KERN_ABORTED:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ec = std::error_code(ret, mach_category());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTSemaphore::try_wait(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
do {
|
||||||
|
const mach_timespec_t timeout = {0, 0};
|
||||||
|
kern_return_t ret = semaphore_timedwait(sem_, timeout);
|
||||||
|
switch (ret) {
|
||||||
|
case KERN_SUCCESS:
|
||||||
|
return true;
|
||||||
|
case KERN_OPERATION_TIMED_OUT:
|
||||||
|
return false;
|
||||||
|
case KERN_ABORTED:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ec = std::error_code(ret, mach_category());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::error_category &RTSemaphore::mach_category()
|
||||||
|
{
|
||||||
|
class mach_category : public std::error_category {
|
||||||
|
public:
|
||||||
|
const char *name() const noexcept override
|
||||||
|
{
|
||||||
|
return "kern_return_t";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message(int condition) const override
|
||||||
|
{
|
||||||
|
const char *str = mach_error_string(condition);
|
||||||
|
return str ? str : "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const mach_category cat;
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
void RTSemaphore::init(std::error_code &ec, unsigned value)
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
sem_ = CreateSemaphore(nullptr, value, LONG_MAX, nullptr);
|
||||||
|
if (!sem_)
|
||||||
|
ec = std::error_code(GetLastError(), std::system_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::destroy(std::error_code &ec)
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
if (CloseHandle(sem_) == 0)
|
||||||
|
ec = std::error_code(GetLastError(), std::system_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::post(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
if (ReleaseSemaphore(sem_, 1, nullptr) == 0)
|
||||||
|
ec = std::error_code(GetLastError(), std::system_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::wait(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
DWORD ret = WaitForSingleObject(sem_, INFINITE);
|
||||||
|
switch (ret) {
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
return;
|
||||||
|
case WAIT_FAILED:
|
||||||
|
ec = std::error_code(GetLastError(), std::system_category());
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
ec = std::error_code(ret, std::system_category());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTSemaphore::try_wait(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
DWORD ret = WaitForSingleObject(sem_, 0);
|
||||||
|
switch (ret) {
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
return true;
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
return false;
|
||||||
|
case WAIT_FAILED:
|
||||||
|
ec = std::error_code(GetLastError(), std::system_category());
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
ec = std::error_code(ret, std::system_category());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void RTSemaphore::init(std::error_code &ec, unsigned value)
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
if (sem_init(&sem_, 0, value) != 0)
|
||||||
|
ec = std::error_code(errno, std::generic_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::destroy(std::error_code &ec)
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
if (sem_destroy(&sem_) != 0)
|
||||||
|
ec = std::error_code(errno, std::generic_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::post(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
while (sem_post(&sem_) != 0) {
|
||||||
|
int e = errno;
|
||||||
|
if (e != EINTR) {
|
||||||
|
ec = std::error_code(e, std::generic_category());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTSemaphore::wait(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
while (sem_wait(&sem_) != 0) {
|
||||||
|
int e = errno;
|
||||||
|
if (e != EINTR) {
|
||||||
|
ec = std::error_code(e, std::generic_category());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTSemaphore::try_wait(std::error_code &ec) noexcept
|
||||||
|
{
|
||||||
|
ec.clear();
|
||||||
|
do {
|
||||||
|
if (sem_trywait(&sem_) == 0)
|
||||||
|
return true;
|
||||||
|
int e = errno;
|
||||||
|
switch (e) {
|
||||||
|
case EINTR:
|
||||||
|
break;
|
||||||
|
case EAGAIN:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
ec = std::error_code(e, std::generic_category());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
50
src/sfizz/RTSemaphore.h
Normal file
50
src/sfizz/RTSemaphore.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <semaphore.h>
|
||||||
|
#endif
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
class RTSemaphore {
|
||||||
|
public:
|
||||||
|
explicit RTSemaphore(unsigned value = 0);
|
||||||
|
explicit RTSemaphore(std::error_code &ec, unsigned value = 0) noexcept;
|
||||||
|
~RTSemaphore() noexcept;
|
||||||
|
|
||||||
|
RTSemaphore(const RTSemaphore &) = delete;
|
||||||
|
RTSemaphore &operator=(const RTSemaphore &) = delete;
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept { return good_; }
|
||||||
|
|
||||||
|
void post();
|
||||||
|
void wait();
|
||||||
|
bool try_wait();
|
||||||
|
|
||||||
|
void post(std::error_code &ec) noexcept;
|
||||||
|
void wait(std::error_code &ec) noexcept;
|
||||||
|
bool try_wait(std::error_code &ec) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init(std::error_code &ec, unsigned value);
|
||||||
|
void destroy(std::error_code &ec);
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
semaphore_t sem_ {};
|
||||||
|
static const std::error_category &mach_category();
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
HANDLE sem_ {};
|
||||||
|
#else
|
||||||
|
sem_t sem_ {};
|
||||||
|
#endif
|
||||||
|
bool good_ {};
|
||||||
|
};
|
||||||
|
|
@ -29,6 +29,7 @@ set(SFIZZ_TEST_SOURCES
|
||||||
RegionTriggersT.cpp
|
RegionTriggersT.cpp
|
||||||
FloatHelpersT.cpp
|
FloatHelpersT.cpp
|
||||||
WavetablesT.cpp
|
WavetablesT.cpp
|
||||||
|
SemaphoreT.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
|
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
|
||||||
|
|
|
||||||
68
tests/SemaphoreT.cpp
Normal file
68
tests/SemaphoreT.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "sfizz/RTSemaphore.h"
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
TEST_CASE("[Semaphore] Basic operations")
|
||||||
|
{
|
||||||
|
RTSemaphore sem;
|
||||||
|
|
||||||
|
REQUIRE(sem.try_wait() == false);
|
||||||
|
|
||||||
|
sem.post();
|
||||||
|
REQUIRE(sem.try_wait() == true);
|
||||||
|
REQUIRE(sem.try_wait() == false);
|
||||||
|
|
||||||
|
sem.post();
|
||||||
|
sem.post();
|
||||||
|
REQUIRE(sem.try_wait() == true);
|
||||||
|
REQUIRE(sem.try_wait() == true);
|
||||||
|
REQUIRE(sem.try_wait() == false);
|
||||||
|
|
||||||
|
sem.post();
|
||||||
|
sem.post();
|
||||||
|
sem.wait();
|
||||||
|
sem.wait();
|
||||||
|
|
||||||
|
REQUIRE(sem.try_wait() == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Semaphore] Counter initialization")
|
||||||
|
{
|
||||||
|
RTSemaphore sem(3);
|
||||||
|
|
||||||
|
REQUIRE(sem.try_wait() == true);
|
||||||
|
REQUIRE(sem.try_wait() == true);
|
||||||
|
REQUIRE(sem.try_wait() == true);
|
||||||
|
REQUIRE(sem.try_wait() == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Semaphore] Thread synchronization")
|
||||||
|
{
|
||||||
|
RTSemaphore sem1;
|
||||||
|
RTSemaphore sem2;
|
||||||
|
constexpr int n = 1000;
|
||||||
|
|
||||||
|
std::thread t1([&]() {
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
sem1.post();
|
||||||
|
sem2.wait();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
std::thread t2([&]() {
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
sem2.post();
|
||||||
|
sem1.wait();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
REQUIRE(sem1.try_wait() == false);
|
||||||
|
REQUIRE(sem2.try_wait() == false);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue