bsnes/higan/emulator/scheduler.hpp

92 lines
2.2 KiB
C++

#pragma once
namespace Emulator {
struct Scheduler {
enum class Mode : uint {
Run,
SynchronizeMaster,
SynchronizeSlave,
};
enum class Event : uint {
Step,
Frame,
Synchronize,
};
inline auto synchronizing() const -> bool { return _mode == Mode::SynchronizeSlave; }
auto reset() -> void {
_host = co_active();
_threads.reset();
}
auto primary(Thread& thread) -> void {
_master = _resume = thread.handle();
}
auto append(Thread& thread) -> bool {
if(_threads.find(&thread)) return false;
thread._clock += _threads.size(); //this bias prioritizes threads appended earlier first
return _threads.append(&thread), true;
}
auto remove(Thread& thread) -> bool {
if(auto offset = _threads.find(&thread)) return _threads.remove(*offset), true;
return false;
}
auto enter(Mode mode = Mode::Run) -> Event {
_mode = mode;
_host = co_active();
co_switch(_resume);
return _event;
}
inline auto resume(Thread& thread) -> void {
if(_mode != Mode::SynchronizeSlave) co_switch(thread.handle());
}
auto exit(Event event) -> void {
uintmax minimum = -1;
for(auto thread : _threads) {
if(thread->_clock < minimum) minimum = thread->_clock;
}
for(auto thread : _threads) {
thread->_clock -= minimum;
}
_event = event;
_resume = co_active();
co_switch(_host);
}
inline auto synchronize(Thread& thread) -> void {
if(thread.handle() == _master) {
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
} else {
_resume = thread.handle();
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
}
}
inline auto synchronize() -> void {
if(co_active() == _master) {
if(_mode == Mode::SynchronizeMaster) return exit(Event::Synchronize);
} else {
if(_mode == Mode::SynchronizeSlave) return exit(Event::Synchronize);
}
}
private:
cothread_t _host = nullptr; //program thread (used to exit scheduler)
cothread_t _resume = nullptr; //resume thread (used to enter scheduler)
cothread_t _master = nullptr; //primary thread (used to synchronize components)
Mode _mode = Mode::Run;
Event _event = Event::Step;
vector<Thread*> _threads;
};
}