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
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
- If you call it with only one argument
run(env::Environment), it steps through the simulation until there are no more events left.
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 reaches
10.0but will not process any events scheduled for time
10.0. This is similar to a new environment where the clock is
0.0but (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
AbstractEventto it. The function returns when this event has been processed. Assuming that the current time is
run(env, Timeout(env, 5.0))is equivalent to
run(env, 5.0). You can also pass other types of events (remember, that
Processis a subtype of
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))
peek(env::Environment)returns the time of the next scheduled event or
Infwhen no more events are scheduled.
step(env::Environment)processes the next scheduled event. It raises an
EmptyScheduleexception 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
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.
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
More details on what events do can be found in the next sections.
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.