Merge pull request #176 from jpcima/rt-semaphore

Add RTSemaphore and tests
This commit is contained in:
JP Cimalando 2020-04-13 18:00:42 +02:00 committed by GitHub
commit 3df57b6fa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 397 additions and 176 deletions

View file

@ -19,6 +19,7 @@ set (SFIZZ_SOURCES
sfizz/SfzFilter.cpp
sfizz/Curve.cpp
sfizz/Wavetables.cpp
sfizz/RTSemaphore.cpp
sfizz/Effects.cpp
sfizz/effects/Nothing.cpp
sfizz/effects/Filter.cpp

254
src/sfizz/RTSemaphore.cpp Normal file
View 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
View 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_ {};
};

View file

@ -29,6 +29,7 @@ set(SFIZZ_TEST_SOURCES
RegionTriggersT.cpp
FloatHelpersT.cpp
WavetablesT.cpp
SemaphoreT.cpp
)
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})

View file

@ -8,6 +8,9 @@
#include "sfizz/SfzHelpers.h"
#include "catch2/catch.hpp"
#include "ghc/fs_std.hpp"
#if defined(__APPLE__)
#include <unistd.h> // pathconf
#endif
using namespace Catch::literals;
using namespace sfz::literals;
@ -529,15 +532,26 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
TEST_CASE("[Files] Case sentitiveness")
{
#ifndef WIN32
sfz::Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/case_insensitive.sfz");
REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav");
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav");
REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav");
REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.wav");
const fs::path sfzFilePath = fs::current_path() /
"tests/TestFiles/case_insensitive.sfz";
#if defined(_WIN32)
const bool caseSensitiveFs = false;
#elif defined(__APPLE__)
const bool caseSensitiveFs = pathconf(sfzFilePath.string().c_str(), _PC_CASE_SENSITIVE) != 0;
#else
const bool caseSensitiveFs = true;
#endif
if (caseSensitiveFs) {
sfz::Synth synth;
synth.loadSfzFile(sfzFilePath);
REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav");
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav");
REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav");
REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.wav");
}
}
TEST_CASE("[Files] Empty file")

68
tests/SemaphoreT.cpp Normal file
View 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);
}

View file

@ -1,167 +0,0 @@
// Copyright Jean Pierre Cimalando 2018-2020.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#if defined(__APPLE__)
#include <mach/mach.h>
#elif defined(_WIN32)
#include <limits.h>
#include <windows.h>
#else
#include <semaphore.h>
#include <errno.h>
#endif
#include <stdexcept>
class RTSemaphore {
public:
explicit RTSemaphore(unsigned value = 0);
~RTSemaphore();
RTSemaphore(const RTSemaphore &) = delete;
RTSemaphore &operator=(const RTSemaphore &) = delete;
void post();
void wait();
bool try_wait();
private:
#if defined(__APPLE__)
semaphore_t sem_;
#elif defined(_WIN32)
HANDLE sem_;
#else
sem_t sem_;
#endif
};
#if defined(__APPLE__)
inline RTSemaphore::RTSemaphore(unsigned value)
{
if (semaphore_create(mach_task_self(), &sem_, SYNC_POLICY_FIFO, value) != 0)
throw std::runtime_error("RTSemaphore::RTSemaphore");
}
inline RTSemaphore::~RTSemaphore()
{
semaphore_destroy(mach_task_self(), sem_);
}
inline void RTSemaphore::post()
{
if (semaphore_signal(sem_) != KERN_SUCCESS)
throw std::runtime_error("RTSemaphore::post");
}
inline void RTSemaphore::wait()
{
do {
switch (semaphore_wait(sem_)) {
case KERN_SUCCESS:
return;
case KERN_ABORTED:
break;
default:
throw std::runtime_error("RTSemaphore::wait");
}
} while (1);
}
inline bool RTSemaphore::try_wait()
{
do {
const mach_timespec_t timeout = {0, 0};
switch (semaphore_timedwait(sem_, timeout)) {
case KERN_SUCCESS:
return true;
case KERN_OPERATION_TIMED_OUT:
return false;
case KERN_ABORTED:
break;
default:
throw std::runtime_error("RTSemaphore::try_wait");
}
} while (1);
}
#elif defined(_WIN32)
inline RTSemaphore::RTSemaphore(unsigned value)
{
sem_ = CreateSemaphore(nullptr, value, LONG_MAX, nullptr);
if (!sem_)
throw std::runtime_error("RTSemaphore::RTSemaphore");
}
inline RTSemaphore::~RTSemaphore()
{
CloseHandle(sem_);
}
inline void RTSemaphore::post()
{
if (!ReleaseSemaphore(sem_, 1, nullptr))
throw std::runtime_error("RTSemaphore::post");
}
inline void RTSemaphore::wait()
{
if (WaitForSingleObject(sem_, INFINITE) != WAIT_OBJECT_0)
throw std::runtime_error("RTSemaphore::wait");
}
inline bool RTSemaphore::try_wait()
{
switch (WaitForSingleObject(sem_, 0)) {
case WAIT_OBJECT_0:
return true;
case WAIT_TIMEOUT:
return false;
default:
throw std::runtime_error("RTSemaphore::try_wait");
}
}
#else
inline RTSemaphore::RTSemaphore(unsigned value)
{
if (sem_init(&sem_, 0, value) != 0)
throw std::runtime_error("RTSemaphore::RTSemaphore");
}
inline RTSemaphore::~RTSemaphore()
{
sem_destroy(&sem_);
}
inline void RTSemaphore::post()
{
while (sem_post(&sem_) != 0) {
if (errno != EINTR)
throw std::runtime_error("RTSemaphore::post");
}
}
inline void RTSemaphore::wait()
{
while (sem_wait(&sem_) != 0) {
if (errno != EINTR)
throw std::runtime_error("RTSemaphore::wait");
}
}
inline bool RTSemaphore::try_wait()
{
do {
if (sem_trywait(&sem_) == 0)
return true;
switch (errno) {
case EINTR:
break;
case EAGAIN:
return false;
default:
throw std::runtime_error("RTSemaphore::try_wait");
}
} while (1);
}
#endif

View file

@ -6,7 +6,7 @@
#pragma once
#include "SfizzVstState.h"
#include "RTSemaphore.h"
#include "sfizz/RTSemaphore.h"
#include "ring_buffer/ring_buffer.h"
#include "public.sdk/source/vst/vstaudioeffect.h"
#include <sfizz.hpp>