-
Notifications
You must be signed in to change notification settings - Fork 46
/
core.jl
110 lines (87 loc) · 2.71 KB
/
core.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import Base: push!, eltype, consume
export Signal, Input, Node, push!, value, step
##### Node #####
type Node{T}
value::T
actions::Vector{WeakRef}
alive::Bool
end
Node(x) = Node(x, WeakRef[], true)
Node{T}(::Type{T}, x) = Node{T}(x, WeakRef[], true)
typealias Signal Node
typealias Input Node
Base.show(io::IO, n::Node) =
write(io, "Node{$(eltype(n))}($(n.value), nactions=$(length(n.actions))$(n.alive ? "" : ", killed!"))")
value(n::Node) = n.value
eltype{T}(::Node{T}) = T
eltype{T}(::Type{Node{T}}) = T
kill(n::Node) = (n.alive = false; n)
##### Connections #####
const _recipients_dict = WeakKeyDict()
function add_action!(f, node, recipient)
push!(node.actions, WeakRef(f))
# hold on to the actions for nodes still in scope
if haskey(_recipients_dict, recipient)
push!(_recipients_dict[recipient], f)
else
_recipients_dict[recipient] = [f]
end
end
function send_value!(node, x, timestep)
# Dead node?
!node.alive && return
# Set the value and do actions
node.value = x
for action in node.actions
do_action(action.value, timestep)
end
end
# Nothing is a weakref gone stale.
do_action(f::Nothing, timestep) = nothing
do_action(f::Function, timestep) = f(timestep)
# If any actions have been gc'd, remove them
cleanup_actions(node::Node) =
node.actions = filter(n -> n.value != nothing, node.actions)
##### Messaging #####
if VERSION < v"0.4.0-dev"
using MessageUtils
queue_size(x) = length(fetch(x.rr).space)
else
channel(;sz=1024) = Channel{Any}(sz)
queue_size = Base.n_avail
end
const CHANNEL_SIZE = 1024
# Global channel for signal updates
const _messages = channel(sz=CHANNEL_SIZE)
# queue an update. meta comes back in a ReactiveException if there is an error
function Base.push!(n::Node, x; meta=nothing)
taken = queue_size(_messages)
if taken >= CHANNEL_SIZE
warn("Message queue is full. Ordering may be incorrect.")
@async put!(_messages, (n, x, meta))
else
put!(_messages, (n, x, meta))
end
nothing
end
include("exception.jl")
# remove messages from the channel and propagate them
global run
let timestep = 0
function run(steps=typemax(Int))
local waiting, node, value, debug_meta, iter = 1
try
while iter <= steps
timestep += 1
iter += 1
waiting = true
(node, value, debug_meta) = take!(_messages)
waiting = false
send_value!(node, value, timestep)
end
catch err
bt = catch_backtrace()
throw(ReactiveException(waiting, node, value, timestep, debug_meta, CapturedException(err, bt)))
end
end
end