mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:35:29 +00:00
rt prio warning behavior is overridable
This commit is contained in:
parent
195ea59209
commit
c96405a091
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
src/os.cpp
17
src/os.cpp
|
@ -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);
|
||||||
|
|
3
src/os.h
3
src/os.h
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue