Skip to content

Commit 91c7e36

Browse files
authored
implement StackStore and QueueStore (#98)
1 parent c8595c6 commit 91c7e36

File tree

6 files changed

+125
-11
lines changed

6 files changed

+125
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# News
22

3+
## v1.3.0 - 2023-08-07
4+
5+
- Implement ordered versions of `Store`, namely `QueueStore` and `StackStore`.
6+
37
## v1.2.0 - 2023-08-06
48

59
- Priorities can now be non-integer.

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ license = "MIT"
55
desc = "A discrete event process oriented simulation framework."
66
authors = ["Ben Lauwens and SimJulia and ConcurrentSim contributors"]
77
repo = "https://github.com/JuliaDynamics/ConcurrentSim.jl.git"
8-
version = "1.2.0"
8+
version = "1.3.0"
99

1010
[deps]
1111
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"

docs/src/guides/blockingandyielding.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ In particular, the `ConcurrentSim.jl` package uses the async "coroutines" model
2121
Without further ado, here is the typical API used with:
2222

2323
- `ConcurrentSim.Resource` which is used to represent scarce resource that can be used by only up to a fixed number of tasks. If the limit is just one task (the default), this is very similar to `Base.ReentrantLock`. `Resource` is a special case of `Container` with an integer "resource counter".
24-
- `ConcurrentSim.Store` which is used to represent a FILO stack.
24+
- `ConcurrentSim.Store` which is used to represent an unordered heap. For the ordered versions, consider `QueueStore` or `StackStore`.
2525

2626
```@raw html
2727
<div style="width:120%;min-width:120%;">

src/ConcurrentSim.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module ConcurrentSim
1717
export @resumable, @yield
1818
export AbstractProcess, Simulation, run, now, active_process, StopSimulation
1919
export Process, @process, interrupt
20-
export Container, Resource, Store, put!, get, cancel, request, tryrequest, release
20+
export Container, Resource, Store, StackStore, QueueStore, put!, get, cancel, request, tryrequest, release
2121
export nowDatetime
2222

2323
include("base.jl")
@@ -28,6 +28,7 @@ module ConcurrentSim
2828
include("resources/base.jl")
2929
include("resources/containers.jl")
3030
include("resources/stores.jl")
31+
include("resources/ordered_stores.jl")
3132
include("utils/time.jl")
3233
include("deprecated_aliased.jl")
3334
end

src/resources/ordered_stores.jl

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
StackStore{N, T<:Number}
3+
4+
A store in which items are stored in a FILO order.
5+
6+
```jldoctest
7+
julia> sim = Simulation()
8+
store = Store{Symbol}(sim)
9+
stack = StackStore{Symbol}(sim)
10+
items = [:a,:b,:a,:c];
11+
12+
julia> [put!(store, item) for item in items];
13+
14+
julia> [value(take!(store)) for _ in 1:length(items)]
15+
4-element Vector{Symbol}:
16+
:a
17+
:a
18+
:b
19+
:c
20+
21+
julia> [put!(stack, item) for item in items];
22+
23+
julia> [value(take!(stack)) for _ in 1:length(items)]
24+
4-element Vector{Symbol}:
25+
:c
26+
:a
27+
:b
28+
:a
29+
```
30+
31+
See also: [`QueueStore`](@ref), [`Store`](@ref)
32+
"""
33+
const StackStore = Store{N, T, DataStructures.Stack{N}} where {N, T<:Number}
34+
StackStore{N}(env::Environment; capacity=typemax(UInt)) where {N} = StackStore{N, Int}(env; capacity)
35+
36+
"""
37+
QueueStore{N, T<:Number}
38+
39+
A store in which items are stored in a FIFO order.
40+
41+
```jldoctest
42+
julia> sim = Simulation()
43+
store = Store{Symbol}(sim)
44+
queue = QueueStore{Symbol}(sim)
45+
items = [:a,:b,:a,:c];
46+
47+
julia> [put!(store, item) for item in items];
48+
49+
julia> [value(take!(store)) for _ in 1:length(items)]
50+
4-element Vector{Symbol}:
51+
:a
52+
:a
53+
:b
54+
:c
55+
56+
julia> [put!(queue, item) for item in items];
57+
58+
julia> [value(take!(queue)) for _ in 1:length(items)]
59+
4-element Vector{Symbol}:
60+
:a
61+
:b
62+
:a
63+
:c
64+
```
65+
66+
See also: [`StackStore`](@ref), [`Store`](@ref)
67+
"""
68+
const QueueStore = Store{N, T, DataStructures.Queue{N}} where {N, T<:Number}
69+
QueueStore{N}(env::Environment; capacity=typemax(UInt)) where {N} = QueueStore{N, Int}(env; capacity)
70+
71+
function do_put(sto::StackStore{N, T}, put_ev::Put, key::StorePutKey{N, T}) where {N, T<:Number}
72+
if sto.load < sto.capacity
73+
sto.load += one(UInt)
74+
push!(sto.items, key.item)
75+
schedule(put_ev)
76+
end
77+
false
78+
end
79+
80+
function do_get(sto::StackStore{N, T}, get_ev::Get, key::StoreGetKey{T}) where {N, T<:Number}
81+
key.filter !== get_any_item && error("Filtering not supported for `StackStore`. Use an unordered store instead, or submit a feature request for implementing filtering to our issue tracker.")
82+
item = pop!(sto.items)
83+
sto.load -= one(UInt)
84+
schedule(get_ev; value=item)
85+
true
86+
end
87+
88+
function do_put(sto::QueueStore{N, T}, put_ev::Put, key::StorePutKey{N, T}) where {N, T<:Number}
89+
if sto.load < sto.capacity
90+
sto.load += one(UInt)
91+
enqueue!(sto.items, key.item)
92+
schedule(put_ev)
93+
end
94+
false
95+
end
96+
97+
function do_get(sto::QueueStore{N, T}, get_ev::Get, key::StoreGetKey{T}) where {N, T<:Number}
98+
key.filter !== get_any_item && error("Filtering not supported for `QueueStore`. Use an unordered store instead, or submit a feature request for implementing filtering to our issue tracker.")
99+
item = dequeue!(sto.items)
100+
sto.load -= one(UInt)
101+
schedule(get_ev; value=item)
102+
true
103+
end

src/resources/stores.jl

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ end
1313
"""
1414
Store{N, T<:Number}(env::Environment; capacity::UInt=typemax(UInt))
1515
16-
A store is a resource that can hold a number of items of type `N` in a FILO stack. It is similar to a `Base.Channel` with a finite capacity ([`put!`](@ref) blocks after reaching capacity).
16+
A store is a resource that can hold a number of items of type `N`. It is similar to a `Base.Channel` with a finite capacity ([`put!`](@ref) blocks after reaching capacity).
1717
The [`put!`](@ref) and [`take!`](@ref) functions are a convenient way to interact with such a "channel" in a way mostly compatible with other discrete event and concurrency frameworks.
1818
1919
See [`Container`](@ref) for a more lock-like resource.
2020
2121
Think of `Resource` and `Container` as locks and of `Store` as channels/stacks. They block only if empty (on taking) or full (on storing).
2222
23+
`Store` does not guarantee any order of items. See [`StackStore`](@ref) and [`QueueStore`](@ref) for ordered variants.
24+
2325
```jldoctest
2426
julia> sim = Simulation(); store = Store{Int}(sim);
2527
@@ -32,19 +34,23 @@ julia> value(take!(store))
3234
1
3335
```
3436
"""
35-
mutable struct Store{N, T<:Number} <: AbstractResource
37+
mutable struct Store{N, T<:Number, D} <: AbstractResource
3638
env :: Environment
3739
capacity :: UInt
3840
load :: UInt
39-
items :: Dict{N, UInt}
41+
items :: D
4042
seid :: UInt
4143
put_queue :: DataStructures.PriorityQueue{Put, StorePutKey{N, T}}
4244
get_queue :: DataStructures.PriorityQueue{Get, StoreGetKey{T}}
43-
function Store{N, T}(env::Environment; capacity=typemax(UInt)) where {N, T<:Number}
44-
new(env, UInt(capacity), zero(UInt), Dict{N, UInt}(), zero(UInt), DataStructures.PriorityQueue{Put, StorePutKey{N, T}}(), DataStructures.PriorityQueue{Get, StoreGetKey{T}}())
45+
function Store{N, T, D}(env::Environment; capacity=typemax(UInt)) where {N, T<:Number, D}
46+
new(env, UInt(capacity), zero(UInt), D(), zero(UInt), DataStructures.PriorityQueue{Put, StorePutKey{N, T}}(), DataStructures.PriorityQueue{Get, StoreGetKey{T}}())
4547
end
4648
end
4749

50+
function Store{N, T}(env::Environment; capacity=typemax(UInt)) where {N, T<:Number}
51+
Store{N, T, Dict{N, UInt}}(env; capacity=UInt(capacity))
52+
end
53+
4854
function Store{N}(env::Environment; capacity=typemax(UInt)) where {N}
4955
Store{N, Int}(env; capacity=UInt(capacity))
5056
end
@@ -64,15 +70,15 @@ end
6470

6571
get_any_item(::N) where N = true
6672

67-
function get(sto::Store{N, T}, filter::Function=get_any_item; priority=zero(T)) where {N, T<:Number}
73+
function get(sto::Store{N, T, D}, filter::Function=get_any_item; priority=zero(T)) where {N, T<:Number, D}
6874
get_ev = Get(sto.env)
6975
sto.get_queue[get_ev] = StoreGetKey(sto.seid+=one(UInt), filter, T(priority))
7076
@callback trigger_put(get_ev, sto)
7177
trigger_get(get_ev, sto)
7278
get_ev
7379
end
7480

75-
function do_put(sto::Store{N, T}, put_ev::Put, key::StorePutKey{N, T}) where {N, T<:Number}
81+
function do_put(sto::Store{N, T, Dict{N,UInt}}, put_ev::Put, key::StorePutKey{N, T}) where {N, T<:Number}
7682
if sto.load < sto.capacity
7783
sto.load += one(UInt)
7884
sto.items[key.item] = get(sto.items, key.item, zero(UInt)) + one(UInt)
@@ -81,7 +87,7 @@ function do_put(sto::Store{N, T}, put_ev::Put, key::StorePutKey{N, T}) where {N,
8187
false
8288
end
8389

84-
function do_get(sto::Store{N, T}, get_ev::Get, key::StoreGetKey{T}) where {N, T<:Number}
90+
function do_get(sto::Store{N, T, Dict{N,UInt}}, get_ev::Get, key::StoreGetKey{T}) where {N, T<:Number}
8591
for (item, number) in sto.items
8692
if key.filter(item)
8793
sto.load -= one(UInt)

0 commit comments

Comments
 (0)