Skip to content

Commit ca235b1

Browse files
committed
Initial commit
1 parent 5ff5824 commit ca235b1

8 files changed

Lines changed: 295 additions & 0 deletions

File tree

src/SimJulia.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
isdefined(Base, :__precompile__) && __precompile__()
2+
3+
"""
4+
Main module for SimJulia.jl – a combined continuous time / discrete event process oriented simulation framework for Julia.
5+
"""
6+
module SimJulia
7+
8+
using DataStructures
9+
10+
import Base.run, Base.now, Base.isless, Base.show
11+
12+
export AbstractEvent
13+
export value, state, environment
14+
export Simulation
15+
export run, now, active_process
16+
export Event, Timeout
17+
export succeed, fail, append_callback, @callback, remove_callback
18+
19+
include("base.jl")
20+
include("simulations.jl")
21+
include("events.jl")
22+
23+
end

src/base.jl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
abstract type AbstractEvent end
2+
abstract type Environment end
3+
4+
@enum EVENT_STATE idle=0 scheduled=1 triggered=2
5+
6+
struct EventTriggered <: Exception
7+
ev :: AbstractEvent
8+
end
9+
10+
struct EventNotIdle <: Exception
11+
ev :: AbstractEvent
12+
end
13+
14+
mutable struct BaseEvent
15+
env :: Environment
16+
id :: UInt
17+
cid :: UInt
18+
callbacks :: DataStructures.PriorityQueue{Function, UInt}
19+
state :: EVENT_STATE
20+
value :: Any
21+
function BaseEvent(env::Environment)
22+
new(env, env.eid+=one(UInt), zero(UInt), DataStructures.PriorityQueue(Function, UInt), idle, nothing)
23+
end
24+
end
25+
26+
function show(io::IO, ev::AbstractEvent)
27+
print(io, "$(typeof(ev)) $(ev.bev.id)")
28+
end
29+
30+
function environment(ev::AbstractEvent) :: Environment
31+
ev.bev.env
32+
end
33+
34+
function value(ev::AbstractEvent) :: Any
35+
ev.bev.value
36+
end
37+
38+
function state(ev::AbstractEvent) :: EVENT_STATE
39+
ev.bev.state
40+
end
41+
42+
function append_callback(func::Function, ev::AbstractEvent, args::Any...) :: Function
43+
ev.bev.state == triggered && throw(EventTriggered(ev))
44+
cb = ()->func(ev, args...)
45+
ev.bev.callbacks[cb] = ev.bev.cid+=one(UInt)
46+
cb
47+
end
48+
49+
macro callback(expr::Expr)
50+
expr.head != :call && error("Expression is not a function call!")
51+
func = esc(expr.args[1])
52+
args = [esc(expr.args[n]) for n in 2:length(expr.args)]
53+
:(append_callback($(func), $(args...)))
54+
end
55+
56+
function remove_callback(cb::Function, ev::AbstractEvent)
57+
DataStructures.dequeue!(ev.bev.callbacks, cb)
58+
end

src/events.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
struct Event <: AbstractEvent
2+
bev :: BaseEvent
3+
function Event(env::Environment)
4+
new(BaseEvent(env))
5+
end
6+
end
7+
8+
function succeed(ev::Event; priority::Int8=zero(Int8), value::Any=nothing) :: Event
9+
sta = state(ev)
10+
(sta == scheduled || sta == triggered) && throw(EventNotIdle(ev))
11+
schedule(ev.bev, priority=priority, value=value)
12+
ev
13+
end
14+
15+
function fail(ev::Event, exc::Exception; priority::Int8=zero(Int8)) :: Event
16+
succeed(ev, priority=priority, value=exc)
17+
end
18+
19+
struct Timeout <: AbstractEvent
20+
bev :: BaseEvent
21+
function Timeout(env::Environment, delay::Number=0; priority::Int8=zero(Int8), value::Any=nothing)
22+
ev = new(BaseEvent(env))
23+
schedule(ev.bev, delay, priority=priority, value=value)
24+
ev
25+
end
26+
end
27+
28+
function run(sim::Simulation, until::Number=typemax(Float64))
29+
run(sim, Timeout(sim, until-now(sim)))
30+
end

src/simulations.jl

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
abstract type AbstractProcess <: AbstractEvent end
2+
3+
struct EventKey
4+
time :: Float64
5+
priority :: Int8
6+
id :: UInt
7+
end
8+
9+
function isless(a::EventKey, b::EventKey) :: Bool
10+
(a.time < b.time) || (a.time == b.time && a.priority > b.priority) || (a.time == b.time && a.priority == b.priority && a.id < b.id)
11+
end
12+
13+
mutable struct Simulation <: Environment
14+
time :: Float64
15+
heap :: DataStructures.PriorityQueue{BaseEvent, EventKey}
16+
eid :: UInt
17+
sid :: UInt
18+
active_proc :: Nullable{AbstractProcess}
19+
function Simulation(initial_time::Number=zero(Float64))
20+
new(initial_time, DataStructures.PriorityQueue(BaseEvent, EventKey), zero(UInt), zero(UInt), Nullable{AbstractProcess}())
21+
end
22+
end
23+
24+
function now(sim::Simulation)
25+
sim.time
26+
end
27+
28+
function active_process(sim::Simulation) :: AbstractProcess
29+
get(sim.active_proc)
30+
end
31+
32+
function reset_active_process(sim::Simulation)
33+
sim.active_proc = Nullable{AbstractProcess}()
34+
end
35+
36+
function set_active_process(sim::Simulation, proc::AbstractProcess)
37+
sim.active_proc = Nullable(proc)
38+
end
39+
40+
struct StopSimulation <: Exception
41+
value :: Any
42+
function StopSimulation(value::Any=nothing)
43+
new(value)
44+
end
45+
end
46+
47+
function stop_simulation(ev::AbstractEvent)
48+
throw(StopSimulation(value(ev)))
49+
end
50+
51+
struct EmptySchedule <: Exception end
52+
53+
function step(sim::Simulation)
54+
isempty(sim.heap) && throw(EmptySchedule())
55+
(bev, key) = DataStructures.peek(sim.heap)
56+
DataStructures.dequeue!(sim.heap)
57+
sim.time = key.time
58+
bev.state = triggered
59+
while !isempty(bev.callbacks)
60+
DataStructures.dequeue!(bev.callbacks)()
61+
end
62+
end
63+
64+
function run(sim::Simulation, until::AbstractEvent)
65+
append_callback(stop_simulation, until)
66+
try
67+
while true
68+
step(sim)
69+
end
70+
catch exc
71+
if isa(exc, StopSimulation)
72+
return exc.value
73+
else
74+
rethrow(exc)
75+
end
76+
end
77+
end
78+
79+
function schedule(bev::BaseEvent, delay::Number=zero(Float64); priority::Int8=zero(Int8), value::Any=nothing)
80+
bev.value = value
81+
bev.env.heap[bev] = EventKey(bev.env.time + delay, priority, bev.env.sid+=one(UInt))
82+
bev.state = scheduled
83+
end
84+
85+
struct InterruptException <: Exception
86+
by :: AbstractProcess
87+
cause :: Any
88+
end

test/base.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using SimJulia
2+
3+
function test_callback(ev::Event)
4+
println("I am a callback function running in $(typeof(environment(ev)))")
5+
end
6+
7+
sim = Simulation()
8+
ev = Event(sim)
9+
println(typeof(ev))
10+
cb = @callback test_callback(ev)
11+
remove_callback(cb, ev)
12+
println(state(ev))
13+
println(value(ev))
14+
succeed(ev, value="Hi")
15+
@callback test_callback(ev)
16+
println(state(ev))
17+
println(value(ev))
18+
run(sim)
19+
try
20+
@callback test_callback(ev)
21+
catch exc
22+
println("$exc has been thrown!")
23+
end
24+
println(state(ev))
25+
println(value(ev))

test/events.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using SimJulia
2+
3+
type TestException <: Exception end
4+
5+
function test_callback_event(ev::Event)
6+
println("Hi $ev has value $(value(ev))")
7+
end
8+
9+
function test_callback_Timeout(ev::AbstractEvent)
10+
println("Hi $ev timed out at $(now(environment(ev)))")
11+
end
12+
13+
sim = Simulation()
14+
ev1 = Event(sim)
15+
@callback test_callback_event(ev1)
16+
succeed(ev1, value="Succes")
17+
ev2 = Event(sim)
18+
@callback test_callback_event(ev2)
19+
fail(ev2, TestException())
20+
try
21+
succeed(ev2)
22+
catch exc
23+
println("$exc has been thrown!")
24+
end
25+
@callback test_callback_Timeout(Timeout(sim, 1))
26+
run(sim)

test/runtests.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
testpath(f) = joinpath(dirname(@__FILE__), f)
2+
3+
for test_file in [
4+
"base.jl",
5+
"simulations.jl",
6+
"events.jl",]
7+
include(testpath(test_file))
8+
end

test/simulations.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using SimJulia
2+
3+
type TestException <: Exception end
4+
5+
function test_callback(ev::AbstractEvent)
6+
println("Hi I timed out at $(now(environment(ev)))")
7+
end
8+
9+
function test_callback_exception(ev::Event)
10+
throw(TestException())
11+
end
12+
13+
sim = Simulation()
14+
@callback test_callback(Timeout(sim, 1))
15+
@callback test_callback(Timeout(sim, 3))
16+
run(sim, 2)
17+
sim = Simulation()
18+
try
19+
run(sim, Event(sim))
20+
catch exc
21+
println("$exc has been thrown!")
22+
end
23+
sim = Simulation(3)
24+
start = now(sim)
25+
@callback test_callback(Timeout(sim, 1))
26+
run(sim, start+2)
27+
println(now(sim)-start)
28+
sim = Simulation()
29+
start = now(sim)
30+
ev = Event(sim)
31+
@callback test_callback_exception(ev)
32+
succeed(ev)
33+
try
34+
run(sim)
35+
catch exc
36+
println("$exc has been thrown after $(now(sim)-start)!")
37+
end

0 commit comments

Comments
 (0)