Skip to content

Commit

Permalink
Merge pull request #35 from Tetr4/triggers
Browse files Browse the repository at this point in the history
Improved Trigger API
  • Loading branch information
Mike Klimek authored Oct 6, 2016
2 parents cc5013e + 12ab14e commit eaba857
Show file tree
Hide file tree
Showing 36 changed files with 845 additions and 664 deletions.
2 changes: 1 addition & 1 deletion src/arden/CommandLineOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public interface CommandLineOptions {
boolean getCompile();

@Option(shortName = "e",
description = "Run event engine that executes MLMs when they are evoked.")
description = "Run evoke engine that runs MLMs when they are triggered.")
boolean getEngine();


Expand Down
13 changes: 7 additions & 6 deletions src/arden/EventServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import arden.runtime.ArdenTime;
import arden.runtime.ArdenEvent;
import arden.runtime.ExecutionContext;

/**
* Listens for events on a Socket. Calls
* {@link ExecutionContext#callEvent(String, ArdenTime)} on the given
* {@link ExecutionContext#callEvent(ArdenEvent)} on the given
* {@link ExecutionContext}. <br/>
* To send an event to the server (in bash):
*
Expand Down Expand Up @@ -85,12 +85,13 @@ public void run() {
InputStream eventStream = connection.getInputStream();
Scanner scanner = new Scanner(eventStream);
while (scanner.hasNext() && !Thread.currentThread().isInterrupted()) {
String event = scanner.nextLine();
String eventName = scanner.nextLine();
if (verbose) {
System.out.println("Received event: " + event);
System.out.println("Received event: " + eventName);
}
// send event to all listeners
context.callEvent(event, context.getCurrentTime());
// send event to context
ArdenEvent event = new ArdenEvent(eventName, context.getCurrentTime().value);
context.callEvent(event);
}
scanner.close();
} catch (IOException e) {
Expand Down
64 changes: 35 additions & 29 deletions src/arden/EventEngine.java → src/arden/EvokeEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,37 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import arden.runtime.ArdenEvent;
import arden.runtime.ArdenRunnable;
import arden.runtime.ArdenTime;
import arden.runtime.ArdenValue;
import arden.runtime.BaseExecutionContext;
import arden.runtime.ExecutionContext;
import arden.runtime.MedicalLogicModule;
import arden.runtime.events.EvokeEvent;
import arden.runtime.evoke.Trigger;

/**
* <p>
* The event engine waits for events and calls and invokes MLMs when they are
* scheduled. It manages delayed calls (e.g. in the actions slot) and periodic
* or delayed triggers (in the evoke slot). MLMs which are waiting for execution
* are called in the order of their priority or their urgency (action slot
* calls).
* The evoke engine waits for periodic or delayed evoke triggers (in the evoke
* slot) and delayed calls (in the action slot) and invokes MLMs when they are
* scheduled. MLMs which are waiting for execution are called in the order of
* their priority (MLMs triggered in the evoke slot) or their urgency (action
* slot calls).
* </p>
* <p>
* Threads can communicate with its scheduling loop via a message queue.
* Threads can communicate with the engines scheduling loop via a message queue.
* Messages are {@link EventCall}s or {@link MlmCall}s. EventCalls are handled
* first, then MlmCalls in order of their priority/urgency.
* first as they only add new MlmCalls to the queue, then MlmCalls are handled
* in order of their priority/urgency.
* </p>
* <p>
* MlmCalls or EventCalls may trigger other MLMs after a delay, so the engine
* uses each MLMs {@link Trigger#getNextRunTime(ExecutionContext)} method to
* check when it should run next. Delayed calls are added to the the queue after
* their delay has passed, via a {@link ScheduledExecutorService}.
* </p>
*/
public class EventEngine implements Runnable {
public class EvokeEngine implements Runnable {
private Comparator<Message> priorityComparator = new Comparator<Message>() {
@Override
public int compare(Message m1, Message m2) {
Expand All @@ -49,22 +57,21 @@ public int compare(Message m1, Message m2) {
private ExecutionContext context;
private List<MedicalLogicModule> mlms;

public EventEngine(BaseExecutionContext context, List<MedicalLogicModule> mlms) {
public EvokeEngine(BaseExecutionContext context, List<MedicalLogicModule> mlms) {
this.mlms = mlms;
this.context = context;

context.setEngine(this);

// TODO schedule first MLM for execution?
}

public void callEvent(String mapping, ArdenTime eventTime) {
public void callEvent(ArdenEvent event) {
/*
* Checking the evoke statements may require running the data slot,
* which should not run concurrent to other (possibly data changing)
* MLMs. Therefore add an EventCall to messages.
* MLMs. Therefore add an EventCall to messages, so it is run on the
* engines thread.
*/
messages.add(new EventCall(mapping, eventTime));
messages.add(new EventCall(event));
}

public void callWithDelay(ArdenRunnable mlm, ArdenValue[] arguments, int urgency, long delay) {
Expand All @@ -85,7 +92,7 @@ public void run() {

@Override
public void run() {
// initialize schedule for fixed time constant triggers
// initialize schedule for fixed time triggers
scheduleTriggers();

// the scheduling loop
Expand Down Expand Up @@ -149,16 +156,16 @@ private Schedule createSchedule(List<MedicalLogicModule> mlms) {

// put MLMs which should run at the same time into groups sorted by time
for (MedicalLogicModule mlm : mlms) {
EvokeEvent evokeEvent;
Trigger trigger;
try {
evokeEvent = mlm.getEvoke(context, null);
trigger = mlm.getTrigger(context, null);
} catch (InvocationTargetException e) {
// print error and skip this MLM
e.printStackTrace();
continue;
}

ArdenTime nextRuntime = evokeEvent.getNextRunTime(context);
ArdenTime nextRuntime = trigger.getNextRunTime(context);
if (nextRuntime == null) {
// not scheduled
continue;
Expand All @@ -184,17 +191,15 @@ public Schedule() {
}

private static interface Message extends Runnable {
// not necessarily the same as an MLMs priority
// not necessarily the same as an MLMs priority!
int getPriority();
}

private class EventCall implements Message {
String mapping;
ArdenTime eventTime;
ArdenEvent event;

public EventCall(String mapping, ArdenTime eventTime) {
this.mapping = mapping;
this.eventTime = eventTime;
public EventCall(ArdenEvent event) {
this.event = event;
}

@Override
Expand All @@ -205,18 +210,19 @@ public int getPriority() {

@Override
public void run() {
// add calls to all MLMs which should run for the event to messages
// add MlmCalls for all MLMs which should run for the event to queue
for (MedicalLogicModule mlm : mlms) {
EvokeEvent evokeEvent;
Trigger trigger;
try {
evokeEvent = mlm.getEvoke(context, null);
trigger = mlm.getTrigger(context, null);
} catch (InvocationTargetException e) {
// print error and skip this MLM
e.printStackTrace();
continue;
}

if (evokeEvent.runOnEvent(mapping, eventTime)) {
trigger.scheduleEvent(event);
if (trigger.runOnEvent(event)) {
messages.add(new MlmCall(mlm, null));
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/arden/MainClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private boolean handleCommandLineArgs(String[] args) {
return runEngine(inputFiles);
} else {
System.err.println("You should specify -r to run the files directly, "
+ "-c to compile the files or -e to start the event engine for the files.");
+ "-c to compile the files or -e to start the evoke engine for the files.");
System.err.println("Specifying files without telling what to do with them is not implemented.");
return false;
}
Expand Down Expand Up @@ -347,7 +347,7 @@ public boolean runEngine(List<File> files) {
@Override
public void run() {
if (options.getVerbose()) {
System.out.println("Shutting down event engine.");
System.out.println("Shutting down evoke engine.");
}
engineThread.interrupt();
try {
Expand All @@ -365,7 +365,7 @@ public void run() {
new EventServer(context, options.getVerbose(), options.getPort()).startServer();
}

EventEngine engine = new EventEngine(context, mlms);
EvokeEngine engine = new EvokeEngine(context, mlms);
// launch engine loop on main thread -> only exits on interrupt
engine.run();

Expand Down
8 changes: 4 additions & 4 deletions src/arden/compiler/CodeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
import arden.runtime.MaintenanceMetadata;
import arden.runtime.MedicalLogicModule;
import arden.runtime.MedicalLogicModuleImplementation;
import arden.runtime.events.EvokeEvent;
import arden.runtime.evoke.Trigger;

/**
* This class is responsible for generating the
Expand Down Expand Up @@ -266,12 +266,12 @@ public CompilerContext createLibrary() {
return new CompilerContext(this, w, 0);
}

public CompilerContext createEvokeEvent() {
public CompilerContext createTrigger() {
MethodWriter w = classFileWriter.createMethod(
"getEvokeEvent",
"getTrigger",
Modifier.PUBLIC,
new Class<?>[] { ExecutionContext.class },
EvokeEvent.class);
Trigger.class);
if (isDebuggingEnabled)
w.enableLineNumberTable();
return new CompilerContext(this, w, 1);
Expand Down
19 changes: 10 additions & 9 deletions src/arden/compiler/CompiledMlm.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import arden.runtime.MaintenanceMetadata;
import arden.runtime.MedicalLogicModule;
import arden.runtime.MedicalLogicModuleImplementation;
import arden.runtime.events.EvokeEvent;
import arden.runtime.evoke.Trigger;

/**
* Represents a compiled MedicalLogicModule with minimal Metadata (as loaded from a .class File)
Expand All @@ -66,7 +66,7 @@ public final class CompiledMlm implements MedicalLogicModule {
Class<? extends MedicalLogicModuleImplementation> clazz = null;
private MedicalLogicModuleImplementation uninitializedInstance = null;
private MedicalLogicModuleImplementation initializedInstance = null;
private EvokeEvent evokeEvent = null;
private Trigger trigger = null;
private String mlmname;

public CompiledMlm(byte[] data, String mlmname) {
Expand Down Expand Up @@ -235,19 +235,20 @@ public double getPriority() {
return getNonInitializedInstance().getPriority();
}

/** Gets an evoke event telling when to run the MLM.
* As that evoke event may depend on data set in the
* constructor, the data section of the MLM is run */
/**
* Gets a trigger telling when to run the MLM. As that trigger may depend on
* data set in the constructor, the data section of the MLM is run.
*/
@Override
public EvokeEvent getEvoke(ExecutionContext context, ArdenValue[] arguments) throws InvocationTargetException {
if (evokeEvent == null) {
public Trigger getTrigger(ExecutionContext context, ArdenValue[] arguments) throws InvocationTargetException {
if (trigger == null) {
MedicalLogicModuleImplementation instance = initializedInstance;
if (instance == null) {
instance = createInstance(context, arguments);
}
evokeEvent = instance.getEvokeEvent(context);
trigger = instance.getTrigger(context);
}
return evokeEvent;
return trigger;
}

public ArdenValue getValue(String name) {
Expand Down
14 changes: 7 additions & 7 deletions src/arden/compiler/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
import arden.runtime.MaintenanceMetadata;
import arden.runtime.MedicalLogicModule;
import arden.runtime.RuntimeHelpers;
import arden.runtime.events.EvokeEvent;
import arden.runtime.evoke.Trigger;

/**
* The main class of the compiler.
Expand Down Expand Up @@ -243,28 +243,28 @@ private void compilePriority(CodeGenerator codeGen, double priority) {
}

private void compileEvoke(CodeGenerator codeGen, PEvokeSlot evokeSlot) {
CompilerContext context = codeGen.createEvokeEvent();
CompilerContext context = codeGen.createTrigger();

// event is a keyword, thus there cannot be another field named 'event'
FieldReference eventField = context.codeGenerator.createField("event", EvokeEvent.class, Modifier.PRIVATE);
FieldReference triggerField = context.codeGenerator.createField("event", Trigger.class, Modifier.PRIVATE);

Label isNull = new Label();

context.writer.loadThis();
context.writer.loadInstanceField(eventField);
context.writer.loadInstanceField(triggerField);
context.writer.jumpIfNull(isNull);
context.writer.loadThis();
context.writer.loadInstanceField(eventField);
context.writer.loadInstanceField(triggerField);
context.writer.returnObjectFromFunction();

context.writer.mark(isNull);
evokeSlot.apply(new EvokeCompiler(context));

// the evoke compiler is supposed to leave an EvokeEvent subclass instance on the stack
// the evoke compiler is supposed to leave a Trigger subclass instance on the stack
context.writer.dup();
context.writer.loadThis();
context.writer.swap();
context.writer.storeInstanceField(eventField);
context.writer.storeInstanceField(triggerField);
context.writer.returnObjectFromFunction();
}

Expand Down
6 changes: 3 additions & 3 deletions src/arden/compiler/EventVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@

import arden.codegenerator.FieldReference;
import arden.compiler.node.TIdentifier;
import arden.runtime.events.EvokeEvent;
import arden.runtime.ArdenEvent;

final class EventVariable extends DataVariable {
private EventVariable(TIdentifier name, FieldReference field) {
super(name, field);
}

public static EventVariable getEventVariable(CodeGenerator codeGen, LeftHandSideResult lhs) {
if (!(lhs instanceof LeftHandSideIdentifier))
throw new RuntimeCompilerException(lhs.getPosition(), "EVENT variables must be simple identifiers");
Expand All @@ -46,7 +46,7 @@ public static EventVariable getEventVariable(CodeGenerator codeGen, LeftHandSide
if (variable instanceof EventVariable) {
return (EventVariable) variable;
} else {
FieldReference mlmField = codeGen.createField(ident.getText(), EvokeEvent.class, Modifier.PRIVATE);
FieldReference mlmField = codeGen.createField(ident.getText(), ArdenEvent.class, Modifier.PRIVATE);
EventVariable ev = new EventVariable(ident, mlmField);
codeGen.addVariable(ev);
return ev;
Expand Down
Loading

0 comments on commit eaba857

Please sign in to comment.