Tcl package Thread source code

Check-in [8ee9e53308]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge trunk. Remove badoctal test, which no longer makes sense with Tcl 9.0
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | novem
Files: files | file ages | folders
SHA3-256: 8ee9e53308debb4c189d4c2e3c9c3b6e8e87e15e3febcd802339aa2416301aa8
User & Date: jan.nijtmans 2018-10-12 18:44:36
Context
2018-10-12
18:50
Merge trunk check-in: 93cca4133e user: jan.nijtmans tags: novem
18:44
Merge trunk. Remove badoctal test, which no longer makes sense with Tcl 9.0 check-in: 8ee9e53308 user: jan.nijtmans tags: novem
18:41
merge-mark check-in: e8dcabfdf7 user: jan.nijtmans tags: trunk
2018-02-26
22:59
Merge trunk check-in: 7b198e45f6 user: jan.nijtmans tags: novem
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to configure.

2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
....
2387
2388
2389
2390
2391
2392
2393
2394
2395

2396
2397
2398
2399
2400




2401
2402
2403
2404
2405
2406
2407
....
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
    # If the user did not set CFLAGS, set it now to keep macros
    # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
    if test "${CFLAGS+set}" != "set" ; then
	CFLAGS=""
    fi

    case "`uname -s`" in
	*win32*|*WIN32*|*MINGW32_*)
	    # Extract the first word of "cygpath", so it can be a program name with args.
set dummy cygpath; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CYGPATH+:} false; then :
  $as_echo_n "(cached) " >&6
else
................................................................................
	    fi

	    # check in a few common install locations
	    if test x"${ac_cv_c_tclconfig}" = x ; then
		for i in `ls -d ${libdir} 2>/dev/null` \
			`ls -d ${exec_prefix}/lib 2>/dev/null` \
			`ls -d ${prefix}/lib 2>/dev/null` \
			`ls -d /usr/contrib/lib 2>/dev/null` \
			`ls -d /usr/local/lib 2>/dev/null` \

			`ls -d /usr/pkg/lib 2>/dev/null` \
			`ls -d /usr/lib 2>/dev/null` \
			`ls -d /usr/lib64 2>/dev/null` \
			`ls -d /usr/lib/tcl8.6 2>/dev/null` \
			`ls -d /usr/lib/tcl8.5 2>/dev/null` \




			; do
		    if test -f "$i/tclConfig.sh" ; then
			ac_cv_c_tclconfig="`(cd $i; pwd)`"
			break
		    fi
		done
	    fi
................................................................................
    # standard manufacturer compiler.

    if test "$GCC" = yes; then :

	case $system in
	    AIX-*) ;;
	    BSD/OS*) ;;
	    CYGWIN_*|MINGW32_*) ;;
	    IRIX*) ;;
	    NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
	    Darwin-*) ;;
	    SCO_SV-3.2*) ;;
	    windows) ;;
	    *) SHLIB_CFLAGS="-fPIC" ;;
	esac






|







 







<

>





>
>
>
>







 







|







2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
....
2387
2388
2389
2390
2391
2392
2393

2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
....
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
    # If the user did not set CFLAGS, set it now to keep macros
    # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
    if test "${CFLAGS+set}" != "set" ; then
	CFLAGS=""
    fi

    case "`uname -s`" in
	*win32*|*WIN32*|*MINGW32_*|*MINGW64_*)
	    # Extract the first word of "cygpath", so it can be a program name with args.
set dummy cygpath; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CYGPATH+:} false; then :
  $as_echo_n "(cached) " >&6
else
................................................................................
	    fi

	    # check in a few common install locations
	    if test x"${ac_cv_c_tclconfig}" = x ; then
		for i in `ls -d ${libdir} 2>/dev/null` \
			`ls -d ${exec_prefix}/lib 2>/dev/null` \
			`ls -d ${prefix}/lib 2>/dev/null` \

			`ls -d /usr/local/lib 2>/dev/null` \
			`ls -d /usr/contrib/lib 2>/dev/null` \
			`ls -d /usr/pkg/lib 2>/dev/null` \
			`ls -d /usr/lib 2>/dev/null` \
			`ls -d /usr/lib64 2>/dev/null` \
			`ls -d /usr/lib/tcl8.6 2>/dev/null` \
			`ls -d /usr/lib/tcl8.5 2>/dev/null` \
			`ls -d /usr/local/lib/tcl8.6 2>/dev/null` \
			`ls -d /usr/local/lib/tcl8.5 2>/dev/null` \
			`ls -d /usr/local/lib/tcl/tcl8.6 2>/dev/null` \
			`ls -d /usr/local/lib/tcl/tcl8.5 2>/dev/null` \
			; do
		    if test -f "$i/tclConfig.sh" ; then
			ac_cv_c_tclconfig="`(cd $i; pwd)`"
			break
		    fi
		done
	    fi
................................................................................
    # standard manufacturer compiler.

    if test "$GCC" = yes; then :

	case $system in
	    AIX-*) ;;
	    BSD/OS*) ;;
	    CYGWIN_*|MINGW32_*|MINGW64_*) ;;
	    IRIX*) ;;
	    NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
	    Darwin-*) ;;
	    SCO_SV-3.2*) ;;
	    windows) ;;
	    *) SHLIB_CFLAGS="-fPIC" ;;
	esac

Changes to doc/html/thread.html.

1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
...
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
<html><head>
<title>thread - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->
<! -- thread.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">thread(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>thread - Extension for script access to Tcl threading</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <b class="package">thread</b> extension creates threads that contain Tcl
interpreters, and it lets you send scripts to those threads for
evaluation.
Additionaly, it provides script-level access to basic thread
synchronization primitives, like mutexes and condition variables.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<p>This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation.</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">thread::create</b> <span class="opt">?-joinable?</span> <span class="opt">?-preserved?</span> <span class="opt">?script?</span></a></dt>
................................................................................
<p>Threads created by the <b class="cmd">thread::create</b> cannot be destroyed
forcefully. Consequently, there is no corresponding thread destroy
command. A thread may only be released using the <b class="cmd">thread::release</b>
and if its internal reference count drops to zero, the thread is
marked for exit. This kicks the thread out of the event loop
servicing and the thread continues to execute commands passed in
the <b class="option">script</b> argument, following the <b class="cmd">thread::wait</b>
command. If this was the last command in the script, as usualy the
case, the thread will exit.</p>
<p>It is possible to create a situation in which it may be impossible
to terminate the thread, for example by putting some endless loop
after the <b class="cmd">thread::wait</b> or entering the event loop again by
doing an vwait-type of command. In such cases, the thread may never
exit. This is considered to be a bad practice and should be avoided
if possible. This is best illustrated by the example below:</p>
................................................................................
wait endlessly for events. There is no way one can terminate such thread,
so you wouldn't want to do this!</p>
<p>Each newly created has its internal reference counter set to 0 (zero),
i.e. it is unreserved. This counter gets incremented by a call to
<b class="cmd">thread::preserve</b> and decremented by a call to <b class="cmd">thread::release</b>
command. These two commands implement simple but effective thread
reservation system and offer predictable and controllable thread
termination capabilities. It is however possible to create initialy
preserved threads by using flag <b class="option">-preserved</b> of the
<b class="cmd">thread::create</b> command. Threads created with this flag have the
initial value of the reference counter of 1 (one), and are thus
initially marked reserved.</p></dd>
<dt><a name="2"><b class="cmd">thread::preserve</b> <span class="opt">?id?</span></a></dt>
<dd><p>This command increments the thread reference counter. Each call
to this command increments the reference counter by one (1).
................................................................................
until there are no further invocations of the interpreter left on the
call stack. If <i class="arg">result</i> is present, it will be used as the error
message string; otherwise, a default error message string will be used.</p></dd>
<dt><a name="7"><b class="cmd">thread::unwind</b></a></dt>
<dd><p>Use of this command is deprecated in favour of more advanced thread
reservation system implemented with <b class="cmd">thread::preserve</b> and
<b class="cmd">thread::release</b> commands. Support for <b class="cmd">thread::unwind</b>
command will dissapear in some future major release of the extension.</p>
<p>This command stops a prior <b class="cmd">thread::wait</b> command. Execution of
the script passed to newly created thread will continue from the
<b class="cmd">thread::wait</b> command. If <b class="cmd">thread::wait</b> was the last command
in the script, the thread will exit. The command returns empty result
but may trigger Tcl error with the message &quot;target thread died&quot; in some
situations.</p></dd>
<dt><a name="8"><b class="cmd">thread::exit</b> <span class="opt">?status?</span></a></dt>
<dd><p>Use of this command is deprecated in favour of more advanced thread
reservation system implemented with <b class="cmd">thread::preserve</b> and
<b class="cmd">thread::release</b> commands. Support for <b class="cmd">thread::exit</b>
command will dissapear in some future major release of the extension.</p>
<p>This command forces a thread stuck in the <b class="cmd">thread::wait</b> command to
unconditionaly exit. The thread's exit status defaults to 666 and can be
specified using the optional <i class="arg">status</i> argument. The execution of
<b class="cmd">thread::exit</b> command is guaranteed to leave the program memory in the
unconsistent state, produce memory leaks and otherwise affect other subsytem(s)
of the Tcl application in an unpredictable manner. The command returns empty
result but may trigger Tcl error with the message &quot;target thread died&quot; in some
situations.</p></dd>
<dt><a name="9"><b class="cmd">thread::names</b></a></dt>
<dd><p>This command returns a list of thread IDs. These are only for
threads that have been created via <b class="cmd">thread::create</b> command.
If your application creates other threads at the C level, they
................................................................................
The target thread must enter it's event loop in order to receive
scripts sent via this command. This is done by default for threads
created without a startup script. Threads can enter the event loop
explicitly by calling <b class="cmd">thread::wait</b> or any other relevant Tcl/Tk
command, like <b class="cmd">update</b>, <b class="cmd">vwait</b>, etc.</p>
<p>Optional <b class="option">varname</b> specifies name of the variable to store
the result of the <i class="arg">script</i>. Without the <b class="option">-async</b> flag,
the command returns the evaluation code, similarily to the standard
Tcl <b class="cmd">catch</b> command. If, however, the <b class="option">-async</b> flag is
specified, the command returns immediately and caller can later
<b class="cmd">vwait</b> on <span class="opt">?varname?</span> to get the result of the passed <i class="arg">script</i></p>
<pre class="doctools_example">
    set t1 [thread::create]
    set t2 [thread::create]
    thread::send -async $t1 &quot;set a 1&quot; result
................................................................................
Upon the join the handle of the thread has gone out of scope and
should not be used any more.</p></dd>
<dt><a name="16"><b class="cmd">thread::configure</b> <i class="arg">id</i> <span class="opt">?option?</span> <span class="opt">?value?</span> <span class="opt">?...?</span></a></dt>
<dd><p>This command configures various low-level aspects of the thread with
ID <i class="arg">id</i> in the similar way as the standard Tcl command
<b class="cmd">fconfigure</b> configures some Tcl channel options. Options currently
supported are: <b class="option">-eventmark</b> and <b class="option">-unwindonerror</b>.</p>
<p>The <b class="option">-eventmark</b> option, when set, limits the number of
asynchronously posted scripts to the thread event loop.
The <b class="cmd">thread::send -async</b> command will block until the number
of pending scripts in the event loop does not drop below the value
configured with <b class="option">-eventmark</b>. Default value for the
<b class="option">-eventmark</b> is 0 (zero) which effectively disables the checking,
i.e. allows for unlimited number of posted scripts.</p>
<p>The <b class="option">-unwindonerror</b> option, when set, causes the
target thread to unwind if the result of the script processing
resulted in error. Default value for the <b class="option">-unwindonerror</b>
is 0 (false), i.e. thread continues to process scripts after one
of the posted scripts fails.</p></dd>
<dt><a name="17"><b class="cmd">thread::transfer</b> <i class="arg">id</i> <i class="arg">channel</i></a></dt>
<dd><p>This moves the specified <i class="arg">channel</i> from the current thread
and interpreter to the main interpreter of the thread with the
given <i class="arg">id</i>. After the move the current interpreter has no
access to the channel any more, but the main interpreter of the
target thread will be able to use it from now on.
The command waits until the other thread has incorporated the
................................................................................
(or the same) thread attaches the channel again with <b class="cmd">thread::attach</b>.
Restrictions: same as for transferring shared channels with the
<b class="cmd">thread::transfer</b> command.</p></dd>
<dt><a name="19"><b class="cmd">thread::attach</b> <i class="arg">channel</i></a></dt>
<dd><p>This attaches the previously detached <i class="arg">channel</i> in the
current thread/interpreter. For already existing channels,
the command does nothing, i.e. it is not an error to attach the
same channel more than once. The first operation will actualy
perform the operation, while all subsequent operation will just
do nothing. Command throws error if the <i class="arg">channel</i> cannot be
found in the list of detached channels and/or in the current
interpreter.</p></dd>
<dt><a name="20"><b class="cmd">thread::mutex</b></a></dt>
<dd><p>Mutexes are most common thread synchronization primitives.
They are used to synchronize access from two or more threads to one or
................................................................................
<dd><p>Unlocks the <i class="arg">mutex</i> so some other thread may lock it again.
Attempt to unlock the already unlocked mutex will throw Tcl error.</p></dd>
</dl></dd>
<dt><a name="25"><b class="cmd">thread::rwmutex</b></a></dt>
<dd><p>This command creates many-readers/single-writer mutexes. Reader/writer
mutexes allow you to serialize access to a shared resource more optimally.
In situations where a shared resource gets mostly read and seldom modified,
you might gain some performace by using reader/writer mutexes instead of
exclusive or recursive mutexes.</p>
<p>For reading the resource, thread should obtain a read lock on the resource.
Read lock is non-exclusive, meaning that more than one thread can
obtain a read lock to the same resource, without waiting on other readers.
For changing the resource, however, a thread must obtain a exclusive
write lock. This lock effectively blocks all threads from gaining the
read-lock while the resource is been modified by the writer thread.
................................................................................
Attempt to unlock already unlocked <i class="arg">mutex</i> will throw Tcl error.</p></dd>
</dl></dd>
<dt><a name="31"><b class="cmd">thread::cond</b></a></dt>
<dd><p>This command provides script-level access to condition variables.
A condition variable creates a safe environment for the program
to test some condition, sleep on it when false and be awakened
when it might have become true. A condition variable is always
used in the conjuction with an exclusive mutex. If you attempt
to use other type of mutex in conjuction with the condition
variable, a Tcl error will be thrown.</p>
<p>The command supports following subcommands and options:</p>
<dl class="doctools_definitions">
<dt><a name="32"><b class="cmd">thread::cond</b> <b class="method">create</b></a></dt>
<dd><p>Creates the condition variable and returns it's opaque handle.
This handle should be used for any future reference to newly
created condition variable.</p></dd>
|







 







|

|







 







|







 







|







 







|







 







|










|

|


|







 







|







 







|
|
|
|
|
|
|
<
<
<
<
<







 







|







 







|







 







|
|







1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
393
394
395
396
397
398
399
400
401
402
403
404
405
406





407
408
409
410
411
412
413
...
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
...
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
<!DOCTYPE html><html><head>
<title>thread - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file '' by tcllib/doctools with format 'html'
   -->
<!-- thread.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">thread(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>thread - Extension for script access to Tcl threading</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <b class="package">thread</b> extension creates threads that contain Tcl
interpreters, and it lets you send scripts to those threads for
evaluation.
Additionally, it provides script-level access to basic thread
synchronization primitives, like mutexes and condition variables.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<p>This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation.</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">thread::create</b> <span class="opt">?-joinable?</span> <span class="opt">?-preserved?</span> <span class="opt">?script?</span></a></dt>
................................................................................
<p>Threads created by the <b class="cmd">thread::create</b> cannot be destroyed
forcefully. Consequently, there is no corresponding thread destroy
command. A thread may only be released using the <b class="cmd">thread::release</b>
and if its internal reference count drops to zero, the thread is
marked for exit. This kicks the thread out of the event loop
servicing and the thread continues to execute commands passed in
the <b class="option">script</b> argument, following the <b class="cmd">thread::wait</b>
command. If this was the last command in the script, as usually the
case, the thread will exit.</p>
<p>It is possible to create a situation in which it may be impossible
to terminate the thread, for example by putting some endless loop
after the <b class="cmd">thread::wait</b> or entering the event loop again by
doing an vwait-type of command. In such cases, the thread may never
exit. This is considered to be a bad practice and should be avoided
if possible. This is best illustrated by the example below:</p>
................................................................................
wait endlessly for events. There is no way one can terminate such thread,
so you wouldn't want to do this!</p>
<p>Each newly created has its internal reference counter set to 0 (zero),
i.e. it is unreserved. This counter gets incremented by a call to
<b class="cmd">thread::preserve</b> and decremented by a call to <b class="cmd">thread::release</b>
command. These two commands implement simple but effective thread
reservation system and offer predictable and controllable thread
termination capabilities. It is however possible to create initially
preserved threads by using flag <b class="option">-preserved</b> of the
<b class="cmd">thread::create</b> command. Threads created with this flag have the
initial value of the reference counter of 1 (one), and are thus
initially marked reserved.</p></dd>
<dt><a name="2"><b class="cmd">thread::preserve</b> <span class="opt">?id?</span></a></dt>
<dd><p>This command increments the thread reference counter. Each call
to this command increments the reference counter by one (1).
................................................................................
until there are no further invocations of the interpreter left on the
call stack. If <i class="arg">result</i> is present, it will be used as the error
message string; otherwise, a default error message string will be used.</p></dd>
<dt><a name="7"><b class="cmd">thread::unwind</b></a></dt>
<dd><p>Use of this command is deprecated in favour of more advanced thread
reservation system implemented with <b class="cmd">thread::preserve</b> and
<b class="cmd">thread::release</b> commands. Support for <b class="cmd">thread::unwind</b>
command will disappear in some future major release of the extension.</p>
<p>This command stops a prior <b class="cmd">thread::wait</b> command. Execution of
the script passed to newly created thread will continue from the
<b class="cmd">thread::wait</b> command. If <b class="cmd">thread::wait</b> was the last command
in the script, the thread will exit. The command returns empty result
but may trigger Tcl error with the message &quot;target thread died&quot; in some
situations.</p></dd>
<dt><a name="8"><b class="cmd">thread::exit</b> <span class="opt">?status?</span></a></dt>
<dd><p>Use of this command is deprecated in favour of more advanced thread
reservation system implemented with <b class="cmd">thread::preserve</b> and
<b class="cmd">thread::release</b> commands. Support for <b class="cmd">thread::exit</b>
command will disappear in some future major release of the extension.</p>
<p>This command forces a thread stuck in the <b class="cmd">thread::wait</b> command to
unconditionally exit. The thread's exit status defaults to 666 and can be
specified using the optional <i class="arg">status</i> argument. The execution of
<b class="cmd">thread::exit</b> command is guaranteed to leave the program memory in the
inconsistent state, produce memory leaks and otherwise affect other subsystem(s)
of the Tcl application in an unpredictable manner. The command returns empty
result but may trigger Tcl error with the message &quot;target thread died&quot; in some
situations.</p></dd>
<dt><a name="9"><b class="cmd">thread::names</b></a></dt>
<dd><p>This command returns a list of thread IDs. These are only for
threads that have been created via <b class="cmd">thread::create</b> command.
If your application creates other threads at the C level, they
................................................................................
The target thread must enter it's event loop in order to receive
scripts sent via this command. This is done by default for threads
created without a startup script. Threads can enter the event loop
explicitly by calling <b class="cmd">thread::wait</b> or any other relevant Tcl/Tk
command, like <b class="cmd">update</b>, <b class="cmd">vwait</b>, etc.</p>
<p>Optional <b class="option">varname</b> specifies name of the variable to store
the result of the <i class="arg">script</i>. Without the <b class="option">-async</b> flag,
the command returns the evaluation code, similarly to the standard
Tcl <b class="cmd">catch</b> command. If, however, the <b class="option">-async</b> flag is
specified, the command returns immediately and caller can later
<b class="cmd">vwait</b> on <span class="opt">?varname?</span> to get the result of the passed <i class="arg">script</i></p>
<pre class="doctools_example">
    set t1 [thread::create]
    set t2 [thread::create]
    thread::send -async $t1 &quot;set a 1&quot; result
................................................................................
Upon the join the handle of the thread has gone out of scope and
should not be used any more.</p></dd>
<dt><a name="16"><b class="cmd">thread::configure</b> <i class="arg">id</i> <span class="opt">?option?</span> <span class="opt">?value?</span> <span class="opt">?...?</span></a></dt>
<dd><p>This command configures various low-level aspects of the thread with
ID <i class="arg">id</i> in the similar way as the standard Tcl command
<b class="cmd">fconfigure</b> configures some Tcl channel options. Options currently
supported are: <b class="option">-eventmark</b> and <b class="option">-unwindonerror</b>.</p>
<p>When <b class="option">-eventmark</b> is provided with a value greater than 0 (zero), that
value is the maximum number of asynchronously posted scripts that may be
pending for the thread.  <b class="cmd">thread::send -async</b> blocks until the number of
pending scripts in the event loop drops below the <b class="option">-eventmark</b> value.</p>
<p>When <b class="option">-unwindonerror</b> is provided with a value of true, an error result
in a script causes the thread to unwind, making it unavailable to evaluate
additional scripts.</p></dd>





<dt><a name="17"><b class="cmd">thread::transfer</b> <i class="arg">id</i> <i class="arg">channel</i></a></dt>
<dd><p>This moves the specified <i class="arg">channel</i> from the current thread
and interpreter to the main interpreter of the thread with the
given <i class="arg">id</i>. After the move the current interpreter has no
access to the channel any more, but the main interpreter of the
target thread will be able to use it from now on.
The command waits until the other thread has incorporated the
................................................................................
(or the same) thread attaches the channel again with <b class="cmd">thread::attach</b>.
Restrictions: same as for transferring shared channels with the
<b class="cmd">thread::transfer</b> command.</p></dd>
<dt><a name="19"><b class="cmd">thread::attach</b> <i class="arg">channel</i></a></dt>
<dd><p>This attaches the previously detached <i class="arg">channel</i> in the
current thread/interpreter. For already existing channels,
the command does nothing, i.e. it is not an error to attach the
same channel more than once. The first operation will actually
perform the operation, while all subsequent operation will just
do nothing. Command throws error if the <i class="arg">channel</i> cannot be
found in the list of detached channels and/or in the current
interpreter.</p></dd>
<dt><a name="20"><b class="cmd">thread::mutex</b></a></dt>
<dd><p>Mutexes are most common thread synchronization primitives.
They are used to synchronize access from two or more threads to one or
................................................................................
<dd><p>Unlocks the <i class="arg">mutex</i> so some other thread may lock it again.
Attempt to unlock the already unlocked mutex will throw Tcl error.</p></dd>
</dl></dd>
<dt><a name="25"><b class="cmd">thread::rwmutex</b></a></dt>
<dd><p>This command creates many-readers/single-writer mutexes. Reader/writer
mutexes allow you to serialize access to a shared resource more optimally.
In situations where a shared resource gets mostly read and seldom modified,
you might gain some performance by using reader/writer mutexes instead of
exclusive or recursive mutexes.</p>
<p>For reading the resource, thread should obtain a read lock on the resource.
Read lock is non-exclusive, meaning that more than one thread can
obtain a read lock to the same resource, without waiting on other readers.
For changing the resource, however, a thread must obtain a exclusive
write lock. This lock effectively blocks all threads from gaining the
read-lock while the resource is been modified by the writer thread.
................................................................................
Attempt to unlock already unlocked <i class="arg">mutex</i> will throw Tcl error.</p></dd>
</dl></dd>
<dt><a name="31"><b class="cmd">thread::cond</b></a></dt>
<dd><p>This command provides script-level access to condition variables.
A condition variable creates a safe environment for the program
to test some condition, sleep on it when false and be awakened
when it might have become true. A condition variable is always
used in the conjunction with an exclusive mutex. If you attempt
to use other type of mutex in conjunction with the condition
variable, a Tcl error will be thrown.</p>
<p>The command supports following subcommands and options:</p>
<dl class="doctools_definitions">
<dt><a name="32"><b class="cmd">thread::cond</b> <b class="method">create</b></a></dt>
<dd><p>Creates the condition variable and returns it's opaque handle.
This handle should be used for any future reference to newly
created condition variable.</p></dd>

Changes to doc/html/tpool.html.

1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<html><head>
<title>tpool - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->
<! -- tpool.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">tpool(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>tpool - Part of the Tcl threading extension implementing pools of worker threads.</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
<dl class="doctools_options">
<dt><b class="option">-minworkers</b> <i class="arg">number</i></dt>
<dd><p>Minimum number of worker threads needed for this threadpool instance.
During threadpool creation, the implementation will create somany
worker threads upfront and will keep at least number of them alive
during the lifetime of the threadpool instance.
Default value of this parameter is 0 (zero). which means that a newly
threadpool will have no worker threads initialy. All worker threads
will be started on demand by callers running <b class="cmd">tpool::post</b> command
and posting jobs to the job queue.</p></dd>
<dt><b class="option">-maxworkers</b> <i class="arg">number</i></dt>
<dd><p>Maximum number of worker threads allowed for this threadpool instance.
If a new job is pending and there are no idle worker threads available,
the implementation will try to create new worker thread. If the number
of available worker threads is lower than the given number,
................................................................................
used to load packages and commands in the worker, set default variables,
create namespaces, and such. If the passed script runs into a Tcl error,
the worker will not be created and the initiating command (either the
<b class="cmd">tpool::create</b> or <b class="cmd">tpool::post</b>) will throw error.
Default value for this option is unspecified, hence, the Tcl interpreter of
the worker thread will contain just the initial set of Tcl commands.</p></dd>
<dt><b class="option">-exitcmd</b> <i class="arg">script</i></dt>
<dd><p>Sets a Tcl script run when the idle worker thread exits. This is normaly
used to cleanup the state of the worker thread, release reserved resources,
cleanup memory and such.
Default value for this option is unspecified, thus no Tcl script will run
on the worker thread exit.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">tpool::names</b></a></dt>
<dd><p>This command returns a list of IDs of threadpools created with the
|







 







|

|







 







|







 







|







1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<!DOCTYPE html><html><head>
<title>tpool - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file '' by tcllib/doctools with format 'html'
   -->
<!-- tpool.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">tpool(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>tpool - Part of the Tcl threading extension implementing pools of worker threads.</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
<dl class="doctools_options">
<dt><b class="option">-minworkers</b> <i class="arg">number</i></dt>
<dd><p>Minimum number of worker threads needed for this threadpool instance.
During threadpool creation, the implementation will create somany
worker threads upfront and will keep at least number of them alive
during the lifetime of the threadpool instance.
Default value of this parameter is 0 (zero). which means that a newly
threadpool will have no worker threads initially. All worker threads
will be started on demand by callers running <b class="cmd">tpool::post</b> command
and posting jobs to the job queue.</p></dd>
<dt><b class="option">-maxworkers</b> <i class="arg">number</i></dt>
<dd><p>Maximum number of worker threads allowed for this threadpool instance.
If a new job is pending and there are no idle worker threads available,
the implementation will try to create new worker thread. If the number
of available worker threads is lower than the given number,
................................................................................
used to load packages and commands in the worker, set default variables,
create namespaces, and such. If the passed script runs into a Tcl error,
the worker will not be created and the initiating command (either the
<b class="cmd">tpool::create</b> or <b class="cmd">tpool::post</b>) will throw error.
Default value for this option is unspecified, hence, the Tcl interpreter of
the worker thread will contain just the initial set of Tcl commands.</p></dd>
<dt><b class="option">-exitcmd</b> <i class="arg">script</i></dt>
<dd><p>Sets a Tcl script run when the idle worker thread exits. This is normally
used to cleanup the state of the worker thread, release reserved resources,
cleanup memory and such.
Default value for this option is unspecified, thus no Tcl script will run
on the worker thread exit.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">tpool::names</b></a></dt>
<dd><p>This command returns a list of IDs of threadpools created with the

Changes to doc/html/tsv.html.

1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
<html><head>
<title>tsv - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->
<! -- tsv.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">tsv(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>tsv - Part of the Tcl threading extension allowing script level manipulation of data shared between threads.</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This section describes commands implementing thread shared variables.
A thread shared variable is very similar to a Tcl array but in
contrast to a Tcl array it is created in shared memory and can
be accessed from many threads at the same time. Important feature of
thread shared variable is that each access to the variable is internaly
protected by a mutex so script programmer does not have to take care
about locking the variable himself.</p>
<p>Thread shared variables are not bound to any thread explicitly. That
means that when a thread which created any of thread shared variables
exits, the variable and associated memory is not unset/reclaimed.
User has to explicitly unset the variable to reclaim the memory
consumed by the variable.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">ELEMENT COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">tsv::names</b> <span class="opt">?pattern?</span></a></dt>
<dd><p>Returns names of shared variables matching optional <span class="opt">?pattern?</span>
or all known variables if pattern is ommited.</p></dd>
<dt><a name="2"><b class="cmd">tsv::object</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>
<dd><p>Creates object accessor command for the <i class="arg">element</i> in the
shared variable <i class="arg">varname</i>. Using this command, one can apply most
of the other shared variable commands as method functions of
the element object command. The object command is automatically
deleted when the element which this command is pointing to is unset.</p>
<pre class="doctools_example">
................................................................................
    % $string append &quot; appended&quot;
    =&gt; A shared string appended
</pre>
</dd>
<dt><a name="3"><b class="cmd">tsv::set</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?value?</span></a></dt>
<dd><p>Sets the value of the <i class="arg">element</i> in the shared variable <i class="arg">varname</i>
to <i class="arg">value</i> and returns the value to caller. The <i class="arg">value</i>
may be ommited, in which case the command will return the current
value of the element. If the element cannot be found, error is triggered.</p></dd>
<dt><a name="4"><b class="cmd">tsv::get</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?namedvar?</span></a></dt>
<dd><p>Retrieves the value of the <i class="arg">element</i> from the shared variable <i class="arg">varname</i>.
If the optional argument <i class="arg">namedvar</i> is given, the value is
stored in the named variable. Return value of the command depends
of the existence of the optional argument <i class="arg">namedvar</i>.
If the argument is ommited and the requested element cannot be found
in the shared array, the command triggers error. If, however, the
optional argument is given on the command line, the command returns
true (1) if the element is found or false (0) if the element is not found.</p></dd>
<dt><a name="5"><b class="cmd">tsv::unset</b> <i class="arg">varname</i> <span class="opt">?element?</span></a></dt>
<dd><p>Unsets the <i class="arg">element</i> from the shared variable <i class="arg">varname</i>.
If the optional element is not given, it deletes the variable.</p></dd>
<dt><a name="6"><b class="cmd">tsv::exists</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>
<dd><p>Checks wether the <i class="arg">element</i> exists in the shared variable <i class="arg">varname</i>
and returns true (1) if it does or false (0) if it doesn't.</p></dd>
<dt><a name="7"><b class="cmd">tsv::pop</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>
<dd><p>Returns value of the <i class="arg">element</i> in the shared variable <i class="arg">varname</i>
and unsets the element, all in one atomic operation.</p></dd>
<dt><a name="8"><b class="cmd">tsv::move</b> <i class="arg">varname</i> <i class="arg">oldname</i> <i class="arg">newname</i></a></dt>
<dd><p>Renames the element <i class="arg">oldname</i> to the <i class="arg">newname</i> in the
shared variable <i class="arg">varname</i>. This effectively performs an get/unset/set
sequence of operations but all in one atomic step.</p></dd>
<dt><a name="9"><b class="cmd">tsv::incr</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?count?</span></a></dt>
<dd><p>Similar to standard Tcl <b class="cmd">incr</b> command but increments the value
of the <i class="arg">element</i> in shared variaboe <i class="arg">varname</i> instead of
the Tcl variable.</p></dd>
<dt><a name="10"><b class="cmd">tsv::append</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">value</i> <span class="opt">?value ...?</span></a></dt>
<dd><p>Similar to standard Tcl <b class="cmd">append</b> command but appends one or more
values to the <i class="arg">element</i> in shared variable <i class="arg">varname</i> instead of the
Tcl variable.</p></dd>
<dt><a name="11"><b class="cmd">tsv::lock</b> <i class="arg">varname</i> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></dt>
<dd><p>This command concatenates passed arguments and evaluates the
................................................................................
<dt><a name="21"><b class="cmd">tsv::lpop</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></dt>
<dd><p>Similar to the standard Tcl <b class="cmd">lindex</b> command but in addition to
returning, it also splices the value out of the <i class="arg">element</i>
from the shared variable <i class="arg">varname</i> in one atomic operation.
In contrast to the Tcl <b class="cmd">lindex</b> command, this command returns
no value to the caller.</p></dd>
<dt><a name="22"><b class="cmd">tsv::lpush</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></dt>
<dd><p>This command performes the opposite of the <b class="cmd">tsv::lpop</b> command.
As its counterpart, it returns no value to the caller.</p></dd>
</dl>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">ARRAY COMMANDS</a></h2>
<p>This command supports most of the options of the standard Tcl
<b class="cmd">array</b> command. In addition to those, it allows binding
a shared variable to some persisten storage databases. Currently the persistent
options supported are the famous GNU Gdbm and LMDB. These options have to be
selected during the package compilation time.
The implementation provides hooks for defining other persistency layers, if
needed.</p>
<dl class="doctools_definitions">
<dt><a name="23"><b class="cmd">tsv::array set</b> <i class="arg">varname</i> <i class="arg">list</i></a></dt>
<dd><p>Does the same as standard Tcl <b class="cmd">array set</b>.</p></dd>
................................................................................
command will return 0, and <i class="arg">retvar</i> will be left unchanged. If {} is
specified for <i class="arg">retvar</i>, the value is not returned, allowing the Tcl
programmer to determine if a <i class="arg">key</i> is present in a keyed list without
setting a variable as a side-effect.</p></dd>
<dt><a name="33"><b class="cmd">tsv::keylkeys</b> <i class="arg">varname</i> <i class="arg">keylist</i> <span class="opt">?key?</span></a></dt>
<dd><p>Return  the a list of the keys in the keyed list <i class="arg">keylist</i> in the
shared variable <i class="arg">varname</i>. If <i class="arg">key</i> is specified, then it is
the name of a key field who's subfield keys are to be retrieved.</p></dd>
<dt><a name="34"><b class="cmd">tsv::keylset</b> <i class="arg">varname</i> <i class="arg">keylist</i> <i class="arg">key</i> <i class="arg">value</i> <span class="opt">?key value..?</span></a></dt>
<dd><p>Set the value associated with <i class="arg">key</i>, in the keyed list <i class="arg">keylist</i>
to <i class="arg">value</i>. If the <i class="arg">keylist</i> does not exists, it is created.
If <i class="arg">key</i> is not currently in the list, it will be added. If it already
exists, <i class="arg">value</i> replaces the existing value. Multiple keywords and
values may be specified, if desired.</p></dd>
</dl>
................................................................................
improving performance. Special care has been taken to assure that all
object data is properly locked and deep-copied when moving objects between
threads.</p>
<p>Due to the internal design of the Tcl core, there is no provision of full
integration of shared variables within the Tcl syntax, unfortunately. All
access to shared data must be performed with the supplied package commands.
Also, variable traces are not supported. But even so, benefits of easy,
simple and safe shared data manipulation outweights imposed limitations.</p>
</div>
<div id="section7" class="doctools_section"><h2><a name="section7">CREDITS</a></h2>
<p>Thread shared variables are inspired by the nsv interface found in
AOLserver, a highly scalable Web server from America Online.</p>
</div>
<div id="see-also" class="doctools_section"><h2><a name="see-also">See Also</a></h2>
<p>thread, tpool, ttrace</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p>locking, synchronization, thread shared data, threads</p>
</div>
</div></body></html>

|







 







|

|







 







|












|







 







|






|







|










|







 







|






|







 







|







 







|













1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
<!DOCTYPE html><html><head>
<title>tsv - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file '' by tcllib/doctools with format 'html'
   -->
<!-- tsv.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">tsv(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>tsv - Part of the Tcl threading extension allowing script level manipulation of data shared between threads.</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This section describes commands implementing thread shared variables.
A thread shared variable is very similar to a Tcl array but in
contrast to a Tcl array it is created in shared memory and can
be accessed from many threads at the same time. Important feature of
thread shared variable is that each access to the variable is internally
protected by a mutex so script programmer does not have to take care
about locking the variable himself.</p>
<p>Thread shared variables are not bound to any thread explicitly. That
means that when a thread which created any of thread shared variables
exits, the variable and associated memory is not unset/reclaimed.
User has to explicitly unset the variable to reclaim the memory
consumed by the variable.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">ELEMENT COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">tsv::names</b> <span class="opt">?pattern?</span></a></dt>
<dd><p>Returns names of shared variables matching optional <span class="opt">?pattern?</span>
or all known variables if pattern is omitted.</p></dd>
<dt><a name="2"><b class="cmd">tsv::object</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>
<dd><p>Creates object accessor command for the <i class="arg">element</i> in the
shared variable <i class="arg">varname</i>. Using this command, one can apply most
of the other shared variable commands as method functions of
the element object command. The object command is automatically
deleted when the element which this command is pointing to is unset.</p>
<pre class="doctools_example">
................................................................................
    % $string append &quot; appended&quot;
    =&gt; A shared string appended
</pre>
</dd>
<dt><a name="3"><b class="cmd">tsv::set</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?value?</span></a></dt>
<dd><p>Sets the value of the <i class="arg">element</i> in the shared variable <i class="arg">varname</i>
to <i class="arg">value</i> and returns the value to caller. The <i class="arg">value</i>
may be omitted, in which case the command will return the current
value of the element. If the element cannot be found, error is triggered.</p></dd>
<dt><a name="4"><b class="cmd">tsv::get</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?namedvar?</span></a></dt>
<dd><p>Retrieves the value of the <i class="arg">element</i> from the shared variable <i class="arg">varname</i>.
If the optional argument <i class="arg">namedvar</i> is given, the value is
stored in the named variable. Return value of the command depends
of the existence of the optional argument <i class="arg">namedvar</i>.
If the argument is omitted and the requested element cannot be found
in the shared array, the command triggers error. If, however, the
optional argument is given on the command line, the command returns
true (1) if the element is found or false (0) if the element is not found.</p></dd>
<dt><a name="5"><b class="cmd">tsv::unset</b> <i class="arg">varname</i> <span class="opt">?element?</span></a></dt>
<dd><p>Unsets the <i class="arg">element</i> from the shared variable <i class="arg">varname</i>.
If the optional element is not given, it deletes the variable.</p></dd>
<dt><a name="6"><b class="cmd">tsv::exists</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>
<dd><p>Checks whether the <i class="arg">element</i> exists in the shared variable <i class="arg">varname</i>
and returns true (1) if it does or false (0) if it doesn't.</p></dd>
<dt><a name="7"><b class="cmd">tsv::pop</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>
<dd><p>Returns value of the <i class="arg">element</i> in the shared variable <i class="arg">varname</i>
and unsets the element, all in one atomic operation.</p></dd>
<dt><a name="8"><b class="cmd">tsv::move</b> <i class="arg">varname</i> <i class="arg">oldname</i> <i class="arg">newname</i></a></dt>
<dd><p>Renames the element <i class="arg">oldname</i> to the <i class="arg">newname</i> in the
shared variable <i class="arg">varname</i>. This effectively performs an get/unset/set
sequence of operations but all in one atomic step.</p></dd>
<dt><a name="9"><b class="cmd">tsv::incr</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?count?</span></a></dt>
<dd><p>Similar to standard Tcl <b class="cmd">incr</b> command but increments the value
of the <i class="arg">element</i> in shared variable <i class="arg">varname</i> instead of
the Tcl variable.</p></dd>
<dt><a name="10"><b class="cmd">tsv::append</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">value</i> <span class="opt">?value ...?</span></a></dt>
<dd><p>Similar to standard Tcl <b class="cmd">append</b> command but appends one or more
values to the <i class="arg">element</i> in shared variable <i class="arg">varname</i> instead of the
Tcl variable.</p></dd>
<dt><a name="11"><b class="cmd">tsv::lock</b> <i class="arg">varname</i> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></dt>
<dd><p>This command concatenates passed arguments and evaluates the
................................................................................
<dt><a name="21"><b class="cmd">tsv::lpop</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></dt>
<dd><p>Similar to the standard Tcl <b class="cmd">lindex</b> command but in addition to
returning, it also splices the value out of the <i class="arg">element</i>
from the shared variable <i class="arg">varname</i> in one atomic operation.
In contrast to the Tcl <b class="cmd">lindex</b> command, this command returns
no value to the caller.</p></dd>
<dt><a name="22"><b class="cmd">tsv::lpush</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></dt>
<dd><p>This command performs the opposite of the <b class="cmd">tsv::lpop</b> command.
As its counterpart, it returns no value to the caller.</p></dd>
</dl>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">ARRAY COMMANDS</a></h2>
<p>This command supports most of the options of the standard Tcl
<b class="cmd">array</b> command. In addition to those, it allows binding
a shared variable to some persistent storage databases. Currently the persistent
options supported are the famous GNU Gdbm and LMDB. These options have to be
selected during the package compilation time.
The implementation provides hooks for defining other persistency layers, if
needed.</p>
<dl class="doctools_definitions">
<dt><a name="23"><b class="cmd">tsv::array set</b> <i class="arg">varname</i> <i class="arg">list</i></a></dt>
<dd><p>Does the same as standard Tcl <b class="cmd">array set</b>.</p></dd>
................................................................................
command will return 0, and <i class="arg">retvar</i> will be left unchanged. If {} is
specified for <i class="arg">retvar</i>, the value is not returned, allowing the Tcl
programmer to determine if a <i class="arg">key</i> is present in a keyed list without
setting a variable as a side-effect.</p></dd>
<dt><a name="33"><b class="cmd">tsv::keylkeys</b> <i class="arg">varname</i> <i class="arg">keylist</i> <span class="opt">?key?</span></a></dt>
<dd><p>Return  the a list of the keys in the keyed list <i class="arg">keylist</i> in the
shared variable <i class="arg">varname</i>. If <i class="arg">key</i> is specified, then it is
the name of a key field whose subfield keys are to be retrieved.</p></dd>
<dt><a name="34"><b class="cmd">tsv::keylset</b> <i class="arg">varname</i> <i class="arg">keylist</i> <i class="arg">key</i> <i class="arg">value</i> <span class="opt">?key value..?</span></a></dt>
<dd><p>Set the value associated with <i class="arg">key</i>, in the keyed list <i class="arg">keylist</i>
to <i class="arg">value</i>. If the <i class="arg">keylist</i> does not exists, it is created.
If <i class="arg">key</i> is not currently in the list, it will be added. If it already
exists, <i class="arg">value</i> replaces the existing value. Multiple keywords and
values may be specified, if desired.</p></dd>
</dl>
................................................................................
improving performance. Special care has been taken to assure that all
object data is properly locked and deep-copied when moving objects between
threads.</p>
<p>Due to the internal design of the Tcl core, there is no provision of full
integration of shared variables within the Tcl syntax, unfortunately. All
access to shared data must be performed with the supplied package commands.
Also, variable traces are not supported. But even so, benefits of easy,
simple and safe shared data manipulation outweighs imposed limitations.</p>
</div>
<div id="section7" class="doctools_section"><h2><a name="section7">CREDITS</a></h2>
<p>Thread shared variables are inspired by the nsv interface found in
AOLserver, a highly scalable Web server from America Online.</p>
</div>
<div id="see-also" class="doctools_section"><h2><a name="see-also">See Also</a></h2>
<p>thread, tpool, ttrace</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p>locking, synchronization, thread shared data, threads</p>
</div>
</div></body></html>

Changes to doc/html/ttrace.html.

1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
<html><head>
<title>ttrace - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->
<! -- ttrace.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">ttrace(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>ttrace - Trace-based interpreter initialization</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
<li><a href="#16"><b class="cmd">ttrace::delentry</b> <i class="arg">cmd</i></a></li>
<li><a href="#17"><b class="cmd">ttrace::preload</b> <i class="arg">cmd</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package creates a framework for on-demand replication of the
interpreter state accross threads in an multithreading application.
It relies on the mechanics of Tcl command tracing and the Tcl
<b class="cmd">unknown</b> command and mechanism.</p>
<p>The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from
America Online.</p>
<p>In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:</p>
................................................................................
epoch change. For the Tcl threading extension, all threads created by
the extension are automatically updated. If the command execution
resulted in Tcl error, no state propagation takes place.</p>
<p>This is the most important user-level command of the package as
it wraps most of the commands described below. This greatly
simplifies things, because user need to learn just this (one)
command in order to effectively use the package. Other commands,
as desribed below, are included mostly for the sake of completeness.</p></dd>
<dt><a name="2"><b class="cmd">ttrace::enable</b></a></dt>
<dd><p>Activates all registered callbacks in the framework
and starts a new trace epoch. The trace epoch encapsulates all
changes done to the interpreter during the time traces are activated.</p></dd>
<dt><a name="3"><b class="cmd">ttrace::disable</b></a></dt>
<dd><p>Deactivates all registered callbacks in the framework
and closes the current trace epoch.</p></dd>
................................................................................
<dd><p>Used to clean-up all on-demand loaded resources in the interpreter.
It effectively brings Tcl interpreter to its pristine state.</p></dd>
<dt><a name="5"><b class="cmd">ttrace::update</b> <span class="opt">?epoch?</span></a></dt>
<dd><p>Used to refresh the state of the interpreter to match the optional
trace <span class="opt">?epoch?</span>. If the optional <span class="opt">?epoch?</span> is not given, it takes
the most recent trace epoch.</p></dd>
<dt><a name="6"><b class="cmd">ttrace::getscript</b></a></dt>
<dd><p>Returns a synthetized Tcl script which may be sourced in any interpreter.
This script sets the stage for the Tcl <b class="cmd">unknown</b> command so it can
load traced resources from the in-memory database. Normally, this command
is automatically invoked by other higher-level commands like
<b class="cmd">ttrace::eval</b> and <b class="cmd">ttrace::update</b>.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">CALLBACK COMMANDS</a></h2>
<p>A word upfront: the package already includes callbacks for tracing
following Tcl commands: <b class="cmd">proc</b>, <b class="cmd">namespace</b>, <b class="cmd">variable</b>,
<b class="cmd">load</b>, and <b class="cmd">rename</b>. Additionaly, a set of callbacks for
tracing resources (object, clasess) for the XOTcl v1.3.8+, an
OO-extension to Tcl, is also provided.
This gives a solid base for solving most of the real-life needs and
serves as an example for people wanting to customize the package
to cover their specific needs.</p>
<p>Below, you can find commands for registering callbacks in the
framework and for writing callback scripts. These callbacks are
invoked by the framework in order to gather interpreter state
................................................................................
creates the requested resource in the interpreter on demand.
This way, users can update just one interpreter (master) in one
thread and replicate that interpreter state (or part of it) to other
threads/interpreters in the process.</p>
<p>Immediate benefit of such approach is the much smaller memory footprint
of the application and much faster thread creation. By not actually
loading all necessary procedures (and other resources) in every thread
at the thread initialization time, but by deffering this to the time the
resource is actually referenced, significant improvements in both
memory consumption and thread initialization time can be achieved. Some
tests have shown that memory footprint of an multithreading Tcl application
went down more than three times and thread startup time was reduced for
about 50 times. Note that your mileage may vary.
Other benefits include much finer control about what (and when) gets
replicated from the master to other Tcl thread/interpreters.</p>
|







 







|

|







 







|







 







|







 







|









|
|







 







|







1
2
3
4
5
6
7
8
9
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
<!DOCTYPE html><html><head>
<title>ttrace - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
................................................................................
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file '' by tcllib/doctools with format 'html'
   -->
<!-- ttrace.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">ttrace(n) 2.8  &quot;Tcl Threading&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>ttrace - Trace-based interpreter initialization</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
................................................................................
<li><a href="#16"><b class="cmd">ttrace::delentry</b> <i class="arg">cmd</i></a></li>
<li><a href="#17"><b class="cmd">ttrace::preload</b> <i class="arg">cmd</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package creates a framework for on-demand replication of the
interpreter state across threads in an multithreading application.
It relies on the mechanics of Tcl command tracing and the Tcl
<b class="cmd">unknown</b> command and mechanism.</p>
<p>The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from
America Online.</p>
<p>In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:</p>
................................................................................
epoch change. For the Tcl threading extension, all threads created by
the extension are automatically updated. If the command execution
resulted in Tcl error, no state propagation takes place.</p>
<p>This is the most important user-level command of the package as
it wraps most of the commands described below. This greatly
simplifies things, because user need to learn just this (one)
command in order to effectively use the package. Other commands,
as described below, are included mostly for the sake of completeness.</p></dd>
<dt><a name="2"><b class="cmd">ttrace::enable</b></a></dt>
<dd><p>Activates all registered callbacks in the framework
and starts a new trace epoch. The trace epoch encapsulates all
changes done to the interpreter during the time traces are activated.</p></dd>
<dt><a name="3"><b class="cmd">ttrace::disable</b></a></dt>
<dd><p>Deactivates all registered callbacks in the framework
and closes the current trace epoch.</p></dd>
................................................................................
<dd><p>Used to clean-up all on-demand loaded resources in the interpreter.
It effectively brings Tcl interpreter to its pristine state.</p></dd>
<dt><a name="5"><b class="cmd">ttrace::update</b> <span class="opt">?epoch?</span></a></dt>
<dd><p>Used to refresh the state of the interpreter to match the optional
trace <span class="opt">?epoch?</span>. If the optional <span class="opt">?epoch?</span> is not given, it takes
the most recent trace epoch.</p></dd>
<dt><a name="6"><b class="cmd">ttrace::getscript</b></a></dt>
<dd><p>Returns a synthesized Tcl script which may be sourced in any interpreter.
This script sets the stage for the Tcl <b class="cmd">unknown</b> command so it can
load traced resources from the in-memory database. Normally, this command
is automatically invoked by other higher-level commands like
<b class="cmd">ttrace::eval</b> and <b class="cmd">ttrace::update</b>.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">CALLBACK COMMANDS</a></h2>
<p>A word upfront: the package already includes callbacks for tracing
following Tcl commands: <b class="cmd">proc</b>, <b class="cmd">namespace</b>, <b class="cmd">variable</b>,
<b class="cmd">load</b>, and <b class="cmd">rename</b>. Additionally, a set of callbacks for
tracing resources (object, classes) for the XOTcl v1.3.8+, an
OO-extension to Tcl, is also provided.
This gives a solid base for solving most of the real-life needs and
serves as an example for people wanting to customize the package
to cover their specific needs.</p>
<p>Below, you can find commands for registering callbacks in the
framework and for writing callback scripts. These callbacks are
invoked by the framework in order to gather interpreter state
................................................................................
creates the requested resource in the interpreter on demand.
This way, users can update just one interpreter (master) in one
thread and replicate that interpreter state (or part of it) to other
threads/interpreters in the process.</p>
<p>Immediate benefit of such approach is the much smaller memory footprint
of the application and much faster thread creation. By not actually
loading all necessary procedures (and other resources) in every thread
at the thread initialization time, but by deferring this to the time the
resource is actually referenced, significant improvements in both
memory consumption and thread initialization time can be achieved. Some
tests have shown that memory footprint of an multithreading Tcl application
went down more than three times and thread startup time was reduced for
about 50 times. Note that your mileage may vary.
Other benefits include much finer control about what (and when) gets
replicated from the master to other Tcl thread/interpreters.</p>

Changes to doc/man/thread.n.

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
...
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642



643
644
645
646
647
648
649
...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
...
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
\fBthread::cond\fR \fBwait\fR \fIcond\fR \fImutex\fR ?ms?
.sp
.BE
.SH DESCRIPTION
The \fBthread\fR extension creates threads that contain Tcl
interpreters, and it lets you send scripts to those threads for
evaluation\&.
Additionaly, it provides script-level access to basic thread
synchronization primitives, like mutexes and condition variables\&.
.SH COMMANDS
This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation\&.
.TP
\fBthread::create\fR ?-joinable? ?-preserved? ?script?
This command creates a thread that contains a Tcl interpreter\&.
................................................................................
Threads created by the \fBthread::create\fR cannot be destroyed
forcefully\&. Consequently, there is no corresponding thread destroy
command\&. A thread may only be released using the \fBthread::release\fR
and if its internal reference count drops to zero, the thread is
marked for exit\&. This kicks the thread out of the event loop
servicing and the thread continues to execute commands passed in
the \fBscript\fR argument, following the \fBthread::wait\fR
command\&. If this was the last command in the script, as usualy the
case, the thread will exit\&.
.sp
It is possible to create a situation in which it may be impossible
to terminate the thread, for example by putting some endless loop
after the \fBthread::wait\fR or entering the event loop again by
doing an vwait-type of command\&. In such cases, the thread may never
exit\&. This is considered to be a bad practice and should be avoided
................................................................................
so you wouldn't want to do this!
.sp
Each newly created has its internal reference counter set to 0 (zero),
i\&.e\&. it is unreserved\&. This counter gets incremented by a call to
\fBthread::preserve\fR and decremented by a call to \fBthread::release\fR
command\&. These two commands implement simple but effective thread
reservation system and offer predictable and controllable thread
termination capabilities\&. It is however possible to create initialy
preserved threads by using flag \fB-preserved\fR of the
\fBthread::create\fR command\&. Threads created with this flag have the
initial value of the reference counter of 1 (one), and are thus
initially marked reserved\&.
.TP
\fBthread::preserve\fR ?id?
This command increments the thread reference counter\&. Each call
................................................................................
call stack\&. If \fIresult\fR is present, it will be used as the error
message string; otherwise, a default error message string will be used\&.
.TP
\fBthread::unwind\fR
Use of this command is deprecated in favour of more advanced thread
reservation system implemented with \fBthread::preserve\fR and
\fBthread::release\fR commands\&. Support for \fBthread::unwind\fR
command will dissapear in some future major release of the extension\&.
.sp
This command stops a prior \fBthread::wait\fR command\&. Execution of
the script passed to newly created thread will continue from the
\fBthread::wait\fR command\&. If \fBthread::wait\fR was the last command
in the script, the thread will exit\&. The command returns empty result
but may trigger Tcl error with the message "target thread died" in some
situations\&.
.TP
\fBthread::exit\fR ?status?
Use of this command is deprecated in favour of more advanced thread
reservation system implemented with \fBthread::preserve\fR and
\fBthread::release\fR commands\&. Support for \fBthread::exit\fR
command will dissapear in some future major release of the extension\&.
.sp
This command forces a thread stuck in the \fBthread::wait\fR command to
unconditionaly exit\&. The thread's exit status defaults to 666 and can be
specified using the optional \fIstatus\fR argument\&. The execution of
\fBthread::exit\fR command is guaranteed to leave the program memory in the
unconsistent state, produce memory leaks and otherwise affect other subsytem(s)
of the Tcl application in an unpredictable manner\&. The command returns empty
result but may trigger Tcl error with the message "target thread died" in some
situations\&.
.TP
\fBthread::names\fR
This command returns a list of thread IDs\&. These are only for
threads that have been created via \fBthread::create\fR command\&.
................................................................................
scripts sent via this command\&. This is done by default for threads
created without a startup script\&. Threads can enter the event loop
explicitly by calling \fBthread::wait\fR or any other relevant Tcl/Tk
command, like \fBupdate\fR, \fBvwait\fR, etc\&.
.sp
Optional \fBvarname\fR specifies name of the variable to store
the result of the \fIscript\fR\&. Without the \fB-async\fR flag,
the command returns the evaluation code, similarily to the standard
Tcl \fBcatch\fR command\&. If, however, the \fB-async\fR flag is
specified, the command returns immediately and caller can later
\fBvwait\fR on ?varname? to get the result of the passed \fIscript\fR
.CS


    set t1 [thread::create]
................................................................................
.TP
\fBthread::configure\fR \fIid\fR ?option? ?value? ?\&.\&.\&.?
This command configures various low-level aspects of the thread with
ID \fIid\fR in the similar way as the standard Tcl command
\fBfconfigure\fR configures some Tcl channel options\&. Options currently
supported are: \fB-eventmark\fR and \fB-unwindonerror\fR\&.
.sp
The \fB-eventmark\fR option, when set, limits the number of
asynchronously posted scripts to the thread event loop\&.
The \fBthread::send -async\fR command will block until the number
of pending scripts in the event loop does not drop below the value
configured with \fB-eventmark\fR\&. Default value for the
\fB-eventmark\fR is 0 (zero) which effectively disables the checking,
i\&.e\&. allows for unlimited number of posted scripts\&.
.sp
The \fB-unwindonerror\fR option, when set, causes the
target thread to unwind if the result of the script processing
resulted in error\&. Default value for the \fB-unwindonerror\fR
is 0 (false), i\&.e\&. thread continues to process scripts after one
of the posted scripts fails\&.



.TP
\fBthread::transfer\fR \fIid\fR \fIchannel\fR
This moves the specified \fIchannel\fR from the current thread
and interpreter to the main interpreter of the thread with the
given \fIid\fR\&. After the move the current interpreter has no
access to the channel any more, but the main interpreter of the
target thread will be able to use it from now on\&.
................................................................................
Restrictions: same as for transferring shared channels with the
\fBthread::transfer\fR command\&.
.TP
\fBthread::attach\fR \fIchannel\fR
This attaches the previously detached \fIchannel\fR in the
current thread/interpreter\&. For already existing channels,
the command does nothing, i\&.e\&. it is not an error to attach the
same channel more than once\&. The first operation will actualy
perform the operation, while all subsequent operation will just
do nothing\&. Command throws error if the \fIchannel\fR cannot be
found in the list of detached channels and/or in the current
interpreter\&.
.TP
\fBthread::mutex\fR
Mutexes are most common thread synchronization primitives\&.
................................................................................
.RE
.sp
.TP
\fBthread::rwmutex\fR
This command creates many-readers/single-writer mutexes\&. Reader/writer
mutexes allow you to serialize access to a shared resource more optimally\&.
In situations where a shared resource gets mostly read and seldom modified,
you might gain some performace by using reader/writer mutexes instead of
exclusive or recursive mutexes\&.
.sp
For reading the resource, thread should obtain a read lock on the resource\&.
Read lock is non-exclusive, meaning that more than one thread can
obtain a read lock to the same resource, without waiting on other readers\&.
For changing the resource, however, a thread must obtain a exclusive
write lock\&. This lock effectively blocks all threads from gaining the
................................................................................
.sp
.TP
\fBthread::cond\fR
This command provides script-level access to condition variables\&.
A condition variable creates a safe environment for the program
to test some condition, sleep on it when false and be awakened
when it might have become true\&. A condition variable is always
used in the conjuction with an exclusive mutex\&. If you attempt
to use other type of mutex in conjuction with the condition
variable, a Tcl error will be thrown\&.
.sp
The command supports following subcommands and options:
.RS
.TP
\fBthread::cond\fR \fBcreate\fR
Creates the condition variable and returns it's opaque handle\&.






|







 







|







 







|







 







|












|


|


|







 







|







 







|
|
|
|
<
<
<

<
<
<
<
<
>
>
>







 







|







 







|







 







|
|







348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
...
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
...
623
624
625
626
627
628
629
630
631
632
633



634





635
636
637
638
639
640
641
642
643
644
...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
...
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
\fBthread::cond\fR \fBwait\fR \fIcond\fR \fImutex\fR ?ms?
.sp
.BE
.SH DESCRIPTION
The \fBthread\fR extension creates threads that contain Tcl
interpreters, and it lets you send scripts to those threads for
evaluation\&.
Additionally, it provides script-level access to basic thread
synchronization primitives, like mutexes and condition variables\&.
.SH COMMANDS
This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation\&.
.TP
\fBthread::create\fR ?-joinable? ?-preserved? ?script?
This command creates a thread that contains a Tcl interpreter\&.
................................................................................
Threads created by the \fBthread::create\fR cannot be destroyed
forcefully\&. Consequently, there is no corresponding thread destroy
command\&. A thread may only be released using the \fBthread::release\fR
and if its internal reference count drops to zero, the thread is
marked for exit\&. This kicks the thread out of the event loop
servicing and the thread continues to execute commands passed in
the \fBscript\fR argument, following the \fBthread::wait\fR
command\&. If this was the last command in the script, as usually the
case, the thread will exit\&.
.sp
It is possible to create a situation in which it may be impossible
to terminate the thread, for example by putting some endless loop
after the \fBthread::wait\fR or entering the event loop again by
doing an vwait-type of command\&. In such cases, the thread may never
exit\&. This is considered to be a bad practice and should be avoided
................................................................................
so you wouldn't want to do this!
.sp
Each newly created has its internal reference counter set to 0 (zero),
i\&.e\&. it is unreserved\&. This counter gets incremented by a call to
\fBthread::preserve\fR and decremented by a call to \fBthread::release\fR
command\&. These two commands implement simple but effective thread
reservation system and offer predictable and controllable thread
termination capabilities\&. It is however possible to create initially
preserved threads by using flag \fB-preserved\fR of the
\fBthread::create\fR command\&. Threads created with this flag have the
initial value of the reference counter of 1 (one), and are thus
initially marked reserved\&.
.TP
\fBthread::preserve\fR ?id?
This command increments the thread reference counter\&. Each call
................................................................................
call stack\&. If \fIresult\fR is present, it will be used as the error
message string; otherwise, a default error message string will be used\&.
.TP
\fBthread::unwind\fR
Use of this command is deprecated in favour of more advanced thread
reservation system implemented with \fBthread::preserve\fR and
\fBthread::release\fR commands\&. Support for \fBthread::unwind\fR
command will disappear in some future major release of the extension\&.
.sp
This command stops a prior \fBthread::wait\fR command\&. Execution of
the script passed to newly created thread will continue from the
\fBthread::wait\fR command\&. If \fBthread::wait\fR was the last command
in the script, the thread will exit\&. The command returns empty result
but may trigger Tcl error with the message "target thread died" in some
situations\&.
.TP
\fBthread::exit\fR ?status?
Use of this command is deprecated in favour of more advanced thread
reservation system implemented with \fBthread::preserve\fR and
\fBthread::release\fR commands\&. Support for \fBthread::exit\fR
command will disappear in some future major release of the extension\&.
.sp
This command forces a thread stuck in the \fBthread::wait\fR command to
unconditionally exit\&. The thread's exit status defaults to 666 and can be
specified using the optional \fIstatus\fR argument\&. The execution of
\fBthread::exit\fR command is guaranteed to leave the program memory in the
inconsistent state, produce memory leaks and otherwise affect other subsystem(s)
of the Tcl application in an unpredictable manner\&. The command returns empty
result but may trigger Tcl error with the message "target thread died" in some
situations\&.
.TP
\fBthread::names\fR
This command returns a list of thread IDs\&. These are only for
threads that have been created via \fBthread::create\fR command\&.
................................................................................
scripts sent via this command\&. This is done by default for threads
created without a startup script\&. Threads can enter the event loop
explicitly by calling \fBthread::wait\fR or any other relevant Tcl/Tk
command, like \fBupdate\fR, \fBvwait\fR, etc\&.
.sp
Optional \fBvarname\fR specifies name of the variable to store
the result of the \fIscript\fR\&. Without the \fB-async\fR flag,
the command returns the evaluation code, similarly to the standard
Tcl \fBcatch\fR command\&. If, however, the \fB-async\fR flag is
specified, the command returns immediately and caller can later
\fBvwait\fR on ?varname? to get the result of the passed \fIscript\fR
.CS


    set t1 [thread::create]
................................................................................
.TP
\fBthread::configure\fR \fIid\fR ?option? ?value? ?\&.\&.\&.?
This command configures various low-level aspects of the thread with
ID \fIid\fR in the similar way as the standard Tcl command
\fBfconfigure\fR configures some Tcl channel options\&. Options currently
supported are: \fB-eventmark\fR and \fB-unwindonerror\fR\&.
.sp
When \fB-eventmark\fR is provided with a value greater than 0 (zero), that
value is the maximum number of asynchronously posted scripts that may be
pending for the thread\&.  \fBthread::send -async\fR blocks until the number of
pending scripts in the event loop drops below the \fB-eventmark\fR value\&.



.sp





When \fB-unwindonerror\fR is provided with a value of true, an error result
in a script causes the thread to unwind, making it unavailable to evaluate
additional scripts\&.
.TP
\fBthread::transfer\fR \fIid\fR \fIchannel\fR
This moves the specified \fIchannel\fR from the current thread
and interpreter to the main interpreter of the thread with the
given \fIid\fR\&. After the move the current interpreter has no
access to the channel any more, but the main interpreter of the
target thread will be able to use it from now on\&.
................................................................................
Restrictions: same as for transferring shared channels with the
\fBthread::transfer\fR command\&.
.TP
\fBthread::attach\fR \fIchannel\fR
This attaches the previously detached \fIchannel\fR in the
current thread/interpreter\&. For already existing channels,
the command does nothing, i\&.e\&. it is not an error to attach the
same channel more than once\&. The first operation will actually
perform the operation, while all subsequent operation will just
do nothing\&. Command throws error if the \fIchannel\fR cannot be
found in the list of detached channels and/or in the current
interpreter\&.
.TP
\fBthread::mutex\fR
Mutexes are most common thread synchronization primitives\&.
................................................................................
.RE
.sp
.TP
\fBthread::rwmutex\fR
This command creates many-readers/single-writer mutexes\&. Reader/writer
mutexes allow you to serialize access to a shared resource more optimally\&.
In situations where a shared resource gets mostly read and seldom modified,
you might gain some performance by using reader/writer mutexes instead of
exclusive or recursive mutexes\&.
.sp
For reading the resource, thread should obtain a read lock on the resource\&.
Read lock is non-exclusive, meaning that more than one thread can
obtain a read lock to the same resource, without waiting on other readers\&.
For changing the resource, however, a thread must obtain a exclusive
write lock\&. This lock effectively blocks all threads from gaining the
................................................................................
.sp
.TP
\fBthread::cond\fR
This command provides script-level access to condition variables\&.
A condition variable creates a safe environment for the program
to test some condition, sleep on it when false and be awakened
when it might have become true\&. A condition variable is always
used in the conjunction with an exclusive mutex\&. If you attempt
to use other type of mutex in conjunction with the condition
variable, a Tcl error will be thrown\&.
.sp
The command supports following subcommands and options:
.RS
.TP
\fBthread::cond\fR \fBcreate\fR
Creates the condition variable and returns it's opaque handle\&.

Changes to doc/man/tpool.n.

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
.TP
\fB-minworkers\fR \fInumber\fR
Minimum number of worker threads needed for this threadpool instance\&.
During threadpool creation, the implementation will create somany
worker threads upfront and will keep at least number of them alive
during the lifetime of the threadpool instance\&.
Default value of this parameter is 0 (zero)\&. which means that a newly
threadpool will have no worker threads initialy\&. All worker threads
will be started on demand by callers running \fBtpool::post\fR command
and posting jobs to the job queue\&.
.TP
\fB-maxworkers\fR \fInumber\fR
Maximum number of worker threads allowed for this threadpool instance\&.
If a new job is pending and there are no idle worker threads available,
the implementation will try to create new worker thread\&. If the number
................................................................................
create namespaces, and such\&. If the passed script runs into a Tcl error,
the worker will not be created and the initiating command (either the
\fBtpool::create\fR or \fBtpool::post\fR) will throw error\&.
Default value for this option is unspecified, hence, the Tcl interpreter of
the worker thread will contain just the initial set of Tcl commands\&.
.TP
\fB-exitcmd\fR \fIscript\fR
Sets a Tcl script run when the idle worker thread exits\&. This is normaly
used to cleanup the state of the worker thread, release reserved resources,
cleanup memory and such\&.
Default value for this option is unspecified, thus no Tcl script will run
on the worker thread exit\&.
.RE
.sp
.TP






|







 







|







318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
.TP
\fB-minworkers\fR \fInumber\fR
Minimum number of worker threads needed for this threadpool instance\&.
During threadpool creation, the implementation will create somany
worker threads upfront and will keep at least number of them alive
during the lifetime of the threadpool instance\&.
Default value of this parameter is 0 (zero)\&. which means that a newly
threadpool will have no worker threads initially\&. All worker threads
will be started on demand by callers running \fBtpool::post\fR command
and posting jobs to the job queue\&.
.TP
\fB-maxworkers\fR \fInumber\fR
Maximum number of worker threads allowed for this threadpool instance\&.
If a new job is pending and there are no idle worker threads available,
the implementation will try to create new worker thread\&. If the number
................................................................................
create namespaces, and such\&. If the passed script runs into a Tcl error,
the worker will not be created and the initiating command (either the
\fBtpool::create\fR or \fBtpool::post\fR) will throw error\&.
Default value for this option is unspecified, hence, the Tcl interpreter of
the worker thread will contain just the initial set of Tcl commands\&.
.TP
\fB-exitcmd\fR \fIscript\fR
Sets a Tcl script run when the idle worker thread exits\&. This is normally
used to cleanup the state of the worker thread, release reserved resources,
cleanup memory and such\&.
Default value for this option is unspecified, thus no Tcl script will run
on the worker thread exit\&.
.RE
.sp
.TP

Changes to doc/man/tsv.n.

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
...
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
...
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
.sp
.BE
.SH DESCRIPTION
This section describes commands implementing thread shared variables\&.
A thread shared variable is very similar to a Tcl array but in
contrast to a Tcl array it is created in shared memory and can
be accessed from many threads at the same time\&. Important feature of
thread shared variable is that each access to the variable is internaly
protected by a mutex so script programmer does not have to take care
about locking the variable himself\&.
.PP
Thread shared variables are not bound to any thread explicitly\&. That
means that when a thread which created any of thread shared variables
exits, the variable and associated memory is not unset/reclaimed\&.
User has to explicitly unset the variable to reclaim the memory
consumed by the variable\&.
.SH "ELEMENT COMMANDS"
.TP
\fBtsv::names\fR ?pattern?
Returns names of shared variables matching optional ?pattern?
or all known variables if pattern is ommited\&.
.TP
\fBtsv::object\fR \fIvarname\fR \fIelement\fR
Creates object accessor command for the \fIelement\fR in the
shared variable \fIvarname\fR\&. Using this command, one can apply most
of the other shared variable commands as method functions of
the element object command\&. The object command is automatically
deleted when the element which this command is pointing to is unset\&.
................................................................................
    => A shared string appended

.CE
.TP
\fBtsv::set\fR \fIvarname\fR \fIelement\fR ?value?
Sets the value of the \fIelement\fR in the shared variable \fIvarname\fR
to \fIvalue\fR and returns the value to caller\&. The \fIvalue\fR
may be ommited, in which case the command will return the current
value of the element\&. If the element cannot be found, error is triggered\&.
.TP
\fBtsv::get\fR \fIvarname\fR \fIelement\fR ?namedvar?
Retrieves the value of the \fIelement\fR from the shared variable \fIvarname\fR\&.
If the optional argument \fInamedvar\fR is given, the value is
stored in the named variable\&. Return value of the command depends
of the existence of the optional argument \fInamedvar\fR\&.
If the argument is ommited and the requested element cannot be found
in the shared array, the command triggers error\&. If, however, the
optional argument is given on the command line, the command returns
true (1) if the element is found or false (0) if the element is not found\&.
.TP
\fBtsv::unset\fR \fIvarname\fR ?element?
Unsets the \fIelement\fR from the shared variable \fIvarname\fR\&.
If the optional element is not given, it deletes the variable\&.
.TP
\fBtsv::exists\fR \fIvarname\fR \fIelement\fR
Checks wether the \fIelement\fR exists in the shared variable \fIvarname\fR
and returns true (1) if it does or false (0) if it doesn't\&.
.TP
\fBtsv::pop\fR \fIvarname\fR \fIelement\fR
Returns value of the \fIelement\fR in the shared variable \fIvarname\fR
and unsets the element, all in one atomic operation\&.
.TP
\fBtsv::move\fR \fIvarname\fR \fIoldname\fR \fInewname\fR
Renames the element \fIoldname\fR to the \fInewname\fR in the
shared variable \fIvarname\fR\&. This effectively performs an get/unset/set
sequence of operations but all in one atomic step\&.
.TP
\fBtsv::incr\fR \fIvarname\fR \fIelement\fR ?count?
Similar to standard Tcl \fBincr\fR command but increments the value
of the \fIelement\fR in shared variaboe \fIvarname\fR instead of
the Tcl variable\&.
.TP
\fBtsv::append\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value \&.\&.\&.?
Similar to standard Tcl \fBappend\fR command but appends one or more
values to the \fIelement\fR in shared variable \fIvarname\fR instead of the
Tcl variable\&.
.TP
................................................................................
Similar to the standard Tcl \fBlindex\fR command but in addition to
returning, it also splices the value out of the \fIelement\fR
from the shared variable \fIvarname\fR in one atomic operation\&.
In contrast to the Tcl \fBlindex\fR command, this command returns
no value to the caller\&.
.TP
\fBtsv::lpush\fR \fIvarname\fR \fIelement\fR ?index?
This command performes the opposite of the \fBtsv::lpop\fR command\&.
As its counterpart, it returns no value to the caller\&.
.PP
.SH "ARRAY COMMANDS"
This command supports most of the options of the standard Tcl
\fBarray\fR command\&. In addition to those, it allows binding
a shared variable to some persisten storage databases\&. Currently the persistent
options supported are the famous GNU Gdbm and LMDB\&. These options have to be
selected during the package compilation time\&.
The implementation provides hooks for defining other persistency layers, if
needed\&.
.TP
\fBtsv::array set\fR \fIvarname\fR \fIlist\fR
Does the same as standard Tcl \fBarray set\fR\&.
................................................................................
specified for \fIretvar\fR, the value is not returned, allowing the Tcl
programmer to determine if a \fIkey\fR is present in a keyed list without
setting a variable as a side-effect\&.
.TP
\fBtsv::keylkeys\fR \fIvarname\fR \fIkeylist\fR ?key?
Return  the a list of the keys in the keyed list \fIkeylist\fR in the
shared variable \fIvarname\fR\&. If \fIkey\fR is specified, then it is
the name of a key field who's subfield keys are to be retrieved\&.
.TP
\fBtsv::keylset\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR \fIvalue\fR ?key value\&.\&.?
Set the value associated with \fIkey\fR, in the keyed list \fIkeylist\fR
to \fIvalue\fR\&. If the \fIkeylist\fR does not exists, it is created\&.
If \fIkey\fR is not currently in the list, it will be added\&. If it already
exists, \fIvalue\fR replaces the existing value\&. Multiple keywords and
values may be specified, if desired\&.
................................................................................
object data is properly locked and deep-copied when moving objects between
threads\&.
.PP
Due to the internal design of the Tcl core, there is no provision of full
integration of shared variables within the Tcl syntax, unfortunately\&. All
access to shared data must be performed with the supplied package commands\&.
Also, variable traces are not supported\&. But even so, benefits of easy,
simple and safe shared data manipulation outweights imposed limitations\&.
.SH CREDITS
Thread shared variables are inspired by the nsv interface found in
AOLserver, a highly scalable Web server from America Online\&.
.SH "SEE ALSO"
thread, tpool, ttrace
.SH KEYWORDS
locking, synchronization, thread shared data, threads






|












|







 







|







|









|













|







 







|





|







 







|







 







|







347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
...
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
...
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
.sp
.BE
.SH DESCRIPTION
This section describes commands implementing thread shared variables\&.
A thread shared variable is very similar to a Tcl array but in
contrast to a Tcl array it is created in shared memory and can
be accessed from many threads at the same time\&. Important feature of
thread shared variable is that each access to the variable is internally
protected by a mutex so script programmer does not have to take care
about locking the variable himself\&.
.PP
Thread shared variables are not bound to any thread explicitly\&. That
means that when a thread which created any of thread shared variables
exits, the variable and associated memory is not unset/reclaimed\&.
User has to explicitly unset the variable to reclaim the memory
consumed by the variable\&.
.SH "ELEMENT COMMANDS"
.TP
\fBtsv::names\fR ?pattern?
Returns names of shared variables matching optional ?pattern?
or all known variables if pattern is omitted\&.
.TP
\fBtsv::object\fR \fIvarname\fR \fIelement\fR
Creates object accessor command for the \fIelement\fR in the
shared variable \fIvarname\fR\&. Using this command, one can apply most
of the other shared variable commands as method functions of
the element object command\&. The object command is automatically
deleted when the element which this command is pointing to is unset\&.
................................................................................
    => A shared string appended

.CE
.TP
\fBtsv::set\fR \fIvarname\fR \fIelement\fR ?value?
Sets the value of the \fIelement\fR in the shared variable \fIvarname\fR
to \fIvalue\fR and returns the value to caller\&. The \fIvalue\fR
may be omitted, in which case the command will return the current
value of the element\&. If the element cannot be found, error is triggered\&.
.TP
\fBtsv::get\fR \fIvarname\fR \fIelement\fR ?namedvar?
Retrieves the value of the \fIelement\fR from the shared variable \fIvarname\fR\&.
If the optional argument \fInamedvar\fR is given, the value is
stored in the named variable\&. Return value of the command depends
of the existence of the optional argument \fInamedvar\fR\&.
If the argument is omitted and the requested element cannot be found
in the shared array, the command triggers error\&. If, however, the
optional argument is given on the command line, the command returns
true (1) if the element is found or false (0) if the element is not found\&.
.TP
\fBtsv::unset\fR \fIvarname\fR ?element?
Unsets the \fIelement\fR from the shared variable \fIvarname\fR\&.
If the optional element is not given, it deletes the variable\&.
.TP
\fBtsv::exists\fR \fIvarname\fR \fIelement\fR
Checks whether the \fIelement\fR exists in the shared variable \fIvarname\fR
and returns true (1) if it does or false (0) if it doesn't\&.
.TP
\fBtsv::pop\fR \fIvarname\fR \fIelement\fR
Returns value of the \fIelement\fR in the shared variable \fIvarname\fR
and unsets the element, all in one atomic operation\&.
.TP
\fBtsv::move\fR \fIvarname\fR \fIoldname\fR \fInewname\fR
Renames the element \fIoldname\fR to the \fInewname\fR in the
shared variable \fIvarname\fR\&. This effectively performs an get/unset/set
sequence of operations but all in one atomic step\&.
.TP
\fBtsv::incr\fR \fIvarname\fR \fIelement\fR ?count?
Similar to standard Tcl \fBincr\fR command but increments the value
of the \fIelement\fR in shared variable \fIvarname\fR instead of
the Tcl variable\&.
.TP
\fBtsv::append\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value \&.\&.\&.?
Similar to standard Tcl \fBappend\fR command but appends one or more
values to the \fIelement\fR in shared variable \fIvarname\fR instead of the
Tcl variable\&.
.TP
................................................................................
Similar to the standard Tcl \fBlindex\fR command but in addition to
returning, it also splices the value out of the \fIelement\fR
from the shared variable \fIvarname\fR in one atomic operation\&.
In contrast to the Tcl \fBlindex\fR command, this command returns
no value to the caller\&.
.TP
\fBtsv::lpush\fR \fIvarname\fR \fIelement\fR ?index?
This command performs the opposite of the \fBtsv::lpop\fR command\&.
As its counterpart, it returns no value to the caller\&.
.PP
.SH "ARRAY COMMANDS"
This command supports most of the options of the standard Tcl
\fBarray\fR command\&. In addition to those, it allows binding
a shared variable to some persistent storage databases\&. Currently the persistent
options supported are the famous GNU Gdbm and LMDB\&. These options have to be
selected during the package compilation time\&.
The implementation provides hooks for defining other persistency layers, if
needed\&.
.TP
\fBtsv::array set\fR \fIvarname\fR \fIlist\fR
Does the same as standard Tcl \fBarray set\fR\&.
................................................................................
specified for \fIretvar\fR, the value is not returned, allowing the Tcl
programmer to determine if a \fIkey\fR is present in a keyed list without
setting a variable as a side-effect\&.
.TP
\fBtsv::keylkeys\fR \fIvarname\fR \fIkeylist\fR ?key?
Return  the a list of the keys in the keyed list \fIkeylist\fR in the
shared variable \fIvarname\fR\&. If \fIkey\fR is specified, then it is
the name of a key field whose subfield keys are to be retrieved\&.
.TP
\fBtsv::keylset\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR \fIvalue\fR ?key value\&.\&.?
Set the value associated with \fIkey\fR, in the keyed list \fIkeylist\fR
to \fIvalue\fR\&. If the \fIkeylist\fR does not exists, it is created\&.
If \fIkey\fR is not currently in the list, it will be added\&. If it already
exists, \fIvalue\fR replaces the existing value\&. Multiple keywords and
values may be specified, if desired\&.
................................................................................
object data is properly locked and deep-copied when moving objects between
threads\&.
.PP
Due to the internal design of the Tcl core, there is no provision of full
integration of shared variables within the Tcl syntax, unfortunately\&. All
access to shared data must be performed with the supplied package commands\&.
Also, variable traces are not supported\&. But even so, benefits of easy,
simple and safe shared data manipulation outweighs imposed limitations\&.
.SH CREDITS
Thread shared variables are inspired by the nsv interface found in
AOLserver, a highly scalable Web server from America Online\&.
.SH "SEE ALSO"
thread, tpool, ttrace
.SH KEYWORDS
locking, synchronization, thread shared data, threads

Changes to doc/man/ttrace.n.

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
\fBttrace::delentry\fR \fIcmd\fR
.sp
\fBttrace::preload\fR \fIcmd\fR
.sp
.BE
.SH DESCRIPTION
This package creates a framework for on-demand replication of the
interpreter state accross threads in an multithreading application\&.
It relies on the mechanics of Tcl command tracing and the Tcl
\fBunknown\fR command and mechanism\&.
.PP
The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from
America Online\&.
.PP
................................................................................
In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:
.CS



    % package require Ttrace
    2\&.8\&.0

    % set t1 [thread::create {package require Ttrace; thread::wait}]
    tid0x1802800

    % ttrace::eval {proc test args {return test-[thread::id]}}
    % thread::send $t1 test
    test-tid0x1802800
................................................................................
the extension are automatically updated\&. If the command execution
resulted in Tcl error, no state propagation takes place\&.
.sp
This is the most important user-level command of the package as
it wraps most of the commands described below\&. This greatly
simplifies things, because user need to learn just this (one)
command in order to effectively use the package\&. Other commands,
as desribed below, are included mostly for the sake of completeness\&.
.TP
\fBttrace::enable\fR
Activates all registered callbacks in the framework
and starts a new trace epoch\&. The trace epoch encapsulates all
changes done to the interpreter during the time traces are activated\&.
.TP
\fBttrace::disable\fR
................................................................................
.TP
\fBttrace::update\fR ?epoch?
Used to refresh the state of the interpreter to match the optional
trace ?epoch?\&. If the optional ?epoch? is not given, it takes
the most recent trace epoch\&.
.TP
\fBttrace::getscript\fR
Returns a synthetized Tcl script which may be sourced in any interpreter\&.
This script sets the stage for the Tcl \fBunknown\fR command so it can
load traced resources from the in-memory database\&. Normally, this command
is automatically invoked by other higher-level commands like
\fBttrace::eval\fR and \fBttrace::update\fR\&.
.PP
.SH "CALLBACK COMMANDS"
A word upfront: the package already includes callbacks for tracing
following Tcl commands: \fBproc\fR, \fBnamespace\fR, \fBvariable\fR,
\fBload\fR, and \fBrename\fR\&. Additionaly, a set of callbacks for
tracing resources (object, clasess) for the XOTcl v1\&.3\&.8+, an
OO-extension to Tcl, is also provided\&.
This gives a solid base for solving most of the real-life needs and
serves as an example for people wanting to customize the package
to cover their specific needs\&.
.PP
Below, you can find commands for registering callbacks in the
framework and for writing callback scripts\&. These callbacks are
................................................................................
This way, users can update just one interpreter (master) in one
thread and replicate that interpreter state (or part of it) to other
threads/interpreters in the process\&.
.PP
Immediate benefit of such approach is the much smaller memory footprint
of the application and much faster thread creation\&. By not actually
loading all necessary procedures (and other resources) in every thread
at the thread initialization time, but by deffering this to the time the
resource is actually referenced, significant improvements in both
memory consumption and thread initialization time can be achieved\&. Some
tests have shown that memory footprint of an multithreading Tcl application
went down more than three times and thread startup time was reduced for
about 50 times\&. Note that your mileage may vary\&.
Other benefits include much finer control about what (and when) gets
replicated from the master to other Tcl thread/interpreters\&.
.SH "SEE ALSO"
thread, tpool, tsv
.SH KEYWORDS
command tracing, introspection






|







 







|







 







|







 







|








|
|







 







|











310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
\fBttrace::delentry\fR \fIcmd\fR
.sp
\fBttrace::preload\fR \fIcmd\fR
.sp
.BE
.SH DESCRIPTION
This package creates a framework for on-demand replication of the
interpreter state across threads in an multithreading application\&.
It relies on the mechanics of Tcl command tracing and the Tcl
\fBunknown\fR command and mechanism\&.
.PP
The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from
America Online\&.
.PP
................................................................................
In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:
.CS



    % package require Ttrace
    2\&.8\&.2

    % set t1 [thread::create {package require Ttrace; thread::wait}]
    tid0x1802800

    % ttrace::eval {proc test args {return test-[thread::id]}}
    % thread::send $t1 test
    test-tid0x1802800
................................................................................
the extension are automatically updated\&. If the command execution
resulted in Tcl error, no state propagation takes place\&.
.sp
This is the most important user-level command of the package as
it wraps most of the commands described below\&. This greatly
simplifies things, because user need to learn just this (one)
command in order to effectively use the package\&. Other commands,
as described below, are included mostly for the sake of completeness\&.
.TP
\fBttrace::enable\fR
Activates all registered callbacks in the framework
and starts a new trace epoch\&. The trace epoch encapsulates all
changes done to the interpreter during the time traces are activated\&.
.TP
\fBttrace::disable\fR
................................................................................
.TP
\fBttrace::update\fR ?epoch?
Used to refresh the state of the interpreter to match the optional
trace ?epoch?\&. If the optional ?epoch? is not given, it takes
the most recent trace epoch\&.
.TP
\fBttrace::getscript\fR
Returns a synthesized Tcl script which may be sourced in any interpreter\&.
This script sets the stage for the Tcl \fBunknown\fR command so it can
load traced resources from the in-memory database\&. Normally, this command
is automatically invoked by other higher-level commands like
\fBttrace::eval\fR and \fBttrace::update\fR\&.
.PP
.SH "CALLBACK COMMANDS"
A word upfront: the package already includes callbacks for tracing
following Tcl commands: \fBproc\fR, \fBnamespace\fR, \fBvariable\fR,
\fBload\fR, and \fBrename\fR\&. Additionally, a set of callbacks for
tracing resources (object, classes) for the XOTcl v1\&.3\&.8+, an
OO-extension to Tcl, is also provided\&.
This gives a solid base for solving most of the real-life needs and
serves as an example for people wanting to customize the package
to cover their specific needs\&.
.PP
Below, you can find commands for registering callbacks in the
framework and for writing callback scripts\&. These callbacks are
................................................................................
This way, users can update just one interpreter (master) in one
thread and replicate that interpreter state (or part of it) to other
threads/interpreters in the process\&.
.PP
Immediate benefit of such approach is the much smaller memory footprint
of the application and much faster thread creation\&. By not actually
loading all necessary procedures (and other resources) in every thread
at the thread initialization time, but by deferring this to the time the
resource is actually referenced, significant improvements in both
memory consumption and thread initialization time can be achieved\&. Some
tests have shown that memory footprint of an multithreading Tcl application
went down more than three times and thread startup time was reduced for
about 50 times\&. Note that your mileage may vary\&.
Other benefits include much finer control about what (and when) gets
replicated from the master to other Tcl thread/interpreters\&.
.SH "SEE ALSO"
thread, tpool, tsv
.SH KEYWORDS
command tracing, introspection

Changes to doc/thread.man.

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
This command configures various low-level aspects of the thread with
ID [arg id] in the similar way as the standard Tcl command
[cmd fconfigure] configures some Tcl channel options. Options currently
supported are: [option -eventmark] and [option -unwindonerror].

[para]

The [option -eventmark] option, when set, limits the number of
asynchronously posted scripts to the thread event loop.
The [cmd {thread::send -async}] command will block until the number
of pending scripts in the event loop does not drop below the value
configured with [option -eventmark]. Default value for the
[option -eventmark] is 0 (zero) which effectively disables the checking,
i.e. allows for unlimited number of posted scripts.

[para]

The [option -unwindonerror] option, when set, causes the
target thread to unwind if the result of the script processing
resulted in error. Default value for the [option -unwindonerror]
is 0 (false), i.e. thread continues to process scripts after one
of the posted scripts fails.


[call [cmd thread::transfer] [arg id] [arg channel]]

This moves the specified [arg channel] from the current thread
and interpreter to the main interpreter of the thread with the
given [arg id]. After the move the current interpreter has no






|
|
|
|
<
<
<



|
|
|
<
<







324
325
326
327
328
329
330
331
332
333
334



335
336
337
338
339
340


341
342
343
344
345
346
347
This command configures various low-level aspects of the thread with
ID [arg id] in the similar way as the standard Tcl command
[cmd fconfigure] configures some Tcl channel options. Options currently
supported are: [option -eventmark] and [option -unwindonerror].

[para]

When [option -eventmark] is provided with a value greater than 0 (zero), that
value is the maximum number of asynchronously posted scripts that may be
pending for the thread.  [cmd {thread::send -async}] blocks until the number of
pending scripts in the event loop drops below the [option -eventmark] value.




[para]

When [option -unwindonerror] is provided with a value of true, an error result
in a script causes the thread to unwind, making it unavailable to evaluate
additional scripts.




[call [cmd thread::transfer] [arg id] [arg channel]]

This moves the specified [arg channel] from the current thread
and interpreter to the main interpreter of the thread with the
given [arg id]. After the move the current interpreter has no

Changes to generic/threadCmd.c.

859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
...
901
902
903
904
905
906
907
908
909
910
911
912
913



914
915
916
917
918
919
920
...
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
...
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
....
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
....
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
....
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
static int
ThreadSendObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    STRLEN_TYPE objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    size_t len, vlen = 0;
    int ret, ii = 0, flags = 0;
    Tcl_ThreadId thrId;
    const char *script, *arg;
    Tcl_Obj *var = NULL;

    ThreadClbkData *clbkPtr = NULL;
    ThreadSendData *sendPtr = NULL;
................................................................................
        return TCL_ERROR;
    }
    if (++ii >= objc) {
        goto usage;
    }

    script = Tcl_GetString(objv[ii]);
    len = objv[ii]->length;
    if (++ii < objc) {
        var = objv[ii];
        vlen = objv[ii]->length;
    }
    if (var && (flags & THREAD_SEND_WAIT) == 0) {



        if (thrId == Tcl_GetCurrentThread()) {
            /*
             * FIXME: Do something for callbacks to self
             */
            Tcl_SetObjResult(interp, Tcl_NewStringObj("can't notify self", TCL_STRLEN));
            return TCL_ERROR;
        }
................................................................................
         */

        clbkPtr = (ThreadClbkData*)ckalloc(sizeof(ThreadClbkData));
        clbkPtr->execProc   = ThreadClbkSetVar;
        clbkPtr->freeProc   = threadSendFree;
        clbkPtr->interp     = interp;
        clbkPtr->threadId   = Tcl_GetCurrentThread();
        clbkPtr->clientData = (ClientData)strcpy(ckalloc(1+vlen), Tcl_GetString(var));
    }

    /*
     * Prepare job record for the target thread
     */

    sendPtr = (ThreadSendData*)ckalloc(sizeof(ThreadSendData));
    sendPtr->interp     = NULL; /* Signal to use thread main interp */
    sendPtr->execProc   = ThreadSendEval;
    sendPtr->freeProc   = threadSendFree;
    sendPtr->clientData = (ClientData)strcpy(ckalloc(1+len), script);

    ret = ThreadSend(interp, thrId, sendPtr, clbkPtr, flags);

    if (var && (flags & THREAD_SEND_WAIT)) {

        /*
         * Leave job's result in passed variable
................................................................................
ThreadBroadcastObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    STRLEN_TYPE objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    int ii, nthreads;
    size_t len;
    const char *script;
    Tcl_ThreadId *thrIdArray;
    ThreadSendData *sendPtr, job;

    Init(interp);

    if (objc != 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "script");
        return TCL_ERROR;
    }

    script = Tcl_GetString(objv[1]);
    len = objv[1]->length;

    /*
     * Get the list of known threads. Note that this one may
     * actually change (thread may exit or otherwise cease to
     * exist) while we circle in the loop below. We really do
     * not care about that here since we don't return any
     * script results to the caller.
................................................................................

    for (ii = 0; ii < nthreads; ii++) {
        if (thrIdArray[ii] == Tcl_GetCurrentThread()) {
            continue; /* Do not broadcast self */
        }
        sendPtr  = (ThreadSendData*)ckalloc(sizeof(ThreadSendData));
        *sendPtr = job;
        sendPtr->clientData = (ClientData)strcpy(ckalloc(1+len), script);
        ThreadSend(interp, thrIdArray[ii], sendPtr, NULL, THREAD_SEND_HEAD);
    }

    ckfree((char*)thrIdArray);
    Tcl_ResetResult(interp);

    return TCL_OK;
................................................................................

static void
ThreadSetResult(interp, code, resultPtr)
    Tcl_Interp *interp;
    int code;
    ThreadEventResult *resultPtr;
{
    size_t reslen;
    const char *errorCode, *errorInfo, *result;

    if (interp == NULL) {
        code      = TCL_ERROR;
        errorInfo = "";
        errorCode = "THREAD";
        result    = "no target interp!";
        reslen    = strlen(result);
        resultPtr->result = (reslen) ?
            strcpy(ckalloc(1+reslen), result) : threadEmptyResult;
    } else {
        result = Tcl_GetString(Tcl_GetObjResult(interp));
        reslen = Tcl_GetObjResult(interp)->length;
        resultPtr->result = (reslen) ?
            strcpy(ckalloc(1+reslen), result) : threadEmptyResult;
        if (code == TCL_ERROR) {
            errorCode = Tcl_GetVar2(interp, "errorCode", NULL, TCL_GLOBAL_ONLY);
            errorInfo = Tcl_GetVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
        } else {
            errorCode = NULL;
            errorInfo = NULL;
        }
    }

    resultPtr->code = code;

    if (errorCode != NULL) {
        resultPtr->errorCode = ckalloc(1+strlen(errorCode));
        strcpy(resultPtr->errorCode, errorCode);
    } else {
        resultPtr->errorCode = NULL;
    }
    if (errorInfo != NULL) {
        resultPtr->errorInfo = ckalloc(1+strlen(errorInfo));
        strcpy(resultPtr->errorInfo, errorInfo);
    } else {
        resultPtr->errorInfo = NULL;
    }
}
 
/*
 *----------------------------------------------------------------------
................................................................................
            code = TCL_OK; /* Return success. */
        }
    }
    if (resultPtr) {
        Tcl_MutexLock(&threadMutex);
        resultPtr->resultCode = code;
        if (msg != NULL) {
            resultPtr->resultMsg = (char*)ckalloc(1+strlen (msg));
            strcpy (resultPtr->resultMsg, msg);
        }
        Tcl_ConditionNotify(&resultPtr->done);
        Tcl_MutexUnlock(&threadMutex);
    }

    return 1;
}






|







 







|


<


>
>
>







 







|










|







 







|












|







 







|







 







|







|
|
|


|
|
|












|
|




|
|







 







|
|







859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
...
901
902
903
904
905
906
907
908
909
910

911
912
913
914
915
916
917
918
919
920
921
922
...
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
...
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
....
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
....
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
....
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
static int
ThreadSendObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    STRLEN_TYPE objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    size_t size;
    int ret, ii = 0, flags = 0;
    Tcl_ThreadId thrId;
    const char *script, *arg;
    Tcl_Obj *var = NULL;

    ThreadClbkData *clbkPtr = NULL;
    ThreadSendData *sendPtr = NULL;
................................................................................
        return TCL_ERROR;
    }
    if (++ii >= objc) {
        goto usage;
    }

    script = Tcl_GetString(objv[ii]);
    size = objv[ii]->length+1;
    if (++ii < objc) {
        var = objv[ii];

    }
    if (var && (flags & THREAD_SEND_WAIT) == 0) {
        const char *varName = Tcl_GetString(var);
        size_t vsize = var->length + 1;

        if (thrId == Tcl_GetCurrentThread()) {
            /*
             * FIXME: Do something for callbacks to self
             */
            Tcl_SetObjResult(interp, Tcl_NewStringObj("can't notify self", TCL_STRLEN));
            return TCL_ERROR;
        }
................................................................................
         */

        clbkPtr = (ThreadClbkData*)ckalloc(sizeof(ThreadClbkData));
        clbkPtr->execProc   = ThreadClbkSetVar;
        clbkPtr->freeProc   = threadSendFree;
        clbkPtr->interp     = interp;
        clbkPtr->threadId   = Tcl_GetCurrentThread();
        clbkPtr->clientData = (ClientData)memcpy(ckalloc(vsize), varName, vsize);
    }

    /*
     * Prepare job record for the target thread
     */

    sendPtr = (ThreadSendData*)ckalloc(sizeof(ThreadSendData));
    sendPtr->interp     = NULL; /* Signal to use thread main interp */
    sendPtr->execProc   = ThreadSendEval;
    sendPtr->freeProc   = threadSendFree;
    sendPtr->clientData = (ClientData)memcpy(ckalloc(size), script, size);

    ret = ThreadSend(interp, thrId, sendPtr, clbkPtr, flags);

    if (var && (flags & THREAD_SEND_WAIT)) {

        /*
         * Leave job's result in passed variable
................................................................................
ThreadBroadcastObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    STRLEN_TYPE objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    int ii, nthreads;
    size_t size;
    const char *script;
    Tcl_ThreadId *thrIdArray;
    ThreadSendData *sendPtr, job;

    Init(interp);

    if (objc != 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "script");
        return TCL_ERROR;
    }

    script = Tcl_GetString(objv[1]);
    size = objv[1]->length + 1;

    /*
     * Get the list of known threads. Note that this one may
     * actually change (thread may exit or otherwise cease to
     * exist) while we circle in the loop below. We really do
     * not care about that here since we don't return any
     * script results to the caller.
................................................................................

    for (ii = 0; ii < nthreads; ii++) {
        if (thrIdArray[ii] == Tcl_GetCurrentThread()) {
            continue; /* Do not broadcast self */
        }
        sendPtr  = (ThreadSendData*)ckalloc(sizeof(ThreadSendData));
        *sendPtr = job;
        sendPtr->clientData = (ClientData)memcpy(ckalloc(size), script, size);
        ThreadSend(interp, thrIdArray[ii], sendPtr, NULL, THREAD_SEND_HEAD);
    }

    ckfree((char*)thrIdArray);
    Tcl_ResetResult(interp);

    return TCL_OK;
................................................................................

static void
ThreadSetResult(interp, code, resultPtr)
    Tcl_Interp *interp;
    int code;
    ThreadEventResult *resultPtr;
{
    size_t size;
    const char *errorCode, *errorInfo, *result;

    if (interp == NULL) {
        code      = TCL_ERROR;
        errorInfo = "";
        errorCode = "THREAD";
        result    = "no target interp!";
        size    = strlen(result);
        resultPtr->result = (size) ?
            memcpy(ckalloc(1+size), result, 1+size) : threadEmptyResult;
    } else {
        result = Tcl_GetString(Tcl_GetObjResult(interp));
        size = Tcl_GetObjResult(interp)->length;
        resultPtr->result = (size) ?
            memcpy(ckalloc(1+size), result, 1+size) : threadEmptyResult;
        if (code == TCL_ERROR) {
            errorCode = Tcl_GetVar2(interp, "errorCode", NULL, TCL_GLOBAL_ONLY);
            errorInfo = Tcl_GetVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
        } else {
            errorCode = NULL;
            errorInfo = NULL;
        }
    }

    resultPtr->code = code;

    if (errorCode != NULL) {
        size = strlen(errorCode) + 1;
        resultPtr->errorCode = memcpy(ckalloc(size), errorCode, size);
    } else {
        resultPtr->errorCode = NULL;
    }
    if (errorInfo != NULL) {
        size = strlen(errorInfo) + 1;
        resultPtr->errorInfo = memcpy(ckalloc(size), errorInfo, size);
    } else {
        resultPtr->errorInfo = NULL;
    }
}
 
/*
 *----------------------------------------------------------------------
................................................................................
            code = TCL_OK; /* Return success. */
        }
    }
    if (resultPtr) {
        Tcl_MutexLock(&threadMutex);
        resultPtr->resultCode = code;
        if (msg != NULL) {
            size_t size = strlen(msg)+1;
            resultPtr->resultMsg = memcpy(ckalloc(size), msg, size);
        }
        Tcl_ConditionNotify(&resultPtr->done);
        Tcl_MutexUnlock(&threadMutex);
    }

    return 1;
}

Changes to generic/threadSvListCmd.c.

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
....
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
static Tcl_ObjCmdProc SvLindexObjCmd;    /* lindex      */
static Tcl_ObjCmdProc SvLinsertObjCmd;   /* linsert     */
static Tcl_ObjCmdProc SvLrangeObjCmd;    /* lrange      */
static Tcl_ObjCmdProc SvLsearchObjCmd;   /* lsearch     */
static Tcl_ObjCmdProc SvLsetObjCmd;      /* lset        */

/*
 * These two are copied verbatim from the tclUtil.c
 * since not found in the public stubs table.
 * I was just too lazy to rewrite them from scratch.
 */

static int SvCheckBadOctal(Tcl_Interp*, const char *);
static int SvGetIntForIndex(Tcl_Interp*,  Tcl_Obj *, int, int*);

/*
 * Inefficient list duplicator function which,
 * however, produces deep list copies, unlike
 * the original, which just makes shallow copies.
 */
................................................................................

    ckfree((char*)newObjList);
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * SvCheckBadOctal --
 *
 *  Exact copy from the TclCheckBadOctal found in tclUtil.c
 *  since this is not in the stubs table.
 *
 *-----------------------------------------------------------------------------
 */

static int
SvCheckBadOctal(interp, value)
    Tcl_Interp *interp;     /* Interpreter to use for error reporting.
                             * If NULL, then no error message is left
                             * after errors. */
    const char *value;      /* String to check. */
{
    register const char *p = value;

    /*
     * A frequent mistake is invalid octal values due to an unwanted
     * leading zero. Try to generate a meaningful error message.
     */

    while (isspace((unsigned char)(*p))) { /* INTL: ISO space. */
        p++;
    }
    if (*p == '+' || *p == '-') {
        p++;
    }
    if (*p == '0') {
        while (isdigit((unsigned char)(*p))) { /* INTL: digit. */
            p++;
        }
        while (isspace((unsigned char)(*p))) { /* INTL: ISO space. */
            p++;
        }
        if (*p == '\0') {
            /* Reached end of string */
            if (interp != NULL) {
                Tcl_AppendResult(interp, " (looks like invalid octal number)",
                        (char *) NULL);
            }
            return 1;
        }
    }
    return 0;
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * SvGetIntForIndex --
 *
 *  Exact copy from the TclGetIntForIndex found in tclUtil.c
 *  since this is not in the stubs table.
 *
 *-----------------------------------------------------------------------------
 */
................................................................................
        *indexPtr = endValue + offset;
    } else {
  intforindex_error:
        if (interp != NULL) {
            Tcl_ResetResult(interp);
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad index \"",
                    bytes, "\": must be integer or end?-integer?",(char*)NULL);
            SvCheckBadOctal(interp, bytes);
        }
        return TCL_ERROR;
    }
    return TCL_OK;
}
 
/*






|

|


<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<







33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
...
914
915
916
917
918
919
920


















































921
922
923
924
925
926
927
...
965
966
967
968
969
970
971

972
973
974
975
976
977
978
static Tcl_ObjCmdProc SvLindexObjCmd;    /* lindex      */
static Tcl_ObjCmdProc SvLinsertObjCmd;   /* linsert     */
static Tcl_ObjCmdProc SvLrangeObjCmd;    /* lrange      */
static Tcl_ObjCmdProc SvLsearchObjCmd;   /* lsearch     */
static Tcl_ObjCmdProc SvLsetObjCmd;      /* lset        */

/*
 * This is copied verbatim from the tclUtil.c
 * since not found in the public stubs table.
 * I was just too lazy to rewrite it from scratch.
 */


static int SvGetIntForIndex(Tcl_Interp*,  Tcl_Obj *, int, int*);

/*
 * Inefficient list duplicator function which,
 * however, produces deep list copies, unlike
 * the original, which just makes shallow copies.
 */
................................................................................

    ckfree((char*)newObjList);
}
 
/*
 *-----------------------------------------------------------------------------
 *


















































 * SvGetIntForIndex --
 *
 *  Exact copy from the TclGetIntForIndex found in tclUtil.c
 *  since this is not in the stubs table.
 *
 *-----------------------------------------------------------------------------
 */
................................................................................
        *indexPtr = endValue + offset;
    } else {
  intforindex_error:
        if (interp != NULL) {
            Tcl_ResetResult(interp);
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad index \"",
                    bytes, "\": must be integer or end?-integer?",(char*)NULL);

        }
        return TCL_ERROR;
    }
    return TCL_OK;
}
 
/*

Changes to tests/thread.test.

249
250
251
252
253
254
255

256
257
258




259
260
261












262
263
264
265
266
267
268
    set five [thread::send $tid {set x}]
    ThreadReap
    set five
} {5}

test thread-11.7 {thread::send - async send with event-loop wait} {
    ThreadReap

    set tid [thread::create]
    thread::send -async $tid {set x 5} five
    vwait five




    ThreadReap
    set five
} {5}













test thread-11.8 {thread::send - send to self directly} {
    thread::send [thread::id] {set x 5} five
    set five
} {5}

test thread-11.9 {thread::send - send to self asynchronously} {






>



>
>
>
>

|
|
>
>
>
>
>
>
>
>
>
>
>
>







249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
    set five [thread::send $tid {set x}]
    ThreadReap
    set five
} {5}

test thread-11.7 {thread::send - async send with event-loop wait} {
    ThreadReap
    set res {}
    set tid [thread::create]
    thread::send -async $tid {set x 5} five
    vwait five
    lappend res $five; set five {}
    thread::send -async $tid {set x 5} [binary format cccc 0x66 0x69 0x76 0x65]; # five as byte array without str-rep.
    vwait five
    lappend res $five; set five {}
    ThreadReap
    set res
} {5 5}

test thread-11.7.1 {thread::send - sync send with var} {
    ThreadReap
    set res {}
    set tid [thread::create]
    thread::send $tid {set x 5} five
    lappend res $five; set five {}
    thread::send $tid {set x 5} [binary format cccc 0x66 0x69 0x76 0x65]; # five as byte array without str-rep.
    lappend res $five; set five {}
    ThreadReap
    set res
} {5 5}

test thread-11.8 {thread::send - send to self directly} {
    thread::send [thread::id] {set x 5} five
    set five
} {5}

test thread-11.9 {thread::send - send to self asynchronously} {