Environments¶
A simulation environment manages the simulation time as well as the scheduling and processing of events. It also provides means to step through or execute the simulation.
The abstract type for all environments is AbstractEnvironment
. “Normal” simulations usually use its subtype Environment
.
Simulation control¶
SimJulia is very flexible in terms of simulation execution. You can run your simulation until there are no more events, until a certain simulation time is reached, or until a certain event is triggered. You can also step through the simulation event by event. Furthermore, you can mix these things as you like. For example, you could run your simulation until an interesting event occurs. You could then step through the simulation event by event for a while; and finally run the simulation until there are no more events left and your processes have all terminated.
The most important method in this section is run()
:
- If you call it with only one argument
run(env::Environment)
, it steps through the simulation until there are no more events left.
Warning
If your process function runs forever, e.g.
while true
yield(Timeout(env, 1.0))
end
run(env)
will never terminate unless you kill your script by pressing Ctrl-C.
In most cases it is advisable to stop your simulation when it reaches a certain simulation time. Therefore, you can pass the desired time via a second argument:
run(env, 10.0)
. The simulation will then stop when the internal clock reaches10.0
but will not process any events scheduled for time10.0
. This is similar to a new environment where the clock is0.0
but (obviously) no events have yet been processed.Instead of passing a floating point value as second argument, you can also pass any instance of a
AbstractEvent
to it. The function returns when this event has been processed. Assuming that the current time is0.0
,run(env, Timeout(env, 5.0))
is equivalent torun(env, 5.0)
. You can also pass other types of events (remember, thatProcess
is a subtype ofAbstractEvent
).using SimJulia function my_proc(env::Environment) yield(Timeout(env, 1.0)) return "Monty Python's Flying Circus" end env = Environment() proc = Process(env, my_proc) println(run(env, proc))
To step through the simulation event by event, the environment offers peek(env::Environment)
and step(env::Environment)
:
peek(env::Environment)
returns the time of the next scheduled event orInf
when no more events are scheduled.step(env::Environment)
processes the next scheduled event. It raises anEmptySchedule
exception if no event is available.
In a typical use case, you use these methods in a loop like:
until = 10.0
while peek(env) < until
step(env)
end
State access¶
The environment allows you to get the current simulation time via the function now(env::Environment)
. The simulation time is a floating point value without unit and is increased via timeout events.
By default, the constructor Environment()
starts the simulation time at 0.0
, but you can pass an initial value, Environment(initial_value::Float64)
to use something else.
The function active_process(env::Environment)
is comparable to Base.getpid()
and returns the currently active Process
. A process is active when its process function is being executed. It becomes inactive (or suspended) when it yields an event.
Thus, it only makes sense to call this function from within a process function or a function that is called by your process function, otherwise, a NullException
is thrown:
using SimJulia
function subfunc(env::Environment)
println("Active process: $(active_process(env))")
end
function my_proc(env::Environment)
println("Active process: $(active_process(env))")
yield(Timeout(env, 1.0))
subfunc(env)
end
env = Environment()
Process(env, my_proc)
println("Time: $(peek(env))")
try
println(active_process(env))
catch exc
println("No active process")
end
step(env)
println("Time: $(peek(env))")
try
println(active_process(env))
catch exc
println("No active process")
end
step(env)
println("Time: $(peek(env))")
step(env)
println("Time: $(peek(env))")
A nice example of this function can be found in the resource system. When a process function calls the constructor Request(res::Resource)
to generate a request event for a resource, the resource determines the requesting process via active_process(env)
.
Event creation¶
To create events, you normally have to use a constructor Event(env::AbstractEnvironment)
to instantiate
the Event
type and pass a reference to the environment to it.
More details on what events do can be found in the next sections.
Miscellaneous¶
A process function can have a return value:
using SimJulia
function my_proc(env::Environment)
yield(Timeout(env, 1.0))
return 42
end
function other_proc(env::Environment)
ret_val = yield(Process(env, my_proc))
@assert(ret_val == 42)
end
env = Environment()
Process(env, other_proc)
run(env)
The simulation can be stopped by throwing a StopSimulation
exception in a process function. To keep your code more readable, the function stop_simulation(env::AbstractEnvironment)
does exactly this.