mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2025-07-07 13:00:45 +00:00
implement conditions and signals with kqueue
now libsoundio builds on OS X and FreeBSD
This commit is contained in:
parent
24557e0ee3
commit
6378e6afd5
22
src/atomics.hpp
Normal file
22
src/atomics.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef SOUNDIO_ATOMICS_HPP
|
||||||
|
#define SOUNDIO_ATOMICS_HPP
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
using std::atomic_flag;
|
||||||
|
using std::atomic_long;
|
||||||
|
using std::atomic_bool;
|
||||||
|
using std::atomic_uintptr_t;
|
||||||
|
|
||||||
|
#if ATOMIC_LONG_LOCK_FREE != 2
|
||||||
|
#error "require atomic_long to be lock free"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ATOMIC_BOOL_LOCK_FREE != 2
|
||||||
|
#error "require atomic_bool to be lock free"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ATOMIC_POINTER_LOCK_FREE != 2
|
||||||
|
#error "require atomic pointers to be lock free"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -9,15 +9,13 @@
|
||||||
#include "soundio.hpp"
|
#include "soundio.hpp"
|
||||||
#include "dummy_ring_buffer.hpp"
|
#include "dummy_ring_buffer.hpp"
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
|
#include "atomics.hpp"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <atomic>
|
|
||||||
using std::atomic_flag;
|
|
||||||
|
|
||||||
struct SoundIoOutputDeviceDummy {
|
struct SoundIoOutputDeviceDummy {
|
||||||
struct SoundIoOsThread *thread;
|
struct SoundIoOsThread *thread;
|
||||||
struct SoundIoOsMutex *mutex;
|
|
||||||
struct SoundIoOsCond *cond;
|
struct SoundIoOsCond *cond;
|
||||||
atomic_flag abort_flag;
|
atomic_flag abort_flag;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
|
@ -45,9 +43,7 @@ static void playback_thread_run(void *arg) {
|
||||||
|
|
||||||
double time_per_frame = 1.0 / (double)device->default_sample_rate;
|
double time_per_frame = 1.0 / (double)device->default_sample_rate;
|
||||||
while (opd->abort_flag.test_and_set()) {
|
while (opd->abort_flag.test_and_set()) {
|
||||||
soundio_os_mutex_lock(opd->mutex);
|
soundio_os_cond_timed_wait(opd->cond, nullptr, opd->period);
|
||||||
soundio_os_cond_timed_wait(opd->cond, opd->mutex, opd->period);
|
|
||||||
soundio_os_mutex_unlock(opd->mutex);
|
|
||||||
|
|
||||||
double now = soundio_os_get_time();
|
double now = soundio_os_get_time();
|
||||||
double total_time = now - start_time;
|
double total_time = now - start_time;
|
||||||
|
@ -104,16 +100,12 @@ static void flush_events(SoundIo *soundio) {
|
||||||
static void wait_events(SoundIo *soundio) {
|
static void wait_events(SoundIo *soundio) {
|
||||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||||
flush_events(soundio);
|
flush_events(soundio);
|
||||||
soundio_os_mutex_lock(sid->mutex);
|
soundio_os_cond_wait(sid->cond, nullptr);
|
||||||
soundio_os_cond_wait(sid->cond, sid->mutex);
|
|
||||||
soundio_os_mutex_unlock(sid->mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wakeup(SoundIo *soundio) {
|
static void wakeup(SoundIo *soundio) {
|
||||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||||
soundio_os_mutex_lock(sid->mutex);
|
soundio_os_cond_signal(sid->cond, nullptr);
|
||||||
soundio_os_cond_signal(sid->cond);
|
|
||||||
soundio_os_mutex_unlock(sid->mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void refresh_devices(SoundIo *soundio) { }
|
static void refresh_devices(SoundIo *soundio) { }
|
||||||
|
@ -125,16 +117,11 @@ static void output_device_destroy_dummy(SoundIo *soundio,
|
||||||
if (opd->thread) {
|
if (opd->thread) {
|
||||||
if (opd->thread) {
|
if (opd->thread) {
|
||||||
opd->abort_flag.clear();
|
opd->abort_flag.clear();
|
||||||
soundio_os_mutex_lock(opd->mutex);
|
soundio_os_cond_signal(opd->cond, nullptr);
|
||||||
soundio_os_cond_signal(opd->cond);
|
|
||||||
soundio_os_mutex_unlock(opd->mutex);
|
|
||||||
soundio_os_thread_destroy(opd->thread);
|
soundio_os_thread_destroy(opd->thread);
|
||||||
opd->thread = nullptr;
|
opd->thread = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
soundio_os_mutex_destroy(opd->mutex);
|
|
||||||
opd->mutex = nullptr;
|
|
||||||
|
|
||||||
soundio_os_cond_destroy(opd->cond);
|
soundio_os_cond_destroy(opd->cond);
|
||||||
opd->cond = nullptr;
|
opd->cond = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -150,12 +137,6 @@ static int output_device_init_dummy(SoundIo *soundio,
|
||||||
|
|
||||||
soundio_dummy_ring_buffer_init(&opd->ring_buffer, opd->buffer_size);
|
soundio_dummy_ring_buffer_init(&opd->ring_buffer, opd->buffer_size);
|
||||||
|
|
||||||
opd->mutex = soundio_os_mutex_create();
|
|
||||||
if (!opd->mutex) {
|
|
||||||
output_device_destroy_dummy(soundio, output_device);
|
|
||||||
return SoundIoErrorNoMem;
|
|
||||||
}
|
|
||||||
|
|
||||||
opd->cond = soundio_os_cond_create();
|
opd->cond = soundio_os_cond_create();
|
||||||
if (!opd->cond) {
|
if (!opd->cond) {
|
||||||
output_device_destroy_dummy(soundio, output_device);
|
output_device_destroy_dummy(soundio, output_device);
|
||||||
|
|
|
@ -2,12 +2,7 @@
|
||||||
#define SOUNDIO_DUMMY_RING_BUFFER_HPP
|
#define SOUNDIO_DUMMY_RING_BUFFER_HPP
|
||||||
|
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include <atomic>
|
#include "atomics.hpp"
|
||||||
using std::atomic_long;
|
|
||||||
|
|
||||||
#ifndef ATOMIC_LONG_LOCK_FREE
|
|
||||||
#error "require atomic long to be lock free"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct SoundIoDummyRingBuffer {
|
struct SoundIoDummyRingBuffer {
|
||||||
char *address;
|
char *address;
|
||||||
|
|
157
src/os.cpp
157
src/os.cpp
|
@ -8,6 +8,7 @@
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
#include "soundio.h"
|
#include "soundio.h"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
#include "atomics.hpp"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
@ -18,6 +19,16 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(__FreeBSD__) || defined(__MACH__)
|
||||||
|
#define SOUNDIO_OS_KQUEUE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/event.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __MACH__
|
#ifdef __MACH__
|
||||||
#include <mach/clock.h>
|
#include <mach/clock.h>
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
|
@ -39,13 +50,27 @@ struct SoundIoOsMutex {
|
||||||
bool id_init;
|
bool id_init;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
static int kq_id;
|
||||||
|
static atomic_uintptr_t next_notify_ident;
|
||||||
|
struct SoundIoOsCond {
|
||||||
|
int notify_ident;
|
||||||
|
};
|
||||||
|
#else
|
||||||
struct SoundIoOsCond {
|
struct SoundIoOsCond {
|
||||||
pthread_cond_t id;
|
pthread_cond_t id;
|
||||||
bool id_init;
|
bool id_init;
|
||||||
|
|
||||||
pthread_condattr_t attr;
|
pthread_condattr_t attr;
|
||||||
bool attr_init;
|
bool attr_init;
|
||||||
|
|
||||||
|
pthread_mutex_t default_mutex_id;
|
||||||
|
bool default_mutex_init;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static atomic_bool initialized = ATOMIC_VAR_INIT(false);
|
||||||
|
static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
double soundio_os_get_time(void) {
|
double soundio_os_get_time(void) {
|
||||||
#ifdef __MACH__
|
#ifdef __MACH__
|
||||||
|
@ -207,6 +232,9 @@ struct SoundIoOsCond * soundio_os_cond_create(void) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
cond->notify_ident = next_notify_ident.fetch_add(1);
|
||||||
|
#else
|
||||||
if (pthread_condattr_init(&cond->attr)) {
|
if (pthread_condattr_init(&cond->attr)) {
|
||||||
soundio_os_cond_destroy(cond);
|
soundio_os_cond_destroy(cond);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -224,6 +252,13 @@ struct SoundIoOsCond * soundio_os_cond_create(void) {
|
||||||
}
|
}
|
||||||
cond->id_init = true;
|
cond->id_init = true;
|
||||||
|
|
||||||
|
if ((err = pthread_mutex_init(&cond->default_mutex_id, NULL))) {
|
||||||
|
soundio_os_cond_destroy(cond);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cond->default_mutex_init = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
return cond;
|
return cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +266,8 @@ void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
|
||||||
if (!cond)
|
if (!cond)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
#else
|
||||||
if (cond->id_init) {
|
if (cond->id_init) {
|
||||||
assert_no_err(pthread_cond_destroy(&cond->id));
|
assert_no_err(pthread_cond_destroy(&cond->id));
|
||||||
}
|
}
|
||||||
|
@ -238,37 +275,139 @@ void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
|
||||||
if (cond->attr_init) {
|
if (cond->attr_init) {
|
||||||
assert_no_err(pthread_condattr_destroy(&cond->attr));
|
assert_no_err(pthread_condattr_destroy(&cond->attr));
|
||||||
}
|
}
|
||||||
|
if (cond->default_mutex_init) {
|
||||||
|
assert_no_err(pthread_mutex_destroy(&cond->default_mutex_id));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
destroy(cond);
|
destroy(cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_os_cond_signal(struct SoundIoOsCond *cond) {
|
void soundio_os_cond_signal(struct SoundIoOsCond *cond,
|
||||||
assert_no_err(pthread_cond_signal(&cond->id));
|
struct SoundIoOsMutex *locked_mutex)
|
||||||
}
|
{
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
struct kevent kev;
|
||||||
|
struct timespec timeout = { 0, 0 };
|
||||||
|
|
||||||
void soundio_os_cond_broadcast(struct SoundIoOsCond *cond) {
|
memset(&kev, 0, sizeof(kev));
|
||||||
assert_no_err(pthread_cond_broadcast(&cond->id));
|
kev.ident = cond->notify_ident;
|
||||||
|
kev.filter = EVFILT_USER;
|
||||||
|
kev.fflags = NOTE_TRIGGER;
|
||||||
|
|
||||||
|
if (kevent(kq_id, &kev, 1, NULL, 0, &timeout) == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
return;
|
||||||
|
soundio_panic("kevent signal error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (locked_mutex) {
|
||||||
|
assert_no_err(pthread_cond_signal(&cond->id));
|
||||||
|
} else {
|
||||||
|
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
|
||||||
|
assert_no_err(pthread_cond_signal(&cond->id));
|
||||||
|
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
|
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
|
||||||
struct SoundIoOsMutex *mutex, double seconds)
|
struct SoundIoOsMutex *locked_mutex, double seconds)
|
||||||
{
|
{
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
struct kevent kev;
|
||||||
|
struct kevent out_kev;
|
||||||
|
|
||||||
|
memset(&kev, 0, sizeof(kev));
|
||||||
|
kev.ident = cond->notify_ident;
|
||||||
|
kev.filter = EVFILT_USER;
|
||||||
|
kev.flags = EV_ADD | EV_CLEAR;
|
||||||
|
|
||||||
|
// this time is relative
|
||||||
|
struct timespec timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_nsec = (seconds * 1000000000L);
|
||||||
|
|
||||||
|
if (kevent(kq_id, &kev, 1, &out_kev, 1, &timeout) == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
return;
|
||||||
|
soundio_panic("kevent wait error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
pthread_mutex_t *target_mutex;
|
||||||
|
if (locked_mutex) {
|
||||||
|
target_mutex = &locked_mutex->id;
|
||||||
|
} else {
|
||||||
|
target_mutex = &cond->default_mutex_id;
|
||||||
|
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
|
||||||
|
}
|
||||||
|
// this time is absolute
|
||||||
struct timespec tms;
|
struct timespec tms;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &tms);
|
clock_gettime(CLOCK_MONOTONIC, &tms);
|
||||||
tms.tv_nsec += (seconds * 1000000000L);
|
tms.tv_nsec += (seconds * 1000000000L);
|
||||||
int err;
|
int err;
|
||||||
if ((err = pthread_cond_timedwait(&cond->id, &mutex->id, &tms))) {
|
if ((err = pthread_cond_timedwait(&cond->id, target_mutex, &tms))) {
|
||||||
assert(err != EPERM);
|
assert(err != EPERM);
|
||||||
assert(err != EINVAL);
|
assert(err != EINVAL);
|
||||||
}
|
}
|
||||||
|
if (!locked_mutex)
|
||||||
|
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
||||||
struct SoundIoOsMutex *mutex)
|
struct SoundIoOsMutex *locked_mutex)
|
||||||
{
|
{
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
struct kevent kev;
|
||||||
|
struct kevent out_kev;
|
||||||
|
|
||||||
|
memset(&kev, 0, sizeof(kev));
|
||||||
|
kev.ident = cond->notify_ident;
|
||||||
|
kev.filter = EVFILT_USER;
|
||||||
|
kev.flags = EV_ADD | EV_CLEAR;
|
||||||
|
|
||||||
|
if (kevent(kq_id, &kev, 1, &out_kev, 1, NULL) == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
return;
|
||||||
|
soundio_panic("kevent wait error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
pthread_mutex_t *target_mutex;
|
||||||
|
if (locked_mutex) {
|
||||||
|
target_mutex = &locked_mutex->id;
|
||||||
|
} else {
|
||||||
|
target_mutex = &cond->default_mutex_id;
|
||||||
|
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
|
||||||
|
}
|
||||||
int err;
|
int err;
|
||||||
if ((err = pthread_cond_wait(&cond->id, &mutex->id))) {
|
if ((err = pthread_cond_wait(&cond->id, target_mutex))) {
|
||||||
assert(err != EPERM);
|
assert(err != EPERM);
|
||||||
assert(err != EINVAL);
|
assert(err != EINVAL);
|
||||||
}
|
}
|
||||||
|
if (!locked_mutex)
|
||||||
|
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void internal_init(void) {
|
||||||
|
#ifdef SOUNDIO_OS_KQUEUE
|
||||||
|
kq_id = kqueue();
|
||||||
|
if (kq_id == -1)
|
||||||
|
soundio_panic("unable to create kqueue: %s", strerror(errno));
|
||||||
|
next_notify_ident.store(1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void soundio_os_init(void) {
|
||||||
|
if (initialized.load())
|
||||||
|
return;
|
||||||
|
assert_no_err(pthread_mutex_lock(&init_mutex));
|
||||||
|
if (initialized.load()) {
|
||||||
|
assert_no_err(pthread_mutex_unlock(&init_mutex));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initialized.store(true);
|
||||||
|
internal_init();
|
||||||
|
assert_no_err(pthread_mutex_unlock(&init_mutex));
|
||||||
}
|
}
|
||||||
|
|
17
src/os.hpp
17
src/os.hpp
|
@ -11,6 +11,10 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
|
// safe to call from any thread(s) multiple times, but
|
||||||
|
// must be called at least once before calling any other os functions
|
||||||
|
void soundio_os_init(void);
|
||||||
|
|
||||||
double soundio_os_get_time(void);
|
double soundio_os_get_time(void);
|
||||||
|
|
||||||
struct SoundIoOsThread;
|
struct SoundIoOsThread;
|
||||||
|
@ -30,12 +34,17 @@ void soundio_os_mutex_unlock(struct SoundIoOsMutex *mutex);
|
||||||
struct SoundIoOsCond;
|
struct SoundIoOsCond;
|
||||||
struct SoundIoOsCond * soundio_os_cond_create(void);
|
struct SoundIoOsCond * soundio_os_cond_create(void);
|
||||||
void soundio_os_cond_destroy(struct SoundIoOsCond *cond);
|
void soundio_os_cond_destroy(struct SoundIoOsCond *cond);
|
||||||
void soundio_os_cond_signal(struct SoundIoOsCond *cond);
|
|
||||||
void soundio_os_cond_broadcast(struct SoundIoOsCond *cond);
|
|
||||||
|
|
||||||
|
// locked_mutex is optional. On systems that use mutexes for conditions, if you
|
||||||
|
// pass NULL, a mutex will be created and locked/unlocked for you. On systems
|
||||||
|
// that do not use mutexes for conditions, no mutex handling is necessary. If
|
||||||
|
// you already have a locked mutex available, pass it; this will be better on
|
||||||
|
// systems that use mutexes for conditions.
|
||||||
|
void soundio_os_cond_signal(struct SoundIoOsCond *cond,
|
||||||
|
struct SoundIoOsMutex *locked_mutex);
|
||||||
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
|
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
|
||||||
struct SoundIoOsMutex *mutex, double seconds);
|
struct SoundIoOsMutex *locked_mutex, double seconds);
|
||||||
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
||||||
struct SoundIoOsMutex *mutex);
|
struct SoundIoOsMutex *locked_mutex);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
#include "pulseaudio.hpp"
|
#include "pulseaudio.hpp"
|
||||||
#include "soundio.hpp"
|
#include "soundio.hpp"
|
||||||
|
#include "atomics.hpp"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
#include <atomic>
|
|
||||||
using std::atomic_bool;
|
|
||||||
|
|
||||||
struct SoundIoOutputDevicePulseAudio {
|
struct SoundIoOutputDevicePulseAudio {
|
||||||
pa_stream *stream;
|
pa_stream *stream;
|
||||||
|
|
Loading…
Reference in a new issue