Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Moved the coroutine registration system from TOOL over to the cron module Refactored how the loop and panic alarm system works for timed events in tool/cron Bumped the versions for both cron and tool |
---|---|
Timelines: | family | ancestors | descendants | both | odie |
Files: | files | file ages | folders |
SHA1: |
e6d8e5ee15284e5d491e3746b2fafe37 |
User & Date: | tne 2016-07-20 19:14:40 |
Context
2016-07-20
| ||
21:47 | Moved the cluster::sleep function to the cron module Added a coroutine aware function called "sleep" to the cron system. This allows either a routine or coroutine to sleep in a multi-tasking friendly way. check-in: bc65bd4004 user: tne tags: odie | |
19:14 | Moved the coroutine registration system from TOOL over to the cron module Refactored how the loop and panic alarm system works for timed events in tool/cron Bumped the versions for both cron and tool check-in: e6d8e5ee15 user: tne tags: odie | |
2016-07-19
| ||
19:12 | Removed the dependency on coroutine-auto for tool. Adds a dependency on coroutine (and on the developer's own head be it if he/she decides to invoke global and update in a coro.) check-in: bc1da3f33c user: tne tags: odie | |
Changes
Changes to modules/cron/cron.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ### # This file implements a process table # Instead of having individual components try to maintain their own timers # we centrally manage how often tasks should be kicked off here. ### # # Author: Sean Woods (for T&E Solutions) ::namespace eval ::cron {} proc ::cron::at args { switch [llength $args] { 2 { variable processuid | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ### # This file implements a process table # Instead of having individual components try to maintain their own timers # we centrally manage how often tasks should be kicked off here. ### # # Author: Sean Woods (for T&E Solutions) package require coroutine ::namespace eval ::cron {} proc ::cron::at args { switch [llength $args] { 2 { variable processuid |
︙ | ︙ | |||
32 33 34 35 36 37 38 | set info [list process $process frequency 0 command $command scheduled $scheduled lastevent $now] if ![info exists processTable($process)] { lappend info lastrun 0 err 0 result {} } foreach {field value} $info { dict set processTable($process) $field $value } | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | set info [list process $process frequency 0 command $command scheduled $scheduled lastevent $now] if ![info exists processTable($process)] { lappend info lastrun 0 err 0 result {} } foreach {field value} $info { dict set processTable($process) $field $value } ::cron::wake NEW return $process } proc ::cron::in args { switch [llength $args] { 2 { variable processuid |
︙ | ︙ | |||
60 61 62 63 64 65 66 | set info [list process $process frequency 0 command $command scheduled $scheduled lastevent $now] if ![info exists processTable($process)] { lappend info lastrun 0 err 0 result {} } foreach {field value} $info { dict set processTable($process) $field $value } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > < | 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | set info [list process $process frequency 0 command $command scheduled $scheduled lastevent $now] if ![info exists processTable($process)] { lappend info lastrun 0 err 0 result {} } foreach {field value} $info { dict set processTable($process) $field $value } ::cron::wake NEW return $process } proc ::cron::cancel {process} { variable processTable unset -nocomplain processTable($process) } proc ::cron::coroutine_register {objname coroutine} { variable all_coroutines variable object_coroutines variable coroutine_object # Wake a sleeping main loop ::cron::wake ::cron::coroutine_register if {$coroutine in $all_coroutines} { return 1 } lappend all_coroutines $coroutine lappend object_coroutines($objname) $coroutine set coroutine_object($coroutine) $objname return 0 } proc ::cron::coroutine_unregister {coroutine} { variable all_coroutines variable object_coroutines variable coroutine_object ldelete all_coroutines $coroutine if {[info exists coroutine_object($coroutine)]} { set objname $coroutine_object($coroutine) ldelete object_coroutines($objname) $coroutine unset coroutine_object($coroutine) } } proc ::cron::do_events {} { # Process coroutines variable all_coroutines variable coroutine_object variable coroutine_busy variable last_event set last_event [clock seconds] set count 0 foreach coro $all_coroutines { if {![info exists coroutine_busy($coro)]} { set coroutine_busy($coro) 0 } # Prevent a stuck coroutine from logjamming the entire event loop if {$coroutine_busy($coro)} continue set coroutine_busy($coro) 1 if {[info command $coro] eq {}} { #puts "$coro quit" coroutine_unregister $coro continue } set deleted 0 #puts [list RUN $coro] try $coro on return {} { # Terminate the coroutine coroutine_unregister $coro } on break {} { # Terminate the coroutine coroutine_unregister $coro } on error {errtxt errdat} { # Coroutine encountered an error coroutine_unregister $coro puts "ERROR $coro" set errorinfo $::errorInfo catch { puts "OBJECT: $coroutine_object($coro)" puts "CLASS: [info object class $coroutine_object($coro)]" } puts "$errtxt" puts *** puts $errorinfo } on continue {result opts} { # Ignore continue if { $result eq "done" } { incr count coroutine_unregister $coro set deleted 1 } } on ok {result opts} { if { $result eq "done" } { coroutine_unregister $coro set deleted 1 } else { incr count } } if {$deleted} { unset -nocomplain coroutine_busy($coro) } else { set coroutine_busy($coro) 0 } } return $count } ### # topic: 0776dccd7e84530fa6412e507c02487c ### proc ::cron::every {process frequency command} { variable processTable set now [clock seconds] set info [list process $process frequency $frequency command $command scheduled [expr {$now + $frequency}] lastevent $now] if ![info exists processTable($process)] { lappend info lastrun 0 err 0 result {} } foreach {field value} $info { dict set processTable($process) $field $value } ::cron::wake NEW } ### # topic: 97015814408714af539f35856f85bce6 ### proc ::cron::run process { variable processTable dict set processTable($process) lastrun 0 ::cron::wake PROCESS } ### # topic: 21de7bb8db019f3a2fd5a6ae9b38fd55 # description: # Called once per second, and timed to ensure # we run in roughly realtime ### proc ::cron::runTasksCoro {} { ### # Do this forever ### variable processTable variable processing while 1 { set lastevent 0 set now [clock seconds] ### # Determine what tasks to run this timestep ### set tasks {} set cancellist {} foreach {process} [lsort -dictionary [array names processTable]] { dict with processTable($process) { if { $scheduled <= $now } { lappend tasks $process if { $frequency <= 0 } { lappend cancellist $process } else { set scheduled [expr {$frequency + $lastrun}] if { $scheduled <= $now } { set scheduled [expr {$frequency + $now}] } } set lastrun $now } set lastevent $now } } foreach task $tasks { dict set processTable($task) lastrun $now dict with processTable($task) { set err [catch {uplevel #0 $command} result] if $err { puts $result } } yield 0 } foreach {task} $cancellist { unset -nocomplain processTable($task) } # Wake me up in 5 minute intervals, just out of principle set nextevent [expr {$now-($now % 300) + 300}] set nexttask {} foreach {process} [lsort -dictionary [array names processTable]] { dict with processTable($process) { if {$scheduled < $nextevent} { set nexttask $process set nextevent $scheduled |
︙ | ︙ | |||
232 233 234 235 236 237 238 | set delay [expr {$delay-($delay % 60) + 60}] } yield $delay } } } | < < | < > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > > | > > > > > > > | | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | set delay [expr {$delay-($delay % 60) + 60}] } yield $delay } } } proc ::cron::wake {{who ???}} { ## # Only triggered by cron jobs kicking off other cron jobs within # the script body ## after cancel $::cron::next_event if {$who eq "PANIC"} { # Cron is overdue and may be stuck set ::cron::busy 0 set ::cron::panic_event {} } if {$::cron::busy && $::cron::panic_event eq {}} { puts "BUSY..." after cancel $::cron::panic_event set ::cron::panic_event [after 120000 {::cron::wake PANIC}] return } set now [clock seconds] set ::cron::busy 1 while {$::cron::busy} { after cancel $::cron::panic_event set ::cron::panic_event [after 120000 {::cron::wake PANIC}] if {[info command ::cron::COROUTINE] eq {}} { coroutine ::cron::COROUTINE ::cron::runTasksCoro } set cron_delay [::cron::COROUTINE] set tool_running [::cron::do_events] if {$cron_delay==0 || $tool_running>0} { set ::cron::wake_time 0 incr ::cron::loops(active) } else { set ::cron::busy 0 incr ::cron::loops(idle) } } ### # Try to get the event to fire off on the border of the # nearest second ### set ::cron::wake_time [expr {[clock seconds]+$cron_delay}] set ctime [clock milliseconds] set next [expr {$cron_delay*1000-1000+($ctime % 1000)}] if {$::cron::trace} { puts [list EVENT LOOP WILL WAKE IN $cron_delay s next: $next active: $::cron::loops(active) idle: $::cron::loops(idle) woken_by: $who] } set ::cron::next_event [after $next {::cron::wake IDLE}] } proc ::cron::main {} { # Never launch from a coroutine if {[info coroutine] ne {}} { return } set ::cron::forever 1 while {$::cron::forever} { ::after 120000 {set ::cron::waiting 0} # Call an update just to give the rest of the event loop a chance incr ::cron::loops(main) ::after cancel $::cron::next_event set ::cron::next_event [::after idle {::cron::wake MAIN}] set ::cron::forever 1 set ::cron::busy 0 ::vwait ::cron::forever if {$::cron::trace} { puts "MAIN LOOP CYCLE $::cron::loops(main)" } } } ### # topic: 4a891d0caabc6e25fbec9514ea8104dd # description: # This file implements a process table # Instead of having individual components try to maintain their own timers # we centrally manage how often tasks should be kicked off here. ### namespace eval ::cron { variable lastcall 0 variable processTable variable busy 0 variable next_event {} variable trace 0 variable event_loops variable panic_event {} if {![info exists event_loops]} { set event_loops 0 } if {![info exists ::cron::loops]} { array set ::cron::loops { active 0 main 0 idle 0 wake 0 } } variable all_coroutines if {![info exists all_coroutines]} { set all_coroutines {} } } ::cron::wake STARTUP package provide cron 1.3 |
Changes to modules/cron/cron.test.
1 2 3 4 5 6 | # Tests for the cron module # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # Tests for the cron module # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright (c) 2016 by Sean Woods # (Insert BSDish style "use at your own risk" license text) source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] package require tcltest testsNeedTcl 8.6 testsNeedTcltest 1.0 testing { useLocal cron.tcl cron } set timecounter 0 |
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 | after 6000 {set pause 0} vwait pause test cron-1.8 {cron::in} { set ::inevent } 1 testsuiteCleanup return | > > > > > > > > > > > > | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | after 6000 {set pause 0} vwait pause test cron-1.8 {cron::in} { set ::inevent } 1 ::cron::in 5 { set ::cron::forever 0 test cron-1.9 {cron::main} { set ::cron::forever } 0 } ::cron::main # If we get to this test, mission successful test cron-1.10 {cron::main} { return 1 } 1 testsuiteCleanup return |
Changes to modules/cron/pkgIndex.tcl.
1 2 3 4 5 6 7 8 9 10 | # Tcl package index file, version 1.1 # This file is generated by the "pkg_mkIndex" command # and sourced either when an application starts up or # by a "package unknown" script. It invokes the # "package ifneeded" command to set up package-related # information so that packages will be loaded automatically # in response to "package require" commands. When this # script is sourced, the variable $dir must contain the # full path name of this file's directory. | | | 1 2 3 4 5 6 7 8 9 10 11 | # Tcl package index file, version 1.1 # This file is generated by the "pkg_mkIndex" command # and sourced either when an application starts up or # by a "package unknown" script. It invokes the # "package ifneeded" command to set up package-related # information so that packages will be loaded automatically # in response to "package require" commands. When this # script is sourced, the variable $dir must contain the # full path name of this file's directory. package ifneeded cron 1.3 [list source [file join $dir cron.tcl]] |
Changes to modules/tool/index.tcl.
1 2 3 4 5 6 7 8 9 10 11 | package require Tcl 8.6 ;# try in pipeline.tcl. Possibly other things. package require dicttool package require TclOO package require sha1 package require oo::meta 0.5.1 package require oo::dialect ::oo::dialect::create ::tool ::namespace eval ::tool {} set ::tool::trace 0 ### | > | 1 2 3 4 5 6 7 8 9 10 11 12 | package require Tcl 8.6 ;# try in pipeline.tcl. Possibly other things. package require dicttool package require TclOO package require sha1 package require cron 1.3 package require oo::meta 0.5.1 package require oo::dialect ::oo::dialect::create ::tool ::namespace eval ::tool {} set ::tool::trace 0 ### |
︙ | ︙ | |||
51 52 53 54 55 56 57 | set ::tool::tool_root [file dirname $cwd] ::tool::pathload $cwd { uuid.tcl ensemble.tcl metaclass.tcl event.tcl } $idxfile | | | 52 53 54 55 56 57 58 59 60 | set ::tool::tool_root [file dirname $cwd] ::tool::pathload $cwd { uuid.tcl ensemble.tcl metaclass.tcl event.tcl } $idxfile package provide tool 0.5.6 |
Changes to modules/tool/pipeline.tcl.
1 | ::namespace eval ::tool::signal {} | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 | ::namespace eval ::tool::signal {} package provide tool::pipeline 0.1 |
Changes to modules/tool/pkgIndex.tcl.
1 2 3 4 5 6 7 8 9 10 11 | # Tcl package index file, version 1.1 # This file is generated by the "pkg_mkIndex" command # and sourced either when an application starts up or # by a "package unknown" script. It invokes the # "package ifneeded" command to set up package-related # information so that packages will be loaded automatically # in response to "package require" commands. When this # script is sourced, the variable $dir must contain the # full path name of this file's directory. if {![package vsatisfies [package provide Tcl] 8.6]} {return} | | | 1 2 3 4 5 6 7 8 9 10 11 12 | # Tcl package index file, version 1.1 # This file is generated by the "pkg_mkIndex" command # and sourced either when an application starts up or # by a "package unknown" script. It invokes the # "package ifneeded" command to set up package-related # information so that packages will be loaded automatically # in response to "package require" commands. When this # script is sourced, the variable $dir must contain the # full path name of this file's directory. if {![package vsatisfies [package provide Tcl] 8.6]} {return} package ifneeded tool 0.5.6 [list source [file join $dir index.tcl]] |