Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
pbayer committed Aug 14, 2020
2 parents b9412b4 + 48be545 commit 98728d4
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 30 deletions.
12 changes: 7 additions & 5 deletions docs/src/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
CurrentModule = DiscreteEvents
```

The following types are handled internally by `DiscreteEvents.jl`. But they maybe interesting
for analyzing and debugging clocks and event schedules.
The following types are handled internally by `DiscreteEvents.jl`, but are necessary for analyzing and debugging clocks and event schedules.

## Events

Expand All @@ -14,17 +13,20 @@ AbstractEvent
DiscreteEvent
DiscreteCond
Sample
ClockException
```

## Clock

```@docs
prettyClock
AbstractClock
ActiveClock
Schedule
ClockChannel
```

`ActiveClock`s are internal since the should not be setup explicitly.
## Error handling and diagnosis

```@docs
ClockException
prettyClock
```
35 changes: 25 additions & 10 deletions docs/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ version

## Clocks

Clocks schedule and execute *events*. They make given computations happen at a specified time (or under specified conditions).
Clocks schedule and execute *actions*, computations that happen as *events* at specified times (or under specified conditions).

- `Clock`s have virtual time and precede as fast as possible when they simulate chains of events. For parallel simulations they can control [`ActiveClock`](@ref)s on parallel threads.
- `RTClock`s schedule and execute events on a real (system) time line.
- `Clock`s have virtual time and precede as fast as possible. They can control [`ActiveClock`](@ref)s on parallel threads to support parallel simulations.
- `RTClock`s schedule and execute actions on a real (system) time line.

Clocks have an identification number:

- a master `Clock` on thread has id = 0,
- worker [`ActiveClock`](@ref)s on parallel threads have id ≥ 1,
- a master `Clock` on thread 1 has id = 1,
- worker [`ActiveClock`](@ref)s on parallel threads have id > 1 identical with the thread number,
- real time `RTClock`s have id ≤ -1.

```@docs
Expand All @@ -42,14 +42,29 @@ There is a default clock `𝐶`, which can be used for experimental work.
𝐶
```

You can create a clock with parallel active clocks on all available threads or fork existing clocks to other threads or collapse them if no longer needed. You can get direct access to parallel [`ActiveClock`](@ref)s and diagnose them.
### Parallel clocks

You can create a clock with parallel active clocks on all available threads. Parallel operations (processes and actors) can get their local clock from it.

```@docs
PClock
localClock
```

Furthermore you can fork existing clocks to other threads or collapse them if no longer needed. You can get direct access to parallel [`ActiveClock`](@ref)s and diagnose them.

```@docs
fork!
pclock
collapse!
diagnose
```

### Real time clocks

Real time clocks allow to schedule and execute events on a physical time line.

```@docs
createRTClock
stopRTClock
```
Expand Down Expand Up @@ -126,13 +141,13 @@ println(::Clock, ::Any)

## Actors

[Actors](https://en.wikipedia.org/wiki/Actor_model) can operate as finite state machines and are more reactive than processes. They run as Julia tasks listening to a (message) channel.

In order to integrate into the `DiscreteEvents` framework, they can `push!` their channels to the `clock.channel` vector. Then the clock will only proceed to the next event if all pushed channels are empty and the associated actors have finished processing the current event.
[Actors](https://en.wikipedia.org/wiki/Actor_model) can operate as finite state machines, are more reactive than processes and can compose. They run as Julia tasks listening to a (message) channel. In order to integrate into the `DiscreteEvents` framework, they can `push!` their channels to the `clock.channel` vector. Then the clock will only proceed to the next event if all pushed channels are empty and the associated actors have finished processing the current event.

!!! note "Actor support is minimal"

`DiscreteEvents` currently does not provide more actor support. See the [companion site](https://pbayer.github.io/DiscreteEventsCompanion.jl/dev/actors/) for code examples with actors and [`YAActL`](https://github.com/pbayer/YAActL.jl).
See the [companion site](https://pbayer.github.io/DiscreteEventsCompanion.jl/dev/actors/) for code examples with actors and [`YAActL`](https://github.com/pbayer/YAActL.jl). `YAActL` provides `register!` for integration into the `DiscreteEvents` framework.

Despite of minimal actor support a lot can be done yet with actors. Actors push the boundaries of discrete event simulation.

## Running simulations

Expand Down
2 changes: 1 addition & 1 deletion src/DiscreteEvents.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export Clock, PClock, RTClock, setUnit!, 𝐶,
tau, sample_time!, fun, event!, periodic!, register!,
incr!, run!, stop!, resume!, sync!, resetClock!,
Prc, process!, interrupt!, delay!, wait!, now!,
fork!, collapse!, pclock, diagnose, onthread,
fork!, collapse!, pclock, localClock, diagnose, onthread,
createRTClock, stopRTClock,
Resource

Expand Down
9 changes: 9 additions & 0 deletions src/threads.jl
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ function pclock(ac::ActiveClock, id::Int)
id == ac.clock.id ? ac : pclock(ac.master[], id)
end

"""
localClock(clk::Clock)
Get the thread local clock from a master clock. Since it involves a
communication over the threads, it should only called once for
setting up a parallel operation.
"""
localClock(clk::Clock) = pclock(clk, threadid()).clock

"""
diagnose(master::Clock, id::Int)
Expand Down
39 changes: 25 additions & 14 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,10 @@ end
"""
ActiveClock{E <: ClockEvent} <: AbstractClock
A thread specific clock which can be operated via a channel.
An active clock is a wrapper around a worker [`Clock`](@ref) on a
parallel thread. Worker clocks are operated by actors and the master
clock on thread 1 communicates with them through messages over the
active clock channels.
# Fields
- `clock::Clock`: the thread specific clock,
Expand All @@ -286,17 +289,22 @@ A thread specific clock which can be operated via a channel.
- `id::Int`: the clocks id/thread number,
- `task::Task`: the active clock`s task.
!!! note
You should not setup an `ActiveClock` explicitly. Rather this is done
implicitly by [`fork!`](@ref)ing a [`Clock`](@ref) to other available threads
or directly with [`PClock`](@ref).
It then can be accessed via [`pclock`](@ref) as in the following example.
!!! note "Don't setup an `ActiveClock` explicitly!"
!!! note
Directly accessing the `clock`-field of parallel `ActiveClock`s in simulations is
possible but not recommended since it breaks parallel operation. The right way is
to pass `event!`s to the `ActiveClock`-variable. The communication then happens
over the channel to the `ActiveClock` as it should be.
It is done implicitly with [`PClock`](@ref) or by [`fork!`](@ref)ing
a [`Clock`](@ref) to other available threads.
An active clock can be accessed via [`pclock`](@ref) as in the following example.
!!! note "Don't access the `clock`-field of `ActiveClock`s!"
This is possible but not recommended since it can break parallel
operation. On a parallel thread processes and actors can access
their local clock with `pclock(master, threadid()).clock`.
Events can be scheduled with `event!` to an `ActiveClock`-variable.
The communication then happens over the channel to the `ActiveClock`
as it should be.
# Example
```jldoctest; filter = r"^.*[0-9]+ ac.*"
Expand All @@ -308,18 +316,21 @@ Clock 1: state=DiscreteEvents.Undefined(), t=0.0 , Δt=0.01 , prc:0
julia> fork!(clk)
julia> clk # now you get parallel active clocks
julia> clk # now you see parallel active clocks
Clock 1 (+7): state=DiscreteEvents.Undefined(), t=0.0 , Δt=0.01 , prc:0
scheduled ev:0, cev:0, sampl:0
julia> clk = PClock()
julia> clk = PClock() # create a parallel clock structure
Clock 1 (+7): state=DiscreteEvents.Undefined(), 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=DiscreteEvents.Idle(), t=0.0 , Δt=0.01 , prc:0
scheduled ev:0, cev:0, sampl:0
julia> ac2.clock # not recommended, access the parallel clock 2
Clock 2: state=DiscreteEvents.Idle(), t=0.0 , Δt=0.01 , prc:0
scheduled ev:0, cev:0, sampl:0
```
"""
mutable struct ActiveClock{E <: ClockEvent} <: AbstractClock
Expand Down

0 comments on commit 98728d4

Please sign in to comment.