diff --git a/src/Instance.cxx b/src/Instance.cxx index 2c27b7938..b3595e995 100644 --- a/src/Instance.cxx +++ b/src/Instance.cxx @@ -40,7 +40,8 @@ #include Instance::Instance() - :idle_monitor(event_loop, BIND_THIS_METHOD(OnIdle)) + :rtio_thread(true), + idle_monitor(event_loop, BIND_THIS_METHOD(OnIdle)) { } diff --git a/src/Instance.hxx b/src/Instance.hxx index bec707219..936923d73 100644 --- a/src/Instance.hxx +++ b/src/Instance.hxx @@ -76,8 +76,19 @@ struct Instance final , public RemoteTagCacheHandler #endif { + /** + * A thread running an #EventLoop for non-blocking (bulk) I/O. + */ EventThread io_thread; + /** + * Another thread running an #EventLoop for non-blocking + * (real-time) I/O. This is used instead of #io_thread for + * events which require low latency, e.g. for filling hardware + * ring buffers. + */ + EventThread rtio_thread; + MaskMonitor idle_monitor; #ifdef ENABLE_NEIGHBOR_PLUGINS diff --git a/src/Main.cxx b/src/Main.cxx index a4baabded..f4477aa13 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -604,7 +604,7 @@ try { command_init(); for (auto &partition : instance->partitions) { - partition.outputs.Configure(instance->io_thread.GetEventLoop(), + partition.outputs.Configure(instance->rtio_thread.GetEventLoop(), config.replay_gain, partition.pc); partition.UpdateEffectiveReplayGainMode(); @@ -625,6 +625,7 @@ try { #endif instance->io_thread.Start(); + instance->rtio_thread.Start(); #ifdef ENABLE_NEIGHBOR_PLUGINS if (instance->neighbors != nullptr) @@ -736,6 +737,7 @@ try { archive_plugin_deinit_all(); #endif config_global_finish(); + instance->rtio_thread.Stop(); instance->io_thread.Stop(); #ifndef ANDROID SignalHandlersFinish(); diff --git a/src/event/Thread.cxx b/src/event/Thread.cxx index f7c2b9a0b..d67585788 100644 --- a/src/event/Thread.cxx +++ b/src/event/Thread.cxx @@ -20,6 +20,8 @@ #include "config.h" #include "Thread.hxx" #include "thread/Name.hxx" +#include "thread/Util.hxx" +#include "Log.hxx" void EventThread::Start() @@ -41,7 +43,16 @@ EventThread::Stop() noexcept void EventThread::Run() noexcept { - SetThreadName("io"); + SetThreadName(realtime ? "rtio" : "io"); + + if (realtime) { + try { + SetThreadRealtime(); + } catch (...) { + LogError(std::current_exception(), + "RTIOThread could not get realtime scheduling, continuing anyway"); + } + } event_loop.Run(); } diff --git a/src/event/Thread.hxx b/src/event/Thread.hxx index b9d2d72b5..cbc773627 100644 --- a/src/event/Thread.hxx +++ b/src/event/Thread.hxx @@ -32,9 +32,12 @@ class EventThread final { Thread thread; + const bool realtime; + public: - EventThread() - :event_loop(ThreadId::Null()), thread(BIND_THIS_METHOD(Run)) {} + explicit EventThread(bool _realtime=false) + :event_loop(ThreadId::Null()), thread(BIND_THIS_METHOD(Run)), + realtime(_realtime) {} ~EventThread() noexcept { Stop();