1
Fork 0

Multi-producer/multi-consumer support in EventDispatcher's queue

With moodycamel::ConcurrentQueue.
This commit is contained in:
gvnnz 2022-08-27 13:45:43 +02:00
parent 7c3a2af804
commit 7a158681cd
6 changed files with 36 additions and 43 deletions

3
.gitmodules vendored
View File

@ -19,3 +19,6 @@
[submodule "src/deps/fltk"]
path = src/deps/fltk
url = https://github.com/fltk/fltk.git
[submodule "src/deps/concurrentqueue"]
path = src/deps/concurrentqueue
url = https://github.com/cameron314/concurrentqueue.git

View File

@ -66,16 +66,16 @@ Engine::Engine()
#ifdef WITH_AUDIO_JACK
jackSynchronizer.onJackRewind = [this]() {
eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_REWIND_JACK});
eventDispatcher.pumpEvent({EventDispatcher::EventType::SEQUENCER_REWIND_JACK});
};
jackSynchronizer.onJackChangeBpm = [this](float bpm) {
eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_BPM_JACK, 0, 0, bpm});
eventDispatcher.pumpEvent({EventDispatcher::EventType::SEQUENCER_BPM_JACK, 0, 0, bpm});
};
jackSynchronizer.onJackStart = [this]() {
eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_START_JACK});
eventDispatcher.pumpEvent({EventDispatcher::EventType::SEQUENCER_START_JACK});
};
jackSynchronizer.onJackStop = [this]() {
eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_STOP_JACK});
eventDispatcher.pumpEvent({EventDispatcher::EventType::SEQUENCER_STOP_JACK});
};
#endif
@ -99,7 +99,7 @@ Engine::Engine()
midiDispatcher.onDispatch = [this](EventDispatcher::EventType event, Action action) {
/* Notify Event Dispatcher when a MIDI signal is received. */
eventDispatcher.pumpMidiEvent({event, 0, 0, action});
eventDispatcher.pumpEvent({event, 0, 0, action});
};
midiDispatcher.onEventReceived = [this]() {
@ -111,12 +111,12 @@ Engine::Engine()
event to the Event Dispatcher, rather than invoking the callback directly.
This is done on purpose: the callback might (and surely will) contain
blocking stuff from model:: that the realtime thread cannot perform directly. */
eventDispatcher.pumpUIevent({EventDispatcher::EventType::MIXER_SIGNAL_CALLBACK});
eventDispatcher.pumpEvent({EventDispatcher::EventType::MIXER_SIGNAL_CALLBACK});
};
mixer.onEndOfRecording = [this]() {
/* Same rationale as above, for the end-of-recording callback. */
eventDispatcher.pumpUIevent({EventDispatcher::EventType::MIXER_END_OF_REC_CALLBACK});
eventDispatcher.pumpEvent({EventDispatcher::EventType::MIXER_END_OF_REC_CALLBACK});
};
channelManager.onChannelsAltered = [this]() {

View File

@ -37,6 +37,7 @@ EventDispatcher::EventDispatcher()
, onProcessSequencer(nullptr)
, onMixerSignalCallback(nullptr)
, onMixerEndOfRecCallback(nullptr)
, m_eventQueue(G_MAX_DISPATCHER_EVENTS)
{
}
@ -49,8 +50,10 @@ void EventDispatcher::start()
/* -------------------------------------------------------------------------- */
void EventDispatcher::pumpUIevent(Event e) { UIevents.push(e); }
void EventDispatcher::pumpMidiEvent(Event e) { MidiEvents.push(e); }
bool EventDispatcher::pumpEvent(const Event& e)
{
return m_eventQueue.try_enqueue(e);
}
/* -------------------------------------------------------------------------- */
@ -97,9 +100,7 @@ void EventDispatcher::process()
m_eventBuffer.clear();
Event e;
while (UIevents.pop(e))
m_eventBuffer.push_back(e);
while (MidiEvents.pop(e))
while (m_eventQueue.try_dequeue(e))
m_eventBuffer.push_back(e);
if (m_eventBuffer.size() == 0)

View File

@ -27,21 +27,21 @@
#ifndef G_EVENT_DISPATCHER_H
#define G_EVENT_DISPATCHER_H
#include "core/actions/action.h"
#include "core/const.h"
#include "core/queue.h"
#include "core/ringBuffer.h"
#include "core/types.h"
#include "core/worker.h"
#include "src/core/actions/action.h"
#include "deps/concurrentqueue/concurrentqueue.h"
#include <any>
#include <atomic>
#include <functional>
#include <thread>
/* giada::m::EventDispatcher
Takes events from the two queues (MIDI and UI) filled by c::events and turns
them into actual changes in the data model. The EventDispatcher runs in a
separate worker thread. */
Takes events from the a lock-free queue filled by c::events and turns them into
actual changes in the data model. The EventDispatcher runs in a separate worker
thread. */
namespace giada::m
{
@ -92,11 +92,9 @@ public:
};
/* EventBuffer
Alias for a RingBuffer containing events to be sent to engine. The double
size is due to the presence of two distinct Queues for collecting events
coming from other threads. See below. */
Alias for a RingBuffer containing events to be sent to engine. */
using EventBuffer = RingBuffer<Event, G_MAX_DISPATCHER_EVENTS * 2>;
using EventBuffer = RingBuffer<Event, G_MAX_DISPATCHER_EVENTS>;
EventDispatcher();
@ -105,17 +103,10 @@ public:
void start();
void pumpUIevent(Event e);
void pumpMidiEvent(Event e);
/* pumpEvent
Inserts a new event in the event queue. Returns false if the queue is full. */
/* Event queues
Collect events coming from the UI or MIDI devices. Our poor man's Queue is a
single-producer/single-consumer one, so we need two queues for two writers.
TODO - let's add a multi-producer queue sooner or later! */
/*TODO - make them private*/
Queue<Event, G_MAX_DISPATCHER_EVENTS> UIevents;
Queue<Event, G_MAX_DISPATCHER_EVENTS> MidiEvents;
bool pumpEvent(const Event&);
/* on[...]
Callbacks fired when something happens in the Event Dispatcher. */
@ -138,9 +129,14 @@ private:
/* m_eventBuffer
Buffer of events sent to channels for event parsing. This is filled with
Events coming from the two event queues.*/
Events coming from the event queue.*/
EventBuffer m_eventBuffer;
/* m_eventQueue
Collects events coming from the UI or MIDI devices. */
moodycamel::ConcurrentQueue<Event> m_eventQueue;
};
} // namespace giada::m

@ -0,0 +1 @@
Subproject commit 65d6970912fc3f6bb62d80edf95ca30e0df85137

View File

@ -66,22 +66,14 @@ namespace
{
void pushEvent_(m::EventDispatcher::Event e, Thread t)
{
bool res = true;
if (t == Thread::MAIN)
const bool res = g_engine.eventDispatcher.pumpEvent(e);
if (t == Thread::MIDI)
{
res = g_engine.eventDispatcher.UIevents.push(e);
}
else if (t == Thread::MIDI)
{
res = g_engine.eventDispatcher.MidiEvents.push(e);
u::gui::ScopedLock lock;
if (e.channelId != 0)
g_ui.mainWindow->keyboard->notifyMidiIn(e.channelId);
}
else
{
assert(false);
}
if (!res)
G_DEBUG("[events] Queue full!", );