rt prio warning behavior is overridable

This commit is contained in:
Andrew Kelley 2015-08-26 12:54:13 -07:00
parent 195ea59209
commit c96405a091
10 changed files with 55 additions and 35 deletions

View file

@ -278,13 +278,13 @@ Then look at `html/index.html` in a browser.
block_until_have_devices block_until_have_devices
0. Integrate into libgroove and test with Groove Basin 0. Integrate into libgroove and test with Groove Basin
0. clear buffer maybe could take an argument to say how many frames to not clear 0. clear buffer maybe could take an argument to say how many frames to not clear
0. create a test for clear buffer; ensure pause/play semantics work
0. Verify that JACK xrun callback context is the same as process callback. 0. Verify that JACK xrun callback context is the same as process callback.
If not, might need to hav xrun callback set a flag and have process callback If not, might need to hav xrun callback set a flag and have process callback
call the underflow callback. call the underflow callback.
0. Create a test for pausing and resuming input and output streams. 0. Create a test for pausing and resuming input and output streams.
- Should pause/resume be callable from outside the callbacks? - Should pause/resume be callable from outside the callbacks?
- Ensure double pausing / double resuming works fine. - Ensure double pausing / double resuming works fine.
- test clearing the buffer
0. Create a test for the latency / synchronization API. 0. Create a test for the latency / synchronization API.
- Input is an audio file and some events indexed at particular frame - when - Input is an audio file and some events indexed at particular frame - when
listening the events should line up exactly with a beat or visual listening the events should line up exactly with a beat or visual
@ -297,17 +297,13 @@ Then look at `html/index.html` in a browser.
call lock and then unlock when done. call lock and then unlock when done.
0. add len arguments to APIs that have char * 0. add len arguments to APIs that have char *
- replace strdup with `soundio_str_dupe` - replace strdup with `soundio_str_dupe`
0. Support PulseAudio proplist properties for main context and streams
0. Expose JACK options in `jack_client_open`
0. mlock memory which is accessed in the real time path
0. make rtprio warning a callback and have existing behavior be the default callback
0. write detailed docs on buffer underflows explaining when they occur, what state 0. write detailed docs on buffer underflows explaining when they occur, what state
changes are related to them, and how to recover from them. changes are related to them, and how to recover from them.
0. Consider testing on FreeBSD 0. Consider testing on FreeBSD
0. In ALSA do we need to wake up the poll when destroying the in or out stream? 0. In ALSA do we need to wake up the poll when destroying the in or out stream?
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`. 0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
0. Add [sndio](http://www.sndio.org/) backend to support OpenBSD.
0. Custom allocator support 0. Custom allocator support
- default allocator mlock memory
0. Support for stream icon. 0. Support for stream icon.
- PulseAudio: XDG icon name - PulseAudio: XDG icon name
- WASAPI: path to .exe, .dll, or .ico - WASAPI: path to .exe, .dll, or .ico

View file

@ -314,12 +314,23 @@ struct SoundIo {
/// variable to wake up. Called when ::soundio_wait_events would be woken up. /// variable to wake up. Called when ::soundio_wait_events would be woken up.
void (*on_events_signal)(struct SoundIo *); void (*on_events_signal)(struct SoundIo *);
/// Read-only. After calling ::soundio_connect or ::soundio_connect_backend,
/// this field tells which backend is currently connected.
enum SoundIoBackend current_backend;
/// Optional: Application name. /// Optional: Application name.
/// PulseAudio uses this for "application name". /// PulseAudio uses this for "application name".
/// JACK uses this for `client_name`. /// JACK uses this for `client_name`.
/// Must not contain a colon (":"). /// Must not contain a colon (":").
const char *app_name; const char *app_name;
/// Optional: Real time priority warning.
/// This callback is fired when making thread real-time priority failed. By
/// default, it will print to stderr only the first time it is called
/// a message instructing the user how to configure their system to allow
/// real-time priority threads.
void (*emit_rtprio_warning)(void);
/// Optional: JACK info callback. /// Optional: JACK info callback.
/// By default, libsoundio sets this to an empty function in order to /// By default, libsoundio sets this to an empty function in order to
/// silence stdio messages from JACK. You may override the behavior by /// silence stdio messages from JACK. You may override the behavior by
@ -330,10 +341,6 @@ struct SoundIo {
/// Optional: JACK error callback. /// Optional: JACK error callback.
/// See SoundIo::jack_info_callback /// See SoundIo::jack_info_callback
void (*jack_error_callback)(const char *msg); void (*jack_error_callback)(const char *msg);
/// Read-only. After calling ::soundio_connect or ::soundio_connect_backend,
/// this field tells which backend is currently connected.
enum SoundIoBackend current_backend;
}; };
/// The size of this struct is not part of the API or ABI. /// The size of this struct is not part of the API or ABI.

View file

@ -1267,12 +1267,13 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa; SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
SoundIo *soundio = &si->pub;
assert(!osa->thread); assert(!osa->thread);
int err; int err;
osa->thread_exit_flag.test_and_set(); osa->thread_exit_flag.test_and_set();
if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osa->thread))) if ((err = soundio_os_thread_create(outstream_thread_run, os, soundio->emit_rtprio_warning, &osa->thread)))
return err; return err;
return 0; return 0;
@ -1538,12 +1539,13 @@ static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamAlsa *isa = &is->backend_data.alsa; SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
SoundIo *soundio = &si->pub;
assert(!isa->thread); assert(!isa->thread);
isa->thread_exit_flag.test_and_set(); isa->thread_exit_flag.test_and_set();
int err; int err;
if ((err = soundio_os_thread_create(instream_thread_run, is, true, &isa->thread))) { if ((err = soundio_os_thread_create(instream_thread_run, is, soundio->emit_rtprio_warning, &isa->thread))) {
instream_destroy_alsa(si, is); instream_destroy_alsa(si, is);
return err; return err;
} }
@ -1706,7 +1708,7 @@ int soundio_alsa_init(SoundIoPrivate *si) {
wakeup_device_poll(sia); wakeup_device_poll(sia);
if ((err = soundio_os_thread_create(device_thread_run, si, false, &sia->thread))) { if ((err = soundio_os_thread_create(device_thread_run, si, nullptr, &sia->thread))) {
destroy_alsa(si); destroy_alsa(si);
return err; return err;
} }

View file

@ -1360,7 +1360,7 @@ int soundio_coreaudio_init(SoundIoPrivate *si) {
return SoundIoErrorSystemResources; return SoundIoErrorSystemResources;
} }
if ((err = soundio_os_thread_create(device_thread_run, si, false, &sica->thread))) { if ((err = soundio_os_thread_create(device_thread_run, si, nullptr, &sica->thread))) {
destroy_ca(si); destroy_ca(si);
return err; return err;
} }

View file

@ -175,6 +175,7 @@ static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
SoundIoOutStreamDummy *osd = &os->backend_data.dummy; SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
SoundIo *soundio = &si->pub;
if (pause) { if (pause) {
if (osd->thread) { if (osd->thread) {
osd->abort_flag.clear(); osd->abort_flag.clear();
@ -186,7 +187,9 @@ static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStr
if (!osd->thread) { if (!osd->thread) {
osd->abort_flag.test_and_set(); osd->abort_flag.test_and_set();
int err; int err;
if ((err = soundio_os_thread_create(playback_thread_run, os, true, &osd->thread))) { if ((err = soundio_os_thread_create(playback_thread_run, os,
soundio->emit_rtprio_warning, &osd->thread)))
{
return err; return err;
} }
} }
@ -281,6 +284,7 @@ static int instream_open_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) { static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) {
SoundIoInStreamDummy *isd = &is->backend_data.dummy; SoundIoInStreamDummy *isd = &is->backend_data.dummy;
SoundIo *soundio = &si->pub;
if (pause) { if (pause) {
if (isd->thread) { if (isd->thread) {
isd->abort_flag.clear(); isd->abort_flag.clear();
@ -292,7 +296,9 @@ static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is,
if (!isd->thread) { if (!isd->thread) {
isd->abort_flag.test_and_set(); isd->abort_flag.test_and_set();
int err; int err;
if ((err = soundio_os_thread_create(capture_thread_run, is, true, &isd->thread))) { if ((err = soundio_os_thread_create(capture_thread_run, is,
soundio->emit_rtprio_warning, &isd->thread)))
{
return err; return err;
} }
} }

View file

@ -15,7 +15,6 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <math.h> #include <math.h>
#if defined(_WIN32) #if defined(_WIN32)
@ -180,18 +179,10 @@ static void *run_pthread(void *userdata) {
} }
#endif #endif
static atomic_flag rtprio_seen = ATOMIC_FLAG_INIT;
static void emit_rtprio_warning(void) {
if (!rtprio_seen.test_and_set()) {
fprintf(stderr, "warning: unable to set high priority thread: Operation not permitted\n");
fprintf(stderr, "See https://github.com/andrewrk/genesis/wiki/"
"warning:-unable-to-set-high-priority-thread:-Operation-not-permitted\n");
}
}
int soundio_os_thread_create( int soundio_os_thread_create(
void (*run)(void *arg), void *arg, void (*run)(void *arg), void *arg,
bool high_priority, struct SoundIoOsThread ** out_thread) void (*emit_rtprio_warning)(void),
struct SoundIoOsThread ** out_thread)
{ {
*out_thread = NULL; *out_thread = NULL;
@ -210,7 +201,7 @@ int soundio_os_thread_create(
soundio_os_thread_destroy(thread); soundio_os_thread_destroy(thread);
return SoundIoErrorSystemResources; return SoundIoErrorSystemResources;
} }
if (high_priority) { if (emit_rtprio_warning) {
if (!SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL)) { if (!SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL)) {
emit_rtprio_warning(); emit_rtprio_warning();
} }
@ -223,7 +214,7 @@ int soundio_os_thread_create(
} }
thread->attr_init = true; thread->attr_init = true;
if (high_priority) { if (emit_rtprio_warning) {
int max_priority = sched_get_priority_max(SCHED_FIFO); int max_priority = sched_get_priority_max(SCHED_FIFO);
if (max_priority == -1) { if (max_priority == -1) {
soundio_os_thread_destroy(thread); soundio_os_thread_destroy(thread);

View file

@ -21,7 +21,8 @@ double soundio_os_get_time(void);
struct SoundIoOsThread; struct SoundIoOsThread;
int soundio_os_thread_create( int soundio_os_thread_create(
void (*run)(void *arg), void *arg, void (*run)(void *arg), void *arg,
bool high_priority, struct SoundIoOsThread ** out_thread); void (*emit_rtprio_warning)(void),
struct SoundIoOsThread ** out_thread);
void soundio_os_thread_destroy(struct SoundIoOsThread *thread); void soundio_os_thread_destroy(struct SoundIoOsThread *thread);

View file

@ -12,6 +12,7 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <stdio.h>
static const SoundIoBackend available_backends[] = { static const SoundIoBackend available_backends[] = {
#ifdef SOUNDIO_HAVE_JACK #ifdef SOUNDIO_HAVE_JACK
@ -168,6 +169,15 @@ static void default_backend_disconnect_cb(struct SoundIo *, int err) {
soundio_panic("libsoundio: backend disconnected: %s", soundio_strerror(err)); soundio_panic("libsoundio: backend disconnected: %s", soundio_strerror(err));
} }
static atomic_flag rtprio_seen = ATOMIC_FLAG_INIT;
static void default_emit_rtprio_warning(void) {
if (!rtprio_seen.test_and_set()) {
fprintf(stderr, "warning: unable to set high priority thread: Operation not permitted\n");
fprintf(stderr, "See "
"https://github.com/andrewrk/genesis/wiki/warning:-unable-to-set-high-priority-thread:-Operation-not-permitted\n");
}
}
struct SoundIo *soundio_create(void) { struct SoundIo *soundio_create(void) {
int err; int err;
if ((err = soundio_os_init())) if ((err = soundio_os_init()))
@ -180,6 +190,7 @@ struct SoundIo *soundio_create(void) {
soundio->on_backend_disconnect = default_backend_disconnect_cb; soundio->on_backend_disconnect = default_backend_disconnect_cb;
soundio->on_events_signal = do_nothing_cb; soundio->on_events_signal = do_nothing_cb;
soundio->app_name = "SoundIo"; soundio->app_name = "SoundIo";
soundio->emit_rtprio_warning = default_emit_rtprio_warning;
soundio->jack_info_callback = default_msg_callback; soundio->jack_info_callback = default_msg_callback;
soundio->jack_error_callback = default_msg_callback; soundio->jack_error_callback = default_msg_callback;
return soundio; return soundio;

View file

@ -1333,6 +1333,7 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi; SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoDevice *device = outstream->device; SoundIoDevice *device = outstream->device;
SoundIo *soundio = &si->pub;
// All the COM functions are supposed to be called from the same thread. libsoundio API does not // All the COM functions are supposed to be called from the same thread. libsoundio API does not
// restrict the calling thread context in this way. Furthermore, the user might have called // restrict the calling thread context in this way. Furthermore, the user might have called
@ -1368,7 +1369,9 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
osw->thread_exit_flag.test_and_set(); osw->thread_exit_flag.test_and_set();
int err; int err;
if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osw->thread))) { if ((err = soundio_os_thread_create(outstream_thread_run, os,
soundio->emit_rtprio_warning, &osw->thread)))
{
outstream_destroy_wasapi(si, os); outstream_destroy_wasapi(si, os);
return err; return err;
} }
@ -1726,6 +1729,7 @@ static int instream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoInStrea
SoundIoInStreamWasapi *isw = &is->backend_data.wasapi; SoundIoInStreamWasapi *isw = &is->backend_data.wasapi;
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
SoundIoDevice *device = instream->device; SoundIoDevice *device = instream->device;
SoundIo *soundio = &si->pub;
// All the COM functions are supposed to be called from the same thread. libsoundio API does not // All the COM functions are supposed to be called from the same thread. libsoundio API does not
// restrict the calling thread context in this way. Furthermore, the user might have called // restrict the calling thread context in this way. Furthermore, the user might have called
@ -1761,7 +1765,9 @@ static int instream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoInStrea
isw->thread_exit_flag.test_and_set(); isw->thread_exit_flag.test_and_set();
int err; int err;
if ((err = soundio_os_thread_create(instream_thread_run, is, true, &isw->thread))) { if ((err = soundio_os_thread_create(instream_thread_run, is,
soundio->emit_rtprio_warning, &isw->thread)))
{
instream_destroy_wasapi(si, is); instream_destroy_wasapi(si, is);
return err; return err;
} }
@ -1982,7 +1988,7 @@ int soundio_wasapi_init(SoundIoPrivate *si) {
siw->device_events.lpVtbl = &soundio_MMNotificationClient; siw->device_events.lpVtbl = &soundio_MMNotificationClient;
siw->device_events_refs = 1; siw->device_events_refs = 1;
if ((err = soundio_os_thread_create(device_thread_run, si, false, &siw->thread))) { if ((err = soundio_os_thread_create(device_thread_run, si, nullptr, &siw->thread))) {
destroy_wasapi(si); destroy_wasapi(si);
return err; return err;
} }

View file

@ -145,10 +145,10 @@ static void test_ring_buffer_threaded(void) {
rb_done = false; rb_done = false;
SoundIoOsThread *reader_thread; SoundIoOsThread *reader_thread;
ok_or_panic(soundio_os_thread_create(reader_thread_run, nullptr, false, &reader_thread)); ok_or_panic(soundio_os_thread_create(reader_thread_run, nullptr, nullptr, &reader_thread));
SoundIoOsThread *writer_thread; SoundIoOsThread *writer_thread;
ok_or_panic(soundio_os_thread_create(writer_thread_run, nullptr, false, &writer_thread)); ok_or_panic(soundio_os_thread_create(writer_thread_run, nullptr, nullptr, &writer_thread));
while (rb_read_it < 100000 || rb_write_it < 100000) {} while (rb_read_it < 100000 || rb_write_it < 100000) {}
rb_done = true; rb_done = true;