Skip to content

Commit 82f6385

Browse files
committed
add resources
1 parent 27646d0 commit 82f6385

9 files changed

Lines changed: 387 additions & 5 deletions

File tree

src/SimJulia.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ module SimJulia
2222
export yield, interrupt
2323
export FiniteStateMachine, @stateful, @yield, iscoroutinedone
2424
export Coroutine, @coroutine
25+
export Container, Resource, Store
26+
export Put, Get, Request, Release, cancel, capacity, request, @request
2527

2628
include("base.jl")
2729
include("simulations.jl")
@@ -31,6 +33,10 @@ module SimJulia
3133
include("processes.jl")
3234
include("finitestatemachines/utils.jl")
3335
include("finitestatemachines/transforms.jl")
34-
include("finitestatemachines/base.jl")
36+
include("finitestatemachines/macro.jl")
3537
include("coroutines.jl")
38+
include("resources/base.jl")
39+
include("resources/containers.jl")
40+
include("resources/stores.jl")
41+
3642
end

src/resources/base.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
abstract type ResourceKey end
2+
3+
abstract type AbstractResource end
4+
5+
abstract type ResourceEvent <: AbstractEvent end
6+
7+
struct Put <: ResourceEvent
8+
bev :: BaseEvent
9+
function Put(env::Environment)
10+
new(BaseEvent(env))
11+
end
12+
end
13+
14+
struct Get <: ResourceEvent
15+
bev :: BaseEvent
16+
function Get(env::Environment)
17+
new(BaseEvent(env))
18+
end
19+
end
20+
21+
function isless(a::ResourceKey, b::ResourceKey)
22+
(a.priority < b.priority) || (a.priority == b.priority && a.id < b.id)
23+
end
24+
25+
function trigger_put(put_ev::ResourceEvent, res::AbstractResource)
26+
queue = DataStructures.PriorityQueue(res.Put_queue)
27+
while length(queue) > 0
28+
(put_ev, key) = DataStructures.peek(queue)
29+
proceed = do_put(res, put_ev, key)
30+
state(put_ev) == scheduled && DataStructures.dequeue!(res.Put_queue, put_ev)
31+
proceed ? DataStructures.dequeue!(queue) : break
32+
end
33+
end
34+
35+
function trigger_get(get_ev::ResourceEvent, res::AbstractResource)
36+
queue = DataStructures.PriorityQueue(res.Get_queue)
37+
while length(queue) > 0
38+
(get_ev, key) = DataStructures.peek(queue)
39+
proceed = do_get(res, get_ev, key)
40+
state(get_ev) == scheduled && DataStructures.dequeue!(res.Get_queue, get_ev)
41+
proceed ? DataStructures.dequeue!(queue) : break
42+
end
43+
end
44+
45+
function cancel(res::AbstractResource, put_ev::Put)
46+
DataStructures.dequeue!(res.Put_queue, put_ev)
47+
end
48+
49+
function cancel(res::AbstractResource, get_ev::Get)
50+
DataStructures.dequeue!(res.Get_queue, get_ev)
51+
end
52+
53+
function capacity(res::AbstractResource)
54+
res.capacity
55+
end

src/resources/containers.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
struct ContainerKey{N<:Number} <: ResourceKey
2+
priority :: Int
3+
id :: UInt
4+
amount :: N
5+
end
6+
7+
mutable struct Container{N<:Number} <: AbstractResource
8+
env :: Environment
9+
capacity :: N
10+
level :: N
11+
seid :: UInt
12+
Put_queue :: DataStructures.PriorityQueue{Put, ContainerKey{N}}
13+
Get_queue :: DataStructures.PriorityQueue{Get, ContainerKey{N}}
14+
function Container{N}(env::Environment, capacity::N, level::N) where {N<:Number}
15+
new(env, capacity, level, zero(UInt), DataStructures.PriorityQueue(Put, ContainerKey{N}), DataStructures.PriorityQueue(Get, ContainerKey{N}))
16+
end
17+
end
18+
19+
function Container{N<:Number}(env::Environment, capacity::N; level::N=zero(N))
20+
Container{N}(env, capacity, level)
21+
end
22+
23+
const Resource = Container{Int}
24+
25+
function Resource(env::Environment, capacity::Int=1; level::Int=0) :: Resource
26+
Resource(env, capacity, level)
27+
end
28+
29+
function Put{N<:Number}(con::Container{N}, amount::N; priority::Int=0) :: Put
30+
put_ev = Put(con.env)
31+
con.Put_queue[put_ev] = ContainerKey(priority, con.seid+=one(UInt), amount)
32+
append_callback(trigger_get, put_ev, con)
33+
trigger_put(put_ev, con)
34+
return put_ev
35+
end
36+
37+
const Request = Put
38+
39+
Request(res::Resource; priority::Int=0) = Put(res, 1, priority=priority)
40+
41+
macro request(res, req, expr)
42+
esc(quote
43+
$req = Request($res)
44+
$expr
45+
if state($req) == SimJulia.triggered
46+
@yield Release($res)
47+
else
48+
cancel($res, $req)
49+
end
50+
end)
51+
end
52+
53+
function request(func::Function, res::Resource; priority::Int=0)
54+
req = Request(res, priority=priority)
55+
try
56+
func(req)
57+
finally
58+
if state(req) == triggered
59+
yield(Release(res, priority=priority))
60+
else
61+
cancel(res, req)
62+
end
63+
end
64+
end
65+
66+
function Get{N<:Number}(con::Container{N}, amount::N; priority::Int=0) :: Get
67+
get_ev = Get(con.env)
68+
con.Get_queue[get_ev] = ContainerKey(priority, con.seid+=one(UInt), amount)
69+
append_callback(trigger_put, get_ev, con)
70+
trigger_get(get_ev, con)
71+
return get_ev
72+
end
73+
74+
const Release = Get
75+
76+
Release(res::Resource; priority::Int=0) = Get(res, 1, priority=priority)
77+
78+
function do_put{N<:Number}(con::Container{N}, put_ev::Put, key::ContainerKey{N}) :: Bool
79+
if con.level + key.amount <= con.capacity
80+
schedule(put_ev.bev)
81+
con.level += key.amount
82+
return true
83+
end
84+
return false
85+
end
86+
87+
function do_get{N<:Number}(con::Container{N}, get_ev::Get, key::ContainerKey{N}) :: Bool
88+
if con.level - key.amount >= zero(N)
89+
schedule(get_ev.bev)
90+
con.level -= key.amount
91+
return true
92+
end
93+
return false
94+
end

src/resources/stores.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
struct StorePutKey{T} <: ResourceKey
2+
priority :: Int
3+
id :: UInt
4+
item :: T
5+
end
6+
7+
struct StoreGetKey <: ResourceKey
8+
priority :: Int
9+
id :: UInt
10+
filter :: Function
11+
end
12+
13+
mutable struct Store{T} <: AbstractResource
14+
env :: Environment
15+
capacity :: UInt
16+
items :: Set{T}
17+
seid :: UInt
18+
Put_queue :: DataStructures.PriorityQueue{Put, StorePutKey{T}}
19+
Get_queue :: DataStructures.PriorityQueue{Get, StoreGetKey}
20+
function Store{T}(env::Environment, capacity::UInt) where {T}
21+
new(env, capacity, Set{T}(), zero(UInt), DataStructures.PriorityQueue(Put, StorePutKey{T}), DataStructures.PriorityQueue(Get, StoreGetKey))
22+
end
23+
end
24+
25+
function Store(t::Type, env::Environment, capacity::UInt=typemax(UInt))
26+
Store{t}(env, capacity)
27+
end
28+
29+
function Put{T}(sto::Store{T}, item::T; priority::Int=0) :: Put
30+
put_ev = Put(sto.env)
31+
sto.Put_queue[put_ev] = StorePutKey(priority, sto.seid+=one(UInt), item)
32+
append_callback(trigger_get, put_ev, sto)
33+
trigger_put(put_ev, sto)
34+
return put_ev
35+
end
36+
37+
function get_any_item{T}(::T) :: Bool
38+
return true
39+
end
40+
41+
function Get{T}(sto::Store{T}, filter::Function=get_any_item; priority::Int=0) :: Get
42+
get_ev = Get(sto.env)
43+
sto.Get_queue[get_ev] = StoreGetKey(priority, sto.seid+=one(UInt), filter)
44+
append_callback(trigger_put, get_ev, sto)
45+
trigger_get(get_ev, sto)
46+
return get_ev
47+
end
48+
49+
function do_put{T}(sto::Store{T}, put_ev::Put, key::StorePutKey{T}) :: Bool
50+
if length(sto.items) < sto.capacity
51+
push!(sto.items, key.item)
52+
schedule(put_ev.bev)
53+
end
54+
return false
55+
end
56+
57+
function do_get{T}(sto::Store{T}, get_ev::Get, key::StoreGetKey) :: Bool
58+
for item in sto.items
59+
if key.filter(item)
60+
delete!(sto.items, item)
61+
schedule(get_ev.bev, value=item)
62+
break
63+
end
64+
end
65+
return true
66+
end

test/benchmarks.jl/coroutines_MM1.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
using SimJulia, Distributions, BenchmarkTools
22

3-
@stateful function exp_source(sim::Simulation{SimJulia.SimulationTime}, lambd::Float64, server::Resource, mu::Float64)
3+
@stateful function exp_source(sim::Simulation, lambd::Float64, server::Resource, mu::Float64)
44
while true
55
dt = rand(Exponential(1/lambd))
66
@yield return Timeout(sim, dt)
77
@coroutine customer2(sim, server, mu)
88
end
99
end
1010

11-
@stateful function customer(sim::Simulation{SimJulia.SimulationTime}, server::Resource{Simulation{SimJulia.SimulationTime}}, mu::Float64)
11+
@stateful function customer(sim::Simulation, server::Resource, mu::Float64)
1212
@yield return Request(server)
1313
dt = rand(Exponential(1/mu))
1414
@yield return Timeout(sim, dt)
1515
@yield return Release(server)
1616
end
1717

18-
@stateful function customer2(sim::Simulation{SimJulia.SimulationTime}, server::Resource{Simulation{SimJulia.SimulationTime}}, mu::Float64)
18+
@stateful function customer2(sim::Simulation, server::Resource, mu::Float64)
1919
@request server req begin
2020
dt = rand(Exponential(1/mu))
2121
@yield return Timeout(sim, dt)

test/containers.jl

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using SimJulia
2+
3+
@stateful function client(sim::Simulation, res::Resource, i::Int, priority::Int)
4+
println("$(now(sim)), client $i is waiting")
5+
@yield return Request(res, priority=priority)
6+
println("$(now(sim)), client $i is being served")
7+
@yield return Timeout(sim, rand())
8+
println("$(now(sim)), client $i has been served")
9+
@yield return Release(res)
10+
end
11+
12+
@stateful function generate(sim::Simulation, res::Resource)
13+
i = 1
14+
while true
15+
@coroutine client(sim, res, i, 10-i)
16+
@yield return Timeout(sim, 0.5*rand())
17+
i == 10 && break
18+
i += 1
19+
end
20+
end
21+
22+
sim = Simulation()
23+
res = Resource(sim, 2; level=1)
24+
@coroutine generate(sim, res)
25+
run(sim)
26+
27+
@stateful function my_consumer(sim::Simulation, con::Container)
28+
i = 1
29+
while true
30+
amount = 3*rand()
31+
println("$(now(sim)), consumer is demanding $amount")
32+
@yield return Timeout(sim, 1.0*rand())
33+
get_ev = Get(con, amount)
34+
val = @yield return get_ev | Timeout(sim, rand())
35+
if val[get_ev].state == SimJulia.triggered
36+
level = con.level
37+
println("$(now(sim)), consumer is being served, level is $level")
38+
@yield return Timeout(sim, 5.0*rand())
39+
else
40+
println("$(now(sim)), consumer has timed out")
41+
cancel(con, get_ev)
42+
end
43+
i == 10 && break
44+
i += 1
45+
end
46+
end
47+
48+
@stateful function my_producer(sim::Simulation, con::Container)
49+
i = 1
50+
while true
51+
amount = 2*rand()
52+
println("$(now(sim)), producer is offering $amount")
53+
@yield return Timeout(sim, 1.0*rand())
54+
@yield return Put(con, amount)
55+
level = con.level
56+
println("$(now(sim)), producer is being served, level is $level")
57+
@yield return Timeout(sim, 5.0*rand())
58+
i == 10 && break
59+
i += 1
60+
end
61+
end
62+
63+
sim = Simulation()
64+
con = Container(sim, 10.0; level=5.0)
65+
@coroutine my_consumer(sim, con)
66+
@coroutine my_producer(sim, con)
67+
run(sim)
68+
69+
@stateful function resource_user(sim::Simulation, res::Resource, i::Int)
70+
@request res req begin
71+
println("Requested $i")
72+
val = @yield return req | Timeout(sim, rand())
73+
if val[req].state == SimJulia.triggered
74+
println("Received $i")
75+
@yield return Timeout(sim, rand())
76+
else
77+
println("Timeout $i")
78+
end
79+
end
80+
println("Released automatically $i")
81+
end
82+
83+
@stateful function create_users(sim::Simulation)
84+
res = Resource(sim)
85+
i = 1
86+
while true
87+
@coroutine resource_user(sim, res, i)
88+
@yield return Timeout(sim, rand())
89+
i == 10 && break
90+
i += 1
91+
end
92+
capacity(res)
93+
end
94+
95+
sim = Simulation()
96+
@coroutine create_users(sim)
97+
run(sim)
98+
99+
function resource_user_process(sim::Simulation, res::Resource, i::Int)
100+
request(res) do req
101+
println("Requested $i")
102+
val = yield(req | Timeout(sim, rand()))
103+
if val[req].state == SimJulia.triggered
104+
println("Received $i")
105+
yield(Timeout(sim, rand()))
106+
else
107+
println("Timeout $i")
108+
end
109+
end
110+
println("Released automatically $i")
111+
end
112+
113+
function create_users_process(sim::Simulation)
114+
res = Resource(sim)
115+
for i = 1:10
116+
@process resource_user_process(sim, res, i)
117+
yield(Timeout(sim, rand()))
118+
end
119+
capacity(res)
120+
end
121+
122+
sim = Simulation()
123+
@process create_users_process(sim)
124+
run(sim)

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ for test_file in [
99
"processes.jl",
1010
"finitestatemachines/utils.jl",
1111
"finitestatemachines/transforms.jl",
12-
"coroutines.jl"]
12+
"coroutines.jl",
13+
"containers.jl",
14+
"stores.jl",]
1315
include(testpath(test_file))
1416
end

0 commit comments

Comments
 (0)