1 # vim: foldmarker=<<<,>>>
4 inherit tlc
::Handlers tlc
::Baselog
6 constructor
{accessvar args
} {}
12 variable output_handler_warntime
3000
15 method set_state
{newstate
}
16 method toggle_state
{}
18 method attach_output
{handler
{cleanup
{}}}
19 method detach_output
{handler
}
22 method explain_txt
{{depth
0}}
24 method waitfor
{sense
{timeout
0}}
35 method update_output
{handler
}
36 method update_outputs
{}
37 method debug
{level msg
}
38 method _on_set_state
{pending
}
39 method throw_hissy
{handler
}
40 method scopevar_unset
{args
}
45 configbody tlc
::Signal::name { #<<<1
46 set baselog_instancename
$name
50 body tlc
::Signal::constructor {accessvar args
} { #<<<1
52 array set changewait
{}
55 upvar $accessvar scopevar
57 #trace variable scopevar u [list delete object $this]
58 trace variable scopevar u
[code
$this scopevar_unset
]
62 body tlc
::Signal::destructor {} { #<<<1
63 debug debug
"tlc::Signal::destructor: $this $name dieing"
65 debug debug
"tlc::Signal::destructor: $this dieing from:\n[tlc::stackdump]"
67 #trace vdelete scopevar u [code $this scopevar_unset]
69 foreach {tag afterid
} [array get afterids
] {
71 array unset afterids
$tag
74 foreach output
$outputs {
75 debug debug
"tlc::Signal::destructor: ------ twitch: ($output)"
79 foreach key
[array names changewait
] {
80 debug debug
"notifying waiting changewait($key) of our death"
81 set changewait
($key) "source_died"
83 debug debug
"tlc::Signal::destructor: $this truely dead"
87 body tlc
::Signal::state {} { #<<<1
92 body tlc
::Signal::set_state {newstate
} { #<<<1
93 if {![string is boolean
-strict $newstate]} {
94 error "newstate must be a valid boolean" "" \
95 [list not_a_boolean
$newstate]
102 _on_set_state
$normstate
103 if {$o_state == $normstate} return
104 set o_state
$normstate
109 body tlc
::Signal::toggle_state {} { #<<<1
110 set_state
[expr {!$o_state}]
114 body tlc
::Signal::attach_output {handler
{cleanup
{}}} { #<<<1
115 if {$handler in
$outputs} {
119 lappend outputs
$handler
121 if {$cleanup != {}} {
122 set cleanups
($handler) $cleanup
124 update_output
$handler
130 body tlc
::Signal::detach_output {handler
} { #<<<1
131 debug debug
"tlc::Signal::detach_output: ($handler)"
132 if {$handler in
$outputs} {
133 set idx
[lsearch $outputs $handler]
134 set outputs
[lreplace $outputs $idx $idx]
136 if {[info exists cleanups
($handler)]} {
137 debug debug
"tlc::Signal::detach_output: cleaning up ($handler)"
138 debug debug
"tlc::Signal::detach_output: foo"
139 uplevel #0 $cleanups($handler)
140 debug debug
"tlc::Signal::detach_output: bar"
141 unset cleanups
($handler)
146 debug debug
"tlc::Signal:detach_output: output not found!!\n($handler)\n[join $outputs \n]]\n============================="
152 body tlc
::Signal::update_output {handler
} { #<<<1
153 #puts stderr "Signal::update_output($o_state): $name ($this) update output ($handler)"
155 set pending_afterid
[after $output_handler_warntime \
156 [code
$this throw_hissy
$handler]]
157 set afterids
(update_output_
$handler) $pending_afterid
160 uplevel #0 $handler [list $o_state]
162 log
error "\n\"$name\" error updating output ($o_state) handler: ($handler) $name ($this): $errmsg\n$::errorInfo"
165 after cancel
$pending_afterid
166 array unset afterids update_output_
$handler
168 #puts stderr "Signal::update_output: $name ($this) done"
172 body tlc
::Signal::update_outputs {} { #<<<1
173 foreach output
$outputs {
174 update_output
$output
176 debug debug
"tlc::Signal::update_outputs: Flagging changewaits: ([array names changewait])"
177 foreach key
[array names changewait
] {
178 debug debug
"tlc::Signal::update_outputs: flagging state change for waiting vwait: changewait($key) to ($o_state)"
179 set changewait
($key) $o_state
184 body tlc
::Signal::name {} { #<<<1
189 body tlc
::Signal::explain_txt {{depth
0}} { #<<<1
190 return "[string repeat { } $depth]$this \"[$this name]\": [$this state]\n"
194 body tlc
::Signal::debug {level msg
} { #<<<1
195 invoke_handlers debug
$level $msg
199 body tlc
::Signal::_on_set_state {pending
} { #<<<1
203 body tlc
::Signal::throw_hissy {handler
} { #<<<1
204 log warning
"name: ($name) obj: ($this) taking way too long to update output for handler: ($handler)"
208 body tlc
::Signal::scopevar_unset {args
} { #<<<1
209 #puts stderr "Signal::scopevar_unset: $name ($this) scopevar unset"
211 debug debug
"tlc::Signal::scopevar_unset:\n[tlc::stackdump]"
218 body tlc
::Signal::waitfor {sense
{timeout
0}} { #<<<1
232 [after $timeout [list set [scope changewait
($myseq)] "timeout"]]
233 set afterids
(waitfor_
$myseq) $afterid
236 debug debug
"tlc::Signal::waitfor: Waiting for [scope changewait($myseq)]"
238 while {!($resolved)} {
239 set changewait
($myseq) "waiting"
240 vwait [scope changewait
($myseq)]
241 set res
$changewait($myseq)
242 if {[string is boolean
$res] && [state
] != $normsense} {
243 log warning
"Woken up by transient spike while waiting for state $sense, waiting for more permanent change"
250 if {[info exists afterid
]} {
251 after cancel
$afterid
255 if {$res != "source_died"} {
256 # in the case where we have died, these data members have disappeared,
257 # and to try to access them (like unsetting them) causes a segfault
259 array unset afterids waitfor_
$myseq
260 array unset changewait
$myseq
270 error "Timeout waiting for signal \"$signame\"" "" \
271 [list timeout
$signame]
275 error "Source died while waiting for signal \"$signame\"" "" \
276 [list source_died
$signame]
280 error "Unexpected result waiting for signal \"$signame\": ($res)"