Error processing request

Parameters

CONTENT_LENGTH0
REQUEST_METHODGET
REQUEST_URI/revision/thread?V=56
QUERY_STRINGV=56
CONTENT_TYPE
DOCUMENT_URI/revision/thread
DOCUMENT_ROOT/var/www/nikit/nikit/nginx/../docroot
SCGI1
SERVER_PROTOCOLHTTP/1.1
HTTPSon
REMOTE_ADDR172.71.190.122
REMOTE_PORT51962
SERVER_PORT4443
SERVER_NAMEwiki.tcl-lang.org
HTTP_HOSTwiki.tcl-lang.org
HTTP_CONNECTIONKeep-Alive
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_X_FORWARDED_FOR44.200.249.42
HTTP_CF_RAY86b7ef5a6d191763-IAD
HTTP_X_FORWARDED_PROTOhttps
HTTP_CF_VISITOR{"scheme":"https"}
HTTP_ACCEPT*/*
HTTP_USER_AGENTclaudebot
HTTP_CF_CONNECTING_IP44.200.249.42
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 thread '''\[http://www.tcl.tk/man/tcl/ThreadCmd/thread.htm%|%thread\]'''\ an\ \[extension\]\ distributed\ with\ \[Tcl\],\ brings\ native\ \[threads%|%thread\]\ capabilities\ to\ \[Tcl\].\n\n\n\n**\ Disambiguation\ **\n\n\[http://www.complang.tuwien.ac.at/forth/threaded-code.html%|%A\ distinct\ meaning\]\ of\ \"thread\"\ occurs\ in\ discussions\ of\ \[bytecode\],\ especially\ when\ \[Forth\]\ is\ nearby.\n\n\n\n**\ Attributes\ **\n\n\ \ \ What:\ \ \ Thread\ extension\n\ \ \ Where:\ \ \ http://tcl.sf.net/\n\ \ \ Where:\ \ \ https://core.tcl.tk/thread/timeline?y=ci\n\ \ \ Description:\ \ \ This\ Tcl\ extension,\ with\ a\ thread-enabled\ core,\ allows\ script\ level\ access\ to\ run\ Tcl\ scripts\ within\ threads.\ \ Currently\ at\ version\ 2.7.0.\n\ \ \ Updated:\ \ \ 2013-01-02\n\ \ \ Contact:\ \ \ See\ web\ site\n\n\n\n**\ Associated\ Modules\ **\n\n\n\ \ \ \[ttrace\]:\ \ \ Replicate\ interpreter\ state\ in\ a\ multithreading\ application.\n\n\ \ \ tpool:\ \ \ Pools\ of\ worker\ threads.\n\n\ \ \ tsv:\ \ \ Share\ data\ between\ threads.\n\n\n\n\n**\ See\ Also\ **\n\n\ \ \ \[Threads\]:\ \ \ general\ information\ about\ threads\n\n\ \ \ \[Tcl\ and\ threads\]:\ \ \ general\ information\ about\ threads\ and\ Tcl\ \n\n\ \ \ \[asynchronous\ threads\],\ by\ \[CmcC\]:\ \ \ An\ \[object\ orientation%|%OO\]\ wrapper\ to\ make\ asynchronous\ threading\ over\ the\ \[Thread\]\ package\ a\ little\ easier.\n\n\ \ \ \[two\ threads\ run\ synchronously\]:\ \ \ an\ example\n\n\ \ \ \[http://paste.tclers.tk/2799%|%Thread\ worker\ example\]:\ \ \ by\ \[dgp\]\n\n\ \ \ \[thread:\ -eventmark\ and\ send\ -async\]:\ \ \ How\ these\ two\ features\ can\ interact\ in\ surprising\ ways.\n\n\ \ \ \[daerth\]:\ \ \ Threaded\ pipelined\ backpressure-mediated\ death\ stations.\ \ Stations\ operate\ in\ '''multi'''\ or\ '''balanced'''\ mode,\ the\ latter\ being\ an\ alternative\ to\ tpool.\n\n\n\ \ \ \[ttrace\]:\ \ \ Replicate\ interpreter\ state\ across\ threads.\n\n\n\n**\ Critique\ **\n\n\[dgp\]\ in\ the\ \[Tcl\ chatroom\],\ 2013-04-04:\n\n======none\n10:41\ <@ijchain>\ <dgp>\ tpool::get\ has\ ability\ to\ capture\ the\ error\ state\ of\ a\ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ worker\ thread\ in\ the\ pool.\n10:43\ <@ijchain>\ <dgp>\ it\ does\ look\ like\ \[tpool::wait\]\ is\ the\ only\ way\ to\ wait\ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ for\ worker\ thread\ completion.\ \ That's\ unfortunate.\n10:45\ <@ijchain>\ <dgp>\ I\ imagine\ you\ can\ work\ around\ the\ design\ defect.\n10:45\ <@ijchain>\ <dgp>\ But\ now\ I've\ got\ reasons\ not\ to\ use\ tpool.\ \ Thanks\ for\ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ that.\n======\n\n\n\n**\ Documentation\ **\n\n\ \ \ \[http://www.tcl.tk/doc/howto/thread_model.html%|%Tcl\ Threading\ Model\]:\ \ \ an\ overview.\n\n\ \ \ \[http://www.beedub.com/book/4th/Threads.pdf%|%Multi-Threaded\ Tcl\ Scripts\]:\ \ \ A\ sample\ chapter\ from\ \[Book\ Practical\ Programming\ in\ Tcl\ and\ Tk%|%Practical\ Programming\ in\ Tcl\ and\ Tk\].\n\n\ \ \ \[http://docs.activestate.com/activetcl/8.5/thread/toc.html%|%man\ pages\]:\ \ \ at\ \[ActiveState\]\n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/thread.html%|%thread\ man\ page\]\ (alternates:\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/thread.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/thread.html%|%(sourceforge)\]):\ \ \ \n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/tpool.html%|%tpool\ man\ page\]\ (alternates:\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/tpool.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/tpool.html%|%sourceforge\]):\ \ \ \n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/tsv.html%|%tsv\ man\ page\]\ (alternates:\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/tsv.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/tsv.html%|%sourceforge\]):\ \ \ \n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/ttrace.html%|%ttrace\ man\ page\]\ (alternates\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/ttrace.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/ttrace.html%|%sourceforge\]:\ \ \ \n\n\n\n**\ Community\ **\n\n\ \ \ \[https://lists.sourceforge.net/mailman/listinfo/tcl-threads%|%tcl-threads\ mailing\ list\]:\ \ \ at\ \[SourceForge\].\n\n\n\n**\ Tools\ **\n\n\ \ \ \[dqkit\]:\ \ \ is\ a\ convenient\ deployment.\ \ \[snichols\]:\ \ I've\ had\ problems\ with\ \[dqkit\]\ and\ Tcl\ threads.\ On\ a\ Win32\ system\ in\ order\ to\ find\ packages\ in\ a\ child\ thread\ I\ had\ to\ put\ the\ package\ files\ outside\ of\ the\ starkit.\ Because\ of\ that\ its\ almost\ easier\ to\ just\ use\ a\ thread\ enabled\ Tcl\ interpreter\ instead\ of\ dqkit\ if\ the\ package\ files\ have\ to\ exist\ outside\ of\ the\ \[starkit\].\n\n\n\n**\ commands\ **\n\n`package\ require\ Thread`\n\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::create\]'''\ ?'''-joinable'''?\ ?'''-preserved'''?\ ?''script''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::preserve\]'''\ ?''id''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::release\]'''\ ?'''-wait'''?\ ?''id''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::id\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::errorproc\]'''\ ?''procname''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::unwind\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::exit\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::names\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::exists\]'''\ ''id''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::send\]'''\ ?'''-async'''?\ ?'''-head'''?\ ''id\ script''\ ?''varname''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::broadcast\]'''\ ''id\ script''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::wait\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::eval\]'''\ ?'''-lock'''\ ''mutex''?\ ''arg''\ ?''arg\ ...''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::join\]'''\ ''id''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::configure\]'''\ ''id''\ ?''option''?\ ?''value''?\ ?...?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::transfer\]'''\ ''id\ channel''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::detach\]'''\ ''channel''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::attach\]'''\ ''channel''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::mutex\]'''\ ...\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::rwmutex\]'''\ ...\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::cond\]'''\ ...\n\n\n\n**\ Description\ **\n\nSome\ resources\ are\ shared\ between\ threads.\ \ The\ effect\ on\ other\ threads\ must\ be\ntaken\ into\ account\ when\ manipulating\ them.\ \ Such\ resources\ include:\n\n\n\ \ \ '''\[env%|%environment\ variables\]''':\ \ \ \n\n\ \ \ '''`\[exit\]`''':\ \ \ Exits\ the\ process,\ regardless\ of\ which\ thread\ executed\ it.\ \ The\ value\ passed\ to\ `\[exit\]`\ only\ propagates\ as\ the\ process\ exit\ code\ if\ `\[exit\]`\ is\ called\ from\ the\ main\ thread.\n\n\ \ \ '''`\[pwd\]`''':\ \ \ `\[cd\]`\ changes\ the\ current\ directory\ for\ all\ threads.\n\n\ \ \ '''\[stdio\]''':\ \ \ \[stdin\],\ \[stdout\],\ and\ \[stderr\]\ are\ between\ all\ threads.\ \ But\ note\ that\ some\ platforms,\ namely\ \[Microsoft\ Windows%|%Windows\],\ do\ not\ provide\ these\ channels\ to\ processes.\ \ On\ those\ platforms,\ \ the\ standard\ channels\ can\ not\ be\ assumed\ to\ be\ shared\ among\ all\ threads.\n\n\ \ \ \[thread::errorproc\]:\ \ \ This\ is\ a\ global\ setting,\ common\ to\ all\ threads.\n\n\n\n\n\n**\ ttrace\ sub-package\ **\n\nThe\ 2.6\ release\ adds\ a\ sub-package\ called\ Ttrace.\ This\ allows\ for\ easy\npropagation\ of\ interpreter\ related\ resources\ (\[procedure\]s,\ \[namespace\]s,\nobjects\ (for\ \[XOTcl\])).\ Usage\ is\ trivial:\ yust\ wrap\ the\ code\ you\ want\ to\nreplicate\ within\ ttrace::eval\ like\ this:\n\n======\npackage\ req\ Thread\npackage\ req\ Ttrace\n\nfor\ \{set\ i\ 0\}\ \{\$i\ <\ 4\}\ \{incr\ i\}\ \{\n\ \ \ \ \ \ \ \ set\ tid(\$i)\ \[thread::create\ -preserved\]\n\}\n\nttrace::eval\ \{\n\ \ \ \ proc\ foo\ args\ \{\n\ \ \ \ \ \ \ puts\ foo\n\ \ \ \ \}\n\ \ \ \ proc\ bar\ args\ \{\n\ \ \ \ \ \ \ puts\ bar\n\ \ \ \ \}\n\}\n======\n\nand\ execute\ this\ from\ any\ thread\ created\ by\ the\ thread\ extension.\ This\ will\nreplicate\ the\ definitions\ of\ 'foo'\ and\ 'bar'\ to\ all\ existing\ threads.\ In\ the\nabove\ example,\ all\ 4\ threads\ will\ be\ seeded.\ One\ very\ important\ design\ feature\nof\ Ttrace\ is\ that\ resources\ are\ propagated\ in\ a\ lazy\ fashion.\ That\ is,\ nothing\nis\ actually\ done\ in\ other\ threads\ until\ the\ resource\ gets\ referenced\ for\ the\nfirst\ time.\ This\ is\ accomplished\ by\ overloading\ the\ \[Tcl\]\ \[unknown\]\ command.\nSo,\ when\ the\ \[unknown\]\ triggers,\ it\ tries\ to\ locate\ the\ resource\ definition\ by\ndoing\ a\ lookup\ in\ the\ Ttrace\ private\ (in-memory)\ database\ first.\ \ On\ hit,\ the\ndefinition\ is\ loaded\ on-the-fly\ in\ the\ current\ interpreter.\ \ On\ miss,\ the\n\[unknown\]\ processing\ is\ delegated\ down-the-road.\ This\ way,\ thread\ startup\ and\nmemory\ consumption\ are\ minimized.\ Thanks\ to\ \[Vince\ Darley\]\ for\ his\ great\ncommand\ \[trace\]\ framework,\ which\ is\ used\ as\ the\ base\ for\ the\ Trace\ package.\nFor\ more\ information\ see\ 'man\ ttrace'.\n\n\n\n**\ Threads\ and\ the\ event\ loop\ **\n\n\[Silas\]\ 2006-12-17:\ By\ default\ threads\ that\ are\ not\ the\ main\ thread\ don't\ have\ an\ \[event\ loop\]\ (Am\ I\ wrong?).\ Do\ add\ it,\ insert\ a\ \[\[\[vwait\]\ forever\]\]\ or\ \[\[vwait\ ''your_variable''\]\]\ inside\ your\ '''thread::create'''\ block.\ <<br>>\ \[AKM\]\ 2006-10-26\ I\ thought\ the\ advice\ was\ to\ use\ '''thread::wait'''\ so\ that\ the\ thread\ can\ be\ terminated\ with\ '''thread::release'''?\n\n\n\n**\ Examples\ **\n\n\ \ \ \[Sharing\ a\ common\ logger\ thread\ example\]:\ \ \ An\ example\ by\ \[Zoran\ Vasiljevic\]\ of\ a\ logger\ thread\ and\ worker\ threads\ on\ c.l.t.\ (add\ google\ groups\ reference\ here):\n\n\n\n**\ Example:\ A\ Cookbook\ Approach**\n\n\[Marty\ Backe\]\ \ 2002-02-21:\n\n''How\ to\ use\ the\ Thread2.1\ package''\n\n======\n#!\ /bin/sh\n#\\\nexec\ tclsh\ \"\$0\"\ \"\$@\"\n\npackage\ require\ Thread\ 2.1\n\n#\n#\ Start\ a\ thread.\ If\ you\ will\ be\ running\ many\ long\ running\ commands\n#\ at\ the\ same\ time,\ just\ create\ multiple\ threads\ identical\ to\ this\ one,\ and\n#\ keep\ track\ of\ the\ threadID's.\n#\nset\ threadID\ \[thread::create\ \{\n\ \ \ \ #\n\ \ \ \ #\ From\ here\ to\ the\ 'thread::wait'\ statement,\ define\ the\ procedure(s)\n\ \ \ \ #\ that\ will\ be\ called\ from\ your\ main\ program\ (which,\ btw,\ is\ thread\ #1)\n\ \ \ \ #\n\ \ \ \ #\ In\ this\ case,\ I've\ defined\ a\ simple\ procedure\ that\ executes\ the\ command\n\ \ \ \ #\ passed\ into\ this\ thread.\ This\ command\ could\ be\ an\ external\ program\ or\n\ \ \ \ #\ long\ running\ tcl\ command.\n\ \ \ \ #\n\ \ \ \ #\ The\ result\ is\ then\ sent\ back\ to\ the\ main\ program\ (thread\ 1)\ via\ a\ call\n\ \ \ \ #\ to\ a\ procedure\ in\ thread\ 1.\ In\ this\ example,\ it's\ a\ procedure\ defined\n\ \ \ \ #\ at\ the\ global\ level.\n\ \ \ \ #\n\ \ \ \ #\ The\ 'thread::wait'\ is\ required\ to\ keep\ this\ thread\ alive\ indefinitely.\n\ \ \ \ #\n\ \ \ \ proc\ runCommand\ \{ID\ command\}\ \{\n\ \ \ \ \ \ \ \ set\ result\ \[eval\ \$command\]\n\ \ \ \ \ \ \ \ eval\ \[subst\ \{thread::send\ -async\ \$ID\ \\\n\ \ \ \ \ \ \ \ \ \ \ \ \{::printResult\ \[list\ \$result\]\}\}\]\n\ \ \ \ \}\n\ \ \ \ thread::wait\n\}\]\ \n\n#\n#\ Here\ is\ the\ procedure\ that\ gets\ called\ from\ the\ thread\ when\ the\ thread\n#\ has\ completed\ its\ work.\n#\nproc\ printResult\ result\ \{\n\ \ \ \ puts\ \$result\n\ \ \ \ exit\n\}\n\nproc\ passTheTime\ \{\}\ \{\n\ \ \ \ puts\ \[clock\ format\ \[clock\ seconds\]\]\n\ \ \ \ after\ 1000\ passTheTime\n\}\n\n#\n#\ Here\ we\ define\ a\ sample\ command,\ and\ pass\ it\ into\ the\ previously\ started\n#\ thread.\n#\nset\ commandString\ \"exec\ du\ -sk\ /usr/local\"\neval\ \[subst\ \{thread::send\ -async\ \$threadID\ \\\n\ \ \ \ \{runCommand\ 1\ \[list\ \$commandString\ \]\}\}\]\n\n#\n#\ In\ this\ example,\ lets\ just\ pass\ the\ time\ until\ the\ thread\ is\ complete\n#\npassTheTime\nvwait\ forever\n======\n\n\[AW\]:\ The\ above\ example\ also\ provides\ a\ nice\ way\ to\ get\ results\ from\ a\ worker\ thread\ back\ to\ your\ main\ program:\ have\ the\ worker\ thread\ send\ them\ back\ to\ the\ main\ thread\ in\ the\ same\ way\ you\ send\ work\ to\ the\ worker\ threads.\nAlso,\ note\ that\ there\ is\ an\ equivalent\ way\ of\ making\ a\ command\ with\ replaced\ params,\ instead\ of\ `eval\ \[\[subst\ ...\]\]`:\ \n\n======\nthread::send\ -async\ \$threadID\ \\\n\ \ \ \ \[list\ runCommand\ 1\ \[list\ \$commandString\ \]\]\n======\n\nAlso\ note\ that\ the\ main\ thread\ is\ not\ `1`\ on\ windows,\ so\ you'll\ need\ to\ pass\ the\ main\ thread\ ID\ to\ the\ worker\ threads.\n\n\n\n**\ To\ Do\ **\n\n\ \ \ update\ ftp://ftp.tcl.tk/pub/tcl/thread/:\ \ \ as\ of\ 2013-05-28,\ the\ latest\ version\ there\ is\ 2.6.7rc1\n\n\n\n**\ Gotcha:\ `tsv::lappend`\ **\n\n\[PYK\]\ 2015-03-27:\ \ Unlike\ `\[lappend\]`,\ which\ can\ leverage\ Tcl's\ \[copy-on-write\]\nsemantics,\ `tsv::lappend`\ must\ copy\ the\ list\ in\ order\ to\ return\ it.\ \ This\ can\nbe\ slow,\ and\ is\ almost\ never\ what\ you\ want\ in\ a\ threaded\ program.\ \ The\ short\nanswer\ is\ ''always\ use\ `::tsv::lpush`\ instead\ of\ `tsv::lappend`.\ \ Other\ncommands\ that\ don't\ return\ the\ entire\ list\ are\ also\ ok:\ `::tsv::set`\n`::tsv::linsert`,\ `::tsv::lpush`\ `::tsv::lpop`,\ and\ `::tsv::keylset`\n\nIn\ the\ future,\ perhaps\ `::tsv::lappend`\ should\ take\ a\ hint\ from\n`::tsv::linsert`\ and\ not\ return\ the\ list.\n\n\n\n**\ Debugging\ **\n\n\[AW\]:\ Debugging\ threads\ is\ non-obvious\ at\ first.\ This\ does\ work\ under\ tclsh,\ but\ not\ under\ wish\ (the\ puts\ from\ inside\ the\ thread\ is\ not\ displayed\ in\ the\ console):\n\n======\npackage\ require\ Thread\ncatch\ \{console\ show\}\nset\ ::gThread\ \[thread::create\ \{thread::wait\}\]\nputs\ \[list\ \{created\ thread\}\ \$::gThread\]\nproc\ test\ \{\}\ \{\n\ \ \ \ puts\ \{test\ starting\}\n\ \ \ \ thread::send\ -async\ \$::gThread\ \{puts\ \[clock\ seconds\]\}\n\ \ \ \ after\ 2000\ test\n\ \ \ \ puts\ \{test\ ending\}\n\}\n\ntest\nputs\ \{started\ first\ test\}\n\n#only\ needed\ for\ tclsh,\ to\ keep\ the\ interpreter\ alive\ and\ keep\ the\ event\ loop\ running\nvwait\ forever\n======\n\nThe\ reason\ is\ described\ here\ \[http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/3aca510e4e456682/e14677c573f79354\].\ Replacing\ the\ thread\ command\ by:\n\n======\nthread::send\ -async\ \$::gThread\ \{\n\ \ \ \ tk_messageBox\ -message\ \[clock\ seconds\]\ -title\ t\ -type\ ok\n\}\n======\n\nAlso\ does\ not\ work\ under\ wish\ (messagebox\ is\ never\ shown).\ The\ reason\ for\ that\ becomes\ obvious\ when\ you\ implement\ a\ thread\ error\ proc:\n\n======\nproc\ ThreadError\ \{thread_id\ errorInfo\}\ \{\n\ \ \ \ puts\ \[list\ \{Error\ in\ thread\ \$thread_id.\ Error:\}\ \$errorInfo\]\n\}\ \ \nthread::errorproc\ ThreadError\n======\n\nThere\ is\ no\ tk\ in\ the\ thread's\ tcl\ interpreter.\ (This\ is\ probably\ due\ to\ tk\ not\ being\ thread\ safe?)\n\nSo\ finally,\ this\ works\ in\ both\ tclsh\ and\ wish:\n\n======\nset\ ::a\ 0\n\npackage\ require\ Thread\ncatch\ \{console\ show\}\n\nset\ ::gThread\ \[thread::create\ \{thread::wait\}\ \]\nputs\ \"created\ thread\ \$::gThread\"\nproc\ test\ \{\}\ \{\n\ \ \ \ puts\ \[list\ \{test\ starting\}\ \$::a\]\n\ \ \ \ thread::send\ -async\ \$::gThread\ \{\ return\ \[clock\ seconds\]\ \}\ ::a\n\ \ \ \ after\ 2000\ test\n\ \ \ \ puts\ \[list\ \{test\ ending\}\ \$::a\]\n\}\n\ntest\nputs\ \{started\ first\ test\}\n\n#only\ needed\ for\ tclsh,\ to\ keep\ the\ interpreter\ alive\ and\ keep\ the\ event\ loop\ running\nvwait\ forever\n======\n\nAlternatively,\ you\ can\ use\ the\ technique\ under\ 'A\ Cookbook\ Approach'\ above\ to\ send\ a\ command\ back\ to\ your\ main\ thread.\n\n\n\n**\ Misc\ **\n\nOne\ curiousity--even\ annoyance--of\ threads\ prior\ to\ the\ 2.6\ release\ is\ that,\ while\ threads\ can\ communicate\nvariables\ and\ other\ resources,\ they\ do\ '''not'''\ have\ an\ easy\ way\ to\ share\ \[proc\]\ definitions.\ \ The\ main\nalternatives\ are\ to:\n\ \ \ *\ re-\[source\]\ in\ definitions\ from\ the\ file\ system\;\ or\n\ \ \ *\ stuff\ a\ proc\ definition\ in\ a\ variable,\ send\ the\ variable,\ and\ \[eval\]\n\n\n2.6\ will\ provide\ \[\[ttrace::eval\]\],\ as\ in\n\n======\nttrace::eval\ source\ myfile.tcl\n======Commands\ proper?\ \ For\ the\ foreseeable\ future,\ each\ thread\ must\ \[load\]\ its\ own.\n\n\n----\n\n\[Punana\]\ 2009-05-05:\ I\ tried\ using\ the\ thread\ callback\ in\ wish,\ on\ no\ matter\ what\ I\ did,\nthe\ thread\ was\ never\ able\ to\ find\ the\ callback\ proc\ in\ the\ main\ thread.\nSo,\ eventually\ I\ found\ a\ work\ around,\nInstead\ of\ using\ \"thread::errorproc\ ThreadError\"\ for\ an\ error\ reporting\ method,\ I\ manually\ generate\ an\ error\ inside\ the\ thread,\ and\nuse\ the\ errorproc\ callback\ to\ do\ what\ I\ wanted\ in\ the\ first\ place.\n\n----\n\n\n\[Ro\]\ 2011-09-16:\ Anyone\ know\ where\ to\ find\ a\ threaded\ build\ of\ tclkit\ for\ OSX?\n8.5\ preferably...\ Roy\ Keene's\ tclkit-8.5.9-macosx-i686\ from\nhttp://rkeene.org/projects/info/wiki/Tclkits\ isn't\ threaded.\n\n----\n\n\[AK\]\ 2011-09-16\ 11:54:21:\ \ ActiveState's\ basekits\ are\ threaded\ for\ OS\ X,\nWindows\ in\ all\ versions,\ and\ threaded\ for\ all\ platforms\ for\ 8.5+.\n\n----\n\[MHo\]\ See\ \[processing\ dirs\ with\ tpool\].\n\n<<categories>>\ Package\ |\ Threads regexp2} CALL {my render thread '''\[http://www.tcl.tk/man/tcl/ThreadCmd/thread.htm%|%thread\]'''\ an\ \[extension\]\ distributed\ with\ \[Tcl\],\ brings\ native\ \[threads%|%thread\]\ capabilities\ to\ \[Tcl\].\n\n\n\n**\ Disambiguation\ **\n\n\[http://www.complang.tuwien.ac.at/forth/threaded-code.html%|%A\ distinct\ meaning\]\ of\ \"thread\"\ occurs\ in\ discussions\ of\ \[bytecode\],\ especially\ when\ \[Forth\]\ is\ nearby.\n\n\n\n**\ Attributes\ **\n\n\ \ \ What:\ \ \ Thread\ extension\n\ \ \ Where:\ \ \ http://tcl.sf.net/\n\ \ \ Where:\ \ \ https://core.tcl.tk/thread/timeline?y=ci\n\ \ \ Description:\ \ \ This\ Tcl\ extension,\ with\ a\ thread-enabled\ core,\ allows\ script\ level\ access\ to\ run\ Tcl\ scripts\ within\ threads.\ \ Currently\ at\ version\ 2.7.0.\n\ \ \ Updated:\ \ \ 2013-01-02\n\ \ \ Contact:\ \ \ See\ web\ site\n\n\n\n**\ Associated\ Modules\ **\n\n\n\ \ \ \[ttrace\]:\ \ \ Replicate\ interpreter\ state\ in\ a\ multithreading\ application.\n\n\ \ \ tpool:\ \ \ Pools\ of\ worker\ threads.\n\n\ \ \ tsv:\ \ \ Share\ data\ between\ threads.\n\n\n\n\n**\ See\ Also\ **\n\n\ \ \ \[Threads\]:\ \ \ general\ information\ about\ threads\n\n\ \ \ \[Tcl\ and\ threads\]:\ \ \ general\ information\ about\ threads\ and\ Tcl\ \n\n\ \ \ \[asynchronous\ threads\],\ by\ \[CmcC\]:\ \ \ An\ \[object\ orientation%|%OO\]\ wrapper\ to\ make\ asynchronous\ threading\ over\ the\ \[Thread\]\ package\ a\ little\ easier.\n\n\ \ \ \[two\ threads\ run\ synchronously\]:\ \ \ an\ example\n\n\ \ \ \[http://paste.tclers.tk/2799%|%Thread\ worker\ example\]:\ \ \ by\ \[dgp\]\n\n\ \ \ \[thread:\ -eventmark\ and\ send\ -async\]:\ \ \ How\ these\ two\ features\ can\ interact\ in\ surprising\ ways.\n\n\ \ \ \[daerth\]:\ \ \ Threaded\ pipelined\ backpressure-mediated\ death\ stations.\ \ Stations\ operate\ in\ '''multi'''\ or\ '''balanced'''\ mode,\ the\ latter\ being\ an\ alternative\ to\ tpool.\n\n\n\ \ \ \[ttrace\]:\ \ \ Replicate\ interpreter\ state\ across\ threads.\n\n\n\n**\ Critique\ **\n\n\[dgp\]\ in\ the\ \[Tcl\ chatroom\],\ 2013-04-04:\n\n======none\n10:41\ <@ijchain>\ <dgp>\ tpool::get\ has\ ability\ to\ capture\ the\ error\ state\ of\ a\ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ worker\ thread\ in\ the\ pool.\n10:43\ <@ijchain>\ <dgp>\ it\ does\ look\ like\ \[tpool::wait\]\ is\ the\ only\ way\ to\ wait\ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ for\ worker\ thread\ completion.\ \ That's\ unfortunate.\n10:45\ <@ijchain>\ <dgp>\ I\ imagine\ you\ can\ work\ around\ the\ design\ defect.\n10:45\ <@ijchain>\ <dgp>\ But\ now\ I've\ got\ reasons\ not\ to\ use\ tpool.\ \ Thanks\ for\ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ that.\n======\n\n\n\n**\ Documentation\ **\n\n\ \ \ \[http://www.tcl.tk/doc/howto/thread_model.html%|%Tcl\ Threading\ Model\]:\ \ \ an\ overview.\n\n\ \ \ \[http://www.beedub.com/book/4th/Threads.pdf%|%Multi-Threaded\ Tcl\ Scripts\]:\ \ \ A\ sample\ chapter\ from\ \[Book\ Practical\ Programming\ in\ Tcl\ and\ Tk%|%Practical\ Programming\ in\ Tcl\ and\ Tk\].\n\n\ \ \ \[http://docs.activestate.com/activetcl/8.5/thread/toc.html%|%man\ pages\]:\ \ \ at\ \[ActiveState\]\n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/thread.html%|%thread\ man\ page\]\ (alternates:\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/thread.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/thread.html%|%(sourceforge)\]):\ \ \ \n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/tpool.html%|%tpool\ man\ page\]\ (alternates:\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/tpool.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/tpool.html%|%sourceforge\]):\ \ \ \n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/tsv.html%|%tsv\ man\ page\]\ (alternates:\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/tsv.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/tsv.html%|%sourceforge\]):\ \ \ \n\n\ \ \ \[http://core.tcl.tk/thread/finfo?name=doc/html/ttrace.html%|%ttrace\ man\ page\]\ (alternates\ \[http://docs.activestate.com/activetcl/8.6/thread/doc/ttrace.html%|%ActiveState\],\ \[http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/ttrace.html%|%sourceforge\]:\ \ \ \n\n\n\n**\ Community\ **\n\n\ \ \ \[https://lists.sourceforge.net/mailman/listinfo/tcl-threads%|%tcl-threads\ mailing\ list\]:\ \ \ at\ \[SourceForge\].\n\n\n\n**\ Tools\ **\n\n\ \ \ \[dqkit\]:\ \ \ is\ a\ convenient\ deployment.\ \ \[snichols\]:\ \ I've\ had\ problems\ with\ \[dqkit\]\ and\ Tcl\ threads.\ On\ a\ Win32\ system\ in\ order\ to\ find\ packages\ in\ a\ child\ thread\ I\ had\ to\ put\ the\ package\ files\ outside\ of\ the\ starkit.\ Because\ of\ that\ its\ almost\ easier\ to\ just\ use\ a\ thread\ enabled\ Tcl\ interpreter\ instead\ of\ dqkit\ if\ the\ package\ files\ have\ to\ exist\ outside\ of\ the\ \[starkit\].\n\n\n\n**\ commands\ **\n\n`package\ require\ Thread`\n\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::create\]'''\ ?'''-joinable'''?\ ?'''-preserved'''?\ ?''script''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::preserve\]'''\ ?''id''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::release\]'''\ ?'''-wait'''?\ ?''id''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::id\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::errorproc\]'''\ ?''procname''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::unwind\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::exit\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::names\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::exists\]'''\ ''id''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::send\]'''\ ?'''-async'''?\ ?'''-head'''?\ ''id\ script''\ ?''varname''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::broadcast\]'''\ ''id\ script''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::wait\]'''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::eval\]'''\ ?'''-lock'''\ ''mutex''?\ ''arg''\ ?''arg\ ...''?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::join\]'''\ ''id''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::configure\]'''\ ''id''\ ?''option''?\ ?''value''?\ ?...?\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::transfer\]'''\ ''id\ channel''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::detach\]'''\ ''channel''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::attach\]'''\ ''channel''\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::mutex\]'''\ ...\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::rwmutex\]'''\ ...\n\ \ \ \ \ \ \ \ :\ \ \ '''\[thread::cond\]'''\ ...\n\n\n\n**\ Description\ **\n\nSome\ resources\ are\ shared\ between\ threads.\ \ The\ effect\ on\ other\ threads\ must\ be\ntaken\ into\ account\ when\ manipulating\ them.\ \ Such\ resources\ include:\n\n\n\ \ \ '''\[env%|%environment\ variables\]''':\ \ \ \n\n\ \ \ '''`\[exit\]`''':\ \ \ Exits\ the\ process,\ regardless\ of\ which\ thread\ executed\ it.\ \ The\ value\ passed\ to\ `\[exit\]`\ only\ propagates\ as\ the\ process\ exit\ code\ if\ `\[exit\]`\ is\ called\ from\ the\ main\ thread.\n\n\ \ \ '''`\[pwd\]`''':\ \ \ `\[cd\]`\ changes\ the\ current\ directory\ for\ all\ threads.\n\n\ \ \ '''\[stdio\]''':\ \ \ \[stdin\],\ \[stdout\],\ and\ \[stderr\]\ are\ between\ all\ threads.\ \ But\ note\ that\ some\ platforms,\ namely\ \[Microsoft\ Windows%|%Windows\],\ do\ not\ provide\ these\ channels\ to\ processes.\ \ On\ those\ platforms,\ \ the\ standard\ channels\ can\ not\ be\ assumed\ to\ be\ shared\ among\ all\ threads.\n\n\ \ \ \[thread::errorproc\]:\ \ \ This\ is\ a\ global\ setting,\ common\ to\ all\ threads.\n\n\n\n\n\n**\ ttrace\ sub-package\ **\n\nThe\ 2.6\ release\ adds\ a\ sub-package\ called\ Ttrace.\ This\ allows\ for\ easy\npropagation\ of\ interpreter\ related\ resources\ (\[procedure\]s,\ \[namespace\]s,\nobjects\ (for\ \[XOTcl\])).\ Usage\ is\ trivial:\ yust\ wrap\ the\ code\ you\ want\ to\nreplicate\ within\ ttrace::eval\ like\ this:\n\n======\npackage\ req\ Thread\npackage\ req\ Ttrace\n\nfor\ \{set\ i\ 0\}\ \{\$i\ <\ 4\}\ \{incr\ i\}\ \{\n\ \ \ \ \ \ \ \ set\ tid(\$i)\ \[thread::create\ -preserved\]\n\}\n\nttrace::eval\ \{\n\ \ \ \ proc\ foo\ args\ \{\n\ \ \ \ \ \ \ puts\ foo\n\ \ \ \ \}\n\ \ \ \ proc\ bar\ args\ \{\n\ \ \ \ \ \ \ puts\ bar\n\ \ \ \ \}\n\}\n======\n\nand\ execute\ this\ from\ any\ thread\ created\ by\ the\ thread\ extension.\ This\ will\nreplicate\ the\ definitions\ of\ 'foo'\ and\ 'bar'\ to\ all\ existing\ threads.\ In\ the\nabove\ example,\ all\ 4\ threads\ will\ be\ seeded.\ One\ very\ important\ design\ feature\nof\ Ttrace\ is\ that\ resources\ are\ propagated\ in\ a\ lazy\ fashion.\ That\ is,\ nothing\nis\ actually\ done\ in\ other\ threads\ until\ the\ resource\ gets\ referenced\ for\ the\nfirst\ time.\ This\ is\ accomplished\ by\ overloading\ the\ \[Tcl\]\ \[unknown\]\ command.\nSo,\ when\ the\ \[unknown\]\ triggers,\ it\ tries\ to\ locate\ the\ resource\ definition\ by\ndoing\ a\ lookup\ in\ the\ Ttrace\ private\ (in-memory)\ database\ first.\ \ On\ hit,\ the\ndefinition\ is\ loaded\ on-the-fly\ in\ the\ current\ interpreter.\ \ On\ miss,\ the\n\[unknown\]\ processing\ is\ delegated\ down-the-road.\ This\ way,\ thread\ startup\ and\nmemory\ consumption\ are\ minimized.\ Thanks\ to\ \[Vince\ Darley\]\ for\ his\ great\ncommand\ \[trace\]\ framework,\ which\ is\ used\ as\ the\ base\ for\ the\ Trace\ package.\nFor\ more\ information\ see\ 'man\ ttrace'.\n\n\n\n**\ Threads\ and\ the\ event\ loop\ **\n\n\[Silas\]\ 2006-12-17:\ By\ default\ threads\ that\ are\ not\ the\ main\ thread\ don't\ have\ an\ \[event\ loop\]\ (Am\ I\ wrong?).\ Do\ add\ it,\ insert\ a\ \[\[\[vwait\]\ forever\]\]\ or\ \[\[vwait\ ''your_variable''\]\]\ inside\ your\ '''thread::create'''\ block.\ <<br>>\ \[AKM\]\ 2006-10-26\ I\ thought\ the\ advice\ was\ to\ use\ '''thread::wait'''\ so\ that\ the\ thread\ can\ be\ terminated\ with\ '''thread::release'''?\n\n\n\n**\ Examples\ **\n\n\ \ \ \[Sharing\ a\ common\ logger\ thread\ example\]:\ \ \ An\ example\ by\ \[Zoran\ Vasiljevic\]\ of\ a\ logger\ thread\ and\ worker\ threads\ on\ c.l.t.\ (add\ google\ groups\ reference\ here):\n\n\n\n**\ Example:\ A\ Cookbook\ Approach**\n\n\[Marty\ Backe\]\ \ 2002-02-21:\n\n''How\ to\ use\ the\ Thread2.1\ package''\n\n======\n#!\ /bin/sh\n#\\\nexec\ tclsh\ \"\$0\"\ \"\$@\"\n\npackage\ require\ Thread\ 2.1\n\n#\n#\ Start\ a\ thread.\ If\ you\ will\ be\ running\ many\ long\ running\ commands\n#\ at\ the\ same\ time,\ just\ create\ multiple\ threads\ identical\ to\ this\ one,\ and\n#\ keep\ track\ of\ the\ threadID's.\n#\nset\ threadID\ \[thread::create\ \{\n\ \ \ \ #\n\ \ \ \ #\ From\ here\ to\ the\ 'thread::wait'\ statement,\ define\ the\ procedure(s)\n\ \ \ \ #\ that\ will\ be\ called\ from\ your\ main\ program\ (which,\ btw,\ is\ thread\ #1)\n\ \ \ \ #\n\ \ \ \ #\ In\ this\ case,\ I've\ defined\ a\ simple\ procedure\ that\ executes\ the\ command\n\ \ \ \ #\ passed\ into\ this\ thread.\ This\ command\ could\ be\ an\ external\ program\ or\n\ \ \ \ #\ long\ running\ tcl\ command.\n\ \ \ \ #\n\ \ \ \ #\ The\ result\ is\ then\ sent\ back\ to\ the\ main\ program\ (thread\ 1)\ via\ a\ call\n\ \ \ \ #\ to\ a\ procedure\ in\ thread\ 1.\ In\ this\ example,\ it's\ a\ procedure\ defined\n\ \ \ \ #\ at\ the\ global\ level.\n\ \ \ \ #\n\ \ \ \ #\ The\ 'thread::wait'\ is\ required\ to\ keep\ this\ thread\ alive\ indefinitely.\n\ \ \ \ #\n\ \ \ \ proc\ runCommand\ \{ID\ command\}\ \{\n\ \ \ \ \ \ \ \ set\ result\ \[eval\ \$command\]\n\ \ \ \ \ \ \ \ eval\ \[subst\ \{thread::send\ -async\ \$ID\ \\\n\ \ \ \ \ \ \ \ \ \ \ \ \{::printResult\ \[list\ \$result\]\}\}\]\n\ \ \ \ \}\n\ \ \ \ thread::wait\n\}\]\ \n\n#\n#\ Here\ is\ the\ procedure\ that\ gets\ called\ from\ the\ thread\ when\ the\ thread\n#\ has\ completed\ its\ work.\n#\nproc\ printResult\ result\ \{\n\ \ \ \ puts\ \$result\n\ \ \ \ exit\n\}\n\nproc\ passTheTime\ \{\}\ \{\n\ \ \ \ puts\ \[clock\ format\ \[clock\ seconds\]\]\n\ \ \ \ after\ 1000\ passTheTime\n\}\n\n#\n#\ Here\ we\ define\ a\ sample\ command,\ and\ pass\ it\ into\ the\ previously\ started\n#\ thread.\n#\nset\ commandString\ \"exec\ du\ -sk\ /usr/local\"\neval\ \[subst\ \{thread::send\ -async\ \$threadID\ \\\n\ \ \ \ \{runCommand\ 1\ \[list\ \$commandString\ \]\}\}\]\n\n#\n#\ In\ this\ example,\ lets\ just\ pass\ the\ time\ until\ the\ thread\ is\ complete\n#\npassTheTime\nvwait\ forever\n======\n\n\[AW\]:\ The\ above\ example\ also\ provides\ a\ nice\ way\ to\ get\ results\ from\ a\ worker\ thread\ back\ to\ your\ main\ program:\ have\ the\ worker\ thread\ send\ them\ back\ to\ the\ main\ thread\ in\ the\ same\ way\ you\ send\ work\ to\ the\ worker\ threads.\nAlso,\ note\ that\ there\ is\ an\ equivalent\ way\ of\ making\ a\ command\ with\ replaced\ params,\ instead\ of\ `eval\ \[\[subst\ ...\]\]`:\ \n\n======\nthread::send\ -async\ \$threadID\ \\\n\ \ \ \ \[list\ runCommand\ 1\ \[list\ \$commandString\ \]\]\n======\n\nAlso\ note\ that\ the\ main\ thread\ is\ not\ `1`\ on\ windows,\ so\ you'll\ need\ to\ pass\ the\ main\ thread\ ID\ to\ the\ worker\ threads.\n\n\n\n**\ To\ Do\ **\n\n\ \ \ update\ ftp://ftp.tcl.tk/pub/tcl/thread/:\ \ \ as\ of\ 2013-05-28,\ the\ latest\ version\ there\ is\ 2.6.7rc1\n\n\n\n**\ Gotcha:\ `tsv::lappend`\ **\n\n\[PYK\]\ 2015-03-27:\ \ Unlike\ `\[lappend\]`,\ which\ can\ leverage\ Tcl's\ \[copy-on-write\]\nsemantics,\ `tsv::lappend`\ must\ copy\ the\ list\ in\ order\ to\ return\ it.\ \ This\ can\nbe\ slow,\ and\ is\ almost\ never\ what\ you\ want\ in\ a\ threaded\ program.\ \ The\ short\nanswer\ is\ ''always\ use\ `::tsv::lpush`\ instead\ of\ `tsv::lappend`.\ \ Other\ncommands\ that\ don't\ return\ the\ entire\ list\ are\ also\ ok:\ `::tsv::set`\n`::tsv::linsert`,\ `::tsv::lpush`\ `::tsv::lpop`,\ and\ `::tsv::keylset`\n\nIn\ the\ future,\ perhaps\ `::tsv::lappend`\ should\ take\ a\ hint\ from\n`::tsv::linsert`\ and\ not\ return\ the\ list.\n\n\n\n**\ Debugging\ **\n\n\[AW\]:\ Debugging\ threads\ is\ non-obvious\ at\ first.\ This\ does\ work\ under\ tclsh,\ but\ not\ under\ wish\ (the\ puts\ from\ inside\ the\ thread\ is\ not\ displayed\ in\ the\ console):\n\n======\npackage\ require\ Thread\ncatch\ \{console\ show\}\nset\ ::gThread\ \[thread::create\ \{thread::wait\}\]\nputs\ \[list\ \{created\ thread\}\ \$::gThread\]\nproc\ test\ \{\}\ \{\n\ \ \ \ puts\ \{test\ starting\}\n\ \ \ \ thread::send\ -async\ \$::gThread\ \{puts\ \[clock\ seconds\]\}\n\ \ \ \ after\ 2000\ test\n\ \ \ \ puts\ \{test\ ending\}\n\}\n\ntest\nputs\ \{started\ first\ test\}\n\n#only\ needed\ for\ tclsh,\ to\ keep\ the\ interpreter\ alive\ and\ keep\ the\ event\ loop\ running\nvwait\ forever\n======\n\nThe\ reason\ is\ described\ here\ \[http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/3aca510e4e456682/e14677c573f79354\].\ Replacing\ the\ thread\ command\ by:\n\n======\nthread::send\ -async\ \$::gThread\ \{\n\ \ \ \ tk_messageBox\ -message\ \[clock\ seconds\]\ -title\ t\ -type\ ok\n\}\n======\n\nAlso\ does\ not\ work\ under\ wish\ (messagebox\ is\ never\ shown).\ The\ reason\ for\ that\ becomes\ obvious\ when\ you\ implement\ a\ thread\ error\ proc:\n\n======\nproc\ ThreadError\ \{thread_id\ errorInfo\}\ \{\n\ \ \ \ puts\ \[list\ \{Error\ in\ thread\ \$thread_id.\ Error:\}\ \$errorInfo\]\n\}\ \ \nthread::errorproc\ ThreadError\n======\n\nThere\ is\ no\ tk\ in\ the\ thread's\ tcl\ interpreter.\ (This\ is\ probably\ due\ to\ tk\ not\ being\ thread\ safe?)\n\nSo\ finally,\ this\ works\ in\ both\ tclsh\ and\ wish:\n\n======\nset\ ::a\ 0\n\npackage\ require\ Thread\ncatch\ \{console\ show\}\n\nset\ ::gThread\ \[thread::create\ \{thread::wait\}\ \]\nputs\ \"created\ thread\ \$::gThread\"\nproc\ test\ \{\}\ \{\n\ \ \ \ puts\ \[list\ \{test\ starting\}\ \$::a\]\n\ \ \ \ thread::send\ -async\ \$::gThread\ \{\ return\ \[clock\ seconds\]\ \}\ ::a\n\ \ \ \ after\ 2000\ test\n\ \ \ \ puts\ \[list\ \{test\ ending\}\ \$::a\]\n\}\n\ntest\nputs\ \{started\ first\ test\}\n\n#only\ needed\ for\ tclsh,\ to\ keep\ the\ interpreter\ alive\ and\ keep\ the\ event\ loop\ running\nvwait\ forever\n======\n\nAlternatively,\ you\ can\ use\ the\ technique\ under\ 'A\ Cookbook\ Approach'\ above\ to\ send\ a\ command\ back\ to\ your\ main\ thread.\n\n\n\n**\ Misc\ **\n\nOne\ curiousity--even\ annoyance--of\ threads\ prior\ to\ the\ 2.6\ release\ is\ that,\ while\ threads\ can\ communicate\nvariables\ and\ other\ resources,\ they\ do\ '''not'''\ have\ an\ easy\ way\ to\ share\ \[proc\]\ definitions.\ \ The\ main\nalternatives\ are\ to:\n\ \ \ *\ re-\[source\]\ in\ definitions\ from\ the\ file\ system\;\ or\n\ \ \ *\ stuff\ a\ proc\ definition\ in\ a\ variable,\ send\ the\ variable,\ and\ \[eval\]\n\n\n2.6\ will\ provide\ \[\[ttrace::eval\]\],\ as\ in\n\n======\nttrace::eval\ source\ myfile.tcl\n======Commands\ proper?\ \ For\ the\ foreseeable\ future,\ each\ thread\ must\ \[load\]\ its\ own.\n\n\n----\n\n\[Punana\]\ 2009-05-05:\ I\ tried\ using\ the\ thread\ callback\ in\ wish,\ on\ no\ matter\ what\ I\ did,\nthe\ thread\ was\ never\ able\ to\ find\ the\ callback\ proc\ in\ the\ main\ thread.\nSo,\ eventually\ I\ found\ a\ work\ around,\nInstead\ of\ using\ \"thread::errorproc\ ThreadError\"\ for\ an\ error\ reporting\ method,\ I\ manually\ generate\ an\ error\ inside\ the\ thread,\ and\nuse\ the\ errorproc\ callback\ to\ do\ what\ I\ wanted\ in\ the\ first\ place.\n\n----\n\n\n\[Ro\]\ 2011-09-16:\ Anyone\ know\ where\ to\ find\ a\ threaded\ build\ of\ tclkit\ for\ OSX?\n8.5\ preferably...\ Roy\ Keene's\ tclkit-8.5.9-macosx-i686\ from\nhttp://rkeene.org/projects/info/wiki/Tclkits\ isn't\ threaded.\n\n----\n\n\[AK\]\ 2011-09-16\ 11:54:21:\ \ ActiveState's\ basekits\ are\ threaded\ for\ OS\ X,\nWindows\ in\ all\ versions,\ and\ threaded\ for\ all\ platforms\ for\ 8.5+.\n\n----\n\[MHo\]\ See\ \[processing\ dirs\ with\ tpool\].\n\n<<categories>>\ Package\ |\ Threads} CALL {my revision thread} CALL {::oo::Obj829987 process revision/thread} CALL {::oo::Obj829985 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