mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2025-01-03 16:45:35 +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 "dummy_ring_buffer.hpp"
|
||||
#include "os.hpp"
|
||||
#include "atomics.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <atomic>
|
||||
using std::atomic_flag;
|
||||
|
||||
struct SoundIoOutputDeviceDummy {
|
||||
struct SoundIoOsThread *thread;
|
||||
struct SoundIoOsMutex *mutex;
|
||||
struct SoundIoOsCond *cond;
|
||||
atomic_flag abort_flag;
|
||||
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;
|
||||
while (opd->abort_flag.test_and_set()) {
|
||||
soundio_os_mutex_lock(opd->mutex);
|
||||
soundio_os_cond_timed_wait(opd->cond, opd->mutex, opd->period);
|
||||
soundio_os_mutex_unlock(opd->mutex);
|
||||
soundio_os_cond_timed_wait(opd->cond, nullptr, opd->period);
|
||||
|
||||
double now = soundio_os_get_time();
|
||||
double total_time = now - start_time;
|
||||
|
@ -104,16 +100,12 @@ static void flush_events(SoundIo *soundio) {
|
|||
static void wait_events(SoundIo *soundio) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||
flush_events(soundio);
|
||||
soundio_os_mutex_lock(sid->mutex);
|
||||
soundio_os_cond_wait(sid->cond, sid->mutex);
|
||||
soundio_os_mutex_unlock(sid->mutex);
|
||||
soundio_os_cond_wait(sid->cond, nullptr);
|
||||
}
|
||||
|
||||
static void wakeup(SoundIo *soundio) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||
soundio_os_mutex_lock(sid->mutex);
|
||||
soundio_os_cond_signal(sid->cond);
|
||||
soundio_os_mutex_unlock(sid->mutex);
|
||||
soundio_os_cond_signal(sid->cond, nullptr);
|
||||
}
|
||||
|
||||
static void refresh_devices(SoundIo *soundio) { }
|
||||
|
@ -125,16 +117,11 @@ static void output_device_destroy_dummy(SoundIo *soundio,
|
|||
if (opd->thread) {
|
||||
if (opd->thread) {
|
||||
opd->abort_flag.clear();
|
||||
soundio_os_mutex_lock(opd->mutex);
|
||||
soundio_os_cond_signal(opd->cond);
|
||||
soundio_os_mutex_unlock(opd->mutex);
|
||||
soundio_os_cond_signal(opd->cond, nullptr);
|
||||
soundio_os_thread_destroy(opd->thread);
|
||||
opd->thread = nullptr;
|
||||
}
|
||||
}
|
||||
soundio_os_mutex_destroy(opd->mutex);
|
||||
opd->mutex = nullptr;
|
||||
|
||||
soundio_os_cond_destroy(opd->cond);
|
||||
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);
|
||||
|
||||
opd->mutex = soundio_os_mutex_create();
|
||||
if (!opd->mutex) {
|
||||
output_device_destroy_dummy(soundio, output_device);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
opd->cond = soundio_os_cond_create();
|
||||
if (!opd->cond) {
|
||||
output_device_destroy_dummy(soundio, output_device);
|
||||
|
|
|
@ -2,12 +2,7 @@
|
|||
#define SOUNDIO_DUMMY_RING_BUFFER_HPP
|
||||
|
||||
#include "util.hpp"
|
||||
#include <atomic>
|
||||
using std::atomic_long;
|
||||
|
||||
#ifndef ATOMIC_LONG_LOCK_FREE
|
||||
#error "require atomic long to be lock free"
|
||||
#endif
|
||||
#include "atomics.hpp"
|
||||
|
||||
struct SoundIoDummyRingBuffer {
|
||||
char *address;
|
||||
|
|
157
src/os.cpp
157
src/os.cpp
|
@ -8,6 +8,7 @@
|
|||
#include "os.hpp"
|
||||
#include "soundio.h"
|
||||
#include "util.hpp"
|
||||
#include "atomics.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
@ -18,6 +19,16 @@
|
|||
#include <errno.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__
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
|
@ -39,13 +50,27 @@ struct SoundIoOsMutex {
|
|||
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 {
|
||||
pthread_cond_t id;
|
||||
bool id_init;
|
||||
|
||||
pthread_condattr_t attr;
|
||||
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) {
|
||||
#ifdef __MACH__
|
||||
|
@ -207,6 +232,9 @@ struct SoundIoOsCond * soundio_os_cond_create(void) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SOUNDIO_OS_KQUEUE
|
||||
cond->notify_ident = next_notify_ident.fetch_add(1);
|
||||
#else
|
||||
if (pthread_condattr_init(&cond->attr)) {
|
||||
soundio_os_cond_destroy(cond);
|
||||
return NULL;
|
||||
|
@ -224,6 +252,13 @@ struct SoundIoOsCond * soundio_os_cond_create(void) {
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -231,6 +266,8 @@ void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
|
|||
if (!cond)
|
||||
return;
|
||||
|
||||
#ifdef SOUNDIO_OS_KQUEUE
|
||||
#else
|
||||
if (cond->id_init) {
|
||||
assert_no_err(pthread_cond_destroy(&cond->id));
|
||||
}
|
||||
|
@ -238,37 +275,139 @@ void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
|
|||
if (cond->attr_init) {
|
||||
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);
|
||||
}
|
||||
|
||||
void soundio_os_cond_signal(struct SoundIoOsCond *cond) {
|
||||
assert_no_err(pthread_cond_signal(&cond->id));
|
||||
}
|
||||
void soundio_os_cond_signal(struct SoundIoOsCond *cond,
|
||||
struct SoundIoOsMutex *locked_mutex)
|
||||
{
|
||||
#ifdef SOUNDIO_OS_KQUEUE
|
||||
struct kevent kev;
|
||||
struct timespec timeout = { 0, 0 };
|
||||
|
||||
void soundio_os_cond_broadcast(struct SoundIoOsCond *cond) {
|
||||
assert_no_err(pthread_cond_broadcast(&cond->id));
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
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,
|
||||
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;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tms);
|
||||
tms.tv_nsec += (seconds * 1000000000L);
|
||||
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 != EINVAL);
|
||||
}
|
||||
if (!locked_mutex)
|
||||
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
if ((err = pthread_cond_wait(&cond->id, &mutex->id))) {
|
||||
if ((err = pthread_cond_wait(&cond->id, target_mutex))) {
|
||||
assert(err != EPERM);
|
||||
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>
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
struct SoundIoOsThread;
|
||||
|
@ -30,12 +34,17 @@ void soundio_os_mutex_unlock(struct SoundIoOsMutex *mutex);
|
|||
struct SoundIoOsCond;
|
||||
struct SoundIoOsCond * soundio_os_cond_create(void);
|
||||
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,
|
||||
struct SoundIoOsMutex *mutex, double seconds);
|
||||
struct SoundIoOsMutex *locked_mutex, double seconds);
|
||||
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
||||
struct SoundIoOsMutex *mutex);
|
||||
struct SoundIoOsMutex *locked_mutex);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#include "pulseaudio.hpp"
|
||||
#include "soundio.hpp"
|
||||
#include "atomics.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <atomic>
|
||||
using std::atomic_bool;
|
||||
|
||||
struct SoundIoOutputDevicePulseAudio {
|
||||
pa_stream *stream;
|
||||
|
|
Loading…
Reference in a new issue