FlowLookahead

class FlowLookahead(name: String, manager: Manager, flow: Flow) extends FlowSimulation with LookingAhead

A trait which provides Lookahead compatibility to FlowSimulations.

Works by parsing the entire flow at the start of the simulation to build a Lookahead automatically.

class Object
trait Matchable
class Any

Document{}

type Callback = Try[(TaskInstance, Long)] => Unit

The type of the callback function.

The type of the callback function.

Its input consists of the generated TaskInstance and the timestamp when it was completed. A Failure input corresponds to an exception happening or an aborted task.

Inherited from
AsyncSimulation
override
def abort(ids: UUID*): Unit

Declare IDs of TaskInstances that need to be aborted.

Declare IDs of TaskInstances that need to be aborted.

Calls respective Callbacks with Failure.

Value Params
id

The UUID of the Tasks.

Definition Classes
Inherited from
AsyncSimulation
def ack(taskIDs: Seq[UUID]): Unit

Declares that the simulation has finished processing one or more completed TaskInstances.

Declares that the simulation has finished processing one or more completed TaskInstances.

Identifies the tasks via their UUID.

Inherited from
Simulation
def done(result: Try[Any]): Unit

Declares that the simulation completed.

Declares that the simulation completed.

Value Params
result

The result of the simulation.

Inherited from
Simulation
def fail(exception: Throwable): Unit

Declares that the simulation has failed or has been aborted.

Declares that the simulation has failed or has been aborted.

Value Params
exception

The Throwable that caused the failure.

Inherited from
Simulation
def ready(): Unit

Declares that the simulation has finished calculating and is ready for virtual time to proceed.

Declares that the simulation has finished calculating and is ready for virtual time to proceed.

Inherited from
Simulation
def simWait(): Unit

Requests that the Manager waits for this simulation before it continues.

Requests that the Manager waits for this simulation before it continues.

The simulation needs to either register more tasks and become ready or finish.

Note

We assume the Manager is already waiting for another simulation when the request is made. Otherwise virtual time may progress unexpectedly and cause unpredictable behaviour depending on the timing of the Manager messages.

Inherited from
Simulation
def succeed(result: Any): Unit

Declares that the simulation completed successfully.

Declares that the simulation completed successfully.

Value Params
result

The successful result of the simulation.

Inherited from
Simulation
def task(t: Task): Unit

Declare a new Task that needs to be sent for simulation.

Declare a new Task that needs to be sent for simulation.

Value Params
t

The Task to send.

Inherited from
Simulation
def task(t: Task, callback: Callback): Unit

Declare a new Task that needs to be sent to the Coordinator for simulation with a pre-determined ID.

Declare a new Task that needs to be sent to the Coordinator for simulation with a pre-determined ID.

The provided callback function will be called when the corresponding Task is completed.

When it finishes executing, it must notify the Coordinator either by acknowledging the completed task using ack or by completing the simulation using done, succeed or fail.

The ready method can also be called if there is no need to acknowledge completed tasks individually. This is unlikely in the current scenario where each task has its own callback, but it's still worth mentioning.

Value Params
callback

The Callback function to be called when the corresponding Task completes.

t

The Task to send.

Inherited from
AsyncSimulation
protected
val tasks: Map[UUID, Callback]

A map of TaskInstance IDs that have been sent to the Manager and the callback functions that need to be called when the tasks complete.

A map of TaskInstance IDs that have been sent to the Manager and the callback functions that need to be called when the tasks complete.

Inherited from
AsyncSimulation

Type members

Types

type IDFunction = Map[UUID, Long] => Option[Long]

Value members

Concrete methods

protected
def parseFlow(flow: Flow, extraFunction: Option[IDFunction], lookaheadStructure: Lookahead): (IDFunction, Lookahead)

Parses a flow in order to build a Lookahead

Parses a flow in order to build a Lookahead

This is a recursive function which uses the fact that Flows are tree-like structures. Each iteration it calls parseFlow on each of its children and then combines the results in a meaningful way. The goal is to build up a Lookahead by combining the structures of each branch. There is also this IDFunction which is passed up and down the tree as the algorithm progresses, which is used to describe the function that will be used to add entries to the lookahead structure; This function can grow/shrink over time as the algorithm progresses, but it is always used to describe the most current prerequisites of any task that it might meet.

Value Params
extraFunction

an optional function which describes the current preconditions for lookahead (if any). @see Lookahead.+

flow

The flow to be parsed

lookaheadStructure

The Lookahead built so far.

Returns

A function that describes the precondition of this node, and the current lookahead structure.

See also

Lookahead.+ for details on the IDfunction itself. As it works through the flow tree, the algorithm does the following:

  1. If the current node is a task (i.e. a leaf node), it should be registered by adding itself to the current running lookahead structure. To do this it uses the IDFunction that it received. If no ID function was provided, this means that there are no prior requirements to starting this task and so it is not registered.
  2. If the current node is a Then, first the left branch is parsed, and then the right branch is parsed. The preconditions (i.e. the IDFunction) of the left branch is the same as the preconditions of this node, however the preconditions of the right branch are that which is returned by the left branch (as opposed to the preconditions of this Then node). This is because, by our definition, the right branch happens after the left branch, and hence the preconditions of the right branch is that the left branch is completed.
  3. If the current node is an And, or Or node, it parses all its child nodes normally, since the child branches are independent. Then, depending on the node type, it returns a function and a lookahead structure. The lookahead structure contains the current lookahead entries, and may be combined with other structures later on. The IDFunction will be used by other nodes to register new lookahead entries, where it will describe the preconditions of that entry; As such, the function should express if this node is "done", as this is necessary in determining if some task in the future may start.
  4. If our current node is a task (leaf node), then we create a new function; The function will return None if this task is not in the map which it receives as input, and Some(value) otherwise. The result of this is that when the function will receive a list of completed tasks, if this task is on the list then the function will indicate that the prerequires are met, and hence (when combined with other functions from other nodes) this function can be used to describe the prerequisites of future tasks.
  5. If our current node is a Then, we only need to return the IDFunction of the right branch, since the left branch was already considered when parsing the right branch. For example, if you had a flow [A THEN B THEN C], we want to express that A is a prerequisite of B, and B is a prerequisite of C. It is unnecessary to say that A is a prerequisite of C since it is already a prerequisite of B.
  6. If our current node is an And, we need to combine the functions of all the child nodes. If all the child nodes are "complete" then this node is complete, hence if any child function returns None, this function should also return None. Otherwise, the maximum value of the child functions is returned, since tasks that come after an And may only begin once all of the previous tasks have finished.
  7. If our current node is an Or, the child node functions are also combined in a similar way to Ands, but this time we return the minimum value of the child functions, since any task that comes after an Or may begin as soon as any of the branches in an Or are completed. It might be easier to picture this algorithim when given some examples. Please refer to the wiki for examples and more detailed explanations.
override
def run(): Unit

Initiates the execution of the simulation. Parses the flow before running it.

Initiates the execution of the simulation. Parses the flow before running it.

Returns

A Future that completes with a custom output when the simulation is completed.

Definition Classes

Inherited methods

def callback(f: (TaskInstance, Long) => Unit): Callback

Creates a simple success callback from a function.

Creates a simple success callback from a function.

Does nothing on failure.

Value Params
f

The function to call on success.

Returns

The created Callback.

Inherited from
AsyncSimulation
override
def complete(task: TaskInstance, time: Long): Unit

Manages a Task whose simulation has completed.

Manages a Task whose simulation has completed.

Removes the task from the lookahead structure and sends this updated structure to the scheduler before calling the complete method implementation of the parent class.

Value Params
task

The TaskInstance that completed.

time

The timestamp of its completion.

Definition Classes
Inherited from
LookingAhead
override
def completed(time: Long, tasks: Seq[TaskInstance]): Unit
Definition Classes
Inherited from
Simulation
override
Definition Classes
Inherited from
LookingAhead
override
def stop(): Unit

Stops/aborts the simuation.

Stops/aborts the simuation.

Triggers all callbacks with a Failure.

Definition Classes
Inherited from
AsyncSimulation

Inherited fields

val completedTasks: Set[(UUID, Long)]
Inherited from
LookingAhead
Inherited from
LookingAhead