C++ standard only interrupts implementation
This commit is contained in:
parent
e26474a2d7
commit
2d61896824
@ -430,7 +430,9 @@ static Result<Value> builtin_par(Interpreter &interpreter, std::vector<Value> ar
|
||||
|
||||
for (auto const& note : chord->notes) {
|
||||
if (note.base) {
|
||||
interpreter.current_context->port->send_note_on(0, *note.into_midi_note(), 127);
|
||||
auto const n = *note.into_midi_note();
|
||||
interpreter.current_context->port->send_note_on(0, n, 127);
|
||||
interpreter.active_notes.insert({ 0, n });
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +440,9 @@ static Result<Value> builtin_par(Interpreter &interpreter, std::vector<Value> ar
|
||||
|
||||
for (auto const& note : chord->notes) {
|
||||
if (note.base) {
|
||||
interpreter.current_context->port->send_note_off(0, *note.into_midi_note(), 127);
|
||||
auto const n = *note.into_midi_note();
|
||||
interpreter.current_context->port->send_note_off(0, n, 127);
|
||||
interpreter.active_notes.erase({ 0, n });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -548,12 +552,16 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
|
||||
for (auto const& instruction : schedule) {
|
||||
auto const dur = ctx.length_to_duration({instruction.when});
|
||||
if (start_time < dur) {
|
||||
std::this_thread::sleep_for(dur - start_time);
|
||||
interpreter.sleep(dur - start_time);
|
||||
start_time = dur;
|
||||
}
|
||||
switch (instruction.action) {
|
||||
break; case Instruction::On: interpreter.current_context->port->send_note_on(0, instruction.note, 127);
|
||||
break; case Instruction::Off: interpreter.current_context->port->send_note_off(0, instruction.note, 127);
|
||||
break; case Instruction::On:
|
||||
interpreter.current_context->port->send_note_on(0, instruction.note, 127);
|
||||
interpreter.active_notes.insert({ 0, instruction.note });
|
||||
break; case Instruction::Off:
|
||||
interpreter.current_context->port->send_note_off(0, instruction.note, 127);
|
||||
interpreter.active_notes.erase({ 0, instruction.note });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
std::unordered_map<std::string, Intrinsic> Interpreter::operators {};
|
||||
|
||||
@ -53,6 +55,8 @@ Interpreter::~Interpreter()
|
||||
|
||||
Result<Value> Interpreter::eval(Ast &&ast)
|
||||
{
|
||||
handle_potential_interrupt();
|
||||
|
||||
switch (ast.type) {
|
||||
case Ast::Type::Literal:
|
||||
switch (ast.token.type) {
|
||||
@ -248,8 +252,10 @@ std::optional<Error> Interpreter::play(Chord chord)
|
||||
Try(ensure_midi_connection_available(*this, "play"));
|
||||
auto &ctx = *current_context;
|
||||
|
||||
handle_potential_interrupt();
|
||||
|
||||
if (chord.notes.size() == 0) {
|
||||
std::this_thread::sleep_for(ctx.length_to_duration(ctx.length));
|
||||
sleep(ctx.length_to_duration(ctx.length));
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -273,7 +279,7 @@ std::optional<Error> Interpreter::play(Chord chord)
|
||||
for (auto const& note : chord.notes) {
|
||||
if (max_time != Number(0)) {
|
||||
max_time -= *note.length;
|
||||
std::this_thread::sleep_for(ctx.length_to_duration(*note.length));
|
||||
sleep(ctx.length_to_duration(*note.length));
|
||||
}
|
||||
if (note.base) {
|
||||
current_context->port->send_note_off(0, *note.into_midi_note(), 127);
|
||||
@ -442,3 +448,31 @@ void Interpreter::snapshot(std::ostream& out)
|
||||
}
|
||||
out << std::flush;
|
||||
}
|
||||
|
||||
// TODO This only supports single-threaded interpreter execution
|
||||
static std::atomic<bool> interrupted = false;
|
||||
static std::condition_variable condvar;
|
||||
static std::mutex mu;
|
||||
|
||||
void Interpreter::handle_potential_interrupt()
|
||||
{
|
||||
if (interrupted) {
|
||||
interrupted = false;
|
||||
throw KeyboardInterrupt{};
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::issue_interrupt()
|
||||
{
|
||||
interrupted = true;
|
||||
condvar.notify_all();
|
||||
}
|
||||
|
||||
void Interpreter::sleep(std::chrono::duration<float> time)
|
||||
{
|
||||
if (std::unique_lock lock(mu); condvar.wait_for(lock, time) == std::cv_status::no_timeout) {
|
||||
ensure(interrupted, "Only interruption can result in quiting conditional variable without timeout");
|
||||
interrupted = false;
|
||||
throw KeyboardInterrupt{};
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,12 @@
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
struct KeyboardInterrupt : std::exception
|
||||
{
|
||||
~KeyboardInterrupt() = default;
|
||||
char const* what() const noexcept override { return "KeyboardInterrupt"; }
|
||||
};
|
||||
|
||||
/// Given program tree evaluates it into Value
|
||||
struct Interpreter
|
||||
{
|
||||
@ -25,7 +31,6 @@ struct Interpreter
|
||||
|
||||
std::multiset<std::pair<unsigned, unsigned>> active_notes;
|
||||
|
||||
|
||||
Starter starter;
|
||||
|
||||
Interpreter();
|
||||
@ -58,10 +63,19 @@ struct Interpreter
|
||||
/// Dumps snapshot of interpreter into stream
|
||||
void snapshot(std::ostream& out);
|
||||
|
||||
/// Turn all notes that have been played but don't finished playing
|
||||
void turn_off_all_active_notes();
|
||||
|
||||
/// Handles interrupt if any occured
|
||||
void handle_potential_interrupt();
|
||||
|
||||
/// Issue new interrupt
|
||||
void issue_interrupt();
|
||||
|
||||
/// Sleep for at least given time or until interrupt
|
||||
void sleep(std::chrono::duration<float>);
|
||||
};
|
||||
|
||||
std::optional<Error> ensure_midi_connection_available(Interpreter&, std::string_view operation_name);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -38,12 +38,6 @@ static unsigned repl_line_number = 1;
|
||||
|
||||
#define Ignore(Call) do { auto const ignore_ ## __LINE__ = (Call); (void) ignore_ ## __LINE__; } while(0)
|
||||
|
||||
struct KeyboardInterrupt : std::exception
|
||||
{
|
||||
~KeyboardInterrupt() = default;
|
||||
char const* what() const noexcept override { return "KeyboardInterrupt"; }
|
||||
};
|
||||
|
||||
|
||||
/// Pop string from front of an array
|
||||
template<typename T = std::string_view>
|
||||
@ -437,33 +431,8 @@ static std::optional<Error> Main(std::span<char const*> args)
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
{
|
||||
static auto thread = pthread_self();
|
||||
static auto thread_id = std::this_thread::get_id();
|
||||
|
||||
// TODO IS this a good solution?
|
||||
static auto *handler = +[](int sig, siginfo_t*, void*) {
|
||||
struct sigaction sa;
|
||||
sigaction(SIGINT, nullptr, &sa);
|
||||
pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, nullptr);
|
||||
if (thread_id == std::this_thread::get_id()) {
|
||||
if (sig == SIGINT) {
|
||||
throw KeyboardInterrupt{};
|
||||
}
|
||||
} else {
|
||||
pthread_kill(thread, SIGINT);
|
||||
}
|
||||
};
|
||||
|
||||
struct sigaction sa = {};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGINT);
|
||||
sa.sa_sigaction = handler;
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
}
|
||||
|
||||
Runner runner;
|
||||
static Runner runner;
|
||||
std::signal(SIGINT, [](int sig) { if (sig == SIGINT) runner.interpreter.issue_interrupt(); });
|
||||
|
||||
for (auto const& [type, argument] : runnables) {
|
||||
if (type == Run::Argument) {
|
||||
|
Loading…
Reference in New Issue
Block a user