To be able to understand entities that have processes that interact
To be able model discrete-event dynamic systems using the process view
To be able build process view models using the KSL
suspend() function.resumeProcess() function provides this capability.ProcessModel.HoldQueue ClassHoldQueue holds an entity within a queue until it is removed.hold(queue) places the entity in the queue and suspends its process.HoldQueue is a subclass of Queue, so waiting statistics are collected and it can be iterated and searched.removeAllAndResume() — remove and resume all held entitiesremoveAndResume(entity) — remove and resume a specific entityQueue methods remove without resuming (you must then resume manually)HoldQueue Exampleclass HoldQExample(parent: ModelElement) : ProcessModel(parent, null) {
private val myHoldQueue: HoldQueue = HoldQueue(this, "hold")
private val myEventActionOne = EventActionOne()
private inner class Customer : Entity() {
val holdProcess: KSLProcess = process() {
hold(myHoldQueue) // enter hold queue; process suspends
delay(10.0) // resumes here after being removed
delay(20.0)
}
}
override fun initialize() {
activate(Customer().holdProcess)
activate(Customer().holdProcess, 1.0)
schedule(myEventActionOne, 5.0) // release event at time 5.0
}
private inner class EventActionOne : EventAction<Nothing>() {
override fun action(event: KSLEvent<Nothing>) {
myHoldQueue.removeAllAndResume() // remove & resume all held
}
}
}Signal ClassSignal class builds on HoldQueue: it uses a hold queue to hold entities until they are notified to move via the index of their rank in the queue.waitFor(signal) — the entity waits (suspends) for the signal.signal.signal(0) signals the first entity; signal.signal(0..4) signals a Kotlin range of ranks.HoldQueue: on a signal the entity is notified and removes itself — the user does not have to remove and resume it.ProcessModel cleans up any still-suspended entities at the end of a replication.Signal Exampleclass SignalExample(parent: ModelElement, name: String? = null) : ProcessModel(parent, name) {
private val signal = Signal(this, "SignalExample")
private inner class SignaledEntity : Entity() {
val waitForSignalProcess: KSLProcess = process {
waitFor(signal) // suspend until signaled
delay(5.0)
}
}
override fun initialize() {
for (i in 1..10) {
activate(SignaledEntity().waitForSignalProcess)
}
schedule(this::signalEvent, 3.0)
}
private fun signalEvent(event: KSLEvent<Nothing>) {
signal.signal(0..4) // signal the first 5 waiting entities
}
}0..4) are signaled and resume; the other 5 remain waiting.BlockingQueue lets two processes communicate: one sends items, another receives them.channelQ — the channel holding items placed for removal (optional capacity; default Int.MAX_VALUE)senderQ — holds entities blocked because the channel is fullreceiverQ — holds entities waiting for requested itemsBlockingQueue Example// capacity limits the channel; omit for effectively infinite
val blockingQ: BlockingQueue<QObject> = BlockingQueue(this, capacity = 10)
private inner class Receiver : Entity() {
val receiving: KSLProcess = process("receiving") {
for (i in 1..15) {
delay(1.0)
waitForItems(blockingQ, 1) // block until 1 item is available
delay(5.0)
}
}
}
private inner class Sender : Entity() {
val sending: KSLProcess = process("sending") {
for (i in 1..15) {
delay(5.0)
val item = QObject()
send(item, blockingQ) // block if the channel is full
}
}
}for loops) work inside a process.waitForItems() until the sender’s send() makes an item available; both processes then continue.waitFor(process: KSLProcess) lets one entity start another process and wait until that process runs to completion (like “run blocking”).waitFor Exampleprivate inner class Customer : Entity() {
val simpleProcess: KSLProcess = process {
wip.increment()
timeStamp = time
use(worker, delayDuration = st) // seize-delay-release combined
tip.value = time - timeStamp
wip.decrement()
}
val wfp = process {
val c = Customer()
waitFor(c.simpleProcess) // start c's process; wait for it to finish
}
}use() is a convenience suspending function that combines seize(), delay(), and release().simpleProcess, suspends until ID_2 completes, then continues.Consider modeling the interaction between a soccer mom and her daughter. The soccer mom has a daughter who plays forward on a soccer team. The game day proceeds as follows. First, the mom drives the daughter to the game. The drive takes 30 minutes. After arriving to the field, the daughter exits the mini-van, which takes 2 minutes. After the daughter exits the van, the mom departs to run some errands, meanwhile the daughter plays soccer. The mom’s errands take approximately 45 minutes. The soccer game takes approximately 60 minutes. If the mom returns from the errands before the game ends, the mom waits patiently to pick up her daughter to go home. If the game ends, before the mom returns, the daughter waits patiently for the mom to return. Once the mother and daughter are together, the daughter loads up her soccer gear and enters the van. This loading time takes 2 minutes. After all is loaded, the happy mom and daughter drive home, which takes 30 minutes.
Suspension ClassSuspension class to capture each suspension point and to share state between the entities.activate()s the daughter, holding a reference to her; the daughter is constructed with a reference to the mom — so each can reach the other’s public Suspension properties.suspendFor(suspension) — suspend at a labeled pointresume(other.suspension) — resume the other entity at that pointsuspension.isSuspended — test whether the other side is currently waitingprivate inner class Mother : Entity() {
val daughterExiting = Suspension(name = "Suspend for daughter to exit van")
val daughterPlaying = Suspension(name = "Suspend for daughter playing")
val daughterLoading = Suspension(name = "Suspend for daughter entering van")
val momProcess = process {
delay(30.0) // drive to game
val daughter = Daughter(this@Mother)
activate(daughter.daughterProcess)
suspendFor(daughterExiting) // wait for daughter to exit van
delay(45.0) // run errands
if (daughter.motherShopping.isSuspended) {
resume(daughter.motherShopping) // daughter done playing: resume her
} else {
suspendFor(daughterPlaying) // daughter still playing: wait
}
suspendFor(daughterLoading) // wait for daughter to load van
delay(30.0) // drive home
}
}private inner class Daughter(val mother: Mother) : Entity() {
val motherShopping = Suspension("Suspend for mother shopping")
val daughterProcess = process {
delay(2.0) // exit the van
resume(mother.daughterExiting) // resume mom
delay(60.0) // play soccer
if (mother.daughterPlaying.isSuspended) {
resume(mother.daughterPlaying) // mom waiting: resume her
} else {
suspendFor(motherShopping) // mom still shopping: wait
}
delay(2.0) // load gear into van
resume(mother.daughterLoading) // resume mom
}
}Daughter holds a reference to mother, so it can check mother.daughterPlaying.isSuspended and resume(...) the mom’s suspensions.0.0> starting mom = ID_1
30.0> mom = ID_1 arrived at game
30.0> mom = ID_1 suspending for daughter to exit van
30.0> starting daughter ID_2
32.0> daughter, ID_2, exited the van
32.0> daughter, ID_2, resuming mom
32.0> daughter, ID_2, starting playing
32.0> mom = ID_1 running errands...
77.0> mom = ID_1 completed errands
77.0> mom, ID_1, mom suspending because daughter is still playing
92.0> daughter, ID_2, finished playing
94.0> daughter, ID_2, entered van, resuming mom
94.0> mom = ID_1 driving home
124.0> mom = ID_1 arrived home
HoldQueue or Signal instead of Suspension.Suspension collects no waiting statistics; use HoldQueue / Signal when you need queue statistics.HoldQueue version — four hold queues stand in for the suspension points; the mom checks whether the daughter is waiting on her:startBlockage(b) … clearBlockage(b) mark the fenced region; another entity calls waitFor(b) to wait for it.BlockingActivity wraps a delay in a blockage; perform(activity) runs it, so you can’t forget to clear it.private inner class Mom : Entity() {
val shopping = BlockingActivity(45.0) // a delay wrapped in a blockage
val momProcess = process {
delay(30.0)
val daughter = Daughter(this@Mom)
activate(daughter.daughterProcess)
waitFor(daughter.unloading) // suspend until daughter clears it
perform(shopping) // start blockage, delay, clear blockage
waitFor(daughter.playing)
waitFor(daughter.loading)
delay(30.0)
}
}perform(unloading), perform(playing), waitFor(mom.shopping), perform(loading).BlockingResourceUsage, BlockingResourcePoolUsage, BlockingMovement.suspend() / resumeProcess().HoldQueue — holds (suspends) entities until removed; removeAllAndResume() / removeAndResume(entity). An entity cannot remove itself.Signal — builds on HoldQueue; waitFor(signal) waits, signal.signal(0..4) releases entities by rank, and they remove themselves.BlockingQueue — communication between two processes; send() blocks when full, waitForItems() blocks when empty (channelQ / senderQ / receiverQ).waitFor(process) — start another process and suspend until it completes.Suspension — direct suspend/resume via suspendFor() / resume() / isSuspended (no queue statistics).Blockage / BlockingActivity — a gate (startBlockage/clearBlockage, or perform()); preferred over raw Suspension.ProcessModel and defining processes on sub-classes of Entity.