Internals
The following types are handled internally by DiscreteEvents.jl
, but maybe necessary for analyzing and debugging clocks and event schedules.
Events
DiscreteEvents.AbstractEvent
— TypeSupertype for events.
DiscreteEvents.DiscreteEvent
— Type`DiscreteEvent{T<:Action, X} <: AbstractEvent
A discrete event is an Action
to be executed at an event time.
Arguments, fields
ex::T
: a function or an expression or a tuple of them,t::Float64
: event time,Δt::X
: repeat interval (Float64, Distribution or Nothing),n::Int
: number of repeats.
DiscreteEvents.DiscreteCond
— TypeDiscreteCond{S<:Action, T<:Action} <: AbstractEvent
A condition to be evaluated repeatedly with expressions or functions to be executed if conditions are met.
Arguments, fields
cond::S
: a conditional function or an expression or a tuple of them (conditions must evaluate toBool
),ex::T
: a function or an expression or a tuple of them to be executed if conditions are met,
DiscreteEvents.Sample
— TypeSample{T<:Action} <: AbstractEvent
Sampling actions are executed at sampling time.
Arguments, fields
ex<:Action
: anAction
to be executed at sample time.
Clocks
There is an abstract type for clocks and an active clock, used for controlling parallel clocks.
DiscreteEvents.AbstractClock
— TypeSupertype for clocks.
DiscreteEvents.GlobalClock
— TypeGlobal clock with vector of active clocks.
DiscreteEvents.ActiveClock
— TypeActiveClock{E <: ClockEvent} <: AbstractClock
An active clock is a wrapper around a local Clock
on a parallel thread. It is operated by a thread local task. The master clock on thread 1 communicates with it through messages over its channels.
Fields
clock::Clock
: the thread specific local clock,master::Ref{Clock}
: a pointer to the master clock (on thread 1),forth::Channel{E}
: the command channel from master,back::Channel{E}
: the response channel to master,id::Int
: the clocks id/thread number,task::Task
: the active clock`s task.
On a parallel thread tasks can access their local clock with pclock(clk)
. Then they can schedule 'thread local' events, delay!
or wait!
on it.
In multithreading we communicate over channels and don't want to share variables between threads. A user can still access active clocks for diagnostic purposes.
DiscreteEvents.LocalClock
— TypeLocal clock with reference to a wrapping active clock.
DiscreteEvents.localClock
— FunctionlocalClock(c::Clock)
Create a thread local clock with an undefined reference to an active clock which inherits parameters of the master clock c
.
An example on active clocks:
julia> using DiscreteEvents
julia> clk = Clock() # create a clock
Clock 1: state=:idle, t=0.0, Δt=0.01, prc:0
scheduled ev:0, cev:0, sampl:0
julia> fork!(clk) # fork it to parallel threads
julia> clk # now you see parallel active clocks
Clock 1 (+1): state=:idle, t=0.0, Δt=0.01, prc:0
scheduled ev:0, cev:0, sampl:0
julia> clk = PClock() # create a parallel clock structure
Clock 1 (+1): state=:idle, t=0.0, Δt=0.01, prc:0
scheduled ev:0, cev:0, sampl:0
julia> ac2 = pclock(clk, 2) # get access to the active clock on thread 2
Active clock 2: state=:idle, t=0.0, Δt=0.01, prc:0
scheduled ev:0, cev:0, sampl:0
julia> ac2.clock # access the parallel clock 2
Clock 2: state=:idle, t=0.0, Δt=0.01, prc:0
scheduled ev:0, cev:0, sampl:0
Schedule
and ClockChannel
are two important Clock
substructures:
DiscreteEvents.Schedule
— TypeSchedule()
A schedule contains events, conditional events and sampling functions to be executed or evaluated on the clock's time line.
Fields
events::PriorityQueue{DiscreteEvent,Float64}
: scheduled events,cevents::Array{DiscreteCond,1}
: conditional events to evaluate at each tick,samples::Array{Sample,1}
: sampling expressions to evaluate at each tick,
DiscreteEvents.ClockChannel
— TypeClockChannel{T <: ClockEvent}
Provide a message channel to an active clock or a real time clock.
Fields
ref::Ref{Task}
: a reference to an active clock task, useful for diagnosis,forth::Channel{T}
: a communication channel to an active clock,back::Channel{T}
: response channel from the active clock,thread::Int
: the thread id of the active clock,done::Bool
: flag indicating if the active clock has completed its cycle,load::Int
: internal flag.
Clock concurrency
If a task after activation by the clock gives control back to the Julia scheduler (e.g. by reading from a channel or by doing an IO-operation), it enqueues for its next schedule behind the clock. The clock may then increment time to $t_{i+1}$ before the task can finish its job at current event time $t_i$.
There are several ways to solve this problem:
- The clock does a 2ⁿᵈ
yield()
after invoking a task and enqueues again at the end of the scheduling queue. This is implemented fordelay!
andwait!
of processes and should be enough for most those cases. - Actors
push!
their message channel to theclock.channels
vector and the clock will only proceed to the next event if all registered channels are empty [1]. - Tasks use
now!
to let the (master) clock do IO-operations for them. They can alsoprint
via the clock.
Error handling and diagnosis
DiscreteEvents.prettyClock
— FunctionprettyClock(on::Bool)
Switch pritty printing for clocks on and off.