Multi-producer/multi-consumer support in EventDispatcher's queue
With moodycamel::ConcurrentQueue.
This commit is contained in:
parent
7c3a2af804
commit
7a158681cd
|
@ -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
|
||||
|
|
|
@ -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]() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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!", );
|
||||
|
|
Loading…
Reference in New Issue