Error processing request

Parameters

CONTENT_LENGTH0
REQUEST_METHODGET
REQUEST_URI/revision/every?V=76
QUERY_STRINGV=76
CONTENT_TYPE
DOCUMENT_URI/revision/every
DOCUMENT_ROOT/var/www/nikit/nikit/nginx/../docroot
SCGI1
SERVER_PROTOCOLHTTP/1.1
HTTPSon
REMOTE_ADDR172.69.7.9
REMOTE_PORT28640
SERVER_PORT4443
SERVER_NAMEwiki.tcl-lang.org
HTTP_HOSTwiki.tcl-lang.org
HTTP_CONNECTIONKeep-Alive
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_X_FORWARDED_FOR18.118.184.237
HTTP_CF_RAY8769e61cfd896187-ORD
HTTP_X_FORWARDED_PROTOhttps
HTTP_CF_VISITOR{"scheme":"https"}
HTTP_ACCEPT*/*
HTTP_USER_AGENTMozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; [email protected])
HTTP_CF_CONNECTING_IP18.118.184.237
HTTP_CDN_LOOPcloudflare
HTTP_CF_IPCOUNTRYUS

Body


Error

Unknow state transition: LINE -> END

-code

1

-level

0

-errorstack

INNER {returnImm {Unknow state transition: LINE -> END} {}} CALL {my render_wikit every **\ Description\ **\n\nVarious\ implementation\ of\ a\ command\ that\ is\ used\ to\ periodically\ invoke\ a\ script\ or\ command\n\n**\ Barebones\ **\n\n\[RS\]:\n\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ if\ 1\ \$body\n\ \ \ \ after\ \$ms\ \[list\ after\ idle\ \[info\ level\ 0\]\]\n\}\n======\n\n\[PYK\]\ 2012-12-04:\ Alternatively,\ use\ a\ command\ instead:\n======\nproc\ every\ \{ms\ cmd\}\ \{\n\ \ \ \ \{*\}\$cmd\n\ \ \ \ after\ \$ms\ \[list\ after\ idle\ \[info\ level\ 0\]\]\n\}\n======\n\n...\ or,\ with\ a\ simple\ cancel\ option:\n\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ global\ every\n\ \ \ \ if\ \{\$ms\ ==\ \"cancel\"\}\ \{after\ cancel\ \$every(\$body)\;\ unset\ every(\$body)\;\ return\}\n\ \ \ \ set\ every(\$body)\ \[info\ level\ 0\]\n\ \ \ \ eval\ \$body\n\ \ \ \ after\ \$ms\ \[info\ level\ 0\]\n\}\n\n======\n\nand\ this\ is\ a\ tidied\ version\ of\ the\ digital\ clock\ that\ started\ this\ page:\n======\npack\ \[label\ .clock\ -textvar\ time\]\nevery\ 1000\ \{set\ ::time\ \[clock\ format\ \[clock\ sec\]\ -format\ %H:%M:%S\]\}\n======\n\nI\ admit\ that\ the\ minimal\ ''every''\ creates\ runaway\ timers\ that\ will\ tick\ on\nforever\ -\ almost:\ you\ can\ reset\ all\ timers\ with\n======\nforeach\ id\ \[after\ info\]\ \{after\ cancel\ \$id\}\ \n======\n\nTo\ limit\ the\ number\ of\ repetitions,\ use\ return:\n\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ eval\ \$body\n\ \ \ \ after\ \$ms\ \[info\ level\ 0\]\n\}\nset\ nmax\ 3\nevery\ 1000\ \{puts\ hello\;\ if\ \{\[incr\ ::nmax\ -1\]<=0\}\ return\}\n======\n\n----\n\[RJM\]:\ In\ the\ case\ of\ a\ body/script\ that\ takes\ a\ considerable\ time\ fraction\ of\ the\ interval\ time,\ the\ following\ \[every\]\ is\ more\ precise,\ provided\ the\ script\ under\ repetitive\ execution\ will\ ''normally''\ not\ execute\ longer\ than\ the\ interval\ duration:\n\n======\nproc\ every\ \{ms\ body\}\ \{after\ \$ms\ \[info\ level\ 0\]\;\ eval\ \$body\}\n======\n\nThe\ after\ command\ is\ set\ up\ ''prior''\ to\ the\ script\ call.\ \ One\ problem\ with\nthis\ approach\ is\ that\ the\ body\ can\ not\ then\ \"cancel\"\ itself\ via\ \[return\]\n\n\[FPX\]:\ Note\ that\ the\ latter\ may\ not\ be\ a\ good\ idea\ if\ the\ body\ (a)\ may\ in\ fact\ntake\ longer\ to\ execute\ than\ the\ interval,\ and\ (b)\ invokes,\ at\ some\ point,\ the\nevent\ loop.\ In\ that\ case,\ you\ might\ want\ to\ guard\ against\ reentrancy.\n\n\[RJM\]:\ Shouldn't\ be\ a\ serious\ problem.\ It\ will\ only\ cause\ more\ stack\ access\ when\ any\ script\ executes\ longer\ than\ the\ after\ interval.\ It\ works\ pretty\ good\ for\ situations\ where\ the\ script\ has\ a\ big\ execution\ time\ standard\ deviation\ for\ each\ invocation.\n\n\[AMG\]:\ Reëntrancy?\ \ The\ event\ loop\ runs\ in\ the\ same\ thread\ as\ the\ rest\ of\ the\ script,\ so\ the\ script\ can't\ \"run\ on\ top\ of\ itself\".\ \ It\ has\ return\ to\ the\ event\ loop\ before\ the\ event\ loop\ can\ start\ it\ again.\ \ Also,\ a\ long\ script/short\ timeout\ won't\ completely\ starve\ out\ other\ events,\ because\ the\ event\ loop\ will\ give\ them\ all\ their\ fair\ turn.\ \ A\ very\ long-running\ script\ will\ result\ in\ poor\ user\ interface\ response\ times,\ but\ it\ won't\ completely\ freeze\ the\ program\ unless\ the\ script\ loops\ indefinitely.\n\n----\n\[Ken\]:\ When\ i\ ran\ the\ code\ above,\ it\ seems\ it\ only\ runs\ when\ the\ event\ loop\ is\nidle.\ If\ the\ event\ loop\ is\ busy\ with\ a\ while\ procedure,\ it\ doesn't\ run.\ What\ is\na\ better\ alternative?\n\n\[Lars\ H\]:\ Yes,\ while\ one\ event\ is\ being\ processed,\ no\ additional\ events\ are\ fetched.\ This\ is\ as\ it\ is\ supposed\ to\ be.\ As\ for\ better\ alternatives...\ The\ hardliners\ would\ tell\ you\ to\ not\ use\ a\ while\ loop,\ but\ instead\ unroll\ it\ into\ the\ event\ loop\ as\ well.\ See\ \[Keep\ a\ GUI\ alive\ during\ a\ long\ calculation\]\ for\ more\ on\ the\ subject.\n\n\[Ken\]:\ As\ currently\ i\ am\ trying\ to\ code\ a\ simulator\ of\ wireless\ sensor\ nodes\ running\ under\ the\ background,\ so\ what\ i\ have\ to\ do\ is\ create\ for\ example\ 10\ nodes\ and\ run\ them\ all\ under\ the\ background\ under\ the\ event\ loop.\ Thus\ allowing\ my\ main\ tcl\ interpreter\ to\ be\ responsive\ or\ running\ to\ user\ requests.\ And\ how\ to\ get\ one\ node\ to\ run\ under\ an\ event\ loop\ is\ it\ to\ create\ a\ 'proc'\ and\ run\ a\ '\ after'\ command\ on\ it?\n\n**\ See\ Also\ **\n\n\ \ \ *\ \[Syncronized\ timer\]\n\n\n**\ bgLoop\ **\n\n======\n##\ ********************************************************\ \n##\n##\ Name:\ bgLoop\ \n##\n##\ Description:\n##\ Start\ (a)synchronous\ looping\ jobs.\ \ Jobs\ are\ ended\ by\n##\ setting\ ::bg::jobs(\$name,run)\ to\ 0.\n##\n##\ Usage:\n##\ \ \ \ \ \ \ \ start:\ bgLoop\ \$name\ \$code\ \$delay\n##\ \ \ \ \ \ \ \ \ stop:\ set\ ::bg::jobs(\$name,run)\ 0\n##\n##\ Comment:\n##\ We\ started\ seeing\ mysterious\ delays\ in\ some\ very\ complex\n##\ event\ code,\ and\ I\ modified\ the\ older\ version\ of\ bgLoop\n##\ to\ provide\ some\ timing\ info...\ what\ I\ learned\ was\ that\n##\ beyond\ a\ certain\ level\ of\ complexity\ it\ is\ better\ to\ know\n##\ what\ is\ really\ going\ on,\ so\ SYNCHRONOUS\ looping\ is\n##\ quite\ useful.\n##\n##\ What\ is\ very\ nice\ is\ that\ the\ event\ loop\ is\ not\ blocked\n##\ for\ the\ entire\ runtime\ of\ the\ multiple\ scheduled\ code\n##\ blocks,\ and\ the\ timing\ diagnostic\ lets\ you\ design\ around\n##\ long\ running\ tasks\ by\ modifying\ the\ delays\ so\ they\ are\n##\ of\ by\ so-many\ seconds...\n##\n##\ Note\ that\ the\ first\ iteration\ \"returns\"\ for\ sanity,\n##\ and\ that\ you\ *should*\ use\ a\ custom\ bgerror\ handler\n##\ if\ you\ are\ doing\ this\ from\ Tcl\ like\ I\ am\ (no\ Tk).\n##\n\nbgLoop\ \{\ \{\ name\ NULL\ \}\ \{\ code\ \"\"\ \}\ \{\ delay\ 2\ \}\ \}\ \{\n\ \ \n\ \ \ \ if\ \{\ !\ \[\ llength\ \[\ namespace\ children\ ::\ bg\ \]\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ namespace\ eval\ bg\ \{\}\n\ \ \ \ \ \ \ \ set\ ::bg::starttime\ \[\ clock\ seconds\ \]\n\ \ \ \ \}\n\ \ \ \ set\ now\ \[\ clock\ seconds\ \]\ \n\ \ \ \ set\ elapsed\ \[\ expr\ \{\ \$now\ -\ \$::bg::starttime\ \}\ \]\n\ \ \ \ \n\ \ \ \ \;##\ register\ a\ new\ job\ if\ it\ has\ valid\ args\n\ \ \ \ if\ \{\ !\ \[\ string\ equal\ NULL\ \$name\ \]\ \ \ \ \ \ &&\ \\\n\ \ \ \ \ \ \ \ \ \ \ \ \ \[\ string\ length\ \[\ join\ \$code\ \]\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ set\ ::bg::jobs(\$name,run)\ \ \ 1\n\ \ \ \ \ \ \ \ set\ ::bg::jobs(\$name,code)\ \ \$code\n\ \ \ \ \ \ \ \ set\ ::bg::jobs(\$name,delay)\ \$delay\n\ \ \ \ \ \ \ \ puts\ stderr\ \"Looping\ process\ \$name\ started\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ if\ \{\ \[\ info\ exists\ ::bg::after\ \]\ &&\ \\\n\ \ \ \ \ \ \ \ \ \ \[\ lsearch\ \[\ after\ info\ \]\ \$::bg::after\ \]\ !=\ -1\ \}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$::bg::after\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ if\ \{\ \[\ string\ equal\ NULL\ \$name\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ set\ dt\ 0\n\ \ \ \ \ \ \ \ foreach\ job\ \[\ array\ names\ ::bg::jobs\ *,run\ \]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ job\ \[\ lindex\ \[\ split\ \$job\ ,\ \]\ 0\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\ \[\ string\ equal\ NULL\ \$job\ \]\ \}\ \{\ continue\ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\ \[\ string\ equal\ 0\ \$::bg::jobs(\$job,run)\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ foreach\ item\ \[\ array\ names\ ::bg::jobs\ \$job,*\ \]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ ::bg::jobs(\$item)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ puts\ stderr\ \"Looping\ process\ \$job\ terminated\"\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ continue\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\ !\ (\$elapsed\ %\ \$::bg::jobs(\$job,delay))\ \}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ ts\ \[\ clock\ clicks\ -milliseconds\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ eval\ \$::bg::jobs(\$job,code)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ te\ \[\ clock\ clicks\ -milliseconds\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ td\ \[\ expr\ \$te\ -\ \$ts\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ dt\ \[\ expr\ \$dt\ +\ \$td\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ data\ \[\ list\ \$job\ \$td\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\n\ \ \ \ \ \ \ \ if\ \{\ \$dt\ >\ 1000\ \}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ puts\ stderr\ \"bgLoop\ runtime\ per\ iteration:\ \$dt\ ms\ (\$data)\"\ \ \n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ set\ ::bg::after\ \[\ after\ 1000\ bgLoop\ \]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ retval\ \[\ eval\ \$::bg::jobs(\$name,code)\ \]\n\ \ \ \ \ \ \ \ set\ ::bg::after\ \[\ after\ 1000\ bgLoop\ \]\n\ \ \ \ \ \ \ \ return\ \$retval\n\ \ \ \ \}\n\}\n======\n\n\[DKF\]:\ Here's\ a\ version\ of\ '''every'''\ that\ can\ be\ cancelled\ too:\n\n======\nproc\ every\ \{interval\ script\}\ \{\n\ \ \ \ global\ everyIds\n\ \ \ \ if\ \{\$interval\ eq\ \"cancel\"\}\ \{\n\ \ \ \ \ \ \ \ catch\ \{after\ cancel\ \$everyIds(\$script)\}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ set\ everyIds(\$script)\ \[after\ \$interval\ \[info\ level\ 0\]\]\n\ \ \ \ uplevel\ #0\ \$script\n\}\n======\n\n\[NEM\]\ ''30\ July\ 2006'':\ And\ here's\ one\ that\ can\ be\ cancelled\ from\ within\ the\ script\ too\ \n(using\ \[break\]):\n======\nproc\ every\ \{interval\ script\}\ \{\n\ \ \ \ global\ everyIds\n\ \ \ \ if\ \{\$interval\ eq\ \"cancel\"\}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$everyIds(\$script)\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ set\ everyIds(\$script)\ \[after\ \$interval\ \[info\ level\ 0\]\]\n\ \ \ \ set\ rc\ \[catch\ \{uplevel\ #0\ \$script\}\ result\]\n\ \ \ \ if\ \{\$rc\ ==\ \[catch\ break\]\}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$everyIds(\$script)\n\ \ \ \ \ \ \ \ set\ rc\ 0\n\ \ \ \ \}\ elseif\ \{\$rc\ ==\ \[catch\ continue\]\}\ \{\n\ \ \ \ \ \ \ \ #\ Ignore\ -\ just\ consume\ the\ return\ code\n\ \ \ \ \ \ \ \ set\ rc\ 0\n\ \ \ \ \}\n\ \ \ \ #\ TODO:\ Need\ better\ handling\ of\ errorInfo\ etc...\n\ \ \ \ return\ -code\ \$rc\ \$result\n\}\n======\nWhich\ allows\ the\ countdown\ example\ to\ be\ written\ as:\n======\nset\ nmax\ 3\nevery\ 1000\ \{\n\ \ \ \ puts\ hello\n\ \ \ \ if\ \{\[incr\ nmax\ -1\]\ <=\ 0\}\ \{\ break\ \}\n\}\n======\n\n\[RS\]\ 2006-07-31:\ Hmm\ yes,\ but\ the\ \[simple\]\ \[every\]\ allows\ that\ too,\ if\ you\ just\ use\ \[return\]:\n\n======\nproc\ every\ \{ms\ body\}\ \{eval\ \$body\;\ after\ \$ms\ \[info\ level\ 0\]\}\nset\ ::nmax\ 3\nevery\ 1000\ \{puts\ hello\;\ if\ \{\[incr\ ::nmax\ -1\]<=0\}\ return\}\n======\n\nI\ prefer\ not\ to\ use\ implicit\ global\ scope,\ for\ environment\ tidyness...\ :)\n\n\[NEM\]\ Well,\ implicit\ global\ scope\ is\ characteristic\ of\ other\ event\ callbacks,\nso\ it\ seems\ like\ the\ least\ surprising\ option.\ Likewise,\ having\ to\ use\ \[return\]\nto\ exit\ something\ that\ isn't\ a\ proc\ seems\ confusing.\ I\ prefer\ a\ simple\ninterface\ to\ a\ simple\ implementation.\ (Also\ the\ simple\ version\ has\ the\ problem\nof\ time\ drift\ if\ you\ have\ a\ long-running\ script\ as\ discussed\ above).\n----\n26-may-2005\n\n----\n\[Jeffrey\ Hobbs\]\ supplies\ a\ comparable,\ but\ distinct,\ version\ of\ \"every\",\ in\ a\npost\ on\n\[http://groups.google.com/groups?selm=37BC45C8.72F9509B%40scriptics.com%|%comp.lang.tcl\n,\ 1999-08-19\].\n\n======\n#\ every\ --\n#\ \ \ Cheap\ rescheduler\n#\ every\ <time>\ cmd\;\ \ \ \ \ \ \ \ #\ cmd\ is\ a\ one\ arg\ (cmd\ as\ list)\n#\ \ \ \ \ \ \ \ schedules\ \$cmd\ to\ be\ run\ every\ <time>\ 1000ths\ of\ a\ sec\n#\ \ \ \ \ \ \ \ IOW,\ \[every\ 1000\ \"puts\ hello\"\]\ prints\ hello\ every\ sec\n#\ every\ cancel\ cmd\n#\ \ \ \ \ \ \ \ cancels\ a\ cmd\ if\ it\ was\ specified\n#\ every\ info\ ?pattern?\n#\ \ \ \ \ \ \ \ returns\ info\ about\ commands\ in\ pairs\ of\ \"time\ cmd\ time\ cmd\ ...\"\n#\nproc\ every\ \{time\ \{cmd\ \{\}\}\}\ \{\n\ \ \ \ global\ EVERY\n\ \ \ \ if\ \{\[regexp\ \{^\[0-9\]+\$\}\ \$time\]\}\ \{\n\ \ \ \ \ \ \ \ #\ A\ time\ was\ given,\ so\ schedule\ a\ command\ to\ run\ every\ \$time\ msecs\n\ \ \ \ \ \ \ \ if\ \{\[string\ compare\ \{\}\ \$cmd\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ EVERY(TIME,\$cmd)\ \$time\n\ \ \ \ \ \ \ \ \ \ \ \ set\ EVERY(CMD,\$cmd)\ \[after\ \$time\ \[list\ every\ eval\ \$cmd\]\]\n\ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ return\ -code\ error\ \"wrong\ \\#\ args:\ should\ be\ \\\"\[lindex\ \[info\ level\ 0\]\n0\]\ <number>\ command\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ switch\ \$time\ \{\n\ \ \ \ \ \ \ \ eval\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[info\ exists\ EVERY(TIME,\$cmd)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ uplevel\ \\#0\ \$cmd\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ EVERY(CMD,\$cmd)\ \[after\ \$EVERY(TIME,\$cmd)\ \\\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \[list\ every\ eval\ \$cmd\]\]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ cancel\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[string\ match\ \"all\"\ \$cmd\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ foreach\ i\ \[array\ names\ EVERY\ CMD,*\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$EVERY(\$i)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ EVERY(\$i)\ EVERY(TIME,\[string\ range\ \$i\ 4\ end\])\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \}\ elseif\ \{\[info\ exists\ EVERY(CMD,\$cmd)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$EVERY(CMD,\$cmd)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ EVERY(CMD,\$cmd)\ EVERY(TIME,\$cmd)\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ info\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ result\ \{\}\n\ \ \ \ \ \ \ \ \ \ \ \ foreach\ i\ \[array\ names\ EVERY\ TIME,\$cmd*\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ cmd\ \[string\ range\ \$i\ 5\ end\]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ result\ \$EVERY(\$i)\ \$cmd\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ return\ \$result\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ return\ -code\ error\ \"bad\ option\ \\\"\$time\\\":\ must\ be\ cancel,\ info\ or\ a\nnumber\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\n\}\n\n\n======\n----\n\n\[DKF\]:\ Here's\ a\ scheduler\ that\ lets\ you\ schedule\ regular\ events\ and\ stop\ them\ whenever\ you\ like,\ using\ a\ similar\ scheme\ to\ \[\[after\]\]/\[\[after\ cancel\]\].\n\n======\n##\ ****************************************************************\n##\ Name:\n##\ \ \ \ \ every\n##\ Description:\n##\ \ \ \ \ Schedules\ a\ script\ for\ being\ regularly\ executed,\ returning\n##\ \ \ \ \ a\ token\ that\ allows\ the\ scheduling\ to\ be\ halted\ at\ some\n##\ \ \ \ \ future\ point.\n##\ Usage:\n##\ \ \ \ \ every\ ms\ script...\n##\ \ \ \ \ every\ cancel\ token\n##\ \ \ \ \ every\ cancel\ script...\n##\ Notes:\n##\ \ \ \ \ The\ script\ is\ executed\ at\ the\ global\ level,\ and\ any\ errors\n##\ \ \ \ \ generated\ by\ the\ script\ will\ NOT\ cause\ a\ cessation\ of\ future\n##\ \ \ \ \ schedulings.\ \ Thus,\ any\ script\ that\ always\ causes\ an\ error\n##\ \ \ \ \ will\ cause\ many\ user-interface\ problems\ when\ used\ with\ a\n##\ \ \ \ \ short\ delay.\n##\ \ \ \ \ While\ differently\ scheduled\ scripts\ do\ not\ need\ to\ be\n##\ \ \ \ \ distinct\ from\ each\ other,\ it\ is\ not\ determined\ which\ one\n##\ \ \ \ \ will\ be\ cancelled\ if\ you\ use\ the\ cancelling\ form\ with\ the\n##\ \ \ \ \ script\ as\ opposed\ to\ the\ token.\n##\ Example:\n##\ \ \ \ \ set\ foo\ \[every\ 500\ \{puts\ \[clock\ format\ \[clock\ seconds\]\]\}\]\n##\ \ \ \ \ every\ 10000\ puts\ Howdy!\n##\ \ \ \ \ #\ ...\n##\ \ \ \ \ after\ cancel\ \$foo\n##\ \ \ \ \ after\ cancel\ puts\ Howdy!\n##\ ****************************************************************\nproc\ every\ \{option\ args\}\ \{\n\ \ \ \ global\ everyPriv\ every:UID\n\ \ \ \ if\ \{\[string\ equal\ -length\ \[string\ length\ \$option\]\ \$option\ cancel\]\}\ \{\n\ \ \ \ \ \ \ \ set\ id\ \{\}\n\ \ \ \ \ \ \ \ if\ \{\[llength\ \$args\]\ ==\ 1\ &&\ \[string\ match\ every#*\ \[lindex\ \$args\ 0\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ id\ \[lindex\ \$args\ 0\]\n\ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ script\ \[eval\ \[list\ concat\]\ \$args\]\n\ \ \ \ \ \ \ \ \ \ \ \ #\ Yuck,\ a\ linear\ search.\ \ A\ reverse\ hash\ would\ be\ faster...\n\ \ \ \ \ \ \ \ \ \ \ \ foreach\ \{key\ value\}\ \[array\ get\ everyPriv\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[string\ equal\ \$script\ \[lindex\ \$value\ 1\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ id\ \$key\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ break\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ if\ \{\[string\ length\ \$id\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \[lindex\ \$everyPriv(\$id)\ 2\]\n\ \ \ \ \ \ \ \ \ \ \ \ unset\ everyPriv(\$id)\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ id\ \[format\ \"every#%d\"\ \[incr\ every:UID\]\]\n\ \ \ \ \ \ \ \ set\ script\ \[eval\ \[list\ concat\]\ \$args\]\n\ \ \ \ \ \ \ \ set\ delay\ \$option\n\ \ \ \ \ \ \ \ set\ aid\ \[after\ \$delay\ \[list\ every:afterHandler\ \$id\]\]\n\ \ \ \ \ \ \ \ set\ everyPriv(\$id)\ \[list\ \$delay\ \$script\ \$aid\]\n\ \ \ \ \ \ \ \ return\ \$id\n\ \ \ \ \}\n\}\n##\ Internal\ stuff\ -\ I\ could\ do\ this\ with\ a\ namespace,\ I\ suppose...\narray\ set\ everyPriv\ \{\}\nset\ every:UID\ 0\nproc\ every:afterHandler\ \{id\}\ \{\n\ \ \ \ global\ everyPriv\n\ \ \ \ foreach\ \{delay\ script\ oldaid\}\ \$everyPriv(\$id)\ \{\}\n\ \ \ \ set\ aid\ \[after\ \$delay\ \[info\ level\ 0\]\]\n\ \ \ \ set\ everyPriv(\$id)\ \[list\ \$delay\ \$script\ \$aid\]\n\ \ \ \ uplevel\ #0\ \$script\n\}\n======\n''(I\ have\ this\ feeling\ that\ my\ definition\ of\ production-quality\ code\ is\ not\ the\ same\ as\ that\ of\ other\ people.)''\n----\ \n\n\[Josua\ Dietze\]\ <digidietze\ at\ t-online.de>\ contributed\ this\ idea\non\ news:comp.lang.tcl\ :\n======\n\ proc\ TimerFunction\ \{state\ \{rate\ \{\}\}\}\ \{\n\ global\ after_id\n\ \ \ \ if\ \{\ \$state\ ==\ \"start\"\ \}\ \{\n\ \ \ \ \ \ \ \ sendVal\ \"send_status\"\n\ \ \ \ \ \ \ \ set\ after_id\ \[after\ \$rate\ TimerFunction\ start\ \$rate\]\n\ \ \ \ \}\ elseif\ \{\ \$state\ ==\ \"stop\"\ \}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$after_id\n\ \ \ \ \}\n\ \}\n\n\ TimerFunction\ start\ 2000\n\ TimerFunction\ stop\n======\nJust\ make\ sure\ you\ start\ and\ stop\ exactly\ once\ ...\n\n----\n\n\[kruzalex\]\ An\ alternative\ to\ the\ stuff\ mentioned\ above:\n======\nproc\ every\ \{interval\ args\}\ \{\n\ \ \ \ global\ everyPriv\ every:UID\n\ \ \ \ if\ \{\[string\ equal\ -length\ \[string\ length\ \$interval\]\ \$interval\ cancel\]\}\ \{\n\ \ \ \ set\ id\ \{\}\n\ \ \ \ if\ \{\[llength\ \$args\]\ ==\ 1\ &&\ \[string\ match\ every#*\ \[lindex\ \$args\ 0\]\]\}\ \{\n\ \ \ \ \ \ \ \ set\ id\ \[lindex\ \$args\ 0\]\n\ \ \ \ \}\ \n\ \ \ \ if\ \{\[string\ length\ \$id\]\}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \[lindex\ \$everyPriv(\$id)\ 2\]\n\ \ \ \ \ \ \ \ unset\ everyPriv(\$id)\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ set\ id\ \[format\ \"every#%d\"\ \[incr\ every:UID\]\]\n\ \ \ \ set\ script\ \[eval\ \[list\ concat\]\ \$args\]\n\ \ \ \ foreach\ \{key\ value\}\ \[array\ get\ everyPriv\]\ \{\n\ \ \ \ \ \ \ \ if\ \{\[string\ equal\ \$script\ \[lindex\ \$value\ 1\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ id\ \$key\n\ \ \ \ \ \ \ \ \ \ \ \ set\ time\ \[lindex\ \$everyPriv(\$id)\ 0\]\n\ \ \ \ \ \ \ \ \ \ \ \ break\n\ \ \ \ \ \ \ \ \}\ \n\ \ \ \ \}\n\ \ \ \ if\ \{!\[info\ exists\ everyPriv(\$id)\]\}\ \{\n\ \ \ \ \ \ \ \ set\ everyPriv(\$id)\ \[concat\ \$interval\ \[list\ \$script\]\ \[after\ \$interval\ \[info\ level\ 0\]\]\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ uplevel\ #0\ \$script\n\ \ \ \ \ \ \ \ set\ everyPriv(\$id)\ \[concat\ \$time\ \[list\ \$script\]\ \[after\ \$interval\ \[info\ level\ 0\]\]\]\n\ \ \ \ \}\n\ \ \ \ return\ \$id\n\}\n\narray\ set\ everyPriv\ \{\}\nset\ every:UID\ 0\n\n#Example\nset\ foo\ \[every\ 1000\ \{puts\ foo\}\]\nset\ foo1\ \[every\ 2000\ \{puts\ foo1\}\]\nafter\ 3000\ \[list\ every\ cancel\ \$foo\]\nvwait\ forever\n======\n\n\n----\n\[XO\]\ 2008-12-03:\ See\ also\ http://code.activestate.com/recipes/68393/%|%Recipe\ 68393:\ Repeat\ procedure\ every\ X\ seconds\ %|%\ \ \n\n----\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ set\ t\ \[string\ range\ \[time\ \$body\]\ 0\ end-27\]\n\ \ \ \ after\ \[expr\ \{\$ms-\$t/1000\}\]\ \[info\ level\ 0\]\ \ \n\}\n======\n\n\n\[http://wiki.tcl.tk/25434%|%rjmcmahon%|%\]\ Here's\ a\ full\ blown\ version\ of\ every\ that\ covers\ most\ cases.\ Calls\ to\ it\ are:\n\ \ \ \ :\ \ \ '''every'''\ ''seconds''\ ?''script\ script\ ...''?\n\ \ \ **\ returns\ an\ everyid\n\ \ \ \ :\ \ \ '''every'''\ ''integer''\ '''-milliseconds'''\ ?''script\ script\ ...''?\n\ \ \ **\ returns\ an\ everyid\n\ \ \ \ :\ \ \ '''every\ cancel'''\ ''everyid''\n\ \ \ \ :\ \ \ '''every\ cancel\ all'''\n\ \ \ \ :\ \ \ '''every\ info'''\n\ \ \ **\ returns\ all\ everyids\n\n======\nproc\ every\ \{args\}\ \{\n\ \ \ \ global\ _everyids\ \ _everyid\ \n\n\ \ \ \ if\ \{!\[llength\ \$args\]\}\ \{\n\ \ \ \ \ \ \ \ if\ \{\[info\ exists\ _everyids\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ parray\ _everyids\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\n\ \ \ \ set\ interval\ \[lindex\ \$args\ 0\]\n\ \ \ \ if\ \{\$interval\ ==\ \"info\"\}\ \{\n\ \ \ \ \ \ \ \ return\ \[array\ names\ _everyids\]\n\ \ \ \ \}\n\ \ \ \ #\n\ \ \ \ #\ See\ if\ arg1\ is\ a\ -milliseconds\ option\n\ \ \ \ #\ \n\ \ \ \ set\ arg1\ \[lindex\ \$args\ 1\]\n\ \ \ \ if\ \{\$arg1\ ==\ \"-milliseconds\"\}\ \{\n\ \ \ \ \ \ \ \ set\ script\ \[lrange\ \$args\ 2\ end\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ #\ \ In\ this\ case\ a\ numeric\ arg1\ is\ given\ in\ seconds\n\ \ \ \ \ \ \ \ #\ \ so\ convert\ to\ an\ integer\ number\ of\ ms.\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ if\ \{\$interval\ !=\ \"cancel\"\ &&\ \$interval\ !=\ \"idle\"\}\ \{\ \n\ \ \ \ \ \ \ \ \ \ \ \ set\ interval\ \[expr\ \{round(\$interval\ *\ 1000)\}\]\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ set\ script\ \[lrange\ \$args\ 1\ end\]\n\ \ \ \ \}\n\n\ \ \ \ #\n\ \ \ \ #\ \ Process\ any\ cancel\ requests.\ \n\ \ \ \ #\ \ Options\ are\n\ \ \ \ #\ \ o\ \ every\ cancel\ all\n\ \ \ \ #\ \ o\ \ every\ cancel\ <everyid>\n\ \ \ \ #\n\ \ \ \ if\ \{\$interval\ eq\ \"cancel\"\}\ \{\n\ \ \ \ \ \ \ \ if\ \{!\[info\ exists\ _everyids\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ return\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ if\ \{\$script\ eq\ \"all\"\}\ \{\ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ set\ idlist\ \[array\ names\ _everyids\]\n\ \ \ \ \ \ \ \ \ \ \ \ foreach\ id\ \$idlist\ \{\ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\$_everyids(\$id)\ !=\ \"RUNNING\"\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$_everyids(\$id)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ _everyids(\$id)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ _everyids(\$id)\ \"CANCELPENDING\"\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ index\ \$script\n\ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[info\ exists\ _everyids(\$index)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ #\ Cancel\ now\ if\ the\ script\ is\ not\ running\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ #\ otherwise\ signal\ the\ underlying\ _every\ not\ to\ reschedule\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\$_everyids(\$index)\ !=\ \"RUNNING\"\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$_everyids(\$index)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ _everyids(\$index)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ _everyids(\$index)\ \"CANCELPENDING\"\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ if\ \{\[info\ exists\ _everyid\]\}\ \{\n\ \ \ \ \ \ \ \ incr\ _everyid\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ _everyid\ 100\n\ \ \ \ \}\n\n\ \ \ \ #\n\ \ \ \ #\ \ Now\ that\ user\ command\ processing\ is\ done,\ call\ the\ \n\ \ \ \ #\ \ underlying\ every\ routine\ to\ start\ the\ script\ on\ \n\ \ \ \ #\ \ its\ periodic\ (per\ interval)\ and\ return\ a\ unique\ everyid.\n\ \ \ \ #\n\ \ \ \ _every\ \$interval\ \$script\ \"every#\$_everyid\"\n\ \ \ \ return\ \"every#\$_everyid\"\n\}\n\nproc\ _every\ \{interval\ script\ id\}\ \{\n\ \ \ \ global\ _everyids\ \n\n\ \ \ \ #\n\ \ \ \ #\ \ Run\ the\ script\ and\ measure\ the\ time\ taken\ to\ run\n\ \ \ \ #\ \n\ \ \ \ set\ starttime\ \[clock\ clicks\ -milliseconds\]\n\ \ \ \ set\ _everyids(\$id)\ \"RUNNING\"\n\ \ \ \ set\ rc\ \[catch\ \{uplevel\ #0\ eval\ \$script\}\ result\]\n\ \ \ \ set\ finishtime\ \[clock\ clicks\ -milliseconds\]\n\ \ \ \ \n\ \ \ \ #\n\ \ \ \ #\ \ Detect\ and\ process\ any\ catch\ codes\ from\ the\ script\n\ \ \ \ #\n\ \ \ \ #\ \ Note:\ The\ script\ returning\ a\ break\ catch\ code\ is\ \n\ \ \ \ #\ \ used\ to\ indicate\ a\ silent\ stop\ of\ the\ rescheduling\ \n\ \ \ \ #\ \n\ \ \ \ if\ \{\$rc\ ==\ \[catch\ error\]\}\ \{\n\ \ \ \ \ \ \ \ error\ \"\$result\ \$script\"\n\ \ \ \ \ \ \ \ return\ \n\ \ \ \ \}\ elseif\ \{\$rc\ ==\ \[catch\ break\]\}\ \{\n\ \ \ \ \ \ \ \ if\ \{\[info\ exists\ _everyids(\$id)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ unset\ _everyids(\$id)\ \n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\ elseif\ \{\$rc\ ==\ \[catch\ continue\]\}\ \{\n\ \ \ \ \ \ \ \ #\ Ignore\ -\ just\ consume\ the\ return\ code\n\ \ \ \ \ \ \ \ set\ rc\ 0\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ #\n\ \ \ \ #\ \ Adjust\ the\ reschedule\ time\ per\ the\ actual\ runtime\n\ \ \ \ #\ \ Provide\ a\ minimum\ of\ 30\ ms\ for\ a\ yield\ \n\ \ \ \ #\n\ \ \ \ if\ \{\$interval\ !=\ \"idle\"\}\ \{\n\ \ \ \ \ \ \ \ set\ runtime\ \[expr\ \{\$finishtime\ -\ \$starttime\}\]\n\ \ \ \ \ \ \ \ set\ adj_interval\ \[expr\ \{\$interval\ -\ \$runtime\}\]\n\ \ \ \ \ \ \ \ if\ \{\$adj_interval\ <\ 0\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ puts\ \"\$script\ runtime\ (\$runtime\ ms)\ exceeded\ reschedule\ interval\ (\$interval\ ms)\"\ \n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ #\ \ Set\ a\ minimum\ of\ 30\ ms\ to\ reschedule\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ if\ \{\$adj_interval\ <\ 30\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ adj_interval\ 30\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ adj_interval\ \"idle\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ #\n\ \ \ \ #\ \ Reschedule\ next\ iteration\ unless\ there\ is\ a\ cancel\ pending.\n\ \ \ \ #\n\ \ \ \ #\ \ Note:\ \ The\ rescheduling\ of\ the\ script\ is\ done\ after\n\ \ \ \ #\ \ calling\ it.\ This\ can\ be\ swapped\ but\ is\ a\ bit\ more\ complex,\n\ \ \ \ #\ \ particularly\ when\ execution\ time\ >\ interval.\n\ \ \ \ #\n\ \ \ \ if\ \{\$_everyids(\$id)\ !=\ \"CANCELPENDING\"\}\ \{\n\ \ \ \ \ \ \ \ set\ _everyids(\$id)\ \[after\ \$adj_interval\ \[list\ _every\ \$interval\ \$script\ \$id\]\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ unset\ _everyids(\$id)\n\ \ \ \ \}\n\}\n======\n\n----\n\[Napier\]\ 2015-12-23\ \nHow\ about\ a\ version\ that\ utilizes\ coroutines\ ?\ \ I\ believe\ this\ implements\ all\ the\ features\ of\ the\ above\ options\ for\ the\ most\ part\ in\ a\ fairly\ clean\ and\ clear\ manner.\ \ It\ also\ implements\ pause\ and\ resume\ capabilities\ for\ both\ scripts\ and\ everyid's\ where\ they\ resume\ as-if\ they\ were\ never\ paused.\n\n\ \ \ \ :\ \ \ '''every'''\ ''milliseconds''\ ?''script...''?\n\ \ \ *\ returns\ an\ everyid\n\ \ \ \ :\ \ \ '''every\ pause'''\ ''everyid''\n\ \ \ *\ pauses\ execution\ of\ ''everyid''\ until\ resumed\ or\ cancelled/killed\n\ \ \ \ :\ \ \ '''every\ pause'''\ ?''script...''?\n\ \ \ *\ pauses\ the\ executions\ of\ ?''script...''?\ until\ resumed\ or\ cancelled/killed\n\ \ \ \ :\ \ \ '''every\ resume'''\ ''everyid''\n\ \ \ *\ resumes\ execution\ of\ ''everyid''\n\ \ \ \ :\ \ \ '''every\ resume'''\ ?''script...''?\n\ \ \ *\ resumes\ execution\ of\ ?''script...''?\n\ \ \ \ :\ \ \ '''every\ cancel'''\ ''everyid''\n\ \ \ *\ cancels\ future\ execution\ of\ ''everyid''\n\ \ \ \ :\ \ \ '''every\ cancel'''\ ?''script...''?\n\ \ \ *\ cancels\ all\ executions\ of\ ?''script...''?\n\ \ \ \ :\ \ \ '''every\ kill'''\n\ \ \ *\ cancels\ all\ executions\ immediately\n\ \ \ \ :\ \ \ '''every\ info'''\n\ \ \ *\ provides\ a\ list\ of\ all\ active\ ''everyid''\ values\n\nOr\ if\ you\ prefer\ you\ may\ abbreviate\ however\ you'd\ like,\ ''every\ c\ \$everyID'',\ ''every\ p\ \$everyID'',\ ''every\ k''\n\nIf\ you\ are\ wrapping\ a\ script,\ do\ so\ the\ same\ way\ you\ would\ handle\ similar\ scripts\ like\ after.\n\n======\nset\ foo\ World\nevery\ 1000\ \{puts\ \"Hey\ There\"\}\nevery\ 1000\ \[list\ puts\ \"Hello,\ \$foo\"\]\n======\n\nproc\ every\ \{option\ args\}\ \{\n\ \ \ \ variable\ evID\n\ \ \ \ if\ \{\[string\ is\ entier\ \$option\]\}\ \{\n\ \ \ \ \ \ \ \ set\ name\ \[coroutine\ every#\[incr\ evID\]\ ::Every::Process\ \$option\ \{*\}\$args\]\n\ \ \ \ \ \ \ \ dict\ lappend\ ::Every::Active\ \$args\ \$name\;\ return\ \$name\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ switch\ -nocase\ -glob\ --\ \$option\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ c*\ \{lappend\ ::Every::Cancel\ \$args\}\n\ \ \ \ \ \ \ \ \ \ \ \ k*\ \{lappend\ ::Every::Cancel\ \{*\}\[concat\ \{*\}\[dict\ values\ \$::Every::Active\]\]\}\n\ \ \ \ \ \ \ \ \ \ \ \ p*\ \{lappend\ ::Every::Pause\ \$args\}\n\ \ \ \ \ \ \ \ \ \ \ \ r*\ \{set\ ::Every::Pause\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$::Every::Pause\ \$args\ \]\}\n\ \ \ \ \ \ \ \ \ \ \ \ i*\ -\n\ \ \ \ \ \ \ \ \ \ \ \ default\ \{return\ \[concat\ \{*\}\[dict\ values\ \$::Every::Active\]\]\}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\}\n\nnamespace\ eval\ Every\ \{\n\ \ \ \ variable\ Active\ \{\}\;\ variable\ Cancel\ \{\}\;\ variable\ Pause\ \{\}\n\ \ \ \ \n\ \ \ \ proc\ Process\ \{delay\ args\}\ \{\n\ \ \ \ \ \ \ \ after\ \$delay\ \[info\ coroutine\]\n\ \ \ \ \ \ \ \ yield\ \[info\ coroutine\]\n\ \ \ \ \ \ \ \ variable\ Cancel\n\ \ \ \ \ \ \ \ variable\ Pause\n\ \ \ \ \ \ \ \ if\ \{\[info\ coroutine\]\ in\ \$Cancel\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ Cancel\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Cancel\ \[info\ coroutine\]\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ set\ Pause\ \ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Pause\ \[info\ coroutine\ \]\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ set\ cancel\ true\n\ \ \ \ \ \ \ \ \}\ elseif\ \{\$args\ in\ \$Cancel\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ cancel\ true\n\ \ \ \ \ \ \ \ \}\ else\ \{\ set\ cancel\ false\ \}\n\ \ \ \ \ \ \ \ if\ \{\$cancel\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ variable\ Active\n\ \ \ \ \ \ \ \ \ \ \ \ dict\ set\ Active\ \$args\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \[dict\ get\ \$Active\ \$args\]\ \[info\ coroutine\]\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[dict\ get\ \$Active\ \$args\]\ eq\ \{\}\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ dict\ unset\ Active\ \$args\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ Cancel\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Cancel\ \$args\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ Pause\ \ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Pause\ \$args\ \ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\ \n\ \ \ \ \ \ \ \ \ \ \ \ return\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ if\ \{\[info\ coroutine\]\ ni\ \$Pause\ &&\ \$args\ ni\ \$Pause\}\ \{uplevel\ #0\ \{*\}\$args\}\n\ \ \ \ \ \ \ \ Process\ \$delay\ \{*\}\$args\n\ \ \ \ \}\n\}\n======\n\nAlso,\ as\ most\ similar\ calls,\ this\ will\ execute\ in\ the\ global\ namespace.\ \ As\ an\ extra\ nice\ function\ that\ has\ made\ things\ a\ lot\ easier,\ see\ the\ \"callback\"\ option\ below\ and\ example\ of\ its\ use\ in\ simplifying\ callback\ requirements\n\n======\nproc\ callback\ \{args\}\ \{tailcall\ namespace\ code\ \$args\}\n\nproc\ myProc\ args\ \{puts\ \"Executed\ in\ \[namespace\ current\]\"\ \}\n\nevery\ 1000\ myProc\n\nnamespace\ eval\ foo\ \{\n\ \ \ \ \ proc\ myProc\ args\ \{puts\ \"Executed\ in\ \[namespace\ current\]\"\ \}\n\ \ \ \ \ every\ 1000\ \[callback\ myProc\]\n\}\n\nvwait\ forever\n======\n----\n\n\n<<categories>>\ Command regexp2} CALL {my render every **\ Description\ **\n\nVarious\ implementation\ of\ a\ command\ that\ is\ used\ to\ periodically\ invoke\ a\ script\ or\ command\n\n**\ Barebones\ **\n\n\[RS\]:\n\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ if\ 1\ \$body\n\ \ \ \ after\ \$ms\ \[list\ after\ idle\ \[info\ level\ 0\]\]\n\}\n======\n\n\[PYK\]\ 2012-12-04:\ Alternatively,\ use\ a\ command\ instead:\n======\nproc\ every\ \{ms\ cmd\}\ \{\n\ \ \ \ \{*\}\$cmd\n\ \ \ \ after\ \$ms\ \[list\ after\ idle\ \[info\ level\ 0\]\]\n\}\n======\n\n...\ or,\ with\ a\ simple\ cancel\ option:\n\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ global\ every\n\ \ \ \ if\ \{\$ms\ ==\ \"cancel\"\}\ \{after\ cancel\ \$every(\$body)\;\ unset\ every(\$body)\;\ return\}\n\ \ \ \ set\ every(\$body)\ \[info\ level\ 0\]\n\ \ \ \ eval\ \$body\n\ \ \ \ after\ \$ms\ \[info\ level\ 0\]\n\}\n\n======\n\nand\ this\ is\ a\ tidied\ version\ of\ the\ digital\ clock\ that\ started\ this\ page:\n======\npack\ \[label\ .clock\ -textvar\ time\]\nevery\ 1000\ \{set\ ::time\ \[clock\ format\ \[clock\ sec\]\ -format\ %H:%M:%S\]\}\n======\n\nI\ admit\ that\ the\ minimal\ ''every''\ creates\ runaway\ timers\ that\ will\ tick\ on\nforever\ -\ almost:\ you\ can\ reset\ all\ timers\ with\n======\nforeach\ id\ \[after\ info\]\ \{after\ cancel\ \$id\}\ \n======\n\nTo\ limit\ the\ number\ of\ repetitions,\ use\ return:\n\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ eval\ \$body\n\ \ \ \ after\ \$ms\ \[info\ level\ 0\]\n\}\nset\ nmax\ 3\nevery\ 1000\ \{puts\ hello\;\ if\ \{\[incr\ ::nmax\ -1\]<=0\}\ return\}\n======\n\n----\n\[RJM\]:\ In\ the\ case\ of\ a\ body/script\ that\ takes\ a\ considerable\ time\ fraction\ of\ the\ interval\ time,\ the\ following\ \[every\]\ is\ more\ precise,\ provided\ the\ script\ under\ repetitive\ execution\ will\ ''normally''\ not\ execute\ longer\ than\ the\ interval\ duration:\n\n======\nproc\ every\ \{ms\ body\}\ \{after\ \$ms\ \[info\ level\ 0\]\;\ eval\ \$body\}\n======\n\nThe\ after\ command\ is\ set\ up\ ''prior''\ to\ the\ script\ call.\ \ One\ problem\ with\nthis\ approach\ is\ that\ the\ body\ can\ not\ then\ \"cancel\"\ itself\ via\ \[return\]\n\n\[FPX\]:\ Note\ that\ the\ latter\ may\ not\ be\ a\ good\ idea\ if\ the\ body\ (a)\ may\ in\ fact\ntake\ longer\ to\ execute\ than\ the\ interval,\ and\ (b)\ invokes,\ at\ some\ point,\ the\nevent\ loop.\ In\ that\ case,\ you\ might\ want\ to\ guard\ against\ reentrancy.\n\n\[RJM\]:\ Shouldn't\ be\ a\ serious\ problem.\ It\ will\ only\ cause\ more\ stack\ access\ when\ any\ script\ executes\ longer\ than\ the\ after\ interval.\ It\ works\ pretty\ good\ for\ situations\ where\ the\ script\ has\ a\ big\ execution\ time\ standard\ deviation\ for\ each\ invocation.\n\n\[AMG\]:\ Reëntrancy?\ \ The\ event\ loop\ runs\ in\ the\ same\ thread\ as\ the\ rest\ of\ the\ script,\ so\ the\ script\ can't\ \"run\ on\ top\ of\ itself\".\ \ It\ has\ return\ to\ the\ event\ loop\ before\ the\ event\ loop\ can\ start\ it\ again.\ \ Also,\ a\ long\ script/short\ timeout\ won't\ completely\ starve\ out\ other\ events,\ because\ the\ event\ loop\ will\ give\ them\ all\ their\ fair\ turn.\ \ A\ very\ long-running\ script\ will\ result\ in\ poor\ user\ interface\ response\ times,\ but\ it\ won't\ completely\ freeze\ the\ program\ unless\ the\ script\ loops\ indefinitely.\n\n----\n\[Ken\]:\ When\ i\ ran\ the\ code\ above,\ it\ seems\ it\ only\ runs\ when\ the\ event\ loop\ is\nidle.\ If\ the\ event\ loop\ is\ busy\ with\ a\ while\ procedure,\ it\ doesn't\ run.\ What\ is\na\ better\ alternative?\n\n\[Lars\ H\]:\ Yes,\ while\ one\ event\ is\ being\ processed,\ no\ additional\ events\ are\ fetched.\ This\ is\ as\ it\ is\ supposed\ to\ be.\ As\ for\ better\ alternatives...\ The\ hardliners\ would\ tell\ you\ to\ not\ use\ a\ while\ loop,\ but\ instead\ unroll\ it\ into\ the\ event\ loop\ as\ well.\ See\ \[Keep\ a\ GUI\ alive\ during\ a\ long\ calculation\]\ for\ more\ on\ the\ subject.\n\n\[Ken\]:\ As\ currently\ i\ am\ trying\ to\ code\ a\ simulator\ of\ wireless\ sensor\ nodes\ running\ under\ the\ background,\ so\ what\ i\ have\ to\ do\ is\ create\ for\ example\ 10\ nodes\ and\ run\ them\ all\ under\ the\ background\ under\ the\ event\ loop.\ Thus\ allowing\ my\ main\ tcl\ interpreter\ to\ be\ responsive\ or\ running\ to\ user\ requests.\ And\ how\ to\ get\ one\ node\ to\ run\ under\ an\ event\ loop\ is\ it\ to\ create\ a\ 'proc'\ and\ run\ a\ '\ after'\ command\ on\ it?\n\n**\ See\ Also\ **\n\n\ \ \ *\ \[Syncronized\ timer\]\n\n\n**\ bgLoop\ **\n\n======\n##\ ********************************************************\ \n##\n##\ Name:\ bgLoop\ \n##\n##\ Description:\n##\ Start\ (a)synchronous\ looping\ jobs.\ \ Jobs\ are\ ended\ by\n##\ setting\ ::bg::jobs(\$name,run)\ to\ 0.\n##\n##\ Usage:\n##\ \ \ \ \ \ \ \ start:\ bgLoop\ \$name\ \$code\ \$delay\n##\ \ \ \ \ \ \ \ \ stop:\ set\ ::bg::jobs(\$name,run)\ 0\n##\n##\ Comment:\n##\ We\ started\ seeing\ mysterious\ delays\ in\ some\ very\ complex\n##\ event\ code,\ and\ I\ modified\ the\ older\ version\ of\ bgLoop\n##\ to\ provide\ some\ timing\ info...\ what\ I\ learned\ was\ that\n##\ beyond\ a\ certain\ level\ of\ complexity\ it\ is\ better\ to\ know\n##\ what\ is\ really\ going\ on,\ so\ SYNCHRONOUS\ looping\ is\n##\ quite\ useful.\n##\n##\ What\ is\ very\ nice\ is\ that\ the\ event\ loop\ is\ not\ blocked\n##\ for\ the\ entire\ runtime\ of\ the\ multiple\ scheduled\ code\n##\ blocks,\ and\ the\ timing\ diagnostic\ lets\ you\ design\ around\n##\ long\ running\ tasks\ by\ modifying\ the\ delays\ so\ they\ are\n##\ of\ by\ so-many\ seconds...\n##\n##\ Note\ that\ the\ first\ iteration\ \"returns\"\ for\ sanity,\n##\ and\ that\ you\ *should*\ use\ a\ custom\ bgerror\ handler\n##\ if\ you\ are\ doing\ this\ from\ Tcl\ like\ I\ am\ (no\ Tk).\n##\n\nbgLoop\ \{\ \{\ name\ NULL\ \}\ \{\ code\ \"\"\ \}\ \{\ delay\ 2\ \}\ \}\ \{\n\ \ \n\ \ \ \ if\ \{\ !\ \[\ llength\ \[\ namespace\ children\ ::\ bg\ \]\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ namespace\ eval\ bg\ \{\}\n\ \ \ \ \ \ \ \ set\ ::bg::starttime\ \[\ clock\ seconds\ \]\n\ \ \ \ \}\n\ \ \ \ set\ now\ \[\ clock\ seconds\ \]\ \n\ \ \ \ set\ elapsed\ \[\ expr\ \{\ \$now\ -\ \$::bg::starttime\ \}\ \]\n\ \ \ \ \n\ \ \ \ \;##\ register\ a\ new\ job\ if\ it\ has\ valid\ args\n\ \ \ \ if\ \{\ !\ \[\ string\ equal\ NULL\ \$name\ \]\ \ \ \ \ \ &&\ \\\n\ \ \ \ \ \ \ \ \ \ \ \ \ \[\ string\ length\ \[\ join\ \$code\ \]\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ set\ ::bg::jobs(\$name,run)\ \ \ 1\n\ \ \ \ \ \ \ \ set\ ::bg::jobs(\$name,code)\ \ \$code\n\ \ \ \ \ \ \ \ set\ ::bg::jobs(\$name,delay)\ \$delay\n\ \ \ \ \ \ \ \ puts\ stderr\ \"Looping\ process\ \$name\ started\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ if\ \{\ \[\ info\ exists\ ::bg::after\ \]\ &&\ \\\n\ \ \ \ \ \ \ \ \ \ \[\ lsearch\ \[\ after\ info\ \]\ \$::bg::after\ \]\ !=\ -1\ \}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$::bg::after\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ if\ \{\ \[\ string\ equal\ NULL\ \$name\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ set\ dt\ 0\n\ \ \ \ \ \ \ \ foreach\ job\ \[\ array\ names\ ::bg::jobs\ *,run\ \]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ job\ \[\ lindex\ \[\ split\ \$job\ ,\ \]\ 0\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\ \[\ string\ equal\ NULL\ \$job\ \]\ \}\ \{\ continue\ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\ \[\ string\ equal\ 0\ \$::bg::jobs(\$job,run)\ \]\ \}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ foreach\ item\ \[\ array\ names\ ::bg::jobs\ \$job,*\ \]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ ::bg::jobs(\$item)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ puts\ stderr\ \"Looping\ process\ \$job\ terminated\"\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ continue\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\ !\ (\$elapsed\ %\ \$::bg::jobs(\$job,delay))\ \}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ ts\ \[\ clock\ clicks\ -milliseconds\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ eval\ \$::bg::jobs(\$job,code)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ te\ \[\ clock\ clicks\ -milliseconds\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ td\ \[\ expr\ \$te\ -\ \$ts\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ dt\ \[\ expr\ \$dt\ +\ \$td\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ data\ \[\ list\ \$job\ \$td\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\n\ \ \ \ \ \ \ \ if\ \{\ \$dt\ >\ 1000\ \}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ puts\ stderr\ \"bgLoop\ runtime\ per\ iteration:\ \$dt\ ms\ (\$data)\"\ \ \n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ set\ ::bg::after\ \[\ after\ 1000\ bgLoop\ \]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ retval\ \[\ eval\ \$::bg::jobs(\$name,code)\ \]\n\ \ \ \ \ \ \ \ set\ ::bg::after\ \[\ after\ 1000\ bgLoop\ \]\n\ \ \ \ \ \ \ \ return\ \$retval\n\ \ \ \ \}\n\}\n======\n\n\[DKF\]:\ Here's\ a\ version\ of\ '''every'''\ that\ can\ be\ cancelled\ too:\n\n======\nproc\ every\ \{interval\ script\}\ \{\n\ \ \ \ global\ everyIds\n\ \ \ \ if\ \{\$interval\ eq\ \"cancel\"\}\ \{\n\ \ \ \ \ \ \ \ catch\ \{after\ cancel\ \$everyIds(\$script)\}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ set\ everyIds(\$script)\ \[after\ \$interval\ \[info\ level\ 0\]\]\n\ \ \ \ uplevel\ #0\ \$script\n\}\n======\n\n\[NEM\]\ ''30\ July\ 2006'':\ And\ here's\ one\ that\ can\ be\ cancelled\ from\ within\ the\ script\ too\ \n(using\ \[break\]):\n======\nproc\ every\ \{interval\ script\}\ \{\n\ \ \ \ global\ everyIds\n\ \ \ \ if\ \{\$interval\ eq\ \"cancel\"\}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$everyIds(\$script)\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ set\ everyIds(\$script)\ \[after\ \$interval\ \[info\ level\ 0\]\]\n\ \ \ \ set\ rc\ \[catch\ \{uplevel\ #0\ \$script\}\ result\]\n\ \ \ \ if\ \{\$rc\ ==\ \[catch\ break\]\}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$everyIds(\$script)\n\ \ \ \ \ \ \ \ set\ rc\ 0\n\ \ \ \ \}\ elseif\ \{\$rc\ ==\ \[catch\ continue\]\}\ \{\n\ \ \ \ \ \ \ \ #\ Ignore\ -\ just\ consume\ the\ return\ code\n\ \ \ \ \ \ \ \ set\ rc\ 0\n\ \ \ \ \}\n\ \ \ \ #\ TODO:\ Need\ better\ handling\ of\ errorInfo\ etc...\n\ \ \ \ return\ -code\ \$rc\ \$result\n\}\n======\nWhich\ allows\ the\ countdown\ example\ to\ be\ written\ as:\n======\nset\ nmax\ 3\nevery\ 1000\ \{\n\ \ \ \ puts\ hello\n\ \ \ \ if\ \{\[incr\ nmax\ -1\]\ <=\ 0\}\ \{\ break\ \}\n\}\n======\n\n\[RS\]\ 2006-07-31:\ Hmm\ yes,\ but\ the\ \[simple\]\ \[every\]\ allows\ that\ too,\ if\ you\ just\ use\ \[return\]:\n\n======\nproc\ every\ \{ms\ body\}\ \{eval\ \$body\;\ after\ \$ms\ \[info\ level\ 0\]\}\nset\ ::nmax\ 3\nevery\ 1000\ \{puts\ hello\;\ if\ \{\[incr\ ::nmax\ -1\]<=0\}\ return\}\n======\n\nI\ prefer\ not\ to\ use\ implicit\ global\ scope,\ for\ environment\ tidyness...\ :)\n\n\[NEM\]\ Well,\ implicit\ global\ scope\ is\ characteristic\ of\ other\ event\ callbacks,\nso\ it\ seems\ like\ the\ least\ surprising\ option.\ Likewise,\ having\ to\ use\ \[return\]\nto\ exit\ something\ that\ isn't\ a\ proc\ seems\ confusing.\ I\ prefer\ a\ simple\ninterface\ to\ a\ simple\ implementation.\ (Also\ the\ simple\ version\ has\ the\ problem\nof\ time\ drift\ if\ you\ have\ a\ long-running\ script\ as\ discussed\ above).\n----\n26-may-2005\n\n----\n\[Jeffrey\ Hobbs\]\ supplies\ a\ comparable,\ but\ distinct,\ version\ of\ \"every\",\ in\ a\npost\ on\n\[http://groups.google.com/groups?selm=37BC45C8.72F9509B%40scriptics.com%|%comp.lang.tcl\n,\ 1999-08-19\].\n\n======\n#\ every\ --\n#\ \ \ Cheap\ rescheduler\n#\ every\ <time>\ cmd\;\ \ \ \ \ \ \ \ #\ cmd\ is\ a\ one\ arg\ (cmd\ as\ list)\n#\ \ \ \ \ \ \ \ schedules\ \$cmd\ to\ be\ run\ every\ <time>\ 1000ths\ of\ a\ sec\n#\ \ \ \ \ \ \ \ IOW,\ \[every\ 1000\ \"puts\ hello\"\]\ prints\ hello\ every\ sec\n#\ every\ cancel\ cmd\n#\ \ \ \ \ \ \ \ cancels\ a\ cmd\ if\ it\ was\ specified\n#\ every\ info\ ?pattern?\n#\ \ \ \ \ \ \ \ returns\ info\ about\ commands\ in\ pairs\ of\ \"time\ cmd\ time\ cmd\ ...\"\n#\nproc\ every\ \{time\ \{cmd\ \{\}\}\}\ \{\n\ \ \ \ global\ EVERY\n\ \ \ \ if\ \{\[regexp\ \{^\[0-9\]+\$\}\ \$time\]\}\ \{\n\ \ \ \ \ \ \ \ #\ A\ time\ was\ given,\ so\ schedule\ a\ command\ to\ run\ every\ \$time\ msecs\n\ \ \ \ \ \ \ \ if\ \{\[string\ compare\ \{\}\ \$cmd\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ EVERY(TIME,\$cmd)\ \$time\n\ \ \ \ \ \ \ \ \ \ \ \ set\ EVERY(CMD,\$cmd)\ \[after\ \$time\ \[list\ every\ eval\ \$cmd\]\]\n\ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ return\ -code\ error\ \"wrong\ \\#\ args:\ should\ be\ \\\"\[lindex\ \[info\ level\ 0\]\n0\]\ <number>\ command\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ switch\ \$time\ \{\n\ \ \ \ \ \ \ \ eval\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[info\ exists\ EVERY(TIME,\$cmd)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ uplevel\ \\#0\ \$cmd\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ EVERY(CMD,\$cmd)\ \[after\ \$EVERY(TIME,\$cmd)\ \\\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \[list\ every\ eval\ \$cmd\]\]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ cancel\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[string\ match\ \"all\"\ \$cmd\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ foreach\ i\ \[array\ names\ EVERY\ CMD,*\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$EVERY(\$i)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ EVERY(\$i)\ EVERY(TIME,\[string\ range\ \$i\ 4\ end\])\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \}\ elseif\ \{\[info\ exists\ EVERY(CMD,\$cmd)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$EVERY(CMD,\$cmd)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ EVERY(CMD,\$cmd)\ EVERY(TIME,\$cmd)\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ info\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ result\ \{\}\n\ \ \ \ \ \ \ \ \ \ \ \ foreach\ i\ \[array\ names\ EVERY\ TIME,\$cmd*\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ cmd\ \[string\ range\ \$i\ 5\ end\]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ result\ \$EVERY(\$i)\ \$cmd\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ return\ \$result\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ return\ -code\ error\ \"bad\ option\ \\\"\$time\\\":\ must\ be\ cancel,\ info\ or\ a\nnumber\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\n\}\n\n\n======\n----\n\n\[DKF\]:\ Here's\ a\ scheduler\ that\ lets\ you\ schedule\ regular\ events\ and\ stop\ them\ whenever\ you\ like,\ using\ a\ similar\ scheme\ to\ \[\[after\]\]/\[\[after\ cancel\]\].\n\n======\n##\ ****************************************************************\n##\ Name:\n##\ \ \ \ \ every\n##\ Description:\n##\ \ \ \ \ Schedules\ a\ script\ for\ being\ regularly\ executed,\ returning\n##\ \ \ \ \ a\ token\ that\ allows\ the\ scheduling\ to\ be\ halted\ at\ some\n##\ \ \ \ \ future\ point.\n##\ Usage:\n##\ \ \ \ \ every\ ms\ script...\n##\ \ \ \ \ every\ cancel\ token\n##\ \ \ \ \ every\ cancel\ script...\n##\ Notes:\n##\ \ \ \ \ The\ script\ is\ executed\ at\ the\ global\ level,\ and\ any\ errors\n##\ \ \ \ \ generated\ by\ the\ script\ will\ NOT\ cause\ a\ cessation\ of\ future\n##\ \ \ \ \ schedulings.\ \ Thus,\ any\ script\ that\ always\ causes\ an\ error\n##\ \ \ \ \ will\ cause\ many\ user-interface\ problems\ when\ used\ with\ a\n##\ \ \ \ \ short\ delay.\n##\ \ \ \ \ While\ differently\ scheduled\ scripts\ do\ not\ need\ to\ be\n##\ \ \ \ \ distinct\ from\ each\ other,\ it\ is\ not\ determined\ which\ one\n##\ \ \ \ \ will\ be\ cancelled\ if\ you\ use\ the\ cancelling\ form\ with\ the\n##\ \ \ \ \ script\ as\ opposed\ to\ the\ token.\n##\ Example:\n##\ \ \ \ \ set\ foo\ \[every\ 500\ \{puts\ \[clock\ format\ \[clock\ seconds\]\]\}\]\n##\ \ \ \ \ every\ 10000\ puts\ Howdy!\n##\ \ \ \ \ #\ ...\n##\ \ \ \ \ after\ cancel\ \$foo\n##\ \ \ \ \ after\ cancel\ puts\ Howdy!\n##\ ****************************************************************\nproc\ every\ \{option\ args\}\ \{\n\ \ \ \ global\ everyPriv\ every:UID\n\ \ \ \ if\ \{\[string\ equal\ -length\ \[string\ length\ \$option\]\ \$option\ cancel\]\}\ \{\n\ \ \ \ \ \ \ \ set\ id\ \{\}\n\ \ \ \ \ \ \ \ if\ \{\[llength\ \$args\]\ ==\ 1\ &&\ \[string\ match\ every#*\ \[lindex\ \$args\ 0\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ id\ \[lindex\ \$args\ 0\]\n\ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ script\ \[eval\ \[list\ concat\]\ \$args\]\n\ \ \ \ \ \ \ \ \ \ \ \ #\ Yuck,\ a\ linear\ search.\ \ A\ reverse\ hash\ would\ be\ faster...\n\ \ \ \ \ \ \ \ \ \ \ \ foreach\ \{key\ value\}\ \[array\ get\ everyPriv\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[string\ equal\ \$script\ \[lindex\ \$value\ 1\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ id\ \$key\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ break\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ if\ \{\[string\ length\ \$id\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \[lindex\ \$everyPriv(\$id)\ 2\]\n\ \ \ \ \ \ \ \ \ \ \ \ unset\ everyPriv(\$id)\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ id\ \[format\ \"every#%d\"\ \[incr\ every:UID\]\]\n\ \ \ \ \ \ \ \ set\ script\ \[eval\ \[list\ concat\]\ \$args\]\n\ \ \ \ \ \ \ \ set\ delay\ \$option\n\ \ \ \ \ \ \ \ set\ aid\ \[after\ \$delay\ \[list\ every:afterHandler\ \$id\]\]\n\ \ \ \ \ \ \ \ set\ everyPriv(\$id)\ \[list\ \$delay\ \$script\ \$aid\]\n\ \ \ \ \ \ \ \ return\ \$id\n\ \ \ \ \}\n\}\n##\ Internal\ stuff\ -\ I\ could\ do\ this\ with\ a\ namespace,\ I\ suppose...\narray\ set\ everyPriv\ \{\}\nset\ every:UID\ 0\nproc\ every:afterHandler\ \{id\}\ \{\n\ \ \ \ global\ everyPriv\n\ \ \ \ foreach\ \{delay\ script\ oldaid\}\ \$everyPriv(\$id)\ \{\}\n\ \ \ \ set\ aid\ \[after\ \$delay\ \[info\ level\ 0\]\]\n\ \ \ \ set\ everyPriv(\$id)\ \[list\ \$delay\ \$script\ \$aid\]\n\ \ \ \ uplevel\ #0\ \$script\n\}\n======\n''(I\ have\ this\ feeling\ that\ my\ definition\ of\ production-quality\ code\ is\ not\ the\ same\ as\ that\ of\ other\ people.)''\n----\ \n\n\[Josua\ Dietze\]\ <digidietze\ at\ t-online.de>\ contributed\ this\ idea\non\ news:comp.lang.tcl\ :\n======\n\ proc\ TimerFunction\ \{state\ \{rate\ \{\}\}\}\ \{\n\ global\ after_id\n\ \ \ \ if\ \{\ \$state\ ==\ \"start\"\ \}\ \{\n\ \ \ \ \ \ \ \ sendVal\ \"send_status\"\n\ \ \ \ \ \ \ \ set\ after_id\ \[after\ \$rate\ TimerFunction\ start\ \$rate\]\n\ \ \ \ \}\ elseif\ \{\ \$state\ ==\ \"stop\"\ \}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \$after_id\n\ \ \ \ \}\n\ \}\n\n\ TimerFunction\ start\ 2000\n\ TimerFunction\ stop\n======\nJust\ make\ sure\ you\ start\ and\ stop\ exactly\ once\ ...\n\n----\n\n\[kruzalex\]\ An\ alternative\ to\ the\ stuff\ mentioned\ above:\n======\nproc\ every\ \{interval\ args\}\ \{\n\ \ \ \ global\ everyPriv\ every:UID\n\ \ \ \ if\ \{\[string\ equal\ -length\ \[string\ length\ \$interval\]\ \$interval\ cancel\]\}\ \{\n\ \ \ \ set\ id\ \{\}\n\ \ \ \ if\ \{\[llength\ \$args\]\ ==\ 1\ &&\ \[string\ match\ every#*\ \[lindex\ \$args\ 0\]\]\}\ \{\n\ \ \ \ \ \ \ \ set\ id\ \[lindex\ \$args\ 0\]\n\ \ \ \ \}\ \n\ \ \ \ if\ \{\[string\ length\ \$id\]\}\ \{\n\ \ \ \ \ \ \ \ after\ cancel\ \[lindex\ \$everyPriv(\$id)\ 2\]\n\ \ \ \ \ \ \ \ unset\ everyPriv(\$id)\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ set\ id\ \[format\ \"every#%d\"\ \[incr\ every:UID\]\]\n\ \ \ \ set\ script\ \[eval\ \[list\ concat\]\ \$args\]\n\ \ \ \ foreach\ \{key\ value\}\ \[array\ get\ everyPriv\]\ \{\n\ \ \ \ \ \ \ \ if\ \{\[string\ equal\ \$script\ \[lindex\ \$value\ 1\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ id\ \$key\n\ \ \ \ \ \ \ \ \ \ \ \ set\ time\ \[lindex\ \$everyPriv(\$id)\ 0\]\n\ \ \ \ \ \ \ \ \ \ \ \ break\n\ \ \ \ \ \ \ \ \}\ \n\ \ \ \ \}\n\ \ \ \ if\ \{!\[info\ exists\ everyPriv(\$id)\]\}\ \{\n\ \ \ \ \ \ \ \ set\ everyPriv(\$id)\ \[concat\ \$interval\ \[list\ \$script\]\ \[after\ \$interval\ \[info\ level\ 0\]\]\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ uplevel\ #0\ \$script\n\ \ \ \ \ \ \ \ set\ everyPriv(\$id)\ \[concat\ \$time\ \[list\ \$script\]\ \[after\ \$interval\ \[info\ level\ 0\]\]\]\n\ \ \ \ \}\n\ \ \ \ return\ \$id\n\}\n\narray\ set\ everyPriv\ \{\}\nset\ every:UID\ 0\n\n#Example\nset\ foo\ \[every\ 1000\ \{puts\ foo\}\]\nset\ foo1\ \[every\ 2000\ \{puts\ foo1\}\]\nafter\ 3000\ \[list\ every\ cancel\ \$foo\]\nvwait\ forever\n======\n\n\n----\n\[XO\]\ 2008-12-03:\ See\ also\ http://code.activestate.com/recipes/68393/%|%Recipe\ 68393:\ Repeat\ procedure\ every\ X\ seconds\ %|%\ \ \n\n----\n======\nproc\ every\ \{ms\ body\}\ \{\n\ \ \ \ set\ t\ \[string\ range\ \[time\ \$body\]\ 0\ end-27\]\n\ \ \ \ after\ \[expr\ \{\$ms-\$t/1000\}\]\ \[info\ level\ 0\]\ \ \n\}\n======\n\n\n\[http://wiki.tcl.tk/25434%|%rjmcmahon%|%\]\ Here's\ a\ full\ blown\ version\ of\ every\ that\ covers\ most\ cases.\ Calls\ to\ it\ are:\n\ \ \ \ :\ \ \ '''every'''\ ''seconds''\ ?''script\ script\ ...''?\n\ \ \ **\ returns\ an\ everyid\n\ \ \ \ :\ \ \ '''every'''\ ''integer''\ '''-milliseconds'''\ ?''script\ script\ ...''?\n\ \ \ **\ returns\ an\ everyid\n\ \ \ \ :\ \ \ '''every\ cancel'''\ ''everyid''\n\ \ \ \ :\ \ \ '''every\ cancel\ all'''\n\ \ \ \ :\ \ \ '''every\ info'''\n\ \ \ **\ returns\ all\ everyids\n\n======\nproc\ every\ \{args\}\ \{\n\ \ \ \ global\ _everyids\ \ _everyid\ \n\n\ \ \ \ if\ \{!\[llength\ \$args\]\}\ \{\n\ \ \ \ \ \ \ \ if\ \{\[info\ exists\ _everyids\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ parray\ _everyids\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\n\ \ \ \ set\ interval\ \[lindex\ \$args\ 0\]\n\ \ \ \ if\ \{\$interval\ ==\ \"info\"\}\ \{\n\ \ \ \ \ \ \ \ return\ \[array\ names\ _everyids\]\n\ \ \ \ \}\n\ \ \ \ #\n\ \ \ \ #\ See\ if\ arg1\ is\ a\ -milliseconds\ option\n\ \ \ \ #\ \n\ \ \ \ set\ arg1\ \[lindex\ \$args\ 1\]\n\ \ \ \ if\ \{\$arg1\ ==\ \"-milliseconds\"\}\ \{\n\ \ \ \ \ \ \ \ set\ script\ \[lrange\ \$args\ 2\ end\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ #\ \ In\ this\ case\ a\ numeric\ arg1\ is\ given\ in\ seconds\n\ \ \ \ \ \ \ \ #\ \ so\ convert\ to\ an\ integer\ number\ of\ ms.\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ if\ \{\$interval\ !=\ \"cancel\"\ &&\ \$interval\ !=\ \"idle\"\}\ \{\ \n\ \ \ \ \ \ \ \ \ \ \ \ set\ interval\ \[expr\ \{round(\$interval\ *\ 1000)\}\]\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ set\ script\ \[lrange\ \$args\ 1\ end\]\n\ \ \ \ \}\n\n\ \ \ \ #\n\ \ \ \ #\ \ Process\ any\ cancel\ requests.\ \n\ \ \ \ #\ \ Options\ are\n\ \ \ \ #\ \ o\ \ every\ cancel\ all\n\ \ \ \ #\ \ o\ \ every\ cancel\ <everyid>\n\ \ \ \ #\n\ \ \ \ if\ \{\$interval\ eq\ \"cancel\"\}\ \{\n\ \ \ \ \ \ \ \ if\ \{!\[info\ exists\ _everyids\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ return\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ if\ \{\$script\ eq\ \"all\"\}\ \{\ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ set\ idlist\ \[array\ names\ _everyids\]\n\ \ \ \ \ \ \ \ \ \ \ \ foreach\ id\ \$idlist\ \{\ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\$_everyids(\$id)\ !=\ \"RUNNING\"\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$_everyids(\$id)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ _everyids(\$id)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ _everyids(\$id)\ \"CANCELPENDING\"\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ index\ \$script\n\ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[info\ exists\ _everyids(\$index)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ #\ Cancel\ now\ if\ the\ script\ is\ not\ running\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ #\ otherwise\ signal\ the\ underlying\ _every\ not\ to\ reschedule\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\$_everyids(\$index)\ !=\ \"RUNNING\"\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ after\ cancel\ \$_everyids(\$index)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ unset\ _everyids(\$index)\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ _everyids(\$index)\ \"CANCELPENDING\"\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \}\ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\n\ \ \ \ if\ \{\[info\ exists\ _everyid\]\}\ \{\n\ \ \ \ \ \ \ \ incr\ _everyid\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ _everyid\ 100\n\ \ \ \ \}\n\n\ \ \ \ #\n\ \ \ \ #\ \ Now\ that\ user\ command\ processing\ is\ done,\ call\ the\ \n\ \ \ \ #\ \ underlying\ every\ routine\ to\ start\ the\ script\ on\ \n\ \ \ \ #\ \ its\ periodic\ (per\ interval)\ and\ return\ a\ unique\ everyid.\n\ \ \ \ #\n\ \ \ \ _every\ \$interval\ \$script\ \"every#\$_everyid\"\n\ \ \ \ return\ \"every#\$_everyid\"\n\}\n\nproc\ _every\ \{interval\ script\ id\}\ \{\n\ \ \ \ global\ _everyids\ \n\n\ \ \ \ #\n\ \ \ \ #\ \ Run\ the\ script\ and\ measure\ the\ time\ taken\ to\ run\n\ \ \ \ #\ \n\ \ \ \ set\ starttime\ \[clock\ clicks\ -milliseconds\]\n\ \ \ \ set\ _everyids(\$id)\ \"RUNNING\"\n\ \ \ \ set\ rc\ \[catch\ \{uplevel\ #0\ eval\ \$script\}\ result\]\n\ \ \ \ set\ finishtime\ \[clock\ clicks\ -milliseconds\]\n\ \ \ \ \n\ \ \ \ #\n\ \ \ \ #\ \ Detect\ and\ process\ any\ catch\ codes\ from\ the\ script\n\ \ \ \ #\n\ \ \ \ #\ \ Note:\ The\ script\ returning\ a\ break\ catch\ code\ is\ \n\ \ \ \ #\ \ used\ to\ indicate\ a\ silent\ stop\ of\ the\ rescheduling\ \n\ \ \ \ #\ \n\ \ \ \ if\ \{\$rc\ ==\ \[catch\ error\]\}\ \{\n\ \ \ \ \ \ \ \ error\ \"\$result\ \$script\"\n\ \ \ \ \ \ \ \ return\ \n\ \ \ \ \}\ elseif\ \{\$rc\ ==\ \[catch\ break\]\}\ \{\n\ \ \ \ \ \ \ \ if\ \{\[info\ exists\ _everyids(\$id)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ unset\ _everyids(\$id)\ \n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ return\n\ \ \ \ \}\ elseif\ \{\$rc\ ==\ \[catch\ continue\]\}\ \{\n\ \ \ \ \ \ \ \ #\ Ignore\ -\ just\ consume\ the\ return\ code\n\ \ \ \ \ \ \ \ set\ rc\ 0\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ #\n\ \ \ \ #\ \ Adjust\ the\ reschedule\ time\ per\ the\ actual\ runtime\n\ \ \ \ #\ \ Provide\ a\ minimum\ of\ 30\ ms\ for\ a\ yield\ \n\ \ \ \ #\n\ \ \ \ if\ \{\$interval\ !=\ \"idle\"\}\ \{\n\ \ \ \ \ \ \ \ set\ runtime\ \[expr\ \{\$finishtime\ -\ \$starttime\}\]\n\ \ \ \ \ \ \ \ set\ adj_interval\ \[expr\ \{\$interval\ -\ \$runtime\}\]\n\ \ \ \ \ \ \ \ if\ \{\$adj_interval\ <\ 0\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ puts\ \"\$script\ runtime\ (\$runtime\ ms)\ exceeded\ reschedule\ interval\ (\$interval\ ms)\"\ \n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ #\ \ Set\ a\ minimum\ of\ 30\ ms\ to\ reschedule\n\ \ \ \ \ \ \ \ #\n\ \ \ \ \ \ \ \ if\ \{\$adj_interval\ <\ 30\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ adj_interval\ 30\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ set\ adj_interval\ \"idle\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ #\n\ \ \ \ #\ \ Reschedule\ next\ iteration\ unless\ there\ is\ a\ cancel\ pending.\n\ \ \ \ #\n\ \ \ \ #\ \ Note:\ \ The\ rescheduling\ of\ the\ script\ is\ done\ after\n\ \ \ \ #\ \ calling\ it.\ This\ can\ be\ swapped\ but\ is\ a\ bit\ more\ complex,\n\ \ \ \ #\ \ particularly\ when\ execution\ time\ >\ interval.\n\ \ \ \ #\n\ \ \ \ if\ \{\$_everyids(\$id)\ !=\ \"CANCELPENDING\"\}\ \{\n\ \ \ \ \ \ \ \ set\ _everyids(\$id)\ \[after\ \$adj_interval\ \[list\ _every\ \$interval\ \$script\ \$id\]\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ unset\ _everyids(\$id)\n\ \ \ \ \}\n\}\n======\n\n----\n\[Napier\]\ 2015-12-23\ \nHow\ about\ a\ version\ that\ utilizes\ coroutines\ ?\ \ I\ believe\ this\ implements\ all\ the\ features\ of\ the\ above\ options\ for\ the\ most\ part\ in\ a\ fairly\ clean\ and\ clear\ manner.\ \ It\ also\ implements\ pause\ and\ resume\ capabilities\ for\ both\ scripts\ and\ everyid's\ where\ they\ resume\ as-if\ they\ were\ never\ paused.\n\n\ \ \ \ :\ \ \ '''every'''\ ''milliseconds''\ ?''script...''?\n\ \ \ *\ returns\ an\ everyid\n\ \ \ \ :\ \ \ '''every\ pause'''\ ''everyid''\n\ \ \ *\ pauses\ execution\ of\ ''everyid''\ until\ resumed\ or\ cancelled/killed\n\ \ \ \ :\ \ \ '''every\ pause'''\ ?''script...''?\n\ \ \ *\ pauses\ the\ executions\ of\ ?''script...''?\ until\ resumed\ or\ cancelled/killed\n\ \ \ \ :\ \ \ '''every\ resume'''\ ''everyid''\n\ \ \ *\ resumes\ execution\ of\ ''everyid''\n\ \ \ \ :\ \ \ '''every\ resume'''\ ?''script...''?\n\ \ \ *\ resumes\ execution\ of\ ?''script...''?\n\ \ \ \ :\ \ \ '''every\ cancel'''\ ''everyid''\n\ \ \ *\ cancels\ future\ execution\ of\ ''everyid''\n\ \ \ \ :\ \ \ '''every\ cancel'''\ ?''script...''?\n\ \ \ *\ cancels\ all\ executions\ of\ ?''script...''?\n\ \ \ \ :\ \ \ '''every\ kill'''\n\ \ \ *\ cancels\ all\ executions\ immediately\n\ \ \ \ :\ \ \ '''every\ info'''\n\ \ \ *\ provides\ a\ list\ of\ all\ active\ ''everyid''\ values\n\nOr\ if\ you\ prefer\ you\ may\ abbreviate\ however\ you'd\ like,\ ''every\ c\ \$everyID'',\ ''every\ p\ \$everyID'',\ ''every\ k''\n\nIf\ you\ are\ wrapping\ a\ script,\ do\ so\ the\ same\ way\ you\ would\ handle\ similar\ scripts\ like\ after.\n\n======\nset\ foo\ World\nevery\ 1000\ \{puts\ \"Hey\ There\"\}\nevery\ 1000\ \[list\ puts\ \"Hello,\ \$foo\"\]\n======\n\nproc\ every\ \{option\ args\}\ \{\n\ \ \ \ variable\ evID\n\ \ \ \ if\ \{\[string\ is\ entier\ \$option\]\}\ \{\n\ \ \ \ \ \ \ \ set\ name\ \[coroutine\ every#\[incr\ evID\]\ ::Every::Process\ \$option\ \{*\}\$args\]\n\ \ \ \ \ \ \ \ dict\ lappend\ ::Every::Active\ \$args\ \$name\;\ return\ \$name\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ switch\ -nocase\ -glob\ --\ \$option\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ c*\ \{lappend\ ::Every::Cancel\ \$args\}\n\ \ \ \ \ \ \ \ \ \ \ \ k*\ \{lappend\ ::Every::Cancel\ \{*\}\[concat\ \{*\}\[dict\ values\ \$::Every::Active\]\]\}\n\ \ \ \ \ \ \ \ \ \ \ \ p*\ \{lappend\ ::Every::Pause\ \$args\}\n\ \ \ \ \ \ \ \ \ \ \ \ r*\ \{set\ ::Every::Pause\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$::Every::Pause\ \$args\ \]\}\n\ \ \ \ \ \ \ \ \ \ \ \ i*\ -\n\ \ \ \ \ \ \ \ \ \ \ \ default\ \{return\ \[concat\ \{*\}\[dict\ values\ \$::Every::Active\]\]\}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\}\n\nnamespace\ eval\ Every\ \{\n\ \ \ \ variable\ Active\ \{\}\;\ variable\ Cancel\ \{\}\;\ variable\ Pause\ \{\}\n\ \ \ \ \n\ \ \ \ proc\ Process\ \{delay\ args\}\ \{\n\ \ \ \ \ \ \ \ after\ \$delay\ \[info\ coroutine\]\n\ \ \ \ \ \ \ \ yield\ \[info\ coroutine\]\n\ \ \ \ \ \ \ \ variable\ Cancel\n\ \ \ \ \ \ \ \ variable\ Pause\n\ \ \ \ \ \ \ \ if\ \{\[info\ coroutine\]\ in\ \$Cancel\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ Cancel\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Cancel\ \[info\ coroutine\]\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ set\ Pause\ \ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Pause\ \[info\ coroutine\ \]\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ set\ cancel\ true\n\ \ \ \ \ \ \ \ \}\ elseif\ \{\$args\ in\ \$Cancel\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ set\ cancel\ true\n\ \ \ \ \ \ \ \ \}\ else\ \{\ set\ cancel\ false\ \}\n\ \ \ \ \ \ \ \ if\ \{\$cancel\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ variable\ Active\n\ \ \ \ \ \ \ \ \ \ \ \ dict\ set\ Active\ \$args\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \[dict\ get\ \$Active\ \$args\]\ \[info\ coroutine\]\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[dict\ get\ \$Active\ \$args\]\ eq\ \{\}\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ dict\ unset\ Active\ \$args\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ Cancel\ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Cancel\ \$args\ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ set\ Pause\ \ \[\ lsearch\ -all\ -inline\ -not\ -exact\ \$Pause\ \$args\ \ \]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\ \n\ \ \ \ \ \ \ \ \ \ \ \ return\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ if\ \{\[info\ coroutine\]\ ni\ \$Pause\ &&\ \$args\ ni\ \$Pause\}\ \{uplevel\ #0\ \{*\}\$args\}\n\ \ \ \ \ \ \ \ Process\ \$delay\ \{*\}\$args\n\ \ \ \ \}\n\}\n======\n\nAlso,\ as\ most\ similar\ calls,\ this\ will\ execute\ in\ the\ global\ namespace.\ \ As\ an\ extra\ nice\ function\ that\ has\ made\ things\ a\ lot\ easier,\ see\ the\ \"callback\"\ option\ below\ and\ example\ of\ its\ use\ in\ simplifying\ callback\ requirements\n\n======\nproc\ callback\ \{args\}\ \{tailcall\ namespace\ code\ \$args\}\n\nproc\ myProc\ args\ \{puts\ \"Executed\ in\ \[namespace\ current\]\"\ \}\n\nevery\ 1000\ myProc\n\nnamespace\ eval\ foo\ \{\n\ \ \ \ \ proc\ myProc\ args\ \{puts\ \"Executed\ in\ \[namespace\ current\]\"\ \}\n\ \ \ \ \ every\ 1000\ \[callback\ myProc\]\n\}\n\nvwait\ forever\n======\n----\n\n\n<<categories>>\ Command} CALL {my revision every} CALL {::oo::Obj3738223 process revision/every} CALL {::oo::Obj3738221 process}

-errorcode

NONE

-errorinfo

Unknow state transition: LINE -> END
    while executing
"error $msg"
    (class "::Wiki" method "render_wikit" line 6)
    invoked from within
"my render_$default_markup $N $C $mkup_rendering_engine"
    (class "::Wiki" method "render" line 8)
    invoked from within
"my render $name $C"
    (class "::Wiki" method "revision" line 31)
    invoked from within
"my revision $page"
    (class "::Wiki" method "process" line 56)
    invoked from within
"$server process [string trim $uri /]"

-errorline

4