Skip to content
This repository has been archived by the owner on Mar 27, 2023. It is now read-only.

Commit

Permalink
added executor close #14
Browse files Browse the repository at this point in the history
  • Loading branch information
V3lop5 committed Apr 5, 2021
1 parent dd8dfb2 commit 6305141
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 54 deletions.
98 changes: 98 additions & 0 deletions src/instructionset/Executor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.adclock.instructionset

import com.adclock.model.ClockWall

class Executor(val wall: ClockWall) {

private val queue = mutableListOf<Task>()
private val stack = mutableListOf<StackEntry>()
private var delayUntil: Long = 0


val active: Boolean
get() = stack.isNotEmpty()

val pendingTasks: Boolean
get() = queue.isNotEmpty()


fun queueTask(task: Task) {
queue += task
if (!active)
startNextQueuedTask()
}

fun executeStep() {
if (!active)
throw IllegalStateException("Not active. Can not execute next step.")

if (delayUntil > System.currentTimeMillis())
return

stack.last().run {
if (currentInstruction.apply(this@Executor, wall))
current++

if (completed)
removeLatestTaskFromStack()
}
}

private fun removeLatestTaskFromStack() {
stack.removeLast()
if (stack.isNotEmpty()) {
stack.last().current++ // Subcall is complete.

// if previous task is complete remove from stack to
if (stack.last().completed)
return removeLatestTaskFromStack()
}

// Check if pending task exists and start it
if (!active && pendingTasks)
startNextQueuedTask()
}

private fun startNextQueuedTask() {
if (active)
throw IllegalStateException("Task already active. Can not run tasks in parallel.")

if (!pendingTasks)
throw IllegalStateException("No task queued.")

startSubTask(queue.removeFirst())
}

internal fun startSubTask(task: Task) {
if (task.instructions.isEmpty())
throw IllegalArgumentException("The Task ${task.name} has no instructions.")

if (stack.any { it.task.name == task.name })
throw IllegalArgumentException("Loop detected. Can not start task ${task.name} because its already in program stack.")

stack += StackEntry(task)
}

/**
* Restarts the execution.
* There are two modes:
* - restart current task = restarts only current executed task
* - restart from begin = clear hole program stack and restarts very first task
*
* @param currentTask Boolean
*/
internal fun restartTask(currentTask: Boolean = false) {
if (!active)
throw IllegalStateException("No Task active. Can not restart current task because there is no one.")

// Remove all Stack Entries. Restart first task
while (!currentTask && stack.size > 1)
stack.removeLast()

stack.last().current = 0
}

internal fun delay(delay: Int) {
delayUntil = System.currentTimeMillis() + delay * 1000
}
}
12 changes: 12 additions & 0 deletions src/instructionset/StackEntry.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.adclock.instructionset

import com.adclock.instructionset.instructions.Instruction

data class StackEntry(val task: Task, var current: Int = 0) {

val completed: Boolean
get() = task.instructions.size == current

val currentInstruction: Instruction
get() = task.instructions[current]
}
44 changes: 1 addition & 43 deletions src/instructionset/Task.kt
Original file line number Diff line number Diff line change
@@ -1,47 +1,5 @@
package com.adclock.instructionset

import com.adclock.instructionset.instructions.Instruction
import com.adclock.model.ClockWall

data class Task(val name: String, val instructions: MutableList<Instruction> = mutableListOf()) {
private var current: Int = 0
var sleepUntil: Long = 0
internal set

fun isCompleted() = current >= instructions.size

fun apply(wall: ClockWall, preview: Boolean = false) {
if (isCompleted())
throw IllegalStateException("This task is already completed. Can't perform more steps.")

if (sleepUntil > System.currentTimeMillis() && !preview)
return

if (applyInstruction(wall, current(), preview))
current++
}

fun current() = instructions[current]

internal fun restart() {
current = 0
}

/**
* Apply instruction to wall.
* In normal Mode just execute once.
* In preview Mode execute until a significant step was made.
*
* @param wall ClockWall
* @param instruction Instruction
* @param preview Boolean
* @return Boolean
*/
private fun applyInstruction(wall: ClockWall, instruction: Instruction, preview: Boolean): Boolean {
var stepMade: Boolean
do {
stepMade = instruction.apply(this, wall)
} while (preview && !stepMade)
return stepMade
}
}
data class Task(val name: String, val instructions: MutableList<Instruction> = mutableListOf())
4 changes: 2 additions & 2 deletions src/instructionset/instructions/Instruction.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.adclock.instructionset.instructions

import com.adclock.instructionset.Task
import com.adclock.instructionset.Executor
import com.adclock.model.ClockWall

interface Instruction {
fun apply(task: Task, wall: ClockWall): Boolean
fun apply(executor: Executor, wall: ClockWall): Boolean
}
7 changes: 4 additions & 3 deletions src/instructionset/instructions/basic/RepeatInstruction.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package com.adclock.instructionset.instructions.basic

import com.adclock.instructionset.Executor
import com.adclock.instructionset.Task
import com.adclock.instructionset.instructions.Instruction
import com.adclock.instructionset.instructions.InstructionParser
import com.adclock.model.ClockWall

class RepeatInstruction : Instruction {

override fun apply(task: Task, wall: ClockWall): Boolean {
task.restart()
return false
override fun apply(executor: Executor, wall: ClockWall): Boolean {
executor.restartTask()
return false // because program pointer already set with restartTask()
}


Expand Down
10 changes: 5 additions & 5 deletions src/instructionset/instructions/basic/SleepInstruction.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.adclock.instructionset.instructions.basic

import com.adclock.instructionset.Task
import com.adclock.instructionset.Executor
import com.adclock.instructionset.instructions.Instruction
import com.adclock.instructionset.instructions.InstructionParser
import com.adclock.model.ClockWall

class SleepInstruction(val sleep: Int) : Instruction {
class SleepInstruction(val sleepSeconds: Int) : Instruction {

override fun apply(task: Task, wall: ClockWall): Boolean {
task.sleepUntil = System.currentTimeMillis() + sleep * 1000
override fun apply(executor: Executor, wall: ClockWall): Boolean {
executor.delay(sleepSeconds)
return true
}

Expand All @@ -21,7 +21,7 @@ class SleepInstruction(val sleep: Int) : Instruction {
return SleepInstruction(input.toInt())
}

override fun serialize(instruction: SleepInstruction) = instruction.sleep.toString()
override fun serialize(instruction: SleepInstruction) = instruction.sleepSeconds.toString()
}

}
3 changes: 2 additions & 1 deletion src/instructionset/instructions/wall/WallInstruction.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.adclock.instructionset.instructions.wall

import com.adclock.instructionset.Executor
import com.adclock.instructionset.Task
import com.adclock.instructionset.instructions.Instruction
import com.adclock.model.ClockWall

interface WallInstruction : Instruction {
fun apply(wall: ClockWall)

override fun apply(task: Task, wall: ClockWall): Boolean {
override fun apply(executor: Executor, wall: ClockWall): Boolean {
apply(wall)
return true // a wall instruction is always completed. It's a single instruction
}
Expand Down

0 comments on commit 6305141

Please sign in to comment.