Skip to content

Commit 062533d

Browse files
committed
Docs added, @append_callback optimised
1 parent b5be5fc commit 062533d

6 files changed

Lines changed: 96 additions & 31 deletions

File tree

docs/make.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ makedocs(
99
pages = [
1010
"Home" => "index.md",
1111
"Tutorial" => "tutorial.md",
12-
"Topical Guides" => ["Basics" => "guides/basics.md",],
12+
"Topical Guides" => ["Basics" => "guides/basics.md",
13+
"Environments" => "guides/environments.md",],
1314
"Examples" => ["Ross" => "examples/ross.md",],
1415
"API" => "api.md"
1516
]

docs/src/examples/ross.md

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,35 +30,26 @@ srand(SEED)
3030
const F = Exponential(LAMBDA)
3131
const G = Exponential(MU)
3232
33-
@resumable function machine(sim::Simulation, repair_facility::Resource, spares::Store{Process})
33+
@resumable function machine(env::Environment, repair_facility::Resource, spares::Store{Process})
3434
while true
35-
try
36-
@yield Timeout(sim, Inf)
37-
catch exc
38-
end
39-
@yield Timeout(sim, rand(F))
35+
try @yield Timeout(env, Inf) end
36+
@yield Timeout(env, rand(F))
4037
get_spare = Get(spares)
41-
@yield get_spare | Timeout(sim, 0.0)
38+
@yield get_spare | Timeout(env)
4239
state(get_spare) != SimJulia.idle ? interrupt(value(get_spare)) : throw(SimJulia.StopSimulation("No more spares!"))
4340
@yield Request(repair_facility)
44-
@yield Timeout(sim, rand(G))
41+
@yield Timeout(env, rand(G))
4542
@yield Release(repair_facility)
46-
@yield Put(spares, active_process(sim))
43+
@yield Put(spares, active_process(env))
4744
end
4845
end
4946
50-
@resumable function start_sim(sim::Simulation, repair_facility::Resource, spares::Store{Process})
47+
@resumable function start_sim(env::Environment, repair_facility::Resource, spares::Store{Process})
5148
procs = Process[]
52-
for i=1:N
53-
push!(procs, @process machine(sim, repair_facility, spares))
54-
end
55-
@yield Timeout(sim, 0.0)
56-
for proc in procs
57-
interrupt(proc)
58-
end
59-
for i=1:S
60-
@yield Put(spares, @process machine(sim, repair_facility, spares))
61-
end
49+
for i in 1:N push!(procs, @process machine(env, repair_facility, spares)) end
50+
@yield Timeout(env)
51+
for proc in procs interrupt(proc) end
52+
for i in 1:S @yield Put(spares, @process machine(env, repair_facility, spares)) end
6253
end
6354
6455
function sim_repair()
@@ -73,9 +64,7 @@ function sim_repair()
7364
end
7465
7566
results = Float64[]
76-
for i=1:RUNS
77-
push!(results, sim_repair())
78-
end
67+
for i in 1:RUNS push!(results, sim_repair()) end
7968
println("Average crash time: ", sum(results)/RUNS)
8069
8170
# output

docs/src/guides/basics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The process function then yields the event and thus gets suspended. It is resume
4141

4242
Finally, the process function prints the current simulation time (that is accessible via the `now` function) and the `Timeout`’s value.
4343

44-
If all required process functions are defined, you can instantiate all objects for your simulation. In most cases, you start by creating an instance of `Environement`, e.g. a `Simulation`, because you’ll need to pass it around a lot when creating everything else.
44+
If all required process functions are defined, you can instantiate all objects for your simulation. In most cases, you start by creating an instance of `Environment`, e.g. a `Simulation`, because you’ll need to pass it around a lot when creating everything else.
4545

4646
Starting a process function involves two things:
4747

docs/src/guides/environments.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Environments
2+
3+
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.
4+
5+
The base type for all environments is `Environment`. “Normal” simulations use its subtype `Simulation`.
6+
7+
## Simulation control
8+
9+
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.
10+
11+
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.
12+
13+
The most important function here is `run`:
14+
15+
- If you call it with an instance of the environment as the only argument (`run(env)`), it steps through the simulation until there are no more events left. If your processes run forever, this function will never terminate (unless you kill your script by e.g., pressing `Ctrl-C`).
16+
17+
- 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, e.g.: `run(env, 10)`.
18+
19+
The simulation will then stop when the internal clock reaches 10 but will not process any events scheduled for time 10. This is similar to a new environment where the clock is 0 but (obviously) no events have yet been processed.
20+
21+
If you want to integrate your simulation in a GUI and want to draw a process bar, you can repeatedly call this function with increasing until values and update your progress bar after each call:
22+
23+
```julia
24+
sim = Simulation()
25+
for t in 1:100
26+
run(sim, t)
27+
update(progressbar, t)
28+
end
29+
```
30+
31+
- Instead of passing a number as second argument to `run`, you can also pass any event to it. `run` will then return when the event has been processed.
32+
33+
Assuming that the current time is 0, `run(env, Timeout(env, 5))` is equivalent to `run(env, 5)`.
34+
35+
You can also pass other types of events (remember, that a `Process` is an event, too):
36+
37+
```jldoctest
38+
using ResumableFunctions
39+
using SimJulia
40+
41+
@resumable function my_process(env::Environment)
42+
@yield Timeout(env, 1)
43+
"Monty Python's Flying Circus"
44+
end
45+
46+
sim = Simulation()
47+
proc = @process my_process(sim)
48+
run(sim, proc)
49+
50+
# output
51+
52+
"Monty Python's Flying Circus"
53+
```
54+
55+
To step through the simulation event by event, the environment offers `step`. This function processes the next scheduled event. It raises an `EmptySchedule` exception if no event is available.
56+
57+
In a typical use case, you use this function in a loop like:
58+
```julia
59+
while now(sim) < 10
60+
step(sim)
61+
end
62+
```
63+
64+
## State access
65+
66+
The environment allows you to get the current simulation time via the function `now`. The simulation time is a number without unit and is increased via `Timeout` events.
67+
68+
By default, the simulation starts at time 0, but you can pass an `initial_time` to the `Simulation` constructor to use something else.
69+
70+
Note
71+
72+
!!! note
73+
Although the simulation time is technically unitless, you can pretend that it is, for example, in milliseconds and use it like a timestamp returned by `Base.Dates.datetime2epochm` to calculate a date or the day of the week. The `Simulation` constructor and the `run` function accept as argument a `Base.Dates.DateTime` and the `Timeout` constructor a `Base.Dates.Delay`. Together with the convenience function `nowDateTime` a simulation can transparantly schedule its events in seconds, minutes, hours, days, ...
74+
75+
The function `active_process` is comparable to `Base.Libc.getpid` and returns the current active `Process`. If no process is active, a `NullException` is thrown. A process is active when its process function is being executed. It becomes inactive (or suspended) when it yields an event.
76+
77+
Thus, it only makes sense to call this function from within a process function or a function that is called by your process function:

src/SimJulia.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ module SimJulia
1515
export AbstractEvent, Environment, value, state, environment
1616
export Event, Timeout, succeed, fail, @callback, remove_callback
1717
export Operator, (&), (|)
18-
export Simulation, run, now, active_process
18+
export AbstractProcess, Simulation, run, now, active_process
1919
export Process, @process, interrupt
20-
export Container, Resource, Store, Put, Get, Request, Release, cancel, request, @request
20+
export Container, Resource, Store, Put, Get, Request, Release, cancel
2121
export nowDatetime
22-
export OldProcess, @oldprocess, yield
22+
export OldProcess, @oldprocess, yield, request
2323

2424
include("base.jl")
2525
include("events.jl")

src/base.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ end
4747

4848
macro callback(expr::Expr)
4949
expr.head != :call && error("Expression is not a function call!")
50-
func = esc(expr.args[1])
51-
args = [esc(expr.args[n]) for n in 2:length(expr.args)]
52-
:(append_callback($(func), $(args...)))
50+
esc(:(SimJulia.append_callback($(expr.args...))))
5351
end
5452

5553
function remove_callback(cb::Function, ev::AbstractEvent)

0 commit comments

Comments
 (0)