Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority as number #90

Merged
merged 31 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6d46c77
Update base.jl
hdavid16 Jul 9, 2023
59d237d
Update events.jl
hdavid16 Jul 9, 2023
f0303d9
Update containers.jl
hdavid16 Jul 9, 2023
d8e1320
Update stores.jl
hdavid16 Jul 9, 2023
ba6f5ea
Update simulations.jl
hdavid16 Jul 9, 2023
2efeae2
Update time.jl
hdavid16 Jul 9, 2023
7913399
Update test_resources_containers.jl
hdavid16 Jul 9, 2023
485d520
update max priority to `Inf`
hdavid16 Jul 9, 2023
a4df1e6
Update max priority to `Inf`
hdavid16 Jul 9, 2023
c19c4f6
Update max priority to `Inf`
hdavid16 Jul 9, 2023
fae28d5
Update base.jl
hdavid16 Jul 9, 2023
deb26f2
Update base.jl
hdavid16 Jul 9, 2023
b60be22
Update base.jl
hdavid16 Jul 9, 2023
4f98ac3
Update base.jl
hdavid16 Jul 9, 2023
61f0693
Update base.jl
hdavid16 Jul 9, 2023
e02cda2
user parametric type
hdavid16 Jul 9, 2023
c3ba26d
Update stores.jl
hdavid16 Jul 9, 2023
1123ccc
use parametric type
hdavid16 Jul 9, 2023
d9e16cc
use parametric type
hdavid16 Jul 9, 2023
aa10136
fix struct internal function
hdavid16 Jul 9, 2023
a292a9c
remove `new` call in StorePutKey
hdavid16 Jul 9, 2023
baa80b4
allow priorities to be Number in Containers and Stores.
hdavid16 Jul 29, 2023
b21674b
make parameter type order consistent btw resources
hdavid16 Jul 29, 2023
3b46b15
Merge branch 'master' into priority_as_number
Krastanov Aug 2, 2023
cac0fc1
fixup merge
Krastanov Aug 2, 2023
32fccd3
fixup merge
Krastanov Aug 2, 2023
cedae0b
add tests for resources with different priorities
hdavid16 Aug 3, 2023
54238ec
allow put! and get to pass objects of different priority type
hdavid16 Aug 3, 2023
136aecf
remove type signatures from kwargs in resources
hdavid16 Aug 3, 2023
944135a
fixup
Krastanov Aug 3, 2023
fbbc732
fixup
Krastanov Aug 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function remove_callback(cb::Function, ev::AbstractEvent)
i != 0 && deleteat!(ev.bev.callbacks, i)
end

function schedule(ev::AbstractEvent, delay::Number=zero(Float64); priority::Int=0, value::Any=nothing)
function schedule(ev::AbstractEvent, delay::Number=0; priority::Number=0, value::Any=nothing)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a lot of these type restriction can simply be dropped. Types in function signatures are used only for dispatch, they do not provide performance improvements. And it seems types in kwargs do not even participate in dispatch (see https://discourse.julialang.org/t/force-specialization-on-kwargs/34789/14 ). So this makes types for kwargs (and in particular abstract types) not particularly valuable. In most of these situations (function signatures) you can just delete the ::Int, no need to add ::Number.

state(ev) === processed && throw(EventProcessed(ev))
env = environment(ev)
bev = ev.bev
Expand Down
2 changes: 1 addition & 1 deletion src/deprecated.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Base.@deprecate put(args...) put!(args...)
Base.@deprecate put(args...; kwargs...) put!(args...; kwargs...)
#Base.@deprecate request(args...; kwargs...) lock(args...; kwargs...) # Not the same: `request` needs to be yielded, while `lock` yields itself
#Base.@deprecate tryrequest(args...; kwargs...) trylock(args...; kwargs...) # Not the same: `request` needs to be yielded, while `lock` yields itself
Base.@deprecate release(args...; kwargs...) unlock(args...; kwargs...)
8 changes: 4 additions & 4 deletions src/events.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ struct Event <: AbstractEvent
end
end

function succeed(ev::Event; priority::Int=0, value::Any=nothing) :: Event
function succeed(ev::Event; priority::Number=0, value::Any=nothing) :: Event
state(ev) !== idle && throw(EventNotIdle(ev))
schedule(ev; priority=priority, value=value)
end

function fail(ev::Event, exc::Exception; priority::Int=0) :: Event
function fail(ev::Event, exc::Exception; priority::Number=0) :: Event
succeed(ev; priority=priority, value=exc)
end

Expand All @@ -21,10 +21,10 @@ struct Timeout <: AbstractEvent
end
end

function timeout(env::Environment, delay::Number=0; priority::Int=0, value::Any=nothing)
function timeout(env::Environment, delay::Number=0; priority::Number=0, value::Any=nothing)
schedule(Timeout(env), delay; priority=priority, value=value)
end

function run(env::Environment, until::Number=typemax(Float64))
function run(env::Environment, until::Number=Inf)
run(env, timeout(env, until-now(env)))
end
2 changes: 1 addition & 1 deletion src/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function check(ev::AbstractEvent, op::Operator, event_state_values::Dict{Abstrac
end
elseif state(op) === scheduled
if isa(val, Exception)
schedule(op; priority=typemax(Int), value=val)
schedule(op; priority=Inf, value=val)
else
event_state_values[ev] = StateValue(state(ev), val)
end
Expand Down
6 changes: 3 additions & 3 deletions src/processes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ end
function interrupt(proc::Process, cause::Any=nothing)
env = environment(proc)
if proc.fsmi._state !== 0xff
proc.target isa Initialize && schedule(proc.target; priority=typemax(Int))
target = schedule(Interrupt(env); priority=typemax(Int), value=InterruptException(active_process(env), cause))
proc.target isa Initialize && schedule(proc.target; priority=Inf)
target = schedule(Interrupt(env); priority=Inf, value=InterruptException(active_process(env), cause))
@callback execute_interrupt(target, proc)
end
timeout(env; priority=typemax(Int))
timeout(env; priority=Inf)
end
34 changes: 17 additions & 17 deletions src/resources/containers.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
struct ContainerKey{N<:Real} <: ResourceKey
priority :: Int
struct ContainerKey{N<:Real, T<:Number} <: ResourceKey
id :: UInt
amount :: N
priority :: T
end

"""
Expand All @@ -19,27 +19,27 @@ See [`Store`](@ref) for a more channel-like resource.

Think of `Resource` and `Container` as locks and of `Store` as channels. They block only if empty (on taking) or full (on storing).
"""
mutable struct Container{N<:Real} <: AbstractResource
mutable struct Container{N<:Real, T<:Number} <: AbstractResource
env :: Environment
capacity :: N
level :: N
seid :: UInt
put_queue :: DataStructures.PriorityQueue{Put, ContainerKey{N}}
get_queue :: DataStructures.PriorityQueue{Get, ContainerKey{N}}
function Container{N}(env::Environment, capacity::N=one(N); level::N=zero(N)) where {N<:Real}
new(env, capacity, level, zero(UInt), DataStructures.PriorityQueue{Put, ContainerKey{N}}(), DataStructures.PriorityQueue{Get, ContainerKey{N}}())
put_queue :: DataStructures.PriorityQueue{Put, ContainerKey{N, T}}
get_queue :: DataStructures.PriorityQueue{Get, ContainerKey{N, T}}
function Container{N, T}(env::Environment, capacity::N=one(N); level::N=zero(N)) where {N<:Real, T<:Number}
new(env, capacity, level, zero(UInt), DataStructures.PriorityQueue{Put, ContainerKey{N, T}}(), DataStructures.PriorityQueue{Get, ContainerKey{N, T}}())
end
end

function Container(env::Environment, capacity::N=one(N); level::N=zero(N)) where {N<:Real}
Container{N}(env, capacity, level=level)
Container{N, Int}(env, capacity, level=level)
end

const Resource = Container{Int}
const Resource = Container{Int, Int}

function put!(con::Container{N}, amount::N; priority::Int=0) where N<:Real
function put!(con::Container{N, T}, amount::N; priority::T=zero(T)) where {N<:Real, T<:Number}
put_ev = Put(con.env)
con.put_queue[put_ev] = ContainerKey(priority, con.seid+=one(UInt), amount)
con.put_queue[put_ev] = ContainerKey(con.seid+=one(UInt), amount, priority)
@callback trigger_get(put_ev, con)
trigger_put(put_ev, con)
put_ev
Expand All @@ -52,7 +52,7 @@ Locks the Container (or Resources) and return the lock event.
If the capacity of the Container is greater than 1,
multiple requests can be made before blocking occurs.
"""
request(res::Container; priority::Int=0) = put!(res, 1; priority=priority)
request(res::Resource; priority::Number=0) = put!(res, 1; priority=priority)

"""
tryrequest(res::Container)
Expand Down Expand Up @@ -81,9 +81,9 @@ function tryrequest(res::Container; priority::Int=0)
request(res; priority)
end

function get(con::Container{N}, amount::N; priority::Int=0) where N<:Real
function get(con::Container{N, T}, amount::N; priority::T=zero(T)) where {N<:Real, T<:Number}
get_ev = Get(con.env)
con.get_queue[get_ev] = ContainerKey(priority, con.seid+=one(UInt), amount)
con.get_queue[get_ev] = ContainerKey(con.seid+=one(UInt), amount, priority)
@callback trigger_put(get_ev, con)
trigger_get(get_ev, con)
get_ev
Expand All @@ -94,16 +94,16 @@ end

Unlocks the Container and return the unlock event.
"""
unlock(res::Container; priority::Int=0) = get(res, 1; priority=priority)
unlock(res::Resource; priority::Number=0) = get(res, 1; priority=priority)

function do_put(con::Container{N}, put_ev::Put, key::ContainerKey{N}) where N<:Real
function do_put(con::Container{N, T}, put_ev::Put, key::ContainerKey{N, T}) where {N<:Real, T<:Number}
con.level + key.amount > con.capacity && return false
schedule(put_ev)
con.level += key.amount
true
end

function do_get(con::Container{N}, get_ev::Get, key::ContainerKey{N}) where N<:Real
function do_get(con::Container{N, T}, get_ev::Get, key::ContainerKey{N, T}) where {N<:Real, T<:Number}
con.level - key.amount < zero(N) && return false
schedule(get_ev)
con.level -= key.amount
Expand Down
39 changes: 21 additions & 18 deletions src/resources/stores.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
struct StorePutKey{T} <: ResourceKey
priority :: Int
struct StorePutKey{N, T<:Number} <: ResourceKey
id :: UInt
item :: T
StorePutKey{T}(priority, id, item) where T = new(priority, id, item)
item :: N
priority :: T
end

struct StoreGetKey <: ResourceKey
priority :: Int
struct StoreGetKey{T<:Number} <: ResourceKey
id :: UInt
filter :: Function
priority :: T
end

"""
Expand All @@ -21,43 +20,47 @@ See [`Container`](@ref) for a more lock-like resource.

Think of `Resource` and `Container` as locks and of `Store` as channels. They block only if empty (on taking) or full (on storing).
"""
mutable struct Store{T} <: AbstractResource
mutable struct Store{N, T<:Number} <: AbstractResource
env :: Environment
capacity :: UInt
load :: UInt
items :: Dict{T, UInt}
items :: Dict{N, UInt}
seid :: UInt
put_queue :: DataStructures.PriorityQueue{Put, StorePutKey{T}}
get_queue :: DataStructures.PriorityQueue{Get, StoreGetKey}
function Store{T}(env::Environment; capacity=typemax(UInt)) where {T}
new(env, UInt(capacity), zero(UInt), Dict{T, UInt}(), zero(UInt), DataStructures.PriorityQueue{Put, StorePutKey{T}}(), DataStructures.PriorityQueue{Get, StoreGetKey}())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The explicit UInt(capacity) cast is removed in the new version. That cast is necessary in order to make capacity=10 work as a keyword argument (which used to be the case until this PR).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimJulia.jl had the UInt signature in the capacity kwarg. I like the idea of using UInt(capacity) instead of imposing this type on the kwarg, making it possible to use capacity=10 as you suggest. However, we need to still convert to UInt so that a Store cannot have a negative capacity.

put_queue :: DataStructures.PriorityQueue{Put, StorePutKey{N, T}}
get_queue :: DataStructures.PriorityQueue{Get, StoreGetKey{T}}
function Store{N, T}(env::Environment; capacity::UInt=typemax(UInt)) where {N, T<:Number}
new(env, capacity, zero(UInt), Dict{N, UInt}(), zero(UInt), DataStructures.PriorityQueue{Put, StorePutKey{N, T}}(), DataStructures.PriorityQueue{Get, StoreGetKey{T}}())
end
end

function Store{N}(env::Environment; capacity::UInt=typemax(UInt)) where {N}
hdavid16 marked this conversation as resolved.
Show resolved Hide resolved
Store{N, Int}(env; capacity)
end

"""
put!(sto::Store, item::T)

Put an item into the store. Returns the put event, blocking if the store is full.
"""
function put!(sto::Store{T}, item::T; priority::Int=0) where T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be modified, but just FYI, the ::T in the priority kwarg (and the previously existing ::Int) are unnecessary (if I understand Julia's dispatch rules correctly). Basically, type signatures of keyword arguments are not used for dispatch, so they are frequently superfluous.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are still useful as a type assert though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now convert to the appropriate type given the type of the Store

function put!(sto::Store{N, T}, item::N; priority::T=zero(T)) where {N, T<:Number}
put_ev = Put(sto.env)
sto.put_queue[put_ev] = StorePutKey{T}(priority, sto.seid+=one(UInt), item)
sto.put_queue[put_ev] = StorePutKey{N, T}(sto.seid+=one(UInt), item, priority)
@callback trigger_get(put_ev, sto)
trigger_put(put_ev, sto)
put_ev
end

get_any_item(::T) where T = true

function get(sto::Store{T}, filter::Function=get_any_item; priority::Int=0) where T
function get(sto::Store{N, T}, filter::Function=get_any_item; priority::T=zero(T)) where {N, T<:Number}
get_ev = Get(sto.env)
sto.get_queue[get_ev] = StoreGetKey(priority, sto.seid+=one(UInt), filter)
sto.get_queue[get_ev] = StoreGetKey(sto.seid+=one(UInt), filter, priority)
@callback trigger_put(get_ev, sto)
trigger_get(get_ev, sto)
get_ev
end

function do_put(sto::Store{T}, put_ev::Put, key::StorePutKey{T}) where {T}
function do_put(sto::Store{N, T}, put_ev::Put, key::StorePutKey{N, T}) where {N, T<:Number}
if sto.load < sto.capacity
sto.load += one(UInt)
sto.items[key.item] = get(sto.items, key.item, zero(UInt)) + one(UInt)
Expand All @@ -66,7 +69,7 @@ function do_put(sto::Store{T}, put_ev::Put, key::StorePutKey{T}) where {T}
false
end

function do_get(sto::Store{T}, get_ev::Get, key::StoreGetKey) where {T}
function do_get(sto::Store{N, T}, get_ev::Get, key::StoreGetKey{T}) where {N, T<:Number}
for (item, number) in sto.items
if key.filter(item)
sto.load -= one(UInt)
Expand Down
4 changes: 2 additions & 2 deletions src/simulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ end

struct EmptySchedule <: Exception end

struct EventKey
struct EventKey{N<:Number}
time :: Float64
priority :: Int
priority :: N
id :: UInt
end

Expand Down
2 changes: 1 addition & 1 deletion src/utils/time.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function run(env::Environment, until::DateTime)
run(env, Dates.datetime2epochms(until))
end

function timeout(env::Environment, delay::Period; priority::Int=0, value::Any=nothing)
function timeout(env::Environment, delay::Period; priority::Number=0, value::Any=nothing)
time = now(env)
del = Dates.datetime2epochms(Dates.epochms2datetime(time)+delay)-time
timeout(env, del; priority=priority, value=value)
Expand Down
2 changes: 1 addition & 1 deletion test/test_resources_containers.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using ConcurrentSim
using ResumableFunctions

@resumable function client(sim::Simulation, res::Resource, i::Int, priority::Int)
@resumable function client(sim::Simulation, res::Resource, i::Int, priority::Number)
println("$(now(sim)), client $i is waiting")
@yield request(res, priority=priority)
println("$(now(sim)), client $i is being served")
Expand Down