openMSX
EventDistributor.cc
Go to the documentation of this file.
1#include "EventDistributor.hh"
2#include "EventListener.hh"
3#include "Reactor.hh"
4#include "RTScheduler.hh"
5#include "Interpreter.hh"
7#include "Thread.hh"
8#include "ranges.hh"
9#include "stl.hh"
10#include <cassert>
11#include <chrono>
12
13namespace openmsx {
14
16 : reactor(reactor_)
17{
18}
19
21 EventType type, EventListener& listener, Priority priority)
22{
23 std::scoped_lock lock(mutex);
24 auto& priorityMap = listeners[size_t(type)];
25 // a listener may only be registered once for each type
26 assert(!contains(priorityMap, &listener, &Entry::listener));
27 // insert at highest position that keeps listeners sorted on priority
28 auto it = ranges::upper_bound(priorityMap, priority, {}, &Entry::priority);
29 priorityMap.emplace(it, Entry{priority, &listener});
30}
31
33 EventType type, EventListener& listener)
34{
35 std::scoped_lock lock(mutex);
36 auto& priorityMap = listeners[size_t(type)];
37 priorityMap.erase(rfind_unguarded(priorityMap, &listener, &Entry::listener));
38}
39
41{
42 // TODO: Implement a real solution against modifying data structure while
43 // iterating through it.
44 // For example, assign nullptr first and then iterate again after
45 // delivering events to remove the nullptr values.
46 // TODO: Is it useful to test for 0 listeners or should we just always
47 // queue the event?
48 std::unique_lock lock(mutex);
49 if (!listeners[size_t(getType(event))].empty()) {
50 scheduledEvents.push_back(std::move(event));
51 // must release lock, otherwise there's a deadlock:
52 // thread 1: Reactor::deleteMotherBoard()
53 // EventDistributor::unregisterEventListener()
54 // thread 2: EventDistributor::distributeEvent()
55 // Reactor::enterMainLoop()
56 condition.notify_all();
57 lock.unlock();
58 reactor.enterMainLoop();
59 }
60}
61
62bool EventDistributor::isRegistered(EventType type, EventListener* listener) const
63{
64 return contains(listeners[size_t(type)], listener, &Entry::listener);
65}
66
68{
69 static PriorityMap priorityMapCopy; // static to preserve capacity
70 static EventQueue eventsCopy; // static to preserve capacity
71
72 assert(Thread::isMainThread());
73
74 reactor.getInputEventGenerator().poll();
75 reactor.getInterpreter().poll();
76 reactor.getRTScheduler().execute();
77
78 std::unique_lock lock(mutex);
79 // It's possible that executing an event triggers scheduling of another
80 // event. We also want to execute those secondary events. That's why
81 // we have this while loop here.
82 // For example the 'loadstate' command event, triggers a machine switch
83 // event and as reaction to the latter event, AfterCommand will
84 // unsubscribe from the ols MSXEventDistributor. This really should be
85 // done before we exit this method.
86 while (!scheduledEvents.empty()) {
87 assert(eventsCopy.empty());
88 swap(eventsCopy, scheduledEvents);
89 for (const auto& event : eventsCopy) {
90 auto type = getType(event);
91 priorityMapCopy = listeners[size_t(type)];
92 lock.unlock();
93 auto allowPriority = Priority::LOWEST; // allow all
94 for (const auto& e : priorityMapCopy) {
95 // It's possible delivery to one of the previous
96 // Listeners unregistered the current Listener.
97 if (!isRegistered(type, e.listener)) continue;
98
99 if (e.priority > allowPriority) break;
100
101 // This might throw, e.g. when failing to initialize video system
102 if (e.listener->signalEvent(event)) {
103 allowPriority = e.priority;
104 }
105 }
106 lock.lock();
107 }
108 eventsCopy.clear();
109 }
110}
111
112bool EventDistributor::sleep(unsigned us)
113{
114 std::chrono::microseconds duration(us);
115 std::unique_lock lock(cvMutex);
116 return condition.wait_for(lock, duration) == std::cv_status::timeout;
117}
118
119} // namespace openmsx
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
EventDistributor(Reactor &reactor)
bool sleep(unsigned us)
Sleep for the specified amount of time, but return early when (another thread) called the distributeE...
void deliverEvents()
This actually delivers the events.
void registerEventListener(EventType type, EventListener &listener, Priority priority=Priority::OTHER)
Registers a given object to receive certain events.
Priority
Priorities from high to low, higher priority listeners can block events for lower priority listeners.
void execute()
Execute all expired RTSchedulables.
Contains the main loop of openMSX.
Definition Reactor.hh:75
void enterMainLoop()
Definition Reactor.cc:520
RTScheduler & getRTScheduler()
Definition Reactor.hh:88
Interpreter & getInterpreter()
Definition Reactor.cc:328
InputEventGenerator & getInputEventGenerator()
Definition Reactor.hh:92
bool isMainThread()
Returns true when called from the main thread.
Definition Thread.cc:15
This file implemented 3 utility functions:
Definition Autofire.cc:11
EventType
Definition Event.hh:454
EventType getType(const Event &event)
Definition Event.hh:517
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:445
auto upper_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition ranges.hh:126
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition stl.hh:109
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:32