Skip to content

Commit d474c3d

Browse files
committed
Merge branch 'stateful'
2 parents ba9ecd8 + 4ee19d0 commit d474c3d

27 files changed

Lines changed: 635 additions & 146 deletions

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ os:
33
- linux
44
- osx
55
julia:
6-
- 0.5
76
- nightly
87
notifications:
98
email: false

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,21 @@ julia> Pkg.add("SimJulia")
3838
#### Release Notes
3939

4040
* Version 0.4 is a complete rewrite: more julian and less pythonic.
41-
* Only supports Julia v0.5 and above.
41+
* Only supports Julia v0.6 and above.
4242
* Scheduling is based on TimeType and Period.
4343
* The discrete event features are on par with version 0.3. (STABLE)
44+
* Two ways of making `Processes` are provided:
45+
- using the existing concept of `Tasks`: previous behaviour
46+
- using a novel finite-statemachine approach: a lot faster but less transparent for the end user
4447
* The continuous time simulation is based on a quantized state system solver. (EXPERIMENTAL)
4548
* Documentation is automated with [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl).
4649

4750

4851
#### Todo
4952

50-
* Integration of stiff ODE.
53+
* Transparent output processing.
5154
* Extension to PDE by method of lines Integration.
55+
* Automatically running a large number of simulations (over a parameter space) on a cluster to do simulation based optimisations.
5256

5357

5458
#### Authors

REQUIRE

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
julia 0.5
1+
julia 0.6
22
DataStructures
3-
Compat

appveyor.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
environment:
22
matrix:
3-
- JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
4-
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
53
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
64
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
75

src/SimJulia.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,26 @@ module SimJulia
1717
export succeed, fail
1818
export Operator
1919
export (&), (|)
20+
export FiniteStateMachine, @stateful, @yield
21+
export Coroutine, @Coroutine
2022
export Process, @Process
21-
export yield, interrupt
23+
export interrupt
2224
export Simulation, StopSimulation
2325
export run, now, active_process
2426
export Container, Resource, Store
25-
export Put, Get, Request, Release, cancel, capacity
27+
export Put, Get, Request, Release, cancel, capacity, @Request
2628

2729
include("base.jl")
2830
include("events.jl")
2931
include("operators.jl")
30-
include("process.jl")
3132
include("time.jl")
3233
include("simulation.jl")
34+
include("finitestatemachines/utils.jl")
35+
include("finitestatemachines/transforms.jl")
36+
include("finitestatemachines/macro.jl")
37+
include("coroutines.jl")
38+
include("tasks/base.jl")
39+
include("processes.jl")
3340
include("resources/base.jl")
3441
include("resources/containers.jl")
3542
include("resources/stores.jl")

src/base.jl

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using Compat
2-
3-
@compat abstract type Environment end
1+
abstract type Environment end
42

53
"""
64
The parent type for all events.
@@ -17,46 +15,34 @@ Once the events is scheduled, it has a value.
1715
1816
An event has also a list of callbacks. A callback can be any function as long as it accepts an instance of a subtype of `AbstractEvent` as its first argument. Once an event gets triggered, all callbacks will be invoked. Callbacks can do further processing with the value it has produced.
1917
"""
20-
@compat abstract type AbstractEvent{E<:Environment} end
18+
abstract type AbstractEvent{E<:Environment} end
2119

2220
@enum EVENT_STATE idle=0 scheduled=1 triggered=2
2321

24-
immutable EventTriggered{E<:Environment} <: Exception
22+
struct EventTriggered{E<:Environment} <: Exception
2523
ev :: AbstractEvent{E}
2624
end
2725

28-
immutable EventNotIdle{E<:Environment} <: Exception
26+
struct EventNotIdle{E<:Environment} <: Exception
2927
ev :: AbstractEvent{E}
3028
end
3129

32-
type BaseEvent{E<:Environment}
30+
mutable struct BaseEvent{E<:Environment}
3331
env :: E
3432
id :: UInt
3533
cid :: UInt
3634
callbacks :: DataStructures.PriorityQueue{Function, UInt}
3735
state :: EVENT_STATE
3836
value :: Any
39-
function BaseEvent(env::E)
37+
function BaseEvent{E}(env::E) where E<:Environment
4038
new(env, env.eid+=one(UInt), zero(UInt), DataStructures.PriorityQueue(Function, UInt), idle, nothing)
4139
end
4240
end
4341

44-
function BaseEvent{E<:Environment}(env::E) :: BaseEvent
42+
function BaseEvent{E<:Environment}(env::E)
4543
BaseEvent{E}(env)
4644
end
4745

48-
# type BaseEvent{E<:Environment}
49-
# env :: E
50-
# id :: UInt
51-
# cid :: UInt
52-
# callbacks :: DataStructures.PriorityQueue{Function, UInt}
53-
# state :: EVENT_STATE
54-
# value :: Any
55-
# function BaseEvent{E}(env::E) where E<:Environment
56-
# new(env, env.eid+=one(UInt), zero(UInt), DataStructures.PriorityQueue(Function, UInt), idle, nothing)
57-
# end
58-
# end
59-
6046
function show(io::IO, ev::AbstractEvent)
6147
print(io, "$(typeof(ev)) $(ev.bev.id)")
6248
end

src/coroutines.jl

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
A `Coroutine` is an abstraction for an event yielding function, i.e. a process function.
3+
4+
The process function can suspend its execution by yielding an instance of `AbstractEvent`. The `Environment` will take care of resuming the process function with the value of that event once it has happened. The exception of failed events is also thrown into the process function.
5+
6+
A `Coroutine` is a subtype of `AbstractEvent`. It is triggered, once the process functions returns or raises an exception. The value of the process is the return value of the process function or the exception, respectively.
7+
8+
**Signature**:
9+
10+
Coroutine{E<:Environment} <: AbstractEvent{E}
11+
12+
**Fields**:
13+
14+
- bev :: BaseEvent{E}
15+
- task :: Task
16+
- target :: AbstractEvent{E}
17+
- resume :: Function
18+
19+
**Constructor**:
20+
21+
Coroutine{E<:Environment}(func::Function, env::E, args::Any...)
22+
"""
23+
type Coroutine{E<:Environment} <: AbstractProcess{E}
24+
bev :: BaseEvent{E}
25+
fsm :: FiniteStateMachine
26+
target :: AbstractEvent{E}
27+
resume :: Function
28+
function Coroutine{E}(func::Function, env::E, args::Any...) where E<:Environment
29+
proc = new()
30+
proc.bev = BaseEvent(env)
31+
proc.fsm = func(env, args...)
32+
proc.target = Timeout(env)
33+
proc.resume = append_callback(execute, proc.target, proc)
34+
return proc
35+
end
36+
end
37+
38+
function Coroutine{E<:Environment}(func::Function, env::E, args::Any...)
39+
Coroutine{E}(func, env, args...)
40+
end
41+
42+
"""
43+
Creates a `Coroutine` with process function `func` having a required argument `env`, i.e. an instance of a subtype of `Environment`, and a variable number of arguments `args...`.
44+
45+
**Signature**:
46+
47+
@Coroutine func(env, args...)
48+
"""
49+
macro Coroutine(ex)
50+
if ex.head == :call
51+
func = esc(ex.args[1])
52+
args = [esc(ex.args[n]) for n in 2:length(ex.args)]
53+
return :(Coroutine($(func), $(args...)))
54+
end
55+
end
56+
57+
function execute{E<:Environment}(ev::AbstractEvent{E}, proc::Coroutine{E})
58+
try
59+
env = environment(ev)
60+
set_active_process(env, proc)
61+
target = proc.fsm(value(ev))
62+
if iscoroutinedone(proc.fsm)
63+
schedule(proc.bev, value=target)
64+
else
65+
if state(target) == triggered
66+
proc.target = Timeout(env, value=value(target))
67+
else
68+
proc.target = target
69+
end
70+
proc.resume = append_callback(execute, proc.target, proc)
71+
end
72+
set_active_process(env)
73+
catch exc
74+
rethrow(exc)
75+
end
76+
end
77+
78+
function interrupt(proc::Coroutine, cause::Any=nothing)
79+
if !iscoroutinedone(proc.fsm)
80+
remove_callback(proc.resume, proc.target)
81+
proc.target = Timeout(environment(proc), priority=true, value=InterruptException(proc, cause))
82+
proc.resume = append_callback(execute, proc.target, proc)
83+
end
84+
end

src/events.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
type Event{E<:Environment} <: AbstractEvent{E}
1+
struct Event{E<:Environment} <: AbstractEvent{E}
22
bev :: BaseEvent{E}
3-
function Event(env::E)
3+
function Event{E}(env::E) where E<:Environment
44
new(BaseEvent(env))
55
end
66
end
77

8-
function Event{E<:Environment}(env::E) :: Event{E}
8+
function Event{E<:Environment}(env::E)
99
Event{E}(env)
1010
end
1111

@@ -41,9 +41,9 @@ Timeout{E<:Environment} <: AbstractEvent{E}
4141
- Timeout{E<:Environment}(env::E, delay::Number=0; priority::Bool=false, value::Any=nothing)
4242
"""
4343

44-
type Timeout{E<:Environment} <: AbstractEvent{E}
44+
struct Timeout{E<:Environment} <: AbstractEvent{E}
4545
bev :: BaseEvent{E}
46-
function Timeout(env::E, delay::Union{Period, Number}, priority::Bool, value::Any)
46+
function Timeout{E}(env::E, delay::Union{Period, Number}, priority::Bool, value::Any) where E<:Environment
4747
ev = new(BaseEvent(env))
4848
schedule(ev.bev, delay, priority=priority, value=value)
4949
return ev

src/finitestatemachines/macro.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
macro yield(val)
2+
:($(esc(val)))
3+
end
4+
5+
abstract type FiniteStateMachine end
6+
7+
iscoroutinedone(fsm::FiniteStateMachine) = fsm._state == 0xff
8+
9+
macro stateful(expr::Expr)
10+
expr.head != :function && error("Not a function definition!")
11+
args = getArguments(expr)
12+
func_name = shift!(args)
13+
type_name = gensym()
14+
slots = getSlots(expr, func_name)
15+
type_expr = :(
16+
type $type_name <: FiniteStateMachine
17+
_state :: UInt8
18+
$((:($slotname :: $(slottype == Union{} ? Any : :($slottype))) for (slotname, slottype) in slots)...)
19+
function $type_name($((:($arg::$(slots[:($arg)])) for arg in args)...))
20+
fsm = new()
21+
fsm._state = 0x00
22+
$((:(fsm.$arg = $arg) for arg in args)...)
23+
fsm
24+
end
25+
end
26+
)
27+
new_expr = deepcopy(expr.args[2])
28+
transformVars!(new_expr, keys(slots))
29+
n = transformYield!(new_expr)
30+
func_expr = :(
31+
function (_fsm::$type_name)(_ret::Any=nothing)
32+
_fsm._state == 0x00 && @goto _STATE_0
33+
$((:(_fsm._state == $i && @goto $(Symbol("_STATE_",:($i)))) for i in 0x01:n)...)
34+
error("Iterator has stopped!")
35+
@label _STATE_0
36+
_fsm._state = 0xff
37+
$((:($arg) for arg in new_expr.args)...)
38+
end
39+
)
40+
if expr.args[1].head == Symbol("::")
41+
func_expr.args[1] = Expr(Symbol("::"), func_expr.args[1], expr.args[1].args[2])
42+
end
43+
call_expr = deepcopy(expr)
44+
if call_expr.args[1].head == Symbol("::")
45+
call_expr.args[1] = call_expr.args[1].args[1]
46+
end
47+
call_expr.head = Symbol("=")
48+
call_expr.args[2] = :($type_name($((:($arg) for arg in args)...)))
49+
esc(quote
50+
$type_expr
51+
$func_expr
52+
$call_expr
53+
end)
54+
end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
function transformVars!(expr::Expr, symbols::Base.KeyIterator{Dict{Symbol,Type}})
2+
for i in 1:length(expr.args)
3+
if expr.head == :kw && i == 1
4+
5+
elseif expr.head == Symbol(".") && i == 2
6+
7+
elseif isa(expr.args[i], Symbol) && in(expr.args[i], symbols)
8+
expr.args[i] = :(_fsm.$(expr.args[i]))
9+
elseif isa(expr.args[i], Expr)
10+
transformVars!(expr.args[i], symbols)
11+
end
12+
end
13+
end
14+
15+
function transformTry!(expr::Expr, n::UInt8=0x00, super::Expr=:(), line_no::Int=0, super_try::Expr=:(), line_try::Int=0) :: UInt8
16+
for (i, arg) in enumerate(expr.args)
17+
if isa(arg, Expr)
18+
if arg.head == :line
19+
line_no = i+1
20+
super = expr
21+
elseif arg.head == :macrocall && arg.args[1] == Symbol("@yield")
22+
n += one(UInt8)
23+
if expr == super
24+
expr.args[i] = :(isa(_ret, Exception) && throw(_ret))
25+
else
26+
expr.args[i] = :(_ret)
27+
insert!(super.args, line_no+1, :(isa(_ret, Exception) && throw(_ret)))
28+
end
29+
insert!(super.args, line_no, :(_fsm._state = 0xff))
30+
insert!(super.args, line_no, arg.args[2])
31+
insert!(super.args, line_no, :(_fsm._state = $n))
32+
insert!(super_try.args, line_try, :(@label $(Symbol("_STATE_",:($n)))))
33+
insert!(super_try.args, line_try, deepcopy(super_try.args[line_try+1]))
34+
for j in length(super_try.args[line_try].args[1].args):-1:line_no+2
35+
deleteat!(super_try.args[line_try].args[1].args, j)
36+
end
37+
for j in 1:line_no+1
38+
deleteat!(super_try.args[line_try+2].args[1].args, 1)
39+
end
40+
break
41+
else
42+
m = transformTry!(arg, n, super, line_no, super_try, line_try)
43+
if m > n
44+
n=m
45+
break
46+
end
47+
end
48+
end
49+
end
50+
n
51+
end
52+
53+
function transformYield!(expr::Expr, n::UInt8=0x00, super::Expr=:(), line_no::Int=0) :: UInt8
54+
for (i, arg) in enumerate(expr.args)
55+
if isa(arg, Expr)
56+
if arg.head == :try
57+
n = transformTry!(arg.args[1], n, super, line_no, expr, i)
58+
println("hi")
59+
elseif arg.head == :line
60+
line_no = i+1
61+
super = expr
62+
elseif arg.head == :macrocall && arg.args[1] == Symbol("@yield")
63+
n += one(UInt8)
64+
if expr == super
65+
expr.args[i] = :(_fsm._state = 0xff)
66+
else
67+
expr.args[i] = :(_ret)
68+
insert!(super.args, line_no, :(_fsm._state = 0xff))
69+
end
70+
insert!(super.args, line_no, :(@label $(Symbol("_STATE_",:($n)))))
71+
insert!(super.args, line_no, arg.args[2])
72+
insert!(super.args, line_no, :(_fsm._state = $n))
73+
else
74+
n = transformYield!(arg, n, super, line_no)
75+
end
76+
end
77+
end
78+
n
79+
end

0 commit comments

Comments
 (0)