Tcl package Thread source code

Check-in [832a5fc594]
Login

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

Overview
Comment:Closing an old fork
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk-fork
Files: files | file ages | folders
SHA1: 832a5fc594b616006f0cdb2604a8f6a2d26990f5
User & Date: seandeelywoods 2016-06-28 12:18:31.646
Context
2016-06-28
12:42
Now really close older fork check-in: 3c606ccbc8 user: jan.nijtmans tags: trunk
12:18
Closing an old fork Closed-Leaf check-in: 832a5fc594 user: seandeelywoods tags: trunk-fork
2016-06-03
16:04
[b35544d2c8] Bump version in docs to 2.8 check-in: 58dcd71724 user: gahr tags: trunk
2015-10-15
22:27
Updating to the latest shed check-in: 343442494c user: seandeelywoods tags: trunk-fork
Changes
Unified Diff Ignore Whitespace Patch
Added .fossil-settings/crnl-glob.










>
>
>
>
>
1
2
3
4
5
win/thread-win.dsp
win/thread-win.dsw
win/makefile.vc
win/pkg.vc
win/rules.vc
Added .fossil-settings/encoding-glob.










>
>
>
>
>
1
2
3
4
5
win/thread-win.dsp
win/thread-win.dsw
win/makefile.vc
win/pkg.vc
win/rules.vc
Changes to ChangeLog.






































































1
2
3
4
5
6
7






































































2013-05-23  Jan Nijtmans  <[email protected]>

	* generic/threadSvKeylistCmd.c: Change a few internal variable
	* generic/threadSvListCmd.c:    from type int to type size_t.
	* generic/threadCmd.c:          Simplify determination of whether
	Tcl is compiled with thread support.
	* configure: re-generate with latest TEA.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2016-06-03  Pietro Cerutti <[email protected]>

	* doc/*: Bump version to 2.8 in docs [Tkt b35544d2c8]

2016-06-03  Pietro Cerutti <[email protected]>

	* generic/threadCmd.c: Add parenthesis to bit-shift macros [Tkt 957dbe231c]

2016-05-31  Pietro Cerutti <[email protected]>

	* generic/threadSvCmd.c: Implement [tsv::handlers] command [Tkt 72b8ee4c76]
	* doc/html/tsv.html
	* doc/man/tsv.n
	* doc/tsv.man
	* tests/tsv.test

2016-05-31  Pietro Cerutti <[email protected]>

	* generic/threadCmd.c: Add status arg to [thread::exit] [Tkt 3407860fff]
	* tests/thread.test
	* doc/thread.man
	* doc/man/thread.n
	* doc/html/thread.html

2016-05-18  Pietro Cerutti <[email protected]>

	* generic/threadSvCmd.c: Fix race condition in thread finalization routine
	[Tkt 3532972fff]
	* tests/tkt-84be1b5a73.test: Add a test for [Tkt 84be1b5a73]

2016-05-17  Pietro Cerutti <[email protected]>

	* generic/threadCmd.c: Fix -async and result trace [Tkt 84be1b5a73]
	* doc/thread.man: Remove "id" arg from [thread::broadcast]'s manpage
	* doc/man/thread.n: Regenerate documentation
	* doc/html/thread.html

2016-05-13  Pietro Cerutti <[email protected]>

	* aclocal.m4: Add support for LMDB persistent storage [Tkt 9378bb6795]
	* configure
	* configure.ac
	* doc/html/tsv.html
	* doc/man/tsv.n
	* doc/tsv.man
	* generic/psGdbm.c
	* generic/psLmdb.c
	* generic/psLmdb.h
	* generic/threadSvCmd.c
	* generic/threadSvCmd.h
	* tests/French.txt version
	* tests/store-load.tcl
	* tests/tsv.test

	* generic/tclThreadInt.h: Use spaces for indentation everywhere
	* generic/tclXkeylist.c
	* generic/threadCmd.c
	* generic/threadNs.c
	* generic/threadSpCmd.c
	* generic/threadSpCmd.h
	* generic/threadSvCmd.c

2016-04-20  Pietro Cerutti <[email protected]>

	* configure, aclocal.m4: Correctly handle --without-gdbm [Tkt f8ff429a39]
	* doc/tsv.man: Document side-effect of [tsv::array unbind] [Tkt be135da5f9]
	* doc/*.(html|n): Regenerate documentation [Tkt 41922d3bb7]
	* generic/threadSvCmd.c: Avoid double query to persistent storage in
	tsv::array bind [Tkt a135697d8c]

2013-05-23  Jan Nijtmans  <[email protected]>

	* generic/threadSvKeylistCmd.c: Change a few internal variable
	* generic/threadSvListCmd.c:    from type int to type size_t.
	* generic/threadCmd.c:          Simplify determination of whether
	Tcl is compiled with thread support.
	* configure: re-generate with latest TEA.
Changes to Makefile.in.
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

INCLUDES	= @PKG_INCLUDES@ @TCL_INCLUDES@
#INCLUDES	= @PKG_INCLUDES@ @TCL_INCLUDES@ @TK_INCLUDES@ @TK_XINCLUDES@

PKG_CFLAGS	= @PKG_CFLAGS@

# TCL_DEFS is not strictly need here, but if you remove it, then you
# must make sure that configure.in checks for the necessary components
# that your library may use.  TCL_DEFS can actually be a problem if
# you do not compile with a similar machine setup as the Tcl core was
# compiled with.
#DEFS		= $(TCL_DEFS) @DEFS@ $(PKG_CFLAGS)
DEFS		= @DEFS@ $(PKG_CFLAGS)

# Move pkgIndex.tcl to 'BINARIES' var if it is generated in the Makefile







|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

INCLUDES	= @PKG_INCLUDES@ @TCL_INCLUDES@
#INCLUDES	= @PKG_INCLUDES@ @TCL_INCLUDES@ @TK_INCLUDES@ @TK_XINCLUDES@

PKG_CFLAGS	= @PKG_CFLAGS@

# TCL_DEFS is not strictly need here, but if you remove it, then you
# must make sure that configure.ac checks for the necessary components
# that your library may use.  TCL_DEFS can actually be a problem if
# you do not compile with a similar machine setup as the Tcl core was
# compiled with.
#DEFS		= $(TCL_DEFS) @DEFS@ $(PKG_CFLAGS)
DEFS		= @DEFS@ $(PKG_CFLAGS)

# Move pkgIndex.tcl to 'BINARIES' var if it is generated in the Makefile
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

#========================================================================
# End of user-definable section
#========================================================================

#========================================================================
# Don't modify the file to clean here.  Instead, set the "CLEANFILES"
# variable in configure.in
#========================================================================

clean:
	-test -z "$(BINARIES)" || rm -f $(BINARIES)
	-rm -f *.$(OBJEXT) core *.core
	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)








|







350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

#========================================================================
# End of user-definable section
#========================================================================

#========================================================================
# Don't modify the file to clean here.  Instead, set the "CLEANFILES"
# variable in configure.ac
#========================================================================

clean:
	-test -z "$(BINARIES)" || rm -f $(BINARIES)
	-rm -f *.$(OBJEXT) core *.core
	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)

Changes to aclocal.m4.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#

AC_DEFUN(TCLTHREAD_WITH_GDBM, [
    AC_ARG_WITH(gdbm,
	[  --with-gdbm             link with optional GDBM support],\
	with_gdbm=${withval})

    if test x"${with_gdbm}" != x; then

    AC_MSG_CHECKING([for GNU gdbm library])

    AC_CACHE_VAL(ac_cv_c_gdbm,[
    if test x"${with_gdbm}" != x -a "${with_gdbm}" != "yes"; then
        if test -f "${with_gdbm}/gdbm.h" -a x"`ls ${with_gdbm}/libgdbm* 2>/dev/null`" != x; then
            ac_cv_c_gdbm=`(cd ${with_gdbm}; pwd)`







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#

AC_DEFUN(TCLTHREAD_WITH_GDBM, [
    AC_ARG_WITH(gdbm,
	[  --with-gdbm             link with optional GDBM support],\
	with_gdbm=${withval})

    if test x"${with_gdbm}" != x -a "${with_gdbm}" != no; then

    AC_MSG_CHECKING([for GNU gdbm library])

    AC_CACHE_VAL(ac_cv_c_gdbm,[
    if test x"${with_gdbm}" != x -a "${with_gdbm}" != "yes"; then
        if test -f "${with_gdbm}/gdbm.h" -a x"`ls ${with_gdbm}/libgdbm* 2>/dev/null`" != x; then
            ac_cv_c_gdbm=`(cd ${with_gdbm}; pwd)`
66
67
68
69
70
71
72






























































73
            GDBM_CFLAGS="-I\"$gincdir\""
            GDBM_LIBS="-L\"$glibdir\" -lgdbm"
        fi
    fi
    fi
])































































# EOF







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
            GDBM_CFLAGS="-I\"$gincdir\""
            GDBM_LIBS="-L\"$glibdir\" -lgdbm"
        fi
    fi
    fi
])


#
# Handle the "--with-lmdb" option for linking-in
# the LMDB-based peristent store for shared arrays.
# It tries to locate LMDB files in couple of standard
# system directories and/or common install locations
# in addition to the directory passed by the user.
# In the latter case, expect all LMDB lib files and
# include files located in the same directory.
#

AC_DEFUN(TCLTHREAD_WITH_LMDB, [
    AC_ARG_WITH(lmdb,
	[  --with-lmdb             link with optional LMDB support],
	with_lmdb=${withval})

    if test x"${with_lmdb}" != "x" -a "${with_lmdb}" != no; then
        AC_MSG_CHECKING([for LMDB library])
        AC_CACHE_VAL(ac_cv_c_lmdb,[
        if test x"${with_lmdb}" != x -a "${with_lmdb}" != "yes"; then
            if test -f "${with_lmdb}/lmdb.h" -a x"`ls ${with_lmdb}/liblmdb* 2>/dev/null`" != x; then
                ac_cv_c_lmdb=`(cd ${with_lmdb}; pwd)`
                lincdir=$ac_cv_c_lmdb
                llibdir=$ac_cv_c_lmdb
                AC_MSG_RESULT([found in $llibdir])
            else
                AC_MSG_ERROR([${with_lmdb} directory doesn't contain lmdb library])
            fi
        fi
        ])
        if test x"${lincdir}" = x -o x"${llibdir}" = x; then
            for i in \
                    `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/lib 2>/dev/null` ; do
                if test x"`ls $i/liblmdb* 2>/dev/null`" != x ; then
                    llibdir=`(cd $i; pwd)`
                    break
                fi
            done
            for i in \
                    `ls -d ${prefix}/include 2>/dev/null`\
                    `ls -d /usr/local/include 2>/dev/null`\
                    `ls -d /usr/include 2>/dev/null` ; do
                if test -f "$i/lmdb.h" ; then
                    lincdir=`(cd $i; pwd)`
                    break
                fi
            done
            if test x"$llibdir" = x -o x"$lincdir" = x ; then
                AC_MSG_ERROR([none found])
            else
                AC_MSG_RESULT([found in $llibdir, includes in $lincdir])
                AC_DEFINE(HAVE_LMDB)
                LMDB_CFLAGS="-I\"$lincdir\""
                LMDB_LIBS="-L\"$llibdir\" -llmdb"
            fi
        fi
    fi
])

# EOF
Changes to configure.

more than 10,000 changes

Changes to configure.ac.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash -norc
dnl	This file is an input file used by the GNU "autoconf" program to
dnl	generate the file "configure", which is run during Tcl installation
dnl	to configure the system for the local environment.

#-----------------------------------------------------------------------
# Sample configure.in for Tcl Extensions.  The only places you should
# need to modify this file are marked by the string __CHANGE__
#-----------------------------------------------------------------------

#-----------------------------------------------------------------------
# __CHANGE__
# Set your package name and version numbers here.
#
# This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION
# set as provided.  These will also be added as -D defs in your Makefile
# so you can encode the package version directly into the source files.
#-----------------------------------------------------------------------

AC_INIT([thread], [2.7.2])

#--------------------------------------------------------------------
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
# This will define a ${TEA_PLATFORM} variable == "unix" or "windows"
# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
#--------------------------------------------------------------------







|












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash -norc
dnl	This file is an input file used by the GNU "autoconf" program to
dnl	generate the file "configure", which is run during Tcl installation
dnl	to configure the system for the local environment.

#-----------------------------------------------------------------------
# Sample configure.ac for Tcl Extensions.  The only places you should
# need to modify this file are marked by the string __CHANGE__
#-----------------------------------------------------------------------

#-----------------------------------------------------------------------
# __CHANGE__
# Set your package name and version numbers here.
#
# This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION
# set as provided.  These will also be added as -D defs in your Makefile
# so you can encode the package version directly into the source files.
#-----------------------------------------------------------------------

AC_INIT([thread], [2.8.0])

#--------------------------------------------------------------------
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
# This will define a ${TEA_PLATFORM} variable == "unix" or "windows"
# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
#--------------------------------------------------------------------

70
71
72
73
74
75
76







77
78
79
80
81
82
83

#--------------------------------------------------------------------
# Check if building with optional Gdbm package. This will declare
# GDBM_CFLAGS and GDBM_LIBS variables.
#--------------------------------------------------------------------

TCLTHREAD_WITH_GDBM








#--------------------------------------------------------------------
# Locate the NaviServer/AOLserver dir for compilation as NaviServer/AOLserver module.
# This will declare NS_INCLUDES, NS_LIBS and define NS_AOLSERVER.
#--------------------------------------------------------------------

NS_PATH_AOLSERVER







>
>
>
>
>
>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

#--------------------------------------------------------------------
# Check if building with optional Gdbm package. This will declare
# GDBM_CFLAGS and GDBM_LIBS variables.
#--------------------------------------------------------------------

TCLTHREAD_WITH_GDBM

#--------------------------------------------------------------------
# Check if building with optional lmdb package. This will declare
# LMDB_CFLAGS and LMDB_LIBS variables.
#--------------------------------------------------------------------

TCLTHREAD_WITH_LMDB

#--------------------------------------------------------------------
# Locate the NaviServer/AOLserver dir for compilation as NaviServer/AOLserver module.
# This will declare NS_INCLUDES, NS_LIBS and define NS_AOLSERVER.
#--------------------------------------------------------------------

NS_PATH_AOLSERVER
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

TEA_ADD_SOURCES([generic/threadNs.c           \
                 generic/threadCmd.c          \
                 generic/threadSvCmd.c        \
                 generic/threadSpCmd.c        \
                 generic/threadPoolCmd.c      \
                 generic/psGdbm.c             \

                 generic/threadSvListCmd.c    \
                 generic/threadSvKeylistCmd.c \
                 generic/tclXkeylist.c        \
])

TEA_ADD_HEADERS([generic/tclThread.h])
TEA_ADD_INCLUDES([${NS_INCLUDES}])
TEA_ADD_LIBS([${GDBM_LIBS} ${NS_LIBS}])
TEA_ADD_CFLAGS([${GDBM_CFLAGS}])
TEA_ADD_STUB_SOURCES([])
TEA_ADD_TCL_SOURCES([lib/ttrace.tcl])

#--------------------------------------------------------------------
# __CHANGE__
# A few miscellaneous platform-specific items:
#







>







|
|







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

TEA_ADD_SOURCES([generic/threadNs.c           \
                 generic/threadCmd.c          \
                 generic/threadSvCmd.c        \
                 generic/threadSpCmd.c        \
                 generic/threadPoolCmd.c      \
                 generic/psGdbm.c             \
                 generic/psLmdb.c             \
                 generic/threadSvListCmd.c    \
                 generic/threadSvKeylistCmd.c \
                 generic/tclXkeylist.c        \
])

TEA_ADD_HEADERS([generic/tclThread.h])
TEA_ADD_INCLUDES([${NS_INCLUDES}])
TEA_ADD_LIBS([${GDBM_LIBS} ${LMDB_LIBS} ${NS_LIBS}])
TEA_ADD_CFLAGS([${GDBM_CFLAGS} ${LMDB_CFLAGS}])
TEA_ADD_STUB_SOURCES([])
TEA_ADD_TCL_SOURCES([lib/ttrace.tcl])

#--------------------------------------------------------------------
# __CHANGE__
# A few miscellaneous platform-specific items:
#
Changes to doc/html/thread.html.
1
2
3
4


























































































5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


27

28
29
30


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67


68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243









244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
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
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

723
724
725

726
727
<! -- -*- tcl -*- doctools manpage
   -->
<html><head>
<title>thread - Tcl Threading </title>


























































































</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->


<body>
<h1> thread(n) 2.7  &quot;Tcl Threading&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> thread - Extension for script access to Tcl threading





<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#commands">COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#discussion">DISCUSSION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#see_also">SEE ALSO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#keywords">KEYWORDS</a><br>


<h2><a name="synopsis">SYNOPSIS</a></h2>

<p>
package require <b>Tcl 8.4</b><br>
package require <b>Thread ?2.7?</b><br>


<br><table border=1 width=100% cellspacing=0 cellpadding=0><tr            bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>thread::create</b> ?-joinable? ?-preserved? ?script?</a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>thread::preserve</b> ?id?</a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>thread::release</b> ?-wait? ?id?</a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>thread::id</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>thread::errorproc</b> ?procname?</a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>thread::unwind</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>thread::exit</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>thread::names</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>thread::exists</b> <i class='arg'>id</i></a></td></tr>
<tr valign=top ><td ><a href="#10"><b class='cmd'>thread::send</b> ?-async? ?-head? <i class='arg'>id</i> <i class='arg'>script</i> ?varname?</a></td></tr>
<tr valign=top ><td ><a href="#11"><b class='cmd'>thread::broadcast</b> <i class='arg'>id</i> <i class='arg'>script</i></a></td></tr>
<tr valign=top ><td ><a href="#12"><b class='cmd'>thread::wait</b> </a></td></tr>
<tr valign=top ><td ><a href="#13"><b class='cmd'>thread::eval</b> ?-lock mutex? <i class='arg'>arg</i> ?arg ...?</a></td></tr>
<tr valign=top ><td ><a href="#14"><b class='cmd'>thread::join</b> <i class='arg'>id</i></a></td></tr>
<tr valign=top ><td ><a href="#15"><b class='cmd'>thread::configure</b> <i class='arg'>id</i> ?option? ?value? ?...?</a></td></tr>
<tr valign=top ><td ><a href="#16"><b class='cmd'>thread::transfer</b> <i class='arg'>id</i> <i class='arg'>channel</i></a></td></tr>
<tr valign=top ><td ><a href="#17"><b class='cmd'>thread::detach</b> <i class='arg'>channel</i></a></td></tr>
<tr valign=top ><td ><a href="#18"><b class='cmd'>thread::attach</b> <i class='arg'>channel</i></a></td></tr>
<tr valign=top ><td ><a href="#19"><b class='cmd'>thread::mutex</b> </a></td></tr>
<tr valign=top ><td ><a href="#20"><b class='cmd'>thread::mutex</b> <strong>create</strong> ?-recursive?</a></td></tr>
<tr valign=top ><td ><a href="#21"><b class='cmd'>thread::mutex</b> <strong>destroy</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#22"><b class='cmd'>thread::mutex</b> <strong>lock</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#23"><b class='cmd'>thread::mutex</b> <strong>unlock</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#24"><b class='cmd'>thread::rwmutex</b> </a></td></tr>
<tr valign=top ><td ><a href="#25"><b class='cmd'>thread::rwmutex</b> <strong>create</strong></a></td></tr>
<tr valign=top ><td ><a href="#26"><b class='cmd'>thread::rwmutex</b> <strong>destroy</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#27"><b class='cmd'>thread::rwmutex</b> <strong>rlock</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#28"><b class='cmd'>thread::rwmutex</b> <strong>wlock</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#29"><b class='cmd'>thread::rwmutex</b> <strong>unlock</strong> <i class='arg'>mutex</i></a></td></tr>
<tr valign=top ><td ><a href="#30"><b class='cmd'>thread::cond</b> </a></td></tr>
<tr valign=top ><td ><a href="#31"><b class='cmd'>thread::cond</b> <strong>create</strong></a></td></tr>
<tr valign=top ><td ><a href="#32"><b class='cmd'>thread::cond</b> <strong>destroy</strong> <i class='arg'>cond</i></a></td></tr>
<tr valign=top ><td ><a href="#33"><b class='cmd'>thread::cond</b> <strong>notify</strong> <i class='arg'>cond</i></a></td></tr>
<tr valign=top ><td ><a href="#34"><b class='cmd'>thread::cond</b> <strong>wait</strong> <i class='arg'>cond</i> <i class='arg'>mutex</i> ?ms?</a></td></tr>

</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>


The <strong>thread</strong> 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.

<h2><a name="commands">COMMANDS</a></h2>
<p>
This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation.



<dl>

<dt><a name="1"><b class='cmd'>thread::create</b> ?-joinable? ?-preserved? ?script?</a><dd>


This command creates a thread that contains a Tcl interpreter. 
The Tcl interpreter either evaluates the optional <strong>script</strong>, if
specified, or it waits in the event loop for scripts that arrive via
the <b class='cmd'>thread::send</b> command. The result, if any, of the
optional <strong>script</strong> is never returned to the caller.
The result of <b class='cmd'>thread::create</b> is the ID of the thread. This is
the opaque handle which identifies the newly created thread for
all other package commands. The handle of the thread goes out of scope
automatically when thread is marked for exit
(see the <b class='cmd'>thread::release</b> command below).

<br><br>

If the optional <strong>script</strong> argument contains the <b class='cmd'>thread::wait</b>
command the thread will enter into the event loop. If such command is not
found  in the <strong>script</strong> the thread will run the <strong>script</strong> to 
the end and exit. In that case, the handle may be safely ignored since it 
refers to a thread which does not exists any more at the time when the 
command returns.

<br><br>

Using flag <strong>-joinable</strong> it is possible to create a joinable
thread, i.e. one upon whose exit can be waited upon by using 
<b class='cmd'>thread::join</b> command. 
Note that failure to join a thread created with <strong>-joinable</strong> flag
results in resource and memory leaks. 


<br><br>

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 <strong>script</strong> 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.

<br><br>

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><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    # You should never do ...
    set tid [thread::create {
        package require Http
        thread::wait
        vwait forever ; # &lt;-- this!
    }]
</pre></td></tr></table></p>

The thread created in the above example will never be able to exit.
After it has been released with the last matching <b class='cmd'>thread::release</b>
call, the thread will jump out of the <b class='cmd'>thread::wait</b> and continue 
to execute commands following. It will enter <b class='cmd'>vwait</b> command and 
wait endlessly for events. There is no way one can terminate such thread,
so you wouldn't want to do this!

<br><br>

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 <strong>-preserved</strong> 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. 


<br><br>
<dt><a name="2"><b class='cmd'>thread::preserve</b> ?id?</a><dd>


This command increments the thread reference counter. Each call
to this command increments the reference counter by one (1). 
Command returns the value of the reference counter after the increment. 
If called with the optional thread <strong>id</strong>, the command preserves
the given thread. Otherwise the current thread is preserved.

<br><br>

With reference counting, one can implement controlled access to a 
shared Tcl thread. By incrementing the reference counter, the 
caller signalizes that he/she wishes to use the thread for a longer
period of time. By decrementing the counter, caller signalizes that 
he/she has finished using the thread.

<br><br>
<dt><a name="3"><b class='cmd'>thread::release</b> ?-wait? ?id?</a><dd>


This command decrements the thread reference counter. Each call to 
this command decrements the reference counter by one (1). 
If called with the optional thread <strong>id</strong>, the command releases
the given thread. Otherwise, the current thread is released.
Command returns the value of the reference counter after the decrement.
When the reference counter reaches zero (0), the target thread is 
marked for termination. You should not reference the thread after the
<b class='cmd'>thread::release</b> command returns zero or negative integer. 
The handle of the thread goes out of scope and should not be used any
more. Any following reference to the same thread handle will result 
in Tcl error.

<br><br>

Optional flag <strong>-wait</strong> instructs the caller thread to wait for 
the target thread to exit, if the effect of the command would result 
in termination of the target thread, i.e. if the return result would
be zero (0). Without the flag, the caller thread does not wait for 
the target thread to exit. Care must be taken when using the 
<strong>-wait</strong>, since this may block the caller thread indefinitely.
This option has been implemented for some special uses of the extension
and is deprecated for regular use. Regular users should create joinable
threads by using the <strong>-joinable</strong> option of the <b class='cmd'>thread::create</b>
command and the <b class='cmd'>thread::join</b> to wait for thread to exit. 

<br><br>
<dt><a name="4"><b class='cmd'>thread::id</b> </a><dd>


This command returns the ID of the current thread.

<br><br>
<dt><a name="5"><b class='cmd'>thread::errorproc</b> ?procname?</a><dd>


This command sets a handler for errors that occur in scripts sent 
asynchronously, using the <strong>-async</strong> flag of the 
<b class='cmd'>thread::send</b> command, to other threads. If no handler 
is specified, the current handler is returned. The empty string
resets the handler to default (unspecified) value.
An uncaught error in a thread causes an error message to be sent
to the standard error channel. This default reporting scheme can
be changed by registering a procedure which is called to report
the error. The <i class='arg'>procname</i> is called in the interpreter that
invoked the <b class='cmd'>thread::errorproc</b> command. The <i class='arg'>procname</i>
is called like this:

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    myerrorproc thread_id errorInfo
</pre></td></tr></table></p>

<br><br>
<dt><a name="6"><b class='cmd'>thread::unwind</b> </a><dd>











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.
<br><br>
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.


<br><br>
<dt><a name="7"><b class='cmd'>thread::exit</b> </a><dd>


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.
<br><br>
This command forces a thread stuck in the <b class='cmd'>thread::wait</b>
command to unconditionaly exit. 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.

<br><br>
<dt><a name="8"><b class='cmd'>thread::names</b> </a><dd>


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
are not reported by this command.


<br><br>
<dt><a name="9"><b class='cmd'>thread::exists</b> <i class='arg'>id</i></a><dd>


Returns true (1) if thread given by the <i class='arg'>id</i> parameter exists, 
false (0) otherwise. This applies only for threads that have
been created via <b class='cmd'>thread::create</b> command.


<br><br>
<dt><a name="10"><b class='cmd'>thread::send</b> ?-async? ?-head? <i class='arg'>id</i> <i class='arg'>script</i> ?varname?</a><dd>


This command passes a <i class='arg'>script</i> to another thread and, optionally,
waits for the result. If the <strong>-async</strong> flag is specified, the 
command does not wait for the result and it returns empty string.
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. 

<br><br>

Optional <strong>varname</strong> specifies name of the variable to store
the result of the <i class='arg'>script</i>. Without the <strong>-async</strong> flag,
the command returns the evaluation code, similarily to the standard 
Tcl <b class='cmd'>catch</b> command. If, however, the <strong>-async</strong> flag is
specified, the command returns immediately and caller can later 
<b class='cmd'>vwait</b> on ?varname? to get the result of the passed <i class='arg'>script</i>

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    set t1 [thread::create]
    set t2 [thread::create] 
    thread::send -async $t1 &quot;set a 1&quot; result
    thread::send -async $t2 &quot;set b 2&quot; result 
    for {set i 0} {$i &lt; 2} {incr i} {
        vwait result
    }
</pre></td></tr></table></p>

In the above example, two threads were fed work and both of them were
instructed to signalize the same variable &quot;result&quot; in the calling thread.
The caller entered the event loop twice to get both results. Note, 
however, that the order of the received results may vary, depending on 
the current system load, type of work done, etc, etc.

<br><br>

Many threads can simultaneously send scripts to the target thread for 
execution. All of them are entered into the event queue of the target 
thread and executed on the FIFO basis, intermingled with optional other
events pending in the event queue of the target thread.
Using the optional ?-head? switch, scripts posted to the thread's
event queue can be placed on the head, instead on the tail of the queue,
thus being executed in the LIFO fashion.


<br><br>
<dt><a name="11"><b class='cmd'>thread::broadcast</b> <i class='arg'>id</i> <i class='arg'>script</i></a><dd>


This command passes a <i class='arg'>script</i> to all threads created by the
package for execution. It does not wait for response from any of
the threads.

<br><br>
<dt><a name="12"><b class='cmd'>thread::wait</b> </a><dd>


This enters the event loop so a thread can receive messages from 
the <b class='cmd'>thread::send</b> command. This command should only be used
within the script passed to the <b class='cmd'>thread::create</b>. It should
be the very last command in the script. If this is not the case,
the exiting thread will continue executing the script lines pass
the <b class='cmd'>thread::wait</b> which is usually not what you want and/or
expect.

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    set t1 [thread::create {
        #
        # Do some initialization work here
        #
        thread::wait ; # Enter the event loop
    }]
</pre></td></tr></table></p>

<br><br>
<dt><a name="13"><b class='cmd'>thread::eval</b> ?-lock mutex? <i class='arg'>arg</i> ?arg ...?</a><dd>


This command concatenates passed arguments and evaluates the 
resulting script under the mutex protection. If no mutex is
specified by using the ?-lock mutex? optional argument,
the internal static mutex is used.


<br><br>
<dt><a name="14"><b class='cmd'>thread::join</b> <i class='arg'>id</i></a><dd>


This command waits for the thread with ID <i class='arg'>id</i> to exit and
then returns it's exit code. Errors will be returned for threads
which are not joinable or already waited upon by another thread.
Upon the join the handle of the thread has gone out of scope and
should not be used any more.


<br><br>
<dt><a name="15"><b class='cmd'>thread::configure</b> <i class='arg'>id</i> ?option? ?value? ?...?</a><dd>


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: <strong>-eventmark</strong> and <strong>-unwindonerror</strong>.

<br><br>

The <strong>-eventmark</strong> 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 <strong>-eventmark</strong>. Default value for the 
<strong>-eventmark</strong> is 0 (zero) which effectively disables the checking,
i.e. allows for unlimited number of posted scripts.

<br><br>

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


<br><br>
<dt><a name="16"><b class='cmd'>thread::transfer</b> <i class='arg'>id</i> <i class='arg'>channel</i></a><dd>


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
channel. Because of this it is possible to deadlock the 
participating threads by commanding the other through a 
synchronous <b class='cmd'>thread::send</b> to transfer a channel to us.
This easily extends into longer loops of threads waiting for 
each other. Other restrictions: the channel in question must 
not be shared among multiple interpreters running in the 
sending thread. This automatically excludes the special channels
for standard input, output and error.

<br><br>

Due to the internal Tcl core implementation and the restriction on 
transferring shared channels, one has to take extra measures when
transferring socket channels created by accepting the connection
out of the <b class='cmd'>socket</b> commands callback procedures:

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    socket -server _Accept 2200
    proc _Accept {s ipaddr port} {
        after idle [list Accept $s $ipaddr $port]
    }
    proc Accept {s ipaddr port} {
        set tid [thread::create]
        thread::transfer $tid $s
    }
</pre></td></tr></table></p>

<br><br>
<dt><a name="17"><b class='cmd'>thread::detach</b> <i class='arg'>channel</i></a><dd>


This detaches the specified <i class='arg'>channel</i> from the current thread and 
interpreter. After that, the current interpreter has no access to the
channel any more. The channel is in the parked state until some other
(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.

<br><br>
<dt><a name="18"><b class='cmd'>thread::attach</b> <i class='arg'>channel</i></a><dd>


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.

<br><br>
<dt><a name="19"><b class='cmd'>thread::mutex</b> </a><dd>


Mutexes are most common thread synchronization primitives. 
They are used to synchronize access from two or more threads to one or 
more shared resources. This command provides script-level access to 
exclusive and/or recursive mutexes. Exclusive mutexes can be locked 
only once by one thread, while recursive mutexes can be locked many 
times by the same thread. For recursive mutexes, number of lock and 
unlock operations must match, otherwise, the mutex will never be 
released, which would lead to various deadlock situations.
<br><br>
Care has to be taken when using mutexes in an multithreading program.
Improper use of mutexes may lead to various deadlock situations, 
especially when using exclusive mutexes.

<br><br>

The <b class='cmd'>thread::mutex</b> command supports following subcommands and options:

<br><br>
<dl>

<dt><a name="20"><b class='cmd'>thread::mutex</b> <strong>create</strong> ?-recursive?</a><dd>


Creates the mutex and returns it's opaque handle. This handle
should be used for any future reference to the newly created mutex.
If no optional ?-recursive? argument was specified, the command
creates the exclusive mutex. With the ?-recursive? argument,
the command creates a recursive mutex.

<br><br>
<dt><a name="21"><b class='cmd'>thread::mutex</b> <strong>destroy</strong> <i class='arg'>mutex</i></a><dd>


Destroys the <i class='arg'>mutex</i>. Mutex should be in unlocked state before
the destroy attempt. If the mutex is locked, the command will throw 
Tcl error. 

<br><br>
<dt><a name="22"><b class='cmd'>thread::mutex</b> <strong>lock</strong> <i class='arg'>mutex</i></a><dd>


Locks the <i class='arg'>mutex</i>. Locking the exclusive mutex may throw Tcl 
error if on attempt to lock the same mutex twice from the same
thread. If your program logic forces you to lock the same mutex 
twice or more from the same thread (this may happen in recursive 
procedure invocations) you should consider using the recursive mutexes. 

<br><br>
<dt><a name="23"><b class='cmd'>thread::mutex</b> <strong>unlock</strong> <i class='arg'>mutex</i></a><dd>


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.

</dl>

<br><br>

<dt><a name="24"><b class='cmd'>thread::rwmutex</b> </a><dd>


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. 
<br><br>
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.
Only after the write lock has been released, the resource may be read-locked
again. 

<br><br>

The <b class='cmd'>thread::rwmutex</b> command supports following subcommands and options:

<br><br>
<dl>

<dt><a name="25"><b class='cmd'>thread::rwmutex</b> <strong>create</strong></a><dd>


Creates the reader/writer mutex and returns it's opaque handle.
This handle should be used for any future reference to the newly 
created mutex.

<br><br>
<dt><a name="26"><b class='cmd'>thread::rwmutex</b> <strong>destroy</strong> <i class='arg'>mutex</i></a><dd>


Destroys the reader/writer <i class='arg'>mutex</i>. If the mutex is already locked,
attempt to destroy it will throw Tcl error.

<br><br>
<dt><a name="27"><b class='cmd'>thread::rwmutex</b> <strong>rlock</strong> <i class='arg'>mutex</i></a><dd>


Locks the <i class='arg'>mutex</i> for reading. More than one thread may read-lock
the same <i class='arg'>mutex</i> at the same time.

<br><br>
<dt><a name="28"><b class='cmd'>thread::rwmutex</b> <strong>wlock</strong> <i class='arg'>mutex</i></a><dd>


Locks the <i class='arg'>mutex</i> for writing. Only one thread may write-lock
the same <i class='arg'>mutex</i> at the same time. Attempt to write-lock same
<i class='arg'>mutex</i> twice from the same thread will throw Tcl error.

<br><br>
<dt><a name="29"><b class='cmd'>thread::rwmutex</b> <strong>unlock</strong> <i class='arg'>mutex</i></a><dd>


Unlocks the <i class='arg'>mutex</i> so some other thread may lock it again.
Attempt to unlock already unlocked <i class='arg'>mutex</i> will throw Tcl error.

</dl>

<br><br>

<dt><a name="30"><b class='cmd'>thread::cond</b> </a><dd>


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.

<br><br>

The command supports following subcommands and options:

<br><br>
<dl>

<dt><a name="31"><b class='cmd'>thread::cond</b> <strong>create</strong></a><dd>


Creates the condition variable and returns it's opaque handle.
This handle should be used for any future reference to newly 
created condition variable.

<br><br>
<dt><a name="32"><b class='cmd'>thread::cond</b> <strong>destroy</strong> <i class='arg'>cond</i></a><dd>


Destroys condition variable <i class='arg'>cond</i>. Extreme care has to be taken 
that nobody is using (i.e. waiting on) the condition variable, 
otherwise unexpected errors may happen.

<br><br>
<dt><a name="33"><b class='cmd'>thread::cond</b> <strong>notify</strong> <i class='arg'>cond</i></a><dd>


Wakes up all threads waiting on the condition variable <i class='arg'>cond</i>.

<br><br>
<dt><a name="34"><b class='cmd'>thread::cond</b> <strong>wait</strong> <i class='arg'>cond</i> <i class='arg'>mutex</i> ?ms?</a><dd>


This command is used to suspend program execution until the condition
variable <i class='arg'>cond</i> has been signalled or the optional timer has expired.
The exclusive <i class='arg'>mutex</i> must be locked by the calling thread on entrance
to this command. If the mutex is not locked, Tcl error is thrown.
While waiting on the <i class='arg'>cond</i>, the command releases <i class='arg'>mutex</i>. 
Before returning to the calling thread, the command re-acquires the 
<i class='arg'>mutex</i> again. Unlocking the <i class='arg'>mutex</i> and waiting on the 
condition variable <i class='arg'>cond</i> is done atomically.

<br><br>

The <strong>ms</strong> command option, if given, must be an integer specifying
time interval in milliseconds the command waits to be signalled. 
Otherwise the command waits on condition notify forever.

<br><br>

In multithreading programs, there are many situations where a thread has
to wait for some event to happen until it is allowed to proceed.
This is usually accomplished by repeatedly testing a condition under the
mutex protection and waiting on the condition variable until the condition
evaluates to true:

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    set mutex [thread::mutex create]
    set cond  [thread::cond  create]

    thread::mutex lock $mutex
    while {&lt;some_condition_is_true&gt;} {
        thread::cond wait $cond $mutex
    }
    # Do some work under mutex protection
    thread::mutex unlock $mutex
</pre></td></tr></table></p>

Repeated testing of the condition is needed since the condition variable 
may get signalled without the condition being actually changed (spurious 
thread wake-ups, for example).

</dl>

</dl>

<h2><a name="discussion">DISCUSSION</a></h2>
<p>
The fundamental threading model in Tcl is that there can be one or
more Tcl interpreters per thread, but each Tcl interpreter should
only be used by a single thread which created it.
A &quot;shared memory&quot; abstraction is awkward to provide in Tcl because
Tcl makes assumptions about variable and data ownership. Therefore
this extension supports a simple form of threading where the main
thread can manage several background, or &quot;worker&quot; threads. 
For example, an event-driven server can pass requests to worker 
threads, and then await responses from worker threads or new client
requests. Everything goes through the common Tcl event loop, so 
message passing between threads works naturally with event-driven I/O, 
<b class='cmd'>vwait</b> on variables, and so forth. For the transfer of bulk
information it is possible to move channels between the threads.

<p>

For advanced multithreading scripts, script-level access to two
basic synchronization primitives, mutex and condition variables,
is also supported.





<h2><a name="see_also">SEE ALSO</a></h2>
<p>
<a href="http://www.tcl.tk/doc/howto/thread_model.html">http://www.tcl.tk/doc/howto/thread_model.html</a>, tpool, tsv, ttrace

<h2><a name="keywords">KEYWORDS</a></h2>
<p>
events, message passing, mutex, synchronization, thread

</body></html>

|
<

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



|
>
|
|
|
<
|
|
|
|
|
|
<
<
|
<
|
|
|
|
>
>
|
>
|
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
<
|
>
>
|


<

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

|
|
|



|
|
<
<
<

|


|
|
<
<
<

|
|
|
<
<
<
<
|

|



|

|
<
<
<
|

|


|
|
<






|
<
|
|
|
|

|
<
<
<
|

|



|
|

|
|
<
<
<
<
<
|


|
|
<
<
<
|



|
|
<
<
<
<
|

|




|


|
<
<
<
|




|


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





|
|
|
|
<

|
|
<
|
|
|
>
>
>
>
>
>
>
>
>
|
|
|
|
<
|

|


|
|
<
<
<
<
<
|
|
|
|
<
|
|
>
|
|
|
|
<
|
<
<
|
<
|
|

|
<
<
<
|
<
<
|

|
|
<
<
<
<
<
|
|




|
<
|
<
<
|
|

|

|
|
<







|
<
|



|
<
<
<
|



|

|
|
<
<
<
<
<
|

|
|
<
<
<
<
|
|
|

|
|
|
|
<






|
|
<
<
|
<
|

|
|
|
<
<
<
<
<
|



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

|

|
|
|
|
<
<
<

|

|
|
<
<
<
<
<
|

|





|




|
<
<
<
|


|
|
<








|
|
<
<
|
<
|


|

|
|
<
<
<
<
|




|

|
|
<
<
<
<
|






|
<
|

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

|
|
|
|
<
<
<
<
|

|
|
<
<
<
<
|



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



|
<
|






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

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





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

|
|
<
<
<
<
|

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

|

|
|
<
<
<
|

|
<
<
<
|



|
|
<


<






|
<
|

|
<
|
<

|
|
<
|










|
|
<
<
<
|

|
|
|
<
<
<
<
<
|
>
|
<
|
>
|

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107


108

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163

164
165
166
167

168
169
170
171






172
173
174
175
176
177
178
179
180
181
182



183
184
185
186
187
188



189
190
191
192




193
194
195
196
197
198
199
200
201



202
203
204
205
206
207
208

209
210
211
212
213
214
215

216
217
218
219
220
221



222
223
224
225
226
227
228
229
230
231
232





233
234
235
236
237



238
239
240
241
242
243




244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298

299
300
301
302
303
304
305





306
307
308
309

310
311
312
313
314
315
316

317


318

319
320
321
322



323


324
325
326
327





328
329
330
331
332
333
334

335


336
337
338
339
340
341
342

343
344
345
346
347
348
349
350

351
352
353
354
355



356
357
358
359
360
361
362
363





364
365
366
367




368
369
370
371
372
373
374
375

376
377
378
379
380
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
428
429
430
431

432
433
434
435
436
437
438
439
440
441


442

443
444
445
446
447
448
449




450
451
452
453
454
455
456
457
458




459
460
461
462
463
464
465
466

467
468
469



470
471


472



473
474
475
476
477
478




479
480
481
482




483
484
485
486
487
488




489
490

491
492





493
494
495
496
497

498
499
500
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
535


536



537
538
539
540




541
542
543
544




545
546




547
548
549
550
551
552
553
554



555
556
557



558
559
560
561
562
563

564
565

566
567
568
569
570
571
572

573
574
575

576

577
578
579

580
581
582
583
584
585
586
587
588
589
590
591
592



593
594
595
596
597





598
599
600

601
602
603
604


<html><head>
<title>thread - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    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 class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>


<li class="doctools_section"><a href="#section1">Description</a></li>

<li class="doctools_section"><a href="#section2">COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">DISCUSSION</a></li>
<li class="doctools_section"><a href="#see-also">See Also</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.4</b></li>
<li>package require <b class="pkgname">Thread <span class="opt">?2.8?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">thread::create</b> <span class="opt">?-joinable?</span> <span class="opt">?-preserved?</span> <span class="opt">?script?</span></a></li>
<li><a href="#2"><b class="cmd">thread::preserve</b> <span class="opt">?id?</span></a></li>
<li><a href="#3"><b class="cmd">thread::release</b> <span class="opt">?-wait?</span> <span class="opt">?id?</span></a></li>
<li><a href="#4"><b class="cmd">thread::id</b></a></li>
<li><a href="#5"><b class="cmd">thread::errorproc</b> <span class="opt">?procname?</span></a></li>
<li><a href="#6"><b class="cmd">thread::cancel</b> <span class="opt">?-unwind?</span> <i class="arg">id</i> <span class="opt">?result?</span></a></li>
<li><a href="#7"><b class="cmd">thread::unwind</b></a></li>
<li><a href="#8"><b class="cmd">thread::exit</b> <span class="opt">?status?</span></a></li>
<li><a href="#9"><b class="cmd">thread::names</b></a></li>
<li><a href="#10"><b class="cmd">thread::exists</b> <i class="arg">id</i></a></li>
<li><a href="#11"><b class="cmd">thread::send</b> <span class="opt">?-async?</span> <span class="opt">?-head?</span> <i class="arg">id</i> <i class="arg">script</i> <span class="opt">?varname?</span></a></li>
<li><a href="#12"><b class="cmd">thread::broadcast</b> <i class="arg">script</i></a></li>
<li><a href="#13"><b class="cmd">thread::wait</b></a></li>
<li><a href="#14"><b class="cmd">thread::eval</b> <span class="opt">?-lock mutex?</span> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></li>
<li><a href="#15"><b class="cmd">thread::join</b> <i class="arg">id</i></a></li>
<li><a href="#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></li>
<li><a href="#17"><b class="cmd">thread::transfer</b> <i class="arg">id</i> <i class="arg">channel</i></a></li>
<li><a href="#18"><b class="cmd">thread::detach</b> <i class="arg">channel</i></a></li>
<li><a href="#19"><b class="cmd">thread::attach</b> <i class="arg">channel</i></a></li>
<li><a href="#20"><b class="cmd">thread::mutex</b></a></li>
<li><a href="#21"><b class="cmd">thread::mutex</b> <b class="method">create</b> <span class="opt">?-recursive?</span></a></li>
<li><a href="#22"><b class="cmd">thread::mutex</b> <b class="method">destroy</b> <i class="arg">mutex</i></a></li>
<li><a href="#23"><b class="cmd">thread::mutex</b> <b class="method">lock</b> <i class="arg">mutex</i></a></li>
<li><a href="#24"><b class="cmd">thread::mutex</b> <b class="method">unlock</b> <i class="arg">mutex</i></a></li>
<li><a href="#25"><b class="cmd">thread::rwmutex</b></a></li>
<li><a href="#26"><b class="cmd">thread::rwmutex</b> <b class="method">create</b></a></li>
<li><a href="#27"><b class="cmd">thread::rwmutex</b> <b class="method">destroy</b> <i class="arg">mutex</i></a></li>
<li><a href="#28"><b class="cmd">thread::rwmutex</b> <b class="method">rlock</b> <i class="arg">mutex</i></a></li>
<li><a href="#29"><b class="cmd">thread::rwmutex</b> <b class="method">wlock</b> <i class="arg">mutex</i></a></li>
<li><a href="#30"><b class="cmd">thread::rwmutex</b> <b class="method">unlock</b> <i class="arg">mutex</i></a></li>
<li><a href="#31"><b class="cmd">thread::cond</b></a></li>
<li><a href="#32"><b class="cmd">thread::cond</b> <b class="method">create</b></a></li>
<li><a href="#33"><b class="cmd">thread::cond</b> <b class="method">destroy</b> <i class="arg">cond</i></a></li>
<li><a href="#34"><b class="cmd">thread::cond</b> <b class="method">notify</b> <i class="arg">cond</i></a></li>
<li><a href="#35"><b class="cmd">thread::cond</b> <b class="method">wait</b> <i class="arg">cond</i> <i class="arg">mutex</i> <span class="opt">?ms?</span></a></li>
</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>






<dd><p>This command creates a thread that contains a Tcl interpreter. 
The Tcl interpreter either evaluates the optional <b class="option">script</b>, if
specified, or it waits in the event loop for scripts that arrive via
the <b class="cmd">thread::send</b> command. The result, if any, of the
optional <b class="option">script</b> is never returned to the caller.
The result of <b class="cmd">thread::create</b> is the ID of the thread. This is
the opaque handle which identifies the newly created thread for
all other package commands. The handle of the thread goes out of scope
automatically when thread is marked for exit
(see the <b class="cmd">thread::release</b> command below).</p>
<p>If the optional <b class="option">script</b> argument contains the <b class="cmd">thread::wait</b>



command the thread will enter into the event loop. If such command is not
found  in the <b class="option">script</b> the thread will run the <b class="option">script</b> to 
the end and exit. In that case, the handle may be safely ignored since it 
refers to a thread which does not exists any more at the time when the 
command returns.</p>
<p>Using flag <b class="option">-joinable</b> it is possible to create a joinable



thread, i.e. one upon whose exit can be waited upon by using 
<b class="cmd">thread::join</b> command. 
Note that failure to join a thread created with <b class="option">-joinable</b> flag
results in resource and memory leaks.</p>




<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>
<pre class="doctools_example">

    # You should never do ...
    set tid [thread::create {
        package require Http
        thread::wait
        vwait forever ; # &lt;-- this!
    }]
</pre>

<p>The thread created in the above example will never be able to exit.
After it has been released with the last matching <b class="cmd">thread::release</b>
call, the thread will jump out of the <b class="cmd">thread::wait</b> and continue 
to execute commands following. It will enter <b class="cmd">vwait</b> command and 
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). 
Command returns the value of the reference counter after the increment. 
If called with the optional thread <b class="option">id</b>, the command preserves
the given thread. Otherwise the current thread is preserved.</p>



<p>With reference counting, one can implement controlled access to a 
shared Tcl thread. By incrementing the reference counter, the 
caller signalizes that he/she wishes to use the thread for a longer
period of time. By decrementing the counter, caller signalizes that 
he/she has finished using the thread.</p></dd>
<dt><a name="3"><b class="cmd">thread::release</b> <span class="opt">?-wait?</span> <span class="opt">?id?</span></a></dt>




<dd><p>This command decrements the thread reference counter. Each call to 
this command decrements the reference counter by one (1). 
If called with the optional thread <b class="option">id</b>, the command releases
the given thread. Otherwise, the current thread is released.
Command returns the value of the reference counter after the decrement.
When the reference counter reaches zero (0), the target thread is 
marked for termination. You should not reference the thread after the
<b class="cmd">thread::release</b> command returns zero or negative integer. 
The handle of the thread goes out of scope and should not be used any
more. Any following reference to the same thread handle will result 
in Tcl error.</p>



<p>Optional flag <b class="option">-wait</b> instructs the caller thread to wait for 
the target thread to exit, if the effect of the command would result 
in termination of the target thread, i.e. if the return result would
be zero (0). Without the flag, the caller thread does not wait for 
the target thread to exit. Care must be taken when using the 
<b class="option">-wait</b>, since this may block the caller thread indefinitely.
This option has been implemented for some special uses of the extension
and is deprecated for regular use. Regular users should create joinable
threads by using the <b class="option">-joinable</b> option of the <b class="cmd">thread::create</b>
command and the <b class="cmd">thread::join</b> to wait for thread to exit.</p></dd>


<dt><a name="4"><b class="cmd">thread::id</b></a></dt>


<dd><p>This command returns the ID of the current thread.</p></dd>
<dt><a name="5"><b class="cmd">thread::errorproc</b> <span class="opt">?procname?</span></a></dt>




<dd><p>This command sets a handler for errors that occur in scripts sent 
asynchronously, using the <b class="option">-async</b> flag of the 
<b class="cmd">thread::send</b> command, to other threads. If no handler 
is specified, the current handler is returned. The empty string
resets the handler to default (unspecified) value.
An uncaught error in a thread causes an error message to be sent
to the standard error channel. This default reporting scheme can
be changed by registering a procedure which is called to report
the error. The <i class="arg">procname</i> is called in the interpreter that
invoked the <b class="cmd">thread::errorproc</b> command. The <i class="arg">procname</i>
is called like this:</p>
<pre class="doctools_example">

    myerrorproc thread_id errorInfo
</pre>
</dd>

<dt><a name="6"><b class="cmd">thread::cancel</b> <span class="opt">?-unwind?</span> <i class="arg">id</i> <span class="opt">?result?</span></a></dt>
<dd><p>This command requires Tcl version 8.6 or higher.</p>
<p>Cancels the script being evaluated in the thread given by the <i class="arg">id</i>
parameter. Without the <b class="option">-unwind</b> switch the evaluation stack for
the interpreter is unwound until an enclosing catch command is found or
there are no further invocations of the interpreter left on the call
stack. With the <b class="option">-unwind</b> switch the evaluation stack for the
interpreter is unwound without regard to any intervening catch command
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
are not reported by this command.</p></dd>



<dt><a name="10"><b class="cmd">thread::exists</b> <i class="arg">id</i></a></dt>


<dd><p>Returns true (1) if thread given by the <i class="arg">id</i> parameter exists, 
false (0) otherwise. This applies only for threads that have
been created via <b class="cmd">thread::create</b> command.</p></dd>
<dt><a name="11"><b class="cmd">thread::send</b> <span class="opt">?-async?</span> <span class="opt">?-head?</span> <i class="arg">id</i> <i class="arg">script</i> <span class="opt">?varname?</span></a></dt>





<dd><p>This command passes a <i class="arg">script</i> to another thread and, optionally,
waits for the result. If the <b class="option">-async</b> flag is specified, the 
command does not wait for the result and it returns empty string.
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
    thread::send -async $t2 &quot;set b 2&quot; result 
    for {set i 0} {$i &lt; 2} {incr i} {
        vwait result
    }
</pre>

<p>In the above example, two threads were fed work and both of them were
instructed to signalize the same variable &quot;result&quot; in the calling thread.
The caller entered the event loop twice to get both results. Note, 
however, that the order of the received results may vary, depending on 
the current system load, type of work done, etc, etc.</p>



<p>Many threads can simultaneously send scripts to the target thread for 
execution. All of them are entered into the event queue of the target 
thread and executed on the FIFO basis, intermingled with optional other
events pending in the event queue of the target thread.
Using the optional <span class="opt">?-head?</span> switch, scripts posted to the thread's
event queue can be placed on the head, instead on the tail of the queue,
thus being executed in the LIFO fashion.</p></dd>
<dt><a name="12"><b class="cmd">thread::broadcast</b> <i class="arg">script</i></a></dt>





<dd><p>This command passes a <i class="arg">script</i> to all threads created by the
package for execution. It does not wait for response from any of
the threads.</p></dd>
<dt><a name="13"><b class="cmd">thread::wait</b></a></dt>




<dd><p>This enters the event loop so a thread can receive messages from 
the <b class="cmd">thread::send</b> command. This command should only be used
within the script passed to the <b class="cmd">thread::create</b>. It should
be the very last command in the script. If this is not the case,
the exiting thread will continue executing the script lines past
the <b class="cmd">thread::wait</b> which is usually not what you want and/or
expect.</p>
<pre class="doctools_example">

    set t1 [thread::create {
        #
        # Do some initialization work here
        #
        thread::wait ; # Enter the event loop
    }]
</pre>
</dd>


<dt><a name="14"><b class="cmd">thread::eval</b> <span class="opt">?-lock mutex?</span> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></dt>

<dd><p>This command concatenates passed arguments and evaluates the 
resulting script under the mutex protection. If no mutex is
specified by using the <span class="opt">?-lock mutex?</span> optional argument,
the internal static mutex is used.</p></dd>
<dt><a name="15"><b class="cmd">thread::join</b> <i class="arg">id</i></a></dt>





<dd><p>This command waits for the thread with ID <i class="arg">id</i> to exit and
then returns it's exit code. Errors will be returned for threads
which are not joinable or already waited upon by another thread.
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
channel. Because of this it is possible to deadlock the 
participating threads by commanding the other through a 
synchronous <b class="cmd">thread::send</b> to transfer a channel to us.
This easily extends into longer loops of threads waiting for 
each other. Other restrictions: the channel in question must 
not be shared among multiple interpreters running in the 
sending thread. This automatically excludes the special channels
for standard input, output and error.</p>



<p>Due to the internal Tcl core implementation and the restriction on 
transferring shared channels, one has to take extra measures when
transferring socket channels created by accepting the connection
out of the <b class="cmd">socket</b> commands callback procedures:</p>
<pre class="doctools_example">

    socket -server _Accept 2200
    proc _Accept {s ipaddr port} {
        after idle [list Accept $s $ipaddr $port]
    }
    proc Accept {s ipaddr port} {
        set tid [thread::create]
        thread::transfer $tid $s
    }
</pre>
</dd>


<dt><a name="18"><b class="cmd">thread::detach</b> <i class="arg">channel</i></a></dt>

<dd><p>This detaches the specified <i class="arg">channel</i> from the current thread and 
interpreter. After that, the current interpreter has no access to the
channel any more. The channel is in the parked state until some other
(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 
more shared resources. This command provides script-level access to 
exclusive and/or recursive mutexes. Exclusive mutexes can be locked 
only once by one thread, while recursive mutexes can be locked many 
times by the same thread. For recursive mutexes, number of lock and 
unlock operations must match, otherwise, the mutex will never be 
released, which would lead to various deadlock situations.</p>

<p>Care has to be taken when using mutexes in an multithreading program.
Improper use of mutexes may lead to various deadlock situations, 
especially when using exclusive mutexes.</p>



<p>The <b class="cmd">thread::mutex</b> command supports following subcommands and options:</p>
<dl class="doctools_definitions">


<dt><a name="21"><b class="cmd">thread::mutex</b> <b class="method">create</b> <span class="opt">?-recursive?</span></a></dt>



<dd><p>Creates the mutex and returns it's opaque handle. This handle
should be used for any future reference to the newly created mutex.
If no optional <span class="opt">?-recursive?</span> argument was specified, the command
creates the exclusive mutex. With the <span class="opt">?-recursive?</span> argument,
the command creates a recursive mutex.</p></dd>
<dt><a name="22"><b class="cmd">thread::mutex</b> <b class="method">destroy</b> <i class="arg">mutex</i></a></dt>




<dd><p>Destroys the <i class="arg">mutex</i>. Mutex should be in unlocked state before
the destroy attempt. If the mutex is locked, the command will throw 
Tcl error.</p></dd>
<dt><a name="23"><b class="cmd">thread::mutex</b> <b class="method">lock</b> <i class="arg">mutex</i></a></dt>




<dd><p>Locks the <i class="arg">mutex</i>. Locking the exclusive mutex may throw Tcl 
error if on attempt to lock the same mutex twice from the same
thread. If your program logic forces you to lock the same mutex 
twice or more from the same thread (this may happen in recursive 
procedure invocations) you should consider using the recursive mutexes.</p></dd>
<dt><a name="24"><b class="cmd">thread::mutex</b> <b class="method">unlock</b> <i class="arg">mutex</i></a></dt>




<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.
Only after the write lock has been released, the resource may be read-locked
again.</p>



<p>The <b class="cmd">thread::rwmutex</b> command supports following subcommands and options:</p>
<dl class="doctools_definitions">


<dt><a name="26"><b class="cmd">thread::rwmutex</b> <b class="method">create</b></a></dt>



<dd><p>Creates the reader/writer mutex and returns it's opaque handle.
This handle should be used for any future reference to the newly 
created mutex.</p></dd>
<dt><a name="27"><b class="cmd">thread::rwmutex</b> <b class="method">destroy</b> <i class="arg">mutex</i></a></dt>




<dd><p>Destroys the reader/writer <i class="arg">mutex</i>. If the mutex is already locked,
attempt to destroy it will throw Tcl error.</p></dd>
<dt><a name="28"><b class="cmd">thread::rwmutex</b> <b class="method">rlock</b> <i class="arg">mutex</i></a></dt>




<dd><p>Locks the <i class="arg">mutex</i> for reading. More than one thread may read-lock
the same <i class="arg">mutex</i> at the same time.</p></dd>
<dt><a name="29"><b class="cmd">thread::rwmutex</b> <b class="method">wlock</b> <i class="arg">mutex</i></a></dt>




<dd><p>Locks the <i class="arg">mutex</i> for writing. Only one thread may write-lock
the same <i class="arg">mutex</i> at the same time. Attempt to write-lock same
<i class="arg">mutex</i> twice from the same thread will throw Tcl error.</p></dd>
<dt><a name="30"><b class="cmd">thread::rwmutex</b> <b class="method">unlock</b> <i class="arg">mutex</i></a></dt>




<dd><p>Unlocks the <i class="arg">mutex</i> so some other thread may lock it again.
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>
<dt><a name="33"><b class="cmd">thread::cond</b> <b class="method">destroy</b> <i class="arg">cond</i></a></dt>




<dd><p>Destroys condition variable <i class="arg">cond</i>. Extreme care has to be taken 
that nobody is using (i.e. waiting on) the condition variable, 
otherwise unexpected errors may happen.</p></dd>
<dt><a name="34"><b class="cmd">thread::cond</b> <b class="method">notify</b> <i class="arg">cond</i></a></dt>




<dd><p>Wakes up all threads waiting on the condition variable <i class="arg">cond</i>.</p></dd>
<dt><a name="35"><b class="cmd">thread::cond</b> <b class="method">wait</b> <i class="arg">cond</i> <i class="arg">mutex</i> <span class="opt">?ms?</span></a></dt>




<dd><p>This command is used to suspend program execution until the condition
variable <i class="arg">cond</i> has been signalled or the optional timer has expired.
The exclusive <i class="arg">mutex</i> must be locked by the calling thread on entrance
to this command. If the mutex is not locked, Tcl error is thrown.
While waiting on the <i class="arg">cond</i>, the command releases <i class="arg">mutex</i>. 
Before returning to the calling thread, the command re-acquires the 
<i class="arg">mutex</i> again. Unlocking the <i class="arg">mutex</i> and waiting on the 
condition variable <i class="arg">cond</i> is done atomically.</p>



<p>The <b class="option">ms</b> command option, if given, must be an integer specifying
time interval in milliseconds the command waits to be signalled. 
Otherwise the command waits on condition notify forever.</p>



<p>In multithreading programs, there are many situations where a thread has
to wait for some event to happen until it is allowed to proceed.
This is usually accomplished by repeatedly testing a condition under the
mutex protection and waiting on the condition variable until the condition
evaluates to true:</p>
<pre class="doctools_example">

    set mutex [thread::mutex create]
    set cond  [thread::cond  create]

    thread::mutex lock $mutex
    while {&lt;some_condition_is_true&gt;} {
        thread::cond wait $cond $mutex
    }
    # Do some work under mutex protection
    thread::mutex unlock $mutex
</pre>

<p>Repeated testing of the condition is needed since the condition variable 
may get signalled without the condition being actually changed (spurious 
thread wake-ups, for example).</p></dd>

</dl></dd>

</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">DISCUSSION</a></h2>

<p>The fundamental threading model in Tcl is that there can be one or
more Tcl interpreters per thread, but each Tcl interpreter should
only be used by a single thread which created it.
A &quot;shared memory&quot; abstraction is awkward to provide in Tcl because
Tcl makes assumptions about variable and data ownership. Therefore
this extension supports a simple form of threading where the main
thread can manage several background, or &quot;worker&quot; threads. 
For example, an event-driven server can pass requests to worker 
threads, and then await responses from worker threads or new client
requests. Everything goes through the common Tcl event loop, so 
message passing between threads works naturally with event-driven I/O, 
<b class="cmd">vwait</b> on variables, and so forth. For the transfer of bulk
information it is possible to move channels between the threads.</p>



<p>For advanced multithreading scripts, script-level access to two
basic synchronization primitives, mutex and condition variables,
is also supported.</p>
</div>
<div id="see-also" class="doctools_section"><h2><a name="see-also">See Also</a></h2>





<p><a href="http://www.tcl.tk/doc/howto/thread_model.html">http://www.tcl.tk/doc/howto/thread_model.html</a>, tpool, tsv, ttrace</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>

<p>events, message passing, mutex, synchronization, thread</p>
</div>
</div></body></html>

Changes to doc/html/tpool.html.
1
2
3
4


























































































5
6
7
8

9
10
11
12
13
14
15
16
17
18
19









20
21
22
23
24
25
26
27
28
29
30
31
32


33
34
35
36
37
38
39
40


41
42
43


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233













234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

269
270
271

272
273
<! -- -*- tcl -*- doctools manpage
   -->
<html><head>
<title>tpool - Tcl Threading </title>


























































































</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->


<body>
<h1> tpool(n) 2.7  &quot;Tcl Threading&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> tpool - 
    Part of the Tcl threading extension implementing pools of worker threads.















<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#commands">COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#discussion">DISCUSSION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#see_also">SEE ALSO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#keywords">KEYWORDS</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
package require <b>Tcl 8.4</b><br>
package require <b>Thread ?2.7?</b><br>


<br><table border=1 width=100% cellspacing=0 cellpadding=0><tr            bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>tpool::create</b> ?options?</a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>tpool::names</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>tpool::post</b> ?-detached? ?-nowait? <i class='arg'>tpool</i> <i class='arg'>script</i></a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>tpool::wait</b> <i class='arg'>tpool</i> <i class='arg'>joblist</i> ?varname?</a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>tpool::cancel</b> <i class='arg'>tpool</i> <i class='arg'>joblist</i> ?varname?</a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>tpool::get</b> <i class='arg'>tpool</i> <i class='arg'>job</i></a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>tpool::preserve</b> <i class='arg'>tpool</i></a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>tpool::release</b> <i class='arg'>tpool</i></a></td></tr>


</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>


This package creates and manages pools of worker threads. It allows you
to post jobs to worker threads and wait for their completion. The 
threadpool implementation is Tcl event-loop aware. That means that any
time a caller is forced to wait for an event (job being completed or 
a worker thread becoming idle or initialized), the implementation will
enter the event loop and allow for servicing of other pending file or
timer (or any other supported) events.

<h2><a name="commands">COMMANDS</a></h2>
<p>

<dl>

<dt><a name="1"><b class='cmd'>tpool::create</b> ?options?</a><dd>


This command creates new threadpool. It accepts several options as
key-value pairs. Options are used to tune some threadpool parameters.
The command returns the ID of the newly created threadpool.
<br><br>
Following options are supported:

<br><br>
<dl>

<dt>-minworkers number<dd>
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.

<br><br>
<dt>-maxworkers number<dd>
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,
new worker thread will start. The caller will automatically enter the
event loop and wait until the worker thread has initialized. If. however,
the number of available worker threads is equal to the given number, 
the caller will enter the event loop and wait for the first worker thread
to get idle, thus ready to run the job.
Default value of this parameter is 4 (four), which means that the 
threadpool instance will allow maximum of 4 worker threads running jobs
or being idle waiting for new jobs to get posted to the job queue.


<br><br>
<dt>-idletime seconds<dd>
Time in seconds an idle worker thread waits for the job to get posted
to the job queue. If no job arrives during this interval and the time
expires, the worker thread will check the number of currently available
worker threads and if the number is higher than the number set by the
<strong>minthreads</strong> option, it will exit. 
If an <strong>exitscript</strong> has been defined, the exiting worker thread 
will first run the script and then exit. Errors from the exit script, 
if any, are ignored.
<br><br>
The idle worker thread is not servicing the event loop. If you, however,
put the worker thread into the event loop, by evaluating the
<b class='cmd'>vwait</b> or other related Tcl commands, the worker thread 
will not be in the idle state, hence the idle timer will not be 
taken into account.
Default value for this option is unspecified.

<br><br>
<dt>-initcmd script<dd>

Sets a Tcl script used to initialize new worker thread. This is usually
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.

<br><br>
<dt>-exitcmd script<dd>

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.

</dl>

<br><br> 

<dt><a name="2"><b class='cmd'>tpool::names</b> </a><dd>


This command returns a list of IDs of threadpools created with the 
<b class='cmd'>tpool::create</b> command. If no threadpools were found, the
command will return empty list.

<br><br>
<dt><a name="3"><b class='cmd'>tpool::post</b> ?-detached? ?-nowait? <i class='arg'>tpool</i> <i class='arg'>script</i></a><dd>


This command sends a <i class='arg'>script</i> to the target <i class='arg'>tpool</i> threadpool
for execution. The script will be executed in the first available idle 
worker thread. If there are no idle worker threads available, the command
will create new one, enter the event loop and service events until the 
newly created thread is initialized. If the current number of worker 
threads is equal to the maximum number of worker threads, as defined 
during the threadpool creation, the command will enter the event loop and
service events while waiting for one of the worker threads to become idle.
If the optional ?-nowait? argument is given, the command will not wait
for one idle worker. It will just place the job in the pool's job queue
and return immediately.
<br><br>
The command returns the ID of the posted job. This ID is used for subsequent
<b class='cmd'>tpool::wait</b>, <b class='cmd'>tpool::get</b> and <b class='cmd'>tpool::cancel</b> commands to wait
for and retrieve result of the posted script, or cancel the posted job
respectively. If the optional ?-detached? argument is specified, the 
command will post a detached job. A detached job can not be cancelled or 
waited upon and is not identified by the job ID.
<br><br>
If the threadpool <i class='arg'>tpool</i> is not found in the list of active
thread pools, the command will throw error. The error will also be triggered
if the newly created worker thread fails to initialize.

<br><br>
<dt><a name="4"><b class='cmd'>tpool::wait</b> <i class='arg'>tpool</i> <i class='arg'>joblist</i> ?varname?</a><dd>


This command waits for one or many jobs, whose job IDs are given in the
<i class='arg'>joblist</i> to get processed by the worker thread(s). If none of the
specified jobs are ready, the command will enter the event loop, service
events and wait for the first job to get ready.
<br><br>
The command returns the list of completed job IDs. If the optional variable
?varname? is given, it will be set to the list of jobs in the 
<i class='arg'>joblist</i> which are still pending. If the threadpool <i class='arg'>tpool</i> 
is not found in the list of active thread pools, the command will throw error.

<br><br>
<dt><a name="5"><b class='cmd'>tpool::cancel</b> <i class='arg'>tpool</i> <i class='arg'>joblist</i> ?varname?</a><dd>


This command cancels the previously posted jobs given by the <i class='arg'>joblist</i>
to the pool <i class='arg'>tpool</i>. Job cancellation succeeds only for job still
waiting to be processed. If the job is already being executed by one of
the worker threads, the job will not be cancelled.
The command returns the list of cancelled job IDs. If the optional variable
?varname? is given, it will be set to the list of jobs in the 
<i class='arg'>joblist</i> which were not cancelled. If the threadpool <i class='arg'>tpool</i> 
is not found in the list of active thread pools, the command will throw error.

<br><br>
<dt><a name="6"><b class='cmd'>tpool::get</b> <i class='arg'>tpool</i> <i class='arg'>job</i></a><dd>


This command retrieves the result of the previously posted <i class='arg'>job</i>.
Only results of jobs waited upon with the <b class='cmd'>tpool::wait</b> command
can be retrieved. If the execution of the script resulted in error, 
the command will throw the error and update the <strong>errorInfo</strong> and
<strong>errorCode</strong> variables correspondingly. If the pool <i class='arg'>tpool</i>
is not found in the list of threadpools, the command will throw error.
If the job <i class='arg'>job</i> is not ready for retrieval, because it is currently
being executed by the worker thread, the command will throw error.

<br><br>
<dt><a name="7"><b class='cmd'>tpool::preserve</b> <i class='arg'>tpool</i></a><dd>


Each call to this command increments the reference counter of the
threadpool <i class='arg'>tpool</i> by one (1). Command returns the value of the 
reference counter after the increment.
By incrementing the reference counter, the caller signalizes that
he/she wishes to use the resource for a longer period of time.

<br><br>
<dt><a name="8"><b class='cmd'>tpool::release</b> <i class='arg'>tpool</i></a><dd>


Each call to this command decrements the reference counter of the 
threadpool <i class='arg'>tpool</i> by one (1).Command returns the value of the 
reference counter after the decrement. 
When the reference counter reaches zero (0), the threadpool <i class='arg'>tpool</i>
is marked for termination. You should not reference the threadpool 
after the <b class='cmd'>tpool::release</b> command returns zero. The <i class='arg'>tpool</i>
handle goes out of scope and should not be used any more. Any following
reference to the same threadpool handle will result in Tcl error.














</dl>


<h2><a name="discussion">DISCUSSION</a></h2>
<p>

Threadpool is one of the most common threading paradigm when it comes
to server applications handling a large number of relatively small tasks.
A very simplistic model for building a server application would be to 
create a new thread each time a request arrives and service the request 
in the new thread. One of the disadvantages of this approach is that 
the overhead of creating a new thread for each request is significant; 
a server that created a new thread for each request would spend more time
and consume more system resources in creating and destroying threads than
in processing actual user requests. In addition to the overhead of 
creating and destroying threads, active threads consume system resources.
Creating too many threads can cause the system to run out of memory or
trash due to excessive memory consumption.
<p>
A thread pool offers a solution to both the problem of thread life-cycle 
overhead and the problem of resource trashing. By reusing threads for 
multiple tasks, the thread-creation overhead is spread over many tasks.
As a bonus, because the thread already exists when a request arrives, 
the delay introduced by thread creation is eliminated. Thus, the request
can be serviced immediately. Furthermore, by properly tuning the number 
of threads in the thread pool, resource thrashing may also be eliminated
by forcing any request to wait until a thread is available to process it.





<h2><a name="see_also">SEE ALSO</a></h2>
<p>
thread, tsv, ttrace

<h2><a name="keywords">KEYWORDS</a></h2>
<p>
thread, threadpool

</body></html>

|
<

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



|
>
|
|
|
<
<
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
<
<
<
|
|
>
>
|
|
|
|
|
|
|
|
>
>
|
<
|
>
>
|





|
|
<
<
|
<
|
<
|
<
|

|
<
|
|
<
<
|
<
|





|
|
|
<
<
|










|
|
<
<
<
|



|
|

|
<
|

|


|
|
<
<
<
|



|

|
|
<
<
<
|



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







|

|
<
|
|

|

|
<
|

|
|
<
<
<
<
|
|

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



|
|
|
|
<
<
<
<
|
|

|
|

|
|
<
<
|
<
<
|
|


|
|
<
<
<
<
|
|

|

|

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

|
|
<
<
<
|










|
<
|






|
|
|
<
<
<
<
<
|
>
|
<
|
>
|

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117










118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141
142
143


144

145

146

147
148
149

150
151


152

153
154
155
156
157
158
159
160
161


162
163
164
165
166
167
168
169
170
171
172
173
174



175
176
177
178
179
180
181
182

183
184
185
186
187
188
189



190
191
192
193
194
195
196
197



198
199
200
201
202

203



204


205
206
207
208


209


210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225

226
227
228
229




230
231
232
233

234
235
236
237
238




239
240
241
242
243
244
245
246
247




248
249
250
251
252
253
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
286
287



288
289
290
291
292
293
294
295
296
297
298
299

300
301
302
303
304
305
306
307
308
309





310
311
312

313
314
315
316


<html><head>
<title>tpool - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    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>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">DISCUSSION</a></li>
<li class="doctools_section"><a href="#see-also">See Also</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">










<li>package require <b class="pkgname">Tcl 8.4</b></li>
<li>package require <b class="pkgname">Thread <span class="opt">?2.8?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">tpool::create</b> <span class="opt">?options?</span></a></li>
<li><a href="#2"><b class="cmd">tpool::names</b></a></li>
<li><a href="#3"><b class="cmd">tpool::post</b> <span class="opt">?-detached?</span> <span class="opt">?-nowait?</span> <i class="arg">tpool</i> <i class="arg">script</i></a></li>
<li><a href="#4"><b class="cmd">tpool::wait</b> <i class="arg">tpool</i> <i class="arg">joblist</i> <span class="opt">?varname?</span></a></li>
<li><a href="#5"><b class="cmd">tpool::cancel</b> <i class="arg">tpool</i> <i class="arg">joblist</i> <span class="opt">?varname?</span></a></li>
<li><a href="#6"><b class="cmd">tpool::get</b> <i class="arg">tpool</i> <i class="arg">job</i></a></li>
<li><a href="#7"><b class="cmd">tpool::preserve</b> <i class="arg">tpool</i></a></li>
<li><a href="#8"><b class="cmd">tpool::release</b> <i class="arg">tpool</i></a></li>
<li><a href="#9"><b class="cmd">tpool::suspend</b> <i class="arg">tpool</i></a></li>
<li><a href="#10"><b class="cmd">tpool::resume</b> <i class="arg">tpool</i></a></li>
</ul>

</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package creates and manages pools of worker threads. It allows you
to post jobs to worker threads and wait for their completion. The 
threadpool implementation is Tcl event-loop aware. That means that any
time a caller is forced to wait for an event (job being completed or 
a worker thread becoming idle or initialized), the implementation will
enter the event loop and allow for servicing of other pending file or
timer (or any other supported) events.</p>
</div>


<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>

<dl class="doctools_definitions">

<dt><a name="1"><b class="cmd">tpool::create</b> <span class="opt">?options?</span></a></dt>

<dd><p>This command creates new threadpool. It accepts several options as
key-value pairs. Options are used to tune some threadpool parameters.
The command returns the ID of the newly created threadpool.</p>

<p>Following options are supported:</p>
<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,
new worker thread will start. The caller will automatically enter the
event loop and wait until the worker thread has initialized. If. however,
the number of available worker threads is equal to the given number, 
the caller will enter the event loop and wait for the first worker thread
to get idle, thus ready to run the job.
Default value of this parameter is 4 (four), which means that the 
threadpool instance will allow maximum of 4 worker threads running jobs
or being idle waiting for new jobs to get posted to the job queue.</p></dd>
<dt><b class="option">-idletime</b> <i class="arg">seconds</i></dt>



<dd><p>Time in seconds an idle worker thread waits for the job to get posted
to the job queue. If no job arrives during this interval and the time
expires, the worker thread will check the number of currently available
worker threads and if the number is higher than the number set by the
<b class="option">minthreads</b> option, it will exit. 
If an <b class="option">exitscript</b> has been defined, the exiting worker thread 
will first run the script and then exit. Errors from the exit script, 
if any, are ignored.</p>

<p>The idle worker thread is not servicing the event loop. If you, however,
put the worker thread into the event loop, by evaluating the
<b class="cmd">vwait</b> or other related Tcl commands, the worker thread 
will not be in the idle state, hence the idle timer will not be 
taken into account.
Default value for this option is unspecified.</p></dd>
<dt><b class="option">-initcmd</b> <i class="arg">script</i></dt>



<dd><p>Sets a Tcl script used to initialize new worker thread. This is usually
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 
<b class="cmd">tpool::create</b> command. If no threadpools were found, the
command will return empty list.</p></dd>
<dt><a name="3"><b class="cmd">tpool::post</b> <span class="opt">?-detached?</span> <span class="opt">?-nowait?</span> <i class="arg">tpool</i> <i class="arg">script</i></a></dt>


<dd><p>This command sends a <i class="arg">script</i> to the target <i class="arg">tpool</i> threadpool


for execution. The script will be executed in the first available idle 
worker thread. If there are no idle worker threads available, the command
will create new one, enter the event loop and service events until the 
newly created thread is initialized. If the current number of worker 
threads is equal to the maximum number of worker threads, as defined 
during the threadpool creation, the command will enter the event loop and
service events while waiting for one of the worker threads to become idle.
If the optional <span class="opt">?-nowait?</span> argument is given, the command will not wait
for one idle worker. It will just place the job in the pool's job queue
and return immediately.</p>

<p>The command returns the ID of the posted job. This ID is used for subsequent
<b class="cmd">tpool::wait</b>, <b class="cmd">tpool::get</b> and <b class="cmd">tpool::cancel</b> commands to wait
for and retrieve result of the posted script, or cancel the posted job
respectively. If the optional <span class="opt">?-detached?</span> argument is specified, the 
command will post a detached job. A detached job can not be cancelled or 
waited upon and is not identified by the job ID.</p>

<p>If the threadpool <i class="arg">tpool</i> is not found in the list of active
thread pools, the command will throw error. The error will also be triggered
if the newly created worker thread fails to initialize.</p></dd>
<dt><a name="4"><b class="cmd">tpool::wait</b> <i class="arg">tpool</i> <i class="arg">joblist</i> <span class="opt">?varname?</span></a></dt>




<dd><p>This command waits for one or many jobs, whose job IDs are given in the
<i class="arg">joblist</i> to get processed by the worker thread(s). If none of the
specified jobs are ready, the command will enter the event loop, service
events and wait for the first job to get ready.</p>

<p>The command returns the list of completed job IDs. If the optional variable
<span class="opt">?varname?</span> is given, it will be set to the list of jobs in the 
<i class="arg">joblist</i> which are still pending. If the threadpool <i class="arg">tpool</i> 
is not found in the list of active thread pools, the command will throw error.</p></dd>
<dt><a name="5"><b class="cmd">tpool::cancel</b> <i class="arg">tpool</i> <i class="arg">joblist</i> <span class="opt">?varname?</span></a></dt>




<dd><p>This command cancels the previously posted jobs given by the <i class="arg">joblist</i>
to the pool <i class="arg">tpool</i>. Job cancellation succeeds only for job still
waiting to be processed. If the job is already being executed by one of
the worker threads, the job will not be cancelled.
The command returns the list of cancelled job IDs. If the optional variable
<span class="opt">?varname?</span> is given, it will be set to the list of jobs in the 
<i class="arg">joblist</i> which were not cancelled. If the threadpool <i class="arg">tpool</i> 
is not found in the list of active thread pools, the command will throw error.</p></dd>
<dt><a name="6"><b class="cmd">tpool::get</b> <i class="arg">tpool</i> <i class="arg">job</i></a></dt>




<dd><p>This command retrieves the result of the previously posted <i class="arg">job</i>.
Only results of jobs waited upon with the <b class="cmd">tpool::wait</b> command
can be retrieved. If the execution of the script resulted in error, 
the command will throw the error and update the <b class="variable">errorInfo</b> and
<b class="variable">errorCode</b> variables correspondingly. If the pool <i class="arg">tpool</i>
is not found in the list of threadpools, the command will throw error.
If the job <i class="arg">job</i> is not ready for retrieval, because it is currently
being executed by the worker thread, the command will throw error.</p></dd>


<dt><a name="7"><b class="cmd">tpool::preserve</b> <i class="arg">tpool</i></a></dt>


<dd><p>Each call to this command increments the reference counter of the
threadpool <i class="arg">tpool</i> by one (1). Command returns the value of the 
reference counter after the increment.
By incrementing the reference counter, the caller signalizes that
he/she wishes to use the resource for a longer period of time.</p></dd>
<dt><a name="8"><b class="cmd">tpool::release</b> <i class="arg">tpool</i></a></dt>




<dd><p>Each call to this command decrements the reference counter of the 
threadpool <i class="arg">tpool</i> by one (1).Command returns the value of the 
reference counter after the decrement. 
When the reference counter reaches zero (0), the threadpool <i class="arg">tpool</i>
is marked for termination. You should not reference the threadpool 
after the <b class="cmd">tpool::release</b> command returns zero. The <i class="arg">tpool</i>
handle goes out of scope and should not be used any more. Any following
reference to the same threadpool handle will result in Tcl error.</p></dd>
<dt><a name="9"><b class="cmd">tpool::suspend</b> <i class="arg">tpool</i></a></dt>
<dd><p>Suspends processing work on this queue. All pool workers are paused
but additional work can be added to the pool. Note that adding the
additional work will not increase the number of workers dynamically
as the pool processing is suspended. Number of workers is maintained
to the count that was found prior suspending worker activity.
If you need to assure certain number of worker threads, use the 
<b class="option">minworkers</b> option of the <b class="cmd">tpool::create</b> command.</p></dd>
<dt><a name="10"><b class="cmd">tpool::resume</b> <i class="arg">tpool</i></a></dt>
<dd><p>Resume processing work on this queue. All paused (suspended) 
workers are free to get work from the pool. Note that resuming pool
operation will just let already created workers to proceed. 
It will not create additional worker threads to handle the work 
posted to the pool's work queue.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">DISCUSSION</a></h2>



<p>Threadpool is one of the most common threading paradigm when it comes
to server applications handling a large number of relatively small tasks.
A very simplistic model for building a server application would be to 
create a new thread each time a request arrives and service the request 
in the new thread. One of the disadvantages of this approach is that 
the overhead of creating a new thread for each request is significant; 
a server that created a new thread for each request would spend more time
and consume more system resources in creating and destroying threads than
in processing actual user requests. In addition to the overhead of 
creating and destroying threads, active threads consume system resources.
Creating too many threads can cause the system to run out of memory or
trash due to excessive memory consumption.</p>

<p>A thread pool offers a solution to both the problem of thread life-cycle 
overhead and the problem of resource trashing. By reusing threads for 
multiple tasks, the thread-creation overhead is spread over many tasks.
As a bonus, because the thread already exists when a request arrives, 
the delay introduced by thread creation is eliminated. Thus, the request
can be serviced immediately. Furthermore, by properly tuning the number 
of threads in the thread pool, resource thrashing may also be eliminated
by forcing any request to wait until a thread is available to process it.</p>
</div>
<div id="see-also" class="doctools_section"><h2><a name="see-also">See Also</a></h2>





<p>thread, tsv, ttrace</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>

<p>thread, threadpool</p>
</div>
</div></body></html>

Changes to doc/html/tsv.html.
1
2
3
4


























































































5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


34
35


36
37


38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466

467
468
469

470
471
<! -- -*- tcl -*- doctools manpage
   -->
<html><head>
<title>tsv - Tcl Threading </title>


























































































</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->


<body>
<h1> tsv(n) 2.7  &quot;Tcl Threading&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> tsv - 
    Part of the Tcl threading extension allowing script level
    manipulation of data shared between threads.






<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#element_commands">ELEMENT COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#list_commands">LIST COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#array_commands">ARRAY COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#keyed_list_commands">KEYED LIST COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#discussion">DISCUSSION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#credits">CREDITS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#see_also">SEE ALSO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#keywords">KEYWORDS</a><br>


<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>


package require <b>Tcl 8.4</b><br>
package require <b>Thread ?2.7?</b><br>


<br><table border=1 width=100% cellspacing=0 cellpadding=0><tr            bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>tsv::names</b> ?pattern?</a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>tsv::object</b> <i class='arg'>varname</i> <i class='arg'>element</i></a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>tsv::set</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?value?</a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>tsv::get</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?namedvar?</a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>tsv::unset</b> <i class='arg'>varname</i> ?element?</a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>tsv::exists</b> <i class='arg'>varname</i> <i class='arg'>element</i></a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>tsv::pop</b> <i class='arg'>varname</i> <i class='arg'>element</i></a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>tsv::move</b> <i class='arg'>varname</i> <i class='arg'>oldname</i> <i class='arg'>newname</i></a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>tsv::incr</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?count?</a></td></tr>
<tr valign=top ><td ><a href="#10"><b class='cmd'>tsv::append</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>value</i> ?value ...?</a></td></tr>
<tr valign=top ><td ><a href="#11"><b class='cmd'>tsv::lock</b> <i class='arg'>varname</i> <i class='arg'>arg</i> ?arg ...?</a></td></tr>
<tr valign=top ><td ><a href="#12"><b class='cmd'>tsv::lappend</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>value</i> ?value ...?</a></td></tr>
<tr valign=top ><td ><a href="#13"><b class='cmd'>tsv::linsert</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>index</i> <i class='arg'>value</i> ?value ...?</a></td></tr>

<tr valign=top ><td ><a href="#14"><b class='cmd'>tsv::lreplace</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>first</i> <i class='arg'>last</i> ?value ...?</a></td></tr>
<tr valign=top ><td ><a href="#15"><b class='cmd'>tsv::llength</b> <i class='arg'>varname</i> <i class='arg'>element</i></a></td></tr>
<tr valign=top ><td ><a href="#16"><b class='cmd'>tsv::lindex</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?index?</a></td></tr>
<tr valign=top ><td ><a href="#17"><b class='cmd'>tsv::lrange</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>from</i> <i class='arg'>to</i></a></td></tr>
<tr valign=top ><td ><a href="#18"><b class='cmd'>tsv::lsearch</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?options? <i class='arg'>pattern</i></a></td></tr>
<tr valign=top ><td ><a href="#19"><b class='cmd'>tsv::lset</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>index</i> ?index ...? <i class='arg'>value</i></a></td></tr>
<tr valign=top ><td ><a href="#20"><b class='cmd'>tsv::lpop</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?index?</a></td></tr>
<tr valign=top ><td ><a href="#21"><b class='cmd'>tsv::lpush</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?index?</a></td></tr>
<tr valign=top ><td ><a href="#22"><b class='cmd'>tsv::array set</b> <i class='arg'>varname</i> <i class='arg'>list</i></a></td></tr>
<tr valign=top ><td ><a href="#23"><b class='cmd'>tsv::array get</b> <i class='arg'>varname</i> ?pattern?</a></td></tr>
<tr valign=top ><td ><a href="#24"><b class='cmd'>tsv::array names</b> <i class='arg'>varname</i> ?pattern?</a></td></tr>
<tr valign=top ><td ><a href="#25"><b class='cmd'>tsv::array size</b> <i class='arg'>varname</i></a></td></tr>
<tr valign=top ><td ><a href="#26"><b class='cmd'>tsv::array reset</b> <i class='arg'>varname</i> <i class='arg'>list</i></a></td></tr>
<tr valign=top ><td ><a href="#27"><b class='cmd'>tsv::array bind</b> <i class='arg'>varname</i> <i class='arg'>handle</i></a></td></tr>
<tr valign=top ><td ><a href="#28"><b class='cmd'>tsv::array unbind</b> <i class='arg'>varname</i></a></td></tr>
<tr valign=top ><td ><a href="#29"><b class='cmd'>tsv::array isbound</b> <i class='arg'>varname</i></a></td></tr>
<tr valign=top ><td ><a href="#30"><b class='cmd'>tsv::keyldel</b> <i class='arg'>varname</i> <i class='arg'>keylist</i> <i class='arg'>key</i></a></td></tr>
<tr valign=top ><td ><a href="#31"><b class='cmd'>tsv::keylget</b> <i class='arg'>varname</i> <i class='arg'>keylist</i> <i class='arg'>key</i> ?retvar?</a></td></tr>
<tr valign=top ><td ><a href="#32"><b class='cmd'>tsv::keylkeys</b> <i class='arg'>varname</i> <i class='arg'>keylist</i> ?key?</a></td></tr>
<tr valign=top ><td ><a href="#33"><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> ?key value..?</a></td></tr>
</table></td></tr></table>
<h2><a name="description">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>
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.

<h2><a name="element_commands">ELEMENT COMMANDS</a></h2>
<p>

<dl>

<dt><a name="1"><b class='cmd'>tsv::names</b> ?pattern?</a><dd>


Returns names of shared variables matching optional ?pattern? 
or all known variables if pattern is ommited.

<br><br>
<dt><a name="2"><b class='cmd'>tsv::object</b> <i class='arg'>varname</i> <i class='arg'>element</i></a><dd>


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><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    % tsv::set foo bar &quot;A shared string&quot;
    % set string [tsv::object foo bar]
    % $string append &quot; appended&quot;
    =&gt; A shared string appended
</pre></td></tr></table></p>

<br><br>
<dt><a name="3"><b class='cmd'>tsv::set</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?value?</a><dd>


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.

<br><br>
<dt><a name="4"><b class='cmd'>tsv::get</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?namedvar?</a><dd>


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.

<br><br>
<dt><a name="5"><b class='cmd'>tsv::unset</b> <i class='arg'>varname</i> ?element?</a><dd>


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.

<br><br>
<dt><a name="6"><b class='cmd'>tsv::exists</b> <i class='arg'>varname</i> <i class='arg'>element</i></a><dd>


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.

<br><br>
<dt><a name="7"><b class='cmd'>tsv::pop</b> <i class='arg'>varname</i> <i class='arg'>element</i></a><dd>


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.

<br><br>
<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><dd>


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.

<br><br>
<dt><a name="9"><b class='cmd'>tsv::incr</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?count?</a><dd>


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.

<br><br>
<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> ?value ...?</a><dd>


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.

<br><br>
<dt><a name="11"><b class='cmd'>tsv::lock</b> <i class='arg'>varname</i> <i class='arg'>arg</i> ?arg ...?</a><dd>


This command concatenates passed arguments and evaluates the
resulting script under the internal mutex protection. During the
script evaluation, the entire shared variable is locked. For shared
variable commands within the script, internal locking is disabled
so no deadlock can occur. It is also allowed to unset the shared
variable from within the script. The shared variable is automatically
created if it did not exists at the time of the first lock operation.

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    % tsv::lock foo {
        tsv::lappend foo bar 1
        tsv::lappend foo bar 2
        puts stderr [tsv::set foo bar]
        tsv::unset foo
    }
</pre></td></tr></table></p>




</dl>

<h2><a name="list_commands">LIST COMMANDS</a></h2>
<p>

Those command are similar to the equivalently named Tcl command. The difference
is that they operate on elements of shared arrays.

<dl>

<dt><a name="12"><b class='cmd'>tsv::lappend</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>value</i> ?value ...?</a><dd>


Similar to standard Tcl <b class='cmd'>lappend</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.

<br><br>
<dt><a name="13"><b class='cmd'>tsv::linsert</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>index</i> <i class='arg'>value</i> ?value ...?</a><dd>


Similar to standard Tcl <b class='cmd'>linsert</b> command but inserts one
or more values at the <i class='arg'>index</i> list position in the 
<i class='arg'>element</i> in the shared variable <i class='arg'>varname</i> instead of the Tcl variable.

<br><br>
<dt><a name="14"><b class='cmd'>tsv::lreplace</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>first</i> <i class='arg'>last</i> ?value ...?</a><dd>


Similar to standard Tcl <b class='cmd'>lreplace</b> command but replaces one
or more values between the <i class='arg'>first</i> and <i class='arg'>last</i> position 
in the <i class='arg'>element</i> of the shared variable <i class='arg'>varname</i> instead of 
the Tcl variable.

<br><br>
<dt><a name="15"><b class='cmd'>tsv::llength</b> <i class='arg'>varname</i> <i class='arg'>element</i></a><dd>


Similar to standard Tcl <b class='cmd'>llength</b> command but returns length 
of the <i class='arg'>element</i> in the shared variable <i class='arg'>varname</i> instead of the Tcl
variable.

<br><br>
<dt><a name="16"><b class='cmd'>tsv::lindex</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?index?</a><dd>


Similar to standard Tcl <b class='cmd'>lindex</b> command but returns the value
at the <i class='arg'>index</i> list position of the <i class='arg'>element</i> from
the shared variable <i class='arg'>varname</i> instead of the Tcl variable.

<br><br>
<dt><a name="17"><b class='cmd'>tsv::lrange</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>from</i> <i class='arg'>to</i></a><dd>


Similar to standard Tcl <b class='cmd'>lrange</b> command but returns values
between <i class='arg'>from</i> and <i class='arg'>to</i> list positions from the
<i class='arg'>element</i> in the shared variable <i class='arg'>varname</i> instead of the Tcl variable.

<br><br>
<dt><a name="18"><b class='cmd'>tsv::lsearch</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?options? <i class='arg'>pattern</i></a><dd>


Similar to standard Tcl <b class='cmd'>lsearch</b> command but searches the <i class='arg'>element</i>
in the shared variable <i class='arg'>varname</i> instead of the Tcl variable.

<br><br>
<dt><a name="19"><b class='cmd'>tsv::lset</b> <i class='arg'>varname</i> <i class='arg'>element</i> <i class='arg'>index</i> ?index ...? <i class='arg'>value</i></a><dd>


Similar to standard Tcl <b class='cmd'>lset</b> command but sets the <i class='arg'>element</i>
in the shared variable <i class='arg'>varname</i> instead of the Tcl variable.

<br><br>
<dt><a name="20"><b class='cmd'>tsv::lpop</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?index?</a><dd>


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.

<br><br>
<dt><a name="21"><b class='cmd'>tsv::lpush</b> <i class='arg'>varname</i> <i class='arg'>element</i> ?index?</a><dd>


This command performes the opposite of the <b class='cmd'>tsv::lpop</b> command.
As its counterpart, it returns no value to the caller.

</dl>

<h2><a name="array_commands">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 only persistent option supported is the famous GNU Gdbm 
database. This option has to be selected during the package 
compilation time. The implementation provides hooks for 
defining other persistency layers, if needed.

<dl>

<dt><a name="22"><b class='cmd'>tsv::array set</b> <i class='arg'>varname</i> <i class='arg'>list</i></a><dd>


Does the same as standard Tcl <b class='cmd'>array set</b>.

<br><br>
<dt><a name="23"><b class='cmd'>tsv::array get</b> <i class='arg'>varname</i> ?pattern?</a><dd>


Does the same as standard Tcl <b class='cmd'>array get</b>.

<br><br>
<dt><a name="24"><b class='cmd'>tsv::array names</b> <i class='arg'>varname</i> ?pattern?</a><dd>


Does the same as standard Tcl <b class='cmd'>array names</b>.

<br><br>
<dt><a name="25"><b class='cmd'>tsv::array size</b> <i class='arg'>varname</i></a><dd>


Does the same as standard Tcl <b class='cmd'>array size</b>.

<br><br>
<dt><a name="26"><b class='cmd'>tsv::array reset</b> <i class='arg'>varname</i> <i class='arg'>list</i></a><dd>


Does the same as standard Tcl <b class='cmd'>array set</b> but it clears
the <i class='arg'>varname</i> and sets new values from the list atomically.

<br><br>
<dt><a name="27"><b class='cmd'>tsv::array bind</b> <i class='arg'>varname</i> <i class='arg'>handle</i></a><dd>

Binds the <i class='arg'>varname</i> to the persistent storage <i class='arg'>handle</i>.
The format of the <i class='arg'>handle</i> is &lt;handler&gt;:&lt;address&gt;. For the built-in
GNU Gdbm persistence layer, the format of the handle is &quot;gdbm:&lt;path&gt;&quot;
where &lt;path&gt; is the path to the Gdbm database file.

<br><br>
<dt><a name="28"><b class='cmd'>tsv::array unbind</b> <i class='arg'>varname</i></a><dd>

Unbinds the shared <i class='arg'>array</i> from its bound persistent storage.

<br><br>
<dt><a name="29"><b class='cmd'>tsv::array isbound</b> <i class='arg'>varname</i></a><dd>

Returns true (1) if the shared <i class='arg'>varname</i> is bound to some 
persistent storage or zero (0) if not.


</dl>

<h2><a name="keyed_list_commands">KEYED LIST COMMANDS</a></h2>
<p>

Keyed list commands are borrowed from the TclX package. Keyed lists provide
a structured data type built upon standard Tcl lists. This is a functionality
similar to structs in the C programming language.
<p>
A keyed list is a list in which each element contains a key and value 
pair. These element pairs are stored as lists themselves, where the key
is the first element of the list, and the value is the second. The 
key-value pairs are referred to as fields.  This is an example of a
keyed list:

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    {{NAME  {Frank  Zappa}} {JOB {musician and composer}}}
</pre></td></tr></table></p>

Fields may contain subfields; `.' is the separator character. Subfields 
are actually fields  where the value is another keyed list. Thus the 
following list has the top level fields ID and NAME, and subfields 
NAME.FIRST and NAME.LAST:

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
    {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}
</pre></td></tr></table></p>

There is no limit to the recursive depth of subfields,
allowing one to build complex data structures. Keyed lists are constructed
and accessed via a number of commands. All  keyed  list management 
commands take the name of the variable containing the keyed list as an 
argument (i.e. passed by reference), rather than passing the list directly.

<dl>

<dt><a name="30"><b class='cmd'>tsv::keyldel</b> <i class='arg'>varname</i> <i class='arg'>keylist</i> <i class='arg'>key</i></a><dd>


Delete the field specified by <i class='arg'>key</i> from the keyed list <i class='arg'>keylist</i>
in the shared variable <i class='arg'>varname</i>.
This removes both the key and the value from the keyed list.

<br><br>
<dt><a name="31"><b class='cmd'>tsv::keylget</b> <i class='arg'>varname</i> <i class='arg'>keylist</i> <i class='arg'>key</i> ?retvar?</a><dd>


Return the value associated with <i class='arg'>key</i> from the keyed list <i class='arg'>keylist</i>
in the shared variable <i class='arg'>varname</i>.
If the optional <i class='arg'>retvar</i> is not specified, then the value will be 
returned as the result of the command. In this case, if key is not found 
in the list, an error will result.
<br><br>
If <i class='arg'>retvar</i> is specified and <i class='arg'>key</i> is in the list, then the value 
is returned in the variable <i class='arg'>retvar</i> and the command returns 1 if the
key was present within the list. If <i class='arg'>key</i> isn't in the list, the 
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.

<br><br>
<dt><a name="32"><b class='cmd'>tsv::keylkeys</b> <i class='arg'>varname</i> <i class='arg'>keylist</i> ?key?</a><dd>

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.


<br><br>
<dt><a name="33"><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> ?key value..?</a><dd>

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.

</dl>


<h2><a name="discussion">DISCUSSION</a></h2>
<p>
The current implementation of thread shared variables allows for easy and
convenient access to data shared between different threads.
Internally, the data is stored in Tcl objects and all package commands
operate on internal data representation, thus minimizing shimmering and
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>
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.

<h2><a name="credits">CREDITS</a></h2>
<p>
Thread shared variables are inspired by the nsv interface found in 
AOLserver, a highly scalable Web server from America Online.





<h2><a name="see_also">SEE ALSO</a></h2>
<p>
thread, tpool, ttrace

<h2><a name="keywords">KEYWORDS</a></h2>
<p>
locking, synchronization, thread shared data, threads

</body></html>

|
<

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



|
>
|
|
|
<
<
|
<
|
|
|
|
|
|
<
<
<
<
|
|
|
|
|
|
|
|
>
>
|
<
>
>
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
>
>
|





|
<
|



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


|
|
<




|
|
<
<
|
<
|
|

|
|
<
<
<
<
|
|

|



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





|
|
<






|
|
>
>
>

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

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

|
|
<
<
|

|
<
|



|
|
<

|
<
|


|
|
<

|
<
|



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

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

|
|
<
<
|





|
<
|



|
|
|
<
|
|
|
|
<
<
<
<
<
|
>
|
<
|
>
|

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101


102

103
104
105
106
107
108




109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177

178

179



180
181
182




183
184
185
186
187
188

189
190
191
192
193
194


195

196
197
198
199
200




201
202
203
204
205
206
207
208
209




210
211
212




213
214
215




216
217
218


219


220
221
222




223
224
225
226




227
228
229
230




231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
246
247
248
249
250
251


252
253
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
286
287




288
289



290
291

292
293
294
295

296
297


298
299
300
301
302
303

304
305

306

307

308
309




310
311




312



313

314
315




316
317
318



319
320

321
322

323

324


325

326
327


328
329
330


331
332
333

334
335
336
337
338
339

340
341

342
343
344
345
346

347
348

349
350
351
352
353
354

355



356
357
358
359




360
361
362
363
364

365
366
367
368
369
370
371
372



373
374
375
376




377
378
379
380
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


<html><head>
<title>tsv - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    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>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>




<li class="doctools_section"><a href="#section2">ELEMENT COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">LIST COMMANDS</a></li>
<li class="doctools_section"><a href="#section4">ARRAY COMMANDS</a></li>
<li class="doctools_section"><a href="#section5">KEYED LIST COMMANDS</a></li>
<li class="doctools_section"><a href="#section6">DISCUSSION</a></li>
<li class="doctools_section"><a href="#section7">CREDITS</a></li>
<li class="doctools_section"><a href="#see-also">See Also</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>

<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.4</b></li>
<li>package require <b class="pkgname">Thread <span class="opt">?2.8?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">tsv::names</b> <span class="opt">?pattern?</span></a></li>
<li><a href="#2"><b class="cmd">tsv::object</b> <i class="arg">varname</i> <i class="arg">element</i></a></li>
<li><a href="#3"><b class="cmd">tsv::set</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?value?</span></a></li>
<li><a href="#4"><b class="cmd">tsv::get</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?namedvar?</span></a></li>
<li><a href="#5"><b class="cmd">tsv::unset</b> <i class="arg">varname</i> <span class="opt">?element?</span></a></li>
<li><a href="#6"><b class="cmd">tsv::exists</b> <i class="arg">varname</i> <i class="arg">element</i></a></li>
<li><a href="#7"><b class="cmd">tsv::pop</b> <i class="arg">varname</i> <i class="arg">element</i></a></li>
<li><a href="#8"><b class="cmd">tsv::move</b> <i class="arg">varname</i> <i class="arg">oldname</i> <i class="arg">newname</i></a></li>
<li><a href="#9"><b class="cmd">tsv::incr</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?count?</span></a></li>
<li><a href="#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></li>
<li><a href="#11"><b class="cmd">tsv::lock</b> <i class="arg">varname</i> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></li>
<li><a href="#12"><b class="cmd">tsv::handlers</b></a></li>
<li><a href="#13"><b class="cmd">tsv::lappend</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">value</i> <span class="opt">?value ...?</span></a></li>
<li><a href="#14"><b class="cmd">tsv::linsert</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">index</i> <i class="arg">value</i> <span class="opt">?value ...?</span></a></li>
<li><a href="#15"><b class="cmd">tsv::lreplace</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">first</i> <i class="arg">last</i> <span class="opt">?value ...?</span></a></li>
<li><a href="#16"><b class="cmd">tsv::llength</b> <i class="arg">varname</i> <i class="arg">element</i></a></li>
<li><a href="#17"><b class="cmd">tsv::lindex</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></li>
<li><a href="#18"><b class="cmd">tsv::lrange</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">from</i> <i class="arg">to</i></a></li>
<li><a href="#19"><b class="cmd">tsv::lsearch</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?options?</span> <i class="arg">pattern</i></a></li>
<li><a href="#20"><b class="cmd">tsv::lset</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">index</i> <span class="opt">?index ...?</span> <i class="arg">value</i></a></li>
<li><a href="#21"><b class="cmd">tsv::lpop</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></li>
<li><a href="#22"><b class="cmd">tsv::lpush</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></li>
<li><a href="#23"><b class="cmd">tsv::array set</b> <i class="arg">varname</i> <i class="arg">list</i></a></li>
<li><a href="#24"><b class="cmd">tsv::array get</b> <i class="arg">varname</i> <span class="opt">?pattern?</span></a></li>
<li><a href="#25"><b class="cmd">tsv::array names</b> <i class="arg">varname</i> <span class="opt">?pattern?</span></a></li>
<li><a href="#26"><b class="cmd">tsv::array size</b> <i class="arg">varname</i></a></li>
<li><a href="#27"><b class="cmd">tsv::array reset</b> <i class="arg">varname</i> <i class="arg">list</i></a></li>
<li><a href="#28"><b class="cmd">tsv::array bind</b> <i class="arg">varname</i> <i class="arg">handle</i></a></li>
<li><a href="#29"><b class="cmd">tsv::array unbind</b> <i class="arg">varname</i></a></li>
<li><a href="#30"><b class="cmd">tsv::array isbound</b> <i class="arg">varname</i></a></li>
<li><a href="#31"><b class="cmd">tsv::keyldel</b> <i class="arg">varname</i> <i class="arg">keylist</i> <i class="arg">key</i></a></li>
<li><a href="#32"><b class="cmd">tsv::keylget</b> <i class="arg">varname</i> <i class="arg">keylist</i> <i class="arg">key</i> <span class="opt">?retvar?</span></a></li>
<li><a href="#33"><b class="cmd">tsv::keylkeys</b> <i class="arg">varname</i> <i class="arg">keylist</i> <span class="opt">?key?</span></a></li>
<li><a href="#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></li>
</ul>

</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">

    % tsv::set foo bar &quot;A shared string&quot;
    % set string [tsv::object foo bar]
    % $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
resulting script under the internal mutex protection. During the
script evaluation, the entire shared variable is locked. For shared
variable commands within the script, internal locking is disabled
so no deadlock can occur. It is also allowed to unset the shared
variable from within the script. The shared variable is automatically
created if it did not exists at the time of the first lock operation.</p>
<pre class="doctools_example">

    % tsv::lock foo {
        tsv::lappend foo bar 1
        tsv::lappend foo bar 2
        puts stderr [tsv::set foo bar]
        tsv::unset foo
    }
</pre>
</dd>
<dt><a name="12"><b class="cmd">tsv::handlers</b></a></dt>
<dd><p>Returns the names of all persistent storage handlers enabled at compile time.
See <span class="sectref"><a href="#section4">ARRAY COMMANDS</a></span> for details.</p></dd>
</dl>
</div>


<div id="section3" class="doctools_section"><h2><a name="section3">LIST COMMANDS</a></h2>
<p>Those command are similar to the equivalently named Tcl command. The difference
is that they operate on elements of shared arrays.</p>
<dl class="doctools_definitions">

<dt><a name="13"><b class="cmd">tsv::lappend</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">lappend</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="14"><b class="cmd">tsv::linsert</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">index</i> <i class="arg">value</i> <span class="opt">?value ...?</span></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">linsert</b> command but inserts one
or more values at the <i class="arg">index</i> list position in the 
<i class="arg">element</i> in the shared variable <i class="arg">varname</i> instead of the Tcl variable.</p></dd>
<dt><a name="15"><b class="cmd">tsv::lreplace</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">first</i> <i class="arg">last</i> <span class="opt">?value ...?</span></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">lreplace</b> command but replaces one
or more values between the <i class="arg">first</i> and <i class="arg">last</i> position 
in the <i class="arg">element</i> of the shared variable <i class="arg">varname</i> instead of 
the Tcl variable.</p></dd>
<dt><a name="16"><b class="cmd">tsv::llength</b> <i class="arg">varname</i> <i class="arg">element</i></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">llength</b> command but returns length 
of the <i class="arg">element</i> in the shared variable <i class="arg">varname</i> instead of the Tcl
variable.</p></dd>
<dt><a name="17"><b class="cmd">tsv::lindex</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?index?</span></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">lindex</b> command but returns the value
at the <i class="arg">index</i> list position of the <i class="arg">element</i> from
the shared variable <i class="arg">varname</i> instead of the Tcl variable.</p></dd>
<dt><a name="18"><b class="cmd">tsv::lrange</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">from</i> <i class="arg">to</i></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">lrange</b> command but returns values
between <i class="arg">from</i> and <i class="arg">to</i> list positions from the
<i class="arg">element</i> in the shared variable <i class="arg">varname</i> instead of the Tcl variable.</p></dd>
<dt><a name="19"><b class="cmd">tsv::lsearch</b> <i class="arg">varname</i> <i class="arg">element</i> <span class="opt">?options?</span> <i class="arg">pattern</i></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">lsearch</b> command but searches the <i class="arg">element</i>
in the shared variable <i class="arg">varname</i> instead of the Tcl variable.</p></dd>
<dt><a name="20"><b class="cmd">tsv::lset</b> <i class="arg">varname</i> <i class="arg">element</i> <i class="arg">index</i> <span class="opt">?index ...?</span> <i class="arg">value</i></a></dt>




<dd><p>Similar to standard Tcl <b class="cmd">lset</b> command but sets the <i class="arg">element</i>
in the shared variable <i class="arg">varname</i> instead of the Tcl variable.</p></dd>
<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>
<dt><a name="24"><b class="cmd">tsv::array get</b> <i class="arg">varname</i> <span class="opt">?pattern?</span></a></dt>




<dd><p>Does the same as standard Tcl <b class="cmd">array get</b>.</p></dd>
<dt><a name="25"><b class="cmd">tsv::array names</b> <i class="arg">varname</i> <span class="opt">?pattern?</span></a></dt>




<dd><p>Does the same as standard Tcl <b class="cmd">array names</b>.</p></dd>



<dt><a name="26"><b class="cmd">tsv::array size</b> <i class="arg">varname</i></a></dt>

<dd><p>Does the same as standard Tcl <b class="cmd">array size</b>.</p></dd>
<dt><a name="27"><b class="cmd">tsv::array reset</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> but it clears
the <i class="arg">varname</i> and sets new values from the list atomically.</p></dd>
<dt><a name="28"><b class="cmd">tsv::array bind</b> <i class="arg">varname</i> <i class="arg">handle</i></a></dt>



<dd><p>Binds the <i class="arg">varname</i> to the persistent storage <i class="arg">handle</i>.
The format of the <i class="arg">handle</i> is &lt;handler&gt;:&lt;address&gt;, where &lt;handler&gt; is

&quot;gdbm&quot; for GNU Gdbm and &quot;lmdb&quot; for LMDB and &lt;address&gt; is the path to the
database file.</p></dd>

<dt><a name="29"><b class="cmd">tsv::array unbind</b> <i class="arg">varname</i></a></dt>

<dd><p>Unbinds the shared <i class="arg">array</i> from its bound persistent storage.</p></dd>


<dt><a name="30"><b class="cmd">tsv::array isbound</b> <i class="arg">varname</i></a></dt>

<dd><p>Returns true (1) if the shared <i class="arg">varname</i> is bound to some 
persistent storage or zero (0) if not.</p></dd>


</dl>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">KEYED LIST COMMANDS</a></h2>


<p>Keyed list commands are borrowed from the TclX package. Keyed lists provide
a structured data type built upon standard Tcl lists. This is a functionality
similar to structs in the C programming language.</p>

<p>A keyed list is a list in which each element contains a key and value 
pair. These element pairs are stored as lists themselves, where the key
is the first element of the list, and the value is the second. The 
key-value pairs are referred to as fields.  This is an example of a
keyed list:</p>
<pre class="doctools_example">

    {{NAME  {Frank  Zappa}} {JOB {musician and composer}}}
</pre>

<p>Fields may contain subfields; `.' is the separator character. Subfields 
are actually fields  where the value is another keyed list. Thus the 
following list has the top level fields ID and NAME, and subfields 
NAME.FIRST and NAME.LAST:</p>
<pre class="doctools_example">

    {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}
</pre>

<p>There is no limit to the recursive depth of subfields,
allowing one to build complex data structures. Keyed lists are constructed
and accessed via a number of commands. All  keyed  list management 
commands take the name of the variable containing the keyed list as an 
argument (i.e. passed by reference), rather than passing the list directly.</p>
<dl class="doctools_definitions">

<dt><a name="31"><b class="cmd">tsv::keyldel</b> <i class="arg">varname</i> <i class="arg">keylist</i> <i class="arg">key</i></a></dt>



<dd><p>Delete the field specified by <i class="arg">key</i> from the keyed list <i class="arg">keylist</i>
in the shared variable <i class="arg">varname</i>.
This removes both the key and the value from the keyed list.</p></dd>
<dt><a name="32"><b class="cmd">tsv::keylget</b> <i class="arg">varname</i> <i class="arg">keylist</i> <i class="arg">key</i> <span class="opt">?retvar?</span></a></dt>




<dd><p>Return the value associated with <i class="arg">key</i> from the keyed list <i class="arg">keylist</i>
in the shared variable <i class="arg">varname</i>.
If the optional <i class="arg">retvar</i> is not specified, then the value will be 
returned as the result of the command. In this case, if key is not found 
in the list, an error will result.</p>

<p>If <i class="arg">retvar</i> is specified and <i class="arg">key</i> is in the list, then the value 
is returned in the variable <i class="arg">retvar</i> and the command returns 1 if the
key was present within the list. If <i class="arg">key</i> isn't in the list, the 
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>
</div>
<div id="section6" class="doctools_section"><h2><a name="section6">DISCUSSION</a></h2>


<p>The current implementation of thread shared variables allows for easy and
convenient access to data shared between different threads.
Internally, the data is stored in Tcl objects and all package commands
operate on internal data representation, thus minimizing shimmering and
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>

Changes to doc/html/ttrace.html.
1
2
3
4


























































































5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


28




29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313

314
315
<! -- -*- tcl -*- doctools manpage
   -->
<html><head>
<title>ttrace - Tcl Threading </title>


























































































</head>
<! -- Generated from file '' by tcllib/doctools with format 'html'
   -->


<body>
<h1> ttrace(n) 2.7  &quot;Tcl Threading&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> ttrace - Trace-based interpreter initialization





<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#user_commands">USER COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#callback_commands">CALLBACK COMMANDS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#discussion">DISCUSSION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#see_also">SEE ALSO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#keywords">KEYWORDS</a><br>


<h2><a name="synopsis">SYNOPSIS</a></h2>




<p>
package require <b>Tcl 8.4</b><br>
package require <b>Thread ?2.7?</b><br>
<br><table border=1 width=100% cellspacing=0 cellpadding=0><tr            bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>ttrace::eval</b> <i class='arg'>arg</i> ?arg ...?</a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>ttrace::enable</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>ttrace::disable</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>ttrace::cleanup</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>ttrace::update</b> ?epoch?</a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>ttrace::getscript</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>ttrace::atenable</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>ttrace::atdisable</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>ttrace::addtrace</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a></td></tr>
<tr valign=top ><td ><a href="#10"><b class='cmd'>ttrace::addscript</b> <i class='arg'>name</i> <i class='arg'>body</i></a></td></tr>
<tr valign=top ><td ><a href="#11"><b class='cmd'>ttrace::addresolver</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a></td></tr>
<tr valign=top ><td ><a href="#12"><b class='cmd'>ttrace::addcleanup</b> <i class='arg'>body</i></a></td></tr>
<tr valign=top ><td ><a href="#13"><b class='cmd'>ttrace::addentry</b> <i class='arg'>cmd</i> <i class='arg'>var</i> <i class='arg'>val</i></a></td></tr>
<tr valign=top ><td ><a href="#14"><b class='cmd'>ttrace::getentry</b> <i class='arg'>cmd</i> <i class='arg'>var</i></a></td></tr>
<tr valign=top ><td ><a href="#15"><b class='cmd'>ttrace::getentries</b> <i class='arg'>cmd</i> ?pattern?</a></td></tr>
<tr valign=top ><td ><a href="#16"><b class='cmd'>ttrace::delentry</b> <i class='arg'>cmd</i></a></td></tr>
<tr valign=top ><td ><a href="#17"><b class='cmd'>ttrace::preload</b> <i class='arg'>cmd</i></a></td></tr>
</table></td></tr></table>
<h2><a name="description">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>
The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from 
America Online.
<p>
In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:

<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>

    % package require Ttrace
    2.7.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

    % set t2 [thread::create {package require Ttrace; thread::wait}]
    tid0x1804000

    % thread::send $t2 test
    test-tid0x1804000

</pre></td></tr></table></p>
<p>
As seen from above, the <b class='cmd'>ttrace::eval</b> and <b class='cmd'>ttrace::update</b>
commands are used to create a thread-wide definition of a simple 
Tcl procedure and replicate that definition to all, already existing
or later created, threads.

<h2><a name="user_commands">USER COMMANDS</a></h2>
<p>
This section describes user-level commands. Those commands can be
used by script writers to control the execution of the tracing
framework.

<dl>

<dt><a name="1"><b class='cmd'>ttrace::eval</b> <i class='arg'>arg</i> ?arg ...?</a><dd>


This command concatenates given arguments and evaluates the resulting
Tcl command with trace framework enabled. If the command execution
was ok, it takes necessary steps to automatically propagate the
trace epoch change to all threads in the application. 
For AOLserver, only newly created threads actually receive the
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.
<br><br>
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.

<br><br>
<dt><a name="2"><b class='cmd'>ttrace::enable</b> </a><dd>


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.

<br><br>
<dt><a name="3"><b class='cmd'>ttrace::disable</b> </a><dd>


Deactivates all registered callbacks in the framework
and closes the current trace epoch.

<br><br>
<dt><a name="4"><b class='cmd'>ttrace::cleanup</b> </a><dd>


Used to clean-up all on-demand loaded resources in the interpreter. 
It effectively brings Tcl interpreter to its pristine state.

<br><br>
<dt><a name="5"><b class='cmd'>ttrace::update</b> ?epoch?</a><dd>


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.

<br><br>
<dt><a name="6"><b class='cmd'>ttrace::getscript</b> </a><dd>


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>.

</dl>

<h2><a name="callback_commands">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>
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
changes, build in-memory database, perform custom-cleanups and
various other tasks.


<dl>

<dt><a name="7"><b class='cmd'>ttrace::atenable</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a><dd>


Registers Tcl callback to be activated at <b class='cmd'>ttrace::enable</b>.
Registered callbacks are activated on FIFO basis. The callback
definition includes the name of the callback, <i class='arg'>cmd</i>, a list
of callback arguments, <i class='arg'>arglist</i> and the <i class='arg'>body</i> of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl <b class='cmd'>proc</b> command.


<br><br>
<dt><a name="8"><b class='cmd'>ttrace::atdisable</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a><dd>


Registers Tcl callback to be activated at <b class='cmd'>ttrace::disable</b>.
Registered callbacks are activated on FIFO basis. The callback
definition includes the name of the callback, <i class='arg'>cmd</i>, a list
of callback arguments, <i class='arg'>arglist</i> and the <i class='arg'>body</i> of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl <b class='cmd'>proc</b> command.


<br><br>
<dt><a name="9"><b class='cmd'>ttrace::addtrace</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a><dd>


Registers Tcl callback to be activated for tracing the Tcl 
<b class='cmd'>cmd</b> command. The callback definition includes the name of 
the Tcl command to trace, <i class='arg'>cmd</i>, a list of callback arguments, 
<i class='arg'>arglist</i> and the <i class='arg'>body</i> of the callback. Effectively, 
this actually resembles the call interface of the standard Tcl 
<b class='cmd'>proc</b> command.


<br><br>
<dt><a name="10"><b class='cmd'>ttrace::addscript</b> <i class='arg'>name</i> <i class='arg'>body</i></a><dd>


Registers Tcl callback to be activated for building a Tcl
script to be passed to other interpreters. This script is
used to set the stage for the Tcl <b class='cmd'>unknown</b> command.
Registered callbacks are activated on FIFO basis.
The callback definition includes the name of the callback,
<i class='arg'>name</i> and the <i class='arg'>body</i> of the callback.

<br><br>
<dt><a name="11"><b class='cmd'>ttrace::addresolver</b> <i class='arg'>cmd</i> <i class='arg'>arglist</i> <i class='arg'>body</i></a><dd>


Registers Tcl callback to be activated by the overloaded Tcl
<b class='cmd'>unknown</b> command.
Registered callbacks are activated on FIFO basis.
This callback is used to resolve the resource and load the 
resource in the current interpreter.

<br><br>
<dt><a name="12"><b class='cmd'>ttrace::addcleanup</b> <i class='arg'>body</i></a><dd>


Registers Tcl callback to be activated by the <b class='cmd'>trace::cleanup</b>.
Registered callbacks are activated on FIFO basis.

<br><br>
<dt><a name="13"><b class='cmd'>ttrace::addentry</b> <i class='arg'>cmd</i> <i class='arg'>var</i> <i class='arg'>val</i></a><dd>


Adds one entry to the named in-memory database.

<br><br>
<dt><a name="14"><b class='cmd'>ttrace::getentry</b> <i class='arg'>cmd</i> <i class='arg'>var</i></a><dd>


Returns the value of the entry from the named in-memory database. 

<br><br>
<dt><a name="15"><b class='cmd'>ttrace::getentries</b> <i class='arg'>cmd</i> ?pattern?</a><dd>


Returns names of all entries from the named in-memory database.

<br><br>
<dt><a name="16"><b class='cmd'>ttrace::delentry</b> <i class='arg'>cmd</i></a><dd>


Deletes an entry from the named in-memory database.

<br><br>
<dt><a name="17"><b class='cmd'>ttrace::preload</b> <i class='arg'>cmd</i></a><dd>


Registers the Tcl command to be loaded in the interpreter.
Commands registered this way will always be the part of 
the interpreter and not be on-demand loaded by the Tcl
<b class='cmd'>unknown</b> command.

</dl>

<h2><a name="discussion">DISCUSSION</a></h2>
<p>
Common introspective state-replication approaches use a custom Tcl
script to introspect the running interpreter and synthesize another
Tcl script to replicate this state in some other interpreter.
This package, on the contrary, uses Tcl command traces. Command 
traces are registered on selected Tcl commands, like <b class='cmd'>proc</b>, 
<b class='cmd'>namespace</b>, <b class='cmd'>load</b> and other standard (and/or user-defined)
Tcl commands. When activated, those traces build an in-memory
database of created resources. This database is used as a resource
repository for the (overloaded) Tcl <b class='cmd'>unknown</b> command which 
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>
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.





<h2><a name="see_also">SEE ALSO</a></h2>
<p>
thread, tpool, tsv

<h2><a name="keywords">KEYWORDS</a></h2>
<p>
command tracing, introspection

</body></html>

|
<

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



|
>
|
|
|
<
|
|
|
|
|
|
<
<
|
<
|
|
|
|
|
>
>
|
>
>
>
>
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
>
>
|


|
<
|

|
<
|
|
|
<
<

|
<


<



<


<


<
|
<
|


|
|
|
<
|

|
|
<
|
<
<
<
|






|
<
|



|
<
<
|
<
<
|

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


|
<

|
|
<
|
|
|




|
<
|



|
|
|
<
<
<
<
<
|

|
|

|
|
<
<
<
<
<
|

|
|

|
|
<
<
<
<
<
|
|
|
|

|
|
<
<
<
<
<
|

|


|
|
<
<
<
<
|
|


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


|
<

|
|
<
|



|
|


|



|
<
|








<

|
|
|
<
<
<
<
<
|
>
|
<
|
>
|

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107


108

109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

141
142
143
144
145
146
147

148
149
150

151
152
153


154
155

156
157

158
159
160

161
162

163
164

165

166
167
168
169
170
171

172
173
174
175

176



177
178
179
180
181
182
183
184

185
186
187
188
189


190


191
192
193


194


195
196


197


198
199
200




201
202
203


204


205
206
207
208
209

210
211
212

213
214
215
216
217
218
219
220

221
222
223
224
225
226
227





228
229
230
231
232
233
234





235
236
237
238
239
240
241





242
243
244
245
246
247
248





249
250
251
252
253
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
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300
301

302
303
304
305





306
307
308

309
310
311
312


<html><head>
<title>ttrace - Tcl Threading</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    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>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>


<li class="doctools_section"><a href="#section1">Description</a></li>

<li class="doctools_section"><a href="#section2">USER COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">CALLBACK COMMANDS</a></li>
<li class="doctools_section"><a href="#section4">DISCUSSION</a></li>
<li class="doctools_section"><a href="#see-also">See Also</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.4</b></li>
<li>package require <b class="pkgname">Thread <span class="opt">?2.8?</span></b></li>
</ul>
<ul class="doctools_syntax">

<li><a href="#1"><b class="cmd">ttrace::eval</b> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></li>
<li><a href="#2"><b class="cmd">ttrace::enable</b></a></li>
<li><a href="#3"><b class="cmd">ttrace::disable</b></a></li>
<li><a href="#4"><b class="cmd">ttrace::cleanup</b></a></li>
<li><a href="#5"><b class="cmd">ttrace::update</b> <span class="opt">?epoch?</span></a></li>
<li><a href="#6"><b class="cmd">ttrace::getscript</b></a></li>
<li><a href="#7"><b class="cmd">ttrace::atenable</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#8"><b class="cmd">ttrace::atdisable</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#9"><b class="cmd">ttrace::addtrace</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#10"><b class="cmd">ttrace::addscript</b> <i class="arg">name</i> <i class="arg">body</i></a></li>
<li><a href="#11"><b class="cmd">ttrace::addresolver</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#12"><b class="cmd">ttrace::addcleanup</b> <i class="arg">body</i></a></li>
<li><a href="#13"><b class="cmd">ttrace::addentry</b> <i class="arg">cmd</i> <i class="arg">var</i> <i class="arg">val</i></a></li>
<li><a href="#14"><b class="cmd">ttrace::getentry</b> <i class="arg">cmd</i> <i class="arg">var</i></a></li>
<li><a href="#15"><b class="cmd">ttrace::getentries</b> <i class="arg">cmd</i> <span class="opt">?pattern?</span></a></li>
<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>
<pre class="doctools_example">


    % 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

    % set t2 [thread::create {package require Ttrace; thread::wait}]
    tid0x1804000

    % thread::send $t2 test
    test-tid0x1804000

</pre>

<p>As seen from above, the <b class="cmd">ttrace::eval</b> and <b class="cmd">ttrace::update</b>
commands are used to create a thread-wide definition of a simple 
Tcl procedure and replicate that definition to all, already existing
or later created, threads.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">USER COMMANDS</a></h2>

<p>This section describes user-level commands. Those commands can be
used by script writers to control the execution of the tracing
framework.</p>
<dl class="doctools_definitions">

<dt><a name="1"><b class="cmd">ttrace::eval</b> <i class="arg">arg</i> <span class="opt">?arg ...?</span></a></dt>



<dd><p>This command concatenates given arguments and evaluates the resulting
Tcl command with trace framework enabled. If the command execution
was ok, it takes necessary steps to automatically propagate the
trace epoch change to all threads in the application. 
For AOLserver, only newly created threads actually receive the
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>


<dt><a name="4"><b class="cmd">ttrace::cleanup</b></a></dt>


<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
changes, build in-memory database, perform custom-cleanups and
various other tasks.</p>
<dl class="doctools_definitions">
<dt><a name="7"><b class="cmd">ttrace::atenable</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>





<dd><p>Registers Tcl callback to be activated at <b class="cmd">ttrace::enable</b>.
Registered callbacks are activated on FIFO basis. The callback
definition includes the name of the callback, <i class="arg">cmd</i>, a list
of callback arguments, <i class="arg">arglist</i> and the <i class="arg">body</i> of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl <b class="cmd">proc</b> command.</p></dd>
<dt><a name="8"><b class="cmd">ttrace::atdisable</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>





<dd><p>Registers Tcl callback to be activated at <b class="cmd">ttrace::disable</b>.
Registered callbacks are activated on FIFO basis. The callback
definition includes the name of the callback, <i class="arg">cmd</i>, a list
of callback arguments, <i class="arg">arglist</i> and the <i class="arg">body</i> of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl <b class="cmd">proc</b> command.</p></dd>
<dt><a name="9"><b class="cmd">ttrace::addtrace</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>





<dd><p>Registers Tcl callback to be activated for tracing the Tcl 
<b class="cmd">cmd</b> command. The callback definition includes the name of 
the Tcl command to trace, <i class="arg">cmd</i>, a list of callback arguments, 
<i class="arg">arglist</i> and the <i class="arg">body</i> of the callback. Effectively, 
this actually resembles the call interface of the standard Tcl 
<b class="cmd">proc</b> command.</p></dd>
<dt><a name="10"><b class="cmd">ttrace::addscript</b> <i class="arg">name</i> <i class="arg">body</i></a></dt>





<dd><p>Registers Tcl callback to be activated for building a Tcl
script to be passed to other interpreters. This script is
used to set the stage for the Tcl <b class="cmd">unknown</b> command.
Registered callbacks are activated on FIFO basis.
The callback definition includes the name of the callback,
<i class="arg">name</i> and the <i class="arg">body</i> of the callback.</p></dd>
<dt><a name="11"><b class="cmd">ttrace::addresolver</b> <i class="arg">cmd</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>




<dd><p>Registers Tcl callback to be activated by the overloaded Tcl
<b class="cmd">unknown</b> command.
Registered callbacks are activated on FIFO basis.
This callback is used to resolve the resource and load the 
resource in the current interpreter.</p></dd>


<dt><a name="12"><b class="cmd">ttrace::addcleanup</b> <i class="arg">body</i></a></dt>


<dd><p>Registers Tcl callback to be activated by the <b class="cmd">trace::cleanup</b>.
Registered callbacks are activated on FIFO basis.</p></dd>
<dt><a name="13"><b class="cmd">ttrace::addentry</b> <i class="arg">cmd</i> <i class="arg">var</i> <i class="arg">val</i></a></dt>




<dd><p>Adds one entry to the named in-memory database.</p></dd>
<dt><a name="14"><b class="cmd">ttrace::getentry</b> <i class="arg">cmd</i> <i class="arg">var</i></a></dt>




<dd><p>Returns the value of the entry from the named in-memory database.</p></dd>
<dt><a name="15"><b class="cmd">ttrace::getentries</b> <i class="arg">cmd</i> <span class="opt">?pattern?</span></a></dt>




<dd><p>Returns names of all entries from the named in-memory database.</p></dd>


<dt><a name="16"><b class="cmd">ttrace::delentry</b> <i class="arg">cmd</i></a></dt>


<dd><p>Deletes an entry from the named in-memory database.</p></dd>
<dt><a name="17"><b class="cmd">ttrace::preload</b> <i class="arg">cmd</i></a></dt>




<dd><p>Registers the Tcl command to be loaded in the interpreter.
Commands registered this way will always be the part of 
the interpreter and not be on-demand loaded by the Tcl
<b class="cmd">unknown</b> command.</p></dd>

</dl>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">DISCUSSION</a></h2>

<p>Common introspective state-replication approaches use a custom Tcl
script to introspect the running interpreter and synthesize another
Tcl script to replicate this state in some other interpreter.
This package, on the contrary, uses Tcl command traces. Command 
traces are registered on selected Tcl commands, like <b class="cmd">proc</b>, 
<b class="cmd">namespace</b>, <b class="cmd">load</b> and other standard (and/or user-defined)
Tcl commands. When activated, those traces build an in-memory
database of created resources. This database is used as a resource
repository for the (overloaded) Tcl <b class="cmd">unknown</b> command which 
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>
</div>
<div id="see-also" class="doctools_section"><h2><a name="see-also">See Also</a></h2>





<p>thread, tpool, tsv</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>

<p>command tracing, introspection</p>
</div>
</div></body></html>

Changes to doc/man/thread.n.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47









48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
'\" -*- tcl -*- doctools manpage
'\" The definitions below are for supplemental macros used in Tcl/Tk
'\" manual entries.
'\"
'\" .AP type name in/out ?indent?
'\"	Start paragraph describing an argument to a library procedure.
'\"	type is type of argument (int, etc.), in/out is either "in", "out",
'\"	or "in/out" to describe whether procedure reads or modifies arg,
'\"	and indent is equivalent to second arg of .IP (shouldn't ever be
'\"	needed;  use .AS below instead)
'\"
'\" .AS ?type? ?name?
'\"	Give maximum sizes of arguments for setting tab stops.  Type and
'\"	name are examples of largest possible arguments that will be passed
'\"	to .AP later.  If args are omitted, default tab stops are used.
'\"
'\" .BS
'\"	Start box enclosure.  From here until next .BE, everything will be
'\"	enclosed in one large box.
'\"
'\" .BE
'\"	End of box enclosure.
'\"
'\" .CS
'\"	Begin code excerpt.
'\"
'\" .CE
'\"	End code excerpt.







'\"
'\" .VS ?version? ?br?
'\"	Begin vertical sidebar, for use in marking newly-changed parts
'\"	of man pages.  The first argument is ignored and used for recording
'\"	the version when the .VS was added, so that the sidebars can be
'\"	found and removed when they reach a certain age.  If another argument
'\"	is present, then a line break is forced before starting the sidebar.
'\"
'\" .VE
'\"	End of vertical sidebar.
'\"
'\" .DS
'\"	Begin an indented unfilled display.
'\"
'\" .DE
'\"	End of indented unfilled display.









'\"
'\" .SO
'\"	Start of list of standard options for a Tk widget.  The
'\"	options follow on successive lines, in four columns separated
'\"	by tabs.
'\"
'\" .SE
'\"	End of list of standard options for a Tk widget.
'\"
'\" .OP cmdName dbName dbClass
'\"	Start of description of a specific option.  cmdName gives the
'\"	option's name as specified in the class command, dbName gives
'\"	the option's name in the option database, and dbClass gives
'\"	the option's class in the option database.
'\"
'\" .UL arg1 arg2
'\"	Print arg1 underlined, then print arg2 normally.
'\"







'\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
'\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1	\\fI\\$2\\fP	(\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
'\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
'\"	# BS - start boxed text
'\"	# ^y = starting y location
'\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
'\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
'\"	# VS - start vertical sidebar
'\"	# ^Y = starting y location
'\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
'\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
'\"	# Special macro to handle page bottom:  finish off current
'\"	# box/sidebar if in box/sidebar mode, then invoked standard
'\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|



|








|












|









|
|
|









|



















|
|
|






|














|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57








58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
.TH "thread" n 2\&.8  "Tcl Threading"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
.\"	and indent is equivalent to second arg of .IP (shouldn't ever be
.\"	needed;  use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\"	Give maximum sizes of arguments for setting tab stops.  Type and
.\"	name are examples of largest possible arguments that will be passed
.\"	to .AP later.  If args are omitted, default tab stops are used.
.\"
.\" .BS
.\"	Start box enclosure.  From here until next .BE, everything will be
.\"	enclosed in one large box.
.\"
.\" .BE
.\"	End of box enclosure.
.\"
.\" .CS
.\"	Begin code excerpt.
.\"
.\" .CE
.\"	End code excerpt.
.\"
.\" .VS ?version? ?br?
.\"	Begin vertical sidebar, for use in marking newly-changed parts
.\"	of man pages.  The first argument is ignored and used for recording
.\"	the version when the .VS was added, so that the sidebars can be
.\"	found and removed when they reach a certain age.  If another argument
.\"	is present, then a line break is forced before starting the sidebar.
.\"







.\" .VE
.\"	End of vertical sidebar.
.\"
.\" .DS
.\"	Begin an indented unfilled display.
.\"
.\" .DE
.\"	End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\"	Start of list of standard options for a Tk widget. The manpage
.\"	argument defines where to look up the standard options; if
.\"	omitted, defaults to "options". The options follow on successive
.\"	lines, in three columns separated by tabs.
.\"
.\" .SE
.\"	End of list of standard options for a Tk widget.
.\"








.\" .OP cmdName dbName dbClass
.\"	Start of description of a specific option.  cmdName gives the
.\"	option's name as specified in the class command, dbName gives
.\"	the option's name in the option database, and dbClass gives
.\"	the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\"	Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\"	Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\"	Print an open parenthesis, arg1 in quotes, then arg2 normally
.\"	(for trailing punctuation) and then a closing parenthesis.
.\"
.\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\"	# BS - start boxed text
.\"	# ^y = starting y location
.\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\"	# VS - start vertical sidebar
.\"	# ^Y = starting y location
.\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\"	# Special macro to handle page bottom:  finish off current
.\"	# box/sidebar if in box/sidebar mode, then invoked standard
.\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241



















242

243
244
245
246
247
248
249
250
251
252
253
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
286
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
'\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
'\"	# DE - end display
.de DE
.fi
.RE
.sp
..
'\"	# SO - start of list of standard options
.de SO


.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
'\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\fBoptions\\fR manual entry for details on the standard options.
..
'\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
'\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
.if t .ft C
..
'\"	# CE - end code excerpt
.de CE
.fi
.if t .ft R
.RE
..

.de UL
\\$1\l'|0\(ul'\\$2
..




















.TH "thread" n 2.7  "Tcl Threading"

.BS
.SH "NAME"
thread \- Extension for script access to Tcl threading
.SH "SYNOPSIS"
package require \fBTcl  8.4\fR
.sp
package require \fBThread  ?2.7?\fR
.sp
\fBthread::create\fR ?-joinable? ?-preserved? ?script?
.sp
\fBthread::preserve\fR ?id?
.sp
\fBthread::release\fR ?-wait? ?id?
.sp
\fBthread::id\fR
.sp
\fBthread::errorproc\fR ?procname?
.sp


\fBthread::unwind\fR
.sp
\fBthread::exit\fR
.sp
\fBthread::names\fR
.sp
\fBthread::exists\fR \fIid\fR
.sp
\fBthread::send\fR ?-async? ?-head? \fIid\fR \fIscript\fR ?varname?
.sp
\fBthread::broadcast\fR \fIid\fR \fIscript\fR
.sp
\fBthread::wait\fR
.sp
\fBthread::eval\fR ?-lock mutex? \fIarg\fR ?arg ...?
.sp
\fBthread::join\fR \fIid\fR
.sp
\fBthread::configure\fR \fIid\fR ?option? ?value? ?...?
.sp
\fBthread::transfer\fR \fIid\fR \fIchannel\fR
.sp
\fBthread::detach\fR \fIchannel\fR
.sp
\fBthread::attach\fR \fIchannel\fR
.sp







|





|





|

>
>






|




|

|










|




<

|


<


>



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

|

|
|

|











>
>


|







|



|



|







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243

244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
.\"	# DE - end display
.de DE
.fi
.RE
.sp
..
.\"	# SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
.\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i

..
.\"	# CE - end code excerpt
.de CE
.fi

.RE
..
.\"	# UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\"	# QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\"	# PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\"	# QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\"	# MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME
thread \- Extension for script access to Tcl threading
.SH SYNOPSIS
package require \fBTcl  8\&.4\fR
.sp
package require \fBThread  ?2\&.8?\fR
.sp
\fBthread::create\fR ?-joinable? ?-preserved? ?script?
.sp
\fBthread::preserve\fR ?id?
.sp
\fBthread::release\fR ?-wait? ?id?
.sp
\fBthread::id\fR
.sp
\fBthread::errorproc\fR ?procname?
.sp
\fBthread::cancel\fR ?-unwind? \fIid\fR ?result?
.sp
\fBthread::unwind\fR
.sp
\fBthread::exit\fR ?status?
.sp
\fBthread::names\fR
.sp
\fBthread::exists\fR \fIid\fR
.sp
\fBthread::send\fR ?-async? ?-head? \fIid\fR \fIscript\fR ?varname?
.sp
\fBthread::broadcast\fR \fIscript\fR
.sp
\fBthread::wait\fR
.sp
\fBthread::eval\fR ?-lock mutex? \fIarg\fR ?arg \&.\&.\&.?
.sp
\fBthread::join\fR \fIid\fR
.sp
\fBthread::configure\fR \fIid\fR ?option? ?value? ?\&.\&.\&.?
.sp
\fBthread::transfer\fR \fIid\fR \fIchannel\fR
.sp
\fBthread::detach\fR \fIchannel\fR
.sp
\fBthread::attach\fR \fIchannel\fR
.sp
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371


372
373
374
375
376
377
378


379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449


450
451














452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543


544
545
546
547
548
549
550

551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605


606
607
608
609
610
611
612
613
614

615
616
617
618
619
620
621
622
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
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763


764
765
766
767
768
769
770
771
772
773


774
775
776
777

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
\fBthread::cond\fR \fBdestroy\fR \fIcond\fR
.sp
\fBthread::cond\fR \fBnotify\fR \fIcond\fR
.sp
\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.
The Tcl interpreter either evaluates the optional \fBscript\fR, if
specified, or it waits in the event loop for scripts that arrive via
the \fBthread::send\fR command. The result, if any, of the
optional \fBscript\fR is never returned to the caller.
The result of \fBthread::create\fR is the ID of the thread. This is
the opaque handle which identifies the newly created thread for
all other package commands. The handle of the thread goes out of scope
automatically when thread is marked for exit
(see the \fBthread::release\fR command below).
.sp
If the optional \fBscript\fR argument contains the \fBthread::wait\fR
command the thread will enter into the event loop. If such command is not
found  in the \fBscript\fR the thread will run the \fBscript\fR to
the end and exit. In that case, the handle may be safely ignored since it
refers to a thread which does not exists any more at the time when the
command returns.
.sp
Using flag \fB-joinable\fR it is possible to create a joinable
thread, i.e. one upon whose exit can be waited upon by using
\fBthread::join\fR command.
Note that failure to join a thread created with \fB-joinable\fR flag
results in resource and memory leaks.
.sp
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
if possible. This is best illustrated by the example below:
.nf


    # You should never do ...
    set tid [thread::create {
        package require Http
        thread::wait
        vwait forever ; # <-- this!
    }]
.fi


The thread created in the above example will never be able to exit.
After it has been released with the last matching \fBthread::release\fR
call, the thread will jump out of the \fBthread::wait\fR and continue
to execute commands following. It will enter \fBvwait\fR command and
wait endlessly for events. There is no way one can terminate such thread,
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
to this command increments the reference counter by one (1).
Command returns the value of the reference counter after the increment.
If called with the optional thread \fBid\fR, the command preserves
the given thread. Otherwise the current thread is preserved.
.sp
With reference counting, one can implement controlled access to a
shared Tcl thread. By incrementing the reference counter, the
caller signalizes that he/she wishes to use the thread for a longer
period of time. By decrementing the counter, caller signalizes that
he/she has finished using the thread.
.TP
\fBthread::release\fR ?-wait? ?id?
This command decrements the thread reference counter. Each call to
this command decrements the reference counter by one (1).
If called with the optional thread \fBid\fR, the command releases
the given thread. Otherwise, the current thread is released.
Command returns the value of the reference counter after the decrement.
When the reference counter reaches zero (0), the target thread is
marked for termination. You should not reference the thread after the
\fBthread::release\fR command returns zero or negative integer.
The handle of the thread goes out of scope and should not be used any
more. Any following reference to the same thread handle will result
in Tcl error.
.sp
Optional flag \fB-wait\fR instructs the caller thread to wait for
the target thread to exit, if the effect of the command would result
in termination of the target thread, i.e. if the return result would
be zero (0). Without the flag, the caller thread does not wait for
the target thread to exit. Care must be taken when using the
\fB-wait\fR, since this may block the caller thread indefinitely.
This option has been implemented for some special uses of the extension
and is deprecated for regular use. Regular users should create joinable
threads by using the \fB-joinable\fR option of the \fBthread::create\fR
command and the \fBthread::join\fR to wait for thread to exit.
.TP
\fBthread::id\fR
This command returns the ID of the current thread.
.TP
\fBthread::errorproc\fR ?procname?
This command sets a handler for errors that occur in scripts sent
asynchronously, using the \fB-async\fR flag of the
\fBthread::send\fR command, to other threads. If no handler
is specified, the current handler is returned. The empty string
resets the handler to default (unspecified) value.
An uncaught error in a thread causes an error message to be sent
to the standard error channel. This default reporting scheme can
be changed by registering a procedure which is called to report
the error. The \fIprocname\fR is called in the interpreter that
invoked the \fBthread::errorproc\fR command. The \fIprocname\fR
is called like this:
.nf


    myerrorproc thread_id errorInfo
.fi














.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
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 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.
If your application creates other threads at the C level, they
are not reported by this command.
.TP
\fBthread::exists\fR \fIid\fR
Returns true (1) if thread given by the \fIid\fR parameter exists,
false (0) otherwise. This applies only for threads that have
been created via \fBthread::create\fR command.
.TP
\fBthread::send\fR ?-async? ?-head? \fIid\fR \fIscript\fR ?varname?
This command passes a \fIscript\fR to another thread and, optionally,
waits for the result. If the \fB-async\fR flag is specified, the
command does not wait for the result and it returns empty string.
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 \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
.nf


    set t1 [thread::create]
    set t2 [thread::create]
    thread::send -async $t1 "set a 1" result
    thread::send -async $t2 "set b 2" result
    for {set i 0} {$i < 2} {incr i} {
        vwait result
    }
.fi


In the above example, two threads were fed work and both of them were
instructed to signalize the same variable "result" in the calling thread.
The caller entered the event loop twice to get both results. Note,
however, that the order of the received results may vary, depending on
the current system load, type of work done, etc, etc.
.sp
Many threads can simultaneously send scripts to the target thread for
execution. All of them are entered into the event queue of the target
thread and executed on the FIFO basis, intermingled with optional other
events pending in the event queue of the target thread.
Using the optional ?-head? switch, scripts posted to the thread's
event queue can be placed on the head, instead on the tail of the queue,
thus being executed in the LIFO fashion.
.TP
\fBthread::broadcast\fR \fIid\fR \fIscript\fR
This command passes a \fIscript\fR to all threads created by the
package for execution. It does not wait for response from any of
the threads.
.TP
\fBthread::wait\fR
This enters the event loop so a thread can receive messages from
the \fBthread::send\fR command. This command should only be used
within the script passed to the \fBthread::create\fR. It should
be the very last command in the script. If this is not the case,
the exiting thread will continue executing the script lines pass
the \fBthread::wait\fR which is usually not what you want and/or
expect.
.nf


    set t1 [thread::create {
        #
        # Do some initialization work here
        #
        thread::wait ; # Enter the event loop
    }]
.fi

.TP
\fBthread::eval\fR ?-lock mutex? \fIarg\fR ?arg ...?
This command concatenates passed arguments and evaluates the
resulting script under the mutex protection. If no mutex is
specified by using the ?-lock mutex? optional argument,
the internal static mutex is used.
.TP
\fBthread::join\fR \fIid\fR
This command waits for the thread with ID \fIid\fR to exit and
then returns it's exit code. Errors will be returned for threads
which are not joinable or already waited upon by another thread.
Upon the join the handle of the thread has gone out of scope and
should not be used any more.
.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.
The command waits until the other thread has incorporated the
channel. Because of this it is possible to deadlock the
participating threads by commanding the other through a
synchronous \fBthread::send\fR to transfer a channel to us.
This easily extends into longer loops of threads waiting for
each other. Other restrictions: the channel in question must
not be shared among multiple interpreters running in the
sending thread. This automatically excludes the special channels
for standard input, output and error.
.sp
Due to the internal Tcl core implementation and the restriction on
transferring shared channels, one has to take extra measures when
transferring socket channels created by accepting the connection
out of the \fBsocket\fR commands callback procedures:
.nf


    socket -server _Accept 2200
    proc _Accept {s ipaddr port} {
        after idle [list Accept $s $ipaddr $port]
    }
    proc Accept {s ipaddr port} {
        set tid [thread::create]
        thread::transfer $tid $s
    }
.fi

.TP
\fBthread::detach\fR \fIchannel\fR
This detaches the specified \fIchannel\fR from the current thread and
interpreter. After that, the current interpreter has no access to the
channel any more. The channel is in the parked state until some other
(or the same) thread attaches the channel again with \fBthread::attach\fR.
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.
They are used to synchronize access from two or more threads to one or
more shared resources. This command provides script-level access to
exclusive and/or recursive mutexes. Exclusive mutexes can be locked
only once by one thread, while recursive mutexes can be locked many
times by the same thread. For recursive mutexes, number of lock and
unlock operations must match, otherwise, the mutex will never be
released, which would lead to various deadlock situations.
.sp
Care has to be taken when using mutexes in an multithreading program.
Improper use of mutexes may lead to various deadlock situations,
especially when using exclusive mutexes.
.sp
The \fBthread::mutex\fR command supports following subcommands and options:
.RS
.TP
\fBthread::mutex\fR \fBcreate\fR ?-recursive?
Creates the mutex and returns it's opaque handle. This handle
should be used for any future reference to the newly created mutex.
If no optional ?-recursive? argument was specified, the command
creates the exclusive mutex. With the ?-recursive? argument,
the command creates a recursive mutex.
.TP
\fBthread::mutex\fR \fBdestroy\fR \fImutex\fR
Destroys the \fImutex\fR. Mutex should be in unlocked state before
the destroy attempt. If the mutex is locked, the command will throw
Tcl error.
.TP
\fBthread::mutex\fR \fBlock\fR \fImutex\fR
Locks the \fImutex\fR. Locking the exclusive mutex may throw Tcl
error if on attempt to lock the same mutex twice from the same
thread. If your program logic forces you to lock the same mutex
twice or more from the same thread (this may happen in recursive
procedure invocations) you should consider using the recursive mutexes.
.TP
\fBthread::mutex\fR \fBunlock\fR \fImutex\fR
Unlocks the \fImutex\fR so some other thread may lock it again.
Attempt to unlock the already unlocked mutex will throw Tcl error.
.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
read-lock while the resource is been modified by the writer thread.
Only after the write lock has been released, the resource may be read-locked
again.
.sp
The \fBthread::rwmutex\fR command supports following subcommands and options:
.RS
.TP
\fBthread::rwmutex\fR \fBcreate\fR
Creates the reader/writer mutex and returns it's opaque handle.
This handle should be used for any future reference to the newly
created mutex.
.TP
\fBthread::rwmutex\fR \fBdestroy\fR \fImutex\fR
Destroys the reader/writer \fImutex\fR. If the mutex is already locked,
attempt to destroy it will throw Tcl error.
.TP
\fBthread::rwmutex\fR \fBrlock\fR \fImutex\fR
Locks the \fImutex\fR for reading. More than one thread may read-lock
the same \fImutex\fR at the same time.
.TP
\fBthread::rwmutex\fR \fBwlock\fR \fImutex\fR
Locks the \fImutex\fR for writing. Only one thread may write-lock
the same \fImutex\fR at the same time. Attempt to write-lock same
\fImutex\fR twice from the same thread will throw Tcl error.
.TP
\fBthread::rwmutex\fR \fBunlock\fR \fImutex\fR
Unlocks the \fImutex\fR so some other thread may lock it again.
Attempt to unlock already unlocked \fImutex\fR will throw Tcl error.
.RE
.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.
This handle should be used for any future reference to newly
created condition variable.
.TP
\fBthread::cond\fR \fBdestroy\fR \fIcond\fR
Destroys condition variable \fIcond\fR. Extreme care has to be taken
that nobody is using (i.e. waiting on) the condition variable,
otherwise unexpected errors may happen.
.TP
\fBthread::cond\fR \fBnotify\fR \fIcond\fR
Wakes up all threads waiting on the condition variable \fIcond\fR.
.TP
\fBthread::cond\fR \fBwait\fR \fIcond\fR \fImutex\fR ?ms?
This command is used to suspend program execution until the condition
variable \fIcond\fR has been signalled or the optional timer has expired.
The exclusive \fImutex\fR must be locked by the calling thread on entrance
to this command. If the mutex is not locked, Tcl error is thrown.
While waiting on the \fIcond\fR, the command releases \fImutex\fR.
Before returning to the calling thread, the command re-acquires the
\fImutex\fR again. Unlocking the \fImutex\fR and waiting on the
condition variable \fIcond\fR is done atomically.
.sp
The \fBms\fR command option, if given, must be an integer specifying
time interval in milliseconds the command waits to be signalled.
Otherwise the command waits on condition notify forever.
.sp
In multithreading programs, there are many situations where a thread has
to wait for some event to happen until it is allowed to proceed.
This is usually accomplished by repeatedly testing a condition under the
mutex protection and waiting on the condition variable until the condition
evaluates to true:
.nf


    set mutex [thread::mutex create]
    set cond  [thread::cond  create]

    thread::mutex lock $mutex
    while {<some_condition_is_true>} {
        thread::cond wait $cond $mutex
    }
    # Do some work under mutex protection
    thread::mutex unlock $mutex
.fi


Repeated testing of the condition is needed since the condition variable
may get signalled without the condition being actually changed (spurious
thread wake-ups, for example).
.RE

.SH "DISCUSSION"
The fundamental threading model in Tcl is that there can be one or
more Tcl interpreters per thread, but each Tcl interpreter should
only be used by a single thread which created it.
A "shared memory" abstraction is awkward to provide in Tcl because
Tcl makes assumptions about variable and data ownership. Therefore
this extension supports a simple form of threading where the main
thread can manage several background, or "worker" threads.
For example, an event-driven server can pass requests to worker
threads, and then await responses from worker threads or new client
requests. Everything goes through the common Tcl event loop, so
message passing between threads works naturally with event-driven I/O,
\fBvwait\fR on variables, and so forth. For the transfer of bulk
information it is possible to move channels between the threads.
.PP
For advanced multithreading scripts, script-level access to two
basic synchronization primitives, mutex and condition variables,
is also supported.
.SH "SEE ALSO"
\fIhttp://www.tcl.tk/doc/howto/thread_model.html\fR, tpool, tsv, ttrace
.SH "KEYWORDS"
events, message passing, mutex, synchronization, thread







|


|

|
|

|


|


|
|
|

|

|


|

|

|


|
|

|


|
|

|


|
|




|
|
|
|
>
>
|





|
>
>
|


|
|



|

|

|

|

|


|
|
|

|


|

|
|


|
|

|
|

|
|

|
|



|
|
|
|

|

|


|




|
|
|

|

|
|

|
>
>

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




|
|

|

|
|

|

|


|
|

|
|
>
|
|
|
|
|


|
|

|



|
|



|
|

|
|

|


|

|


|
>
>







|
>
>

|
|

|


|

|


|

|

|
|



|
|
|
|

|
|
>
>






|
>

|

|

|



|
|

|

|


|
|


|


|

|



|
|
|




|

|

|

|

|

|
|





|
>
>








|
>



|
|
|

|



|
|
|

|

|


|

|
|

|

|

|

|





|
|

|
|


|
|
|


|

|

|


|
|




|
|


|

|

|

|
|

|





|

|


|
|


|
|


|
|
|


|
|




|


|
|

|





|

|


|
|
|


|



|

|
|

|
|


|
|


|



|
>
>









|
>
>


|

>
|


|

|

|


|

|
|



|

|
|

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
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
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
\fBthread::cond\fR \fBdestroy\fR \fIcond\fR
.sp
\fBthread::cond\fR \fBnotify\fR \fIcond\fR
.sp
\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\&.
The Tcl interpreter either evaluates the optional \fBscript\fR, if
specified, or it waits in the event loop for scripts that arrive via
the \fBthread::send\fR command\&. The result, if any, of the
optional \fBscript\fR is never returned to the caller\&.
The result of \fBthread::create\fR is the ID of the thread\&. This is
the opaque handle which identifies the newly created thread for
all other package commands\&. The handle of the thread goes out of scope
automatically when thread is marked for exit
(see the \fBthread::release\fR command below)\&.
.sp
If the optional \fBscript\fR argument contains the \fBthread::wait\fR
command the thread will enter into the event loop\&. If such command is not
found  in the \fBscript\fR the thread will run the \fBscript\fR to
the end and exit\&. In that case, the handle may be safely ignored since it
refers to a thread which does not exists any more at the time when the
command returns\&.
.sp
Using flag \fB-joinable\fR it is possible to create a joinable
thread, i\&.e\&. one upon whose exit can be waited upon by using
\fBthread::join\fR command\&.
Note that failure to join a thread created with \fB-joinable\fR flag
results in resource and memory leaks\&.
.sp
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
if possible\&. This is best illustrated by the example below:
.CS


    # You should never do \&.\&.\&.
    set tid [thread::create {
        package require Http
        thread::wait
        vwait forever ; # <-- this!
    }]

.CE
.IP
The thread created in the above example will never be able to exit\&.
After it has been released with the last matching \fBthread::release\fR
call, the thread will jump out of the \fBthread::wait\fR and continue
to execute commands following\&. It will enter \fBvwait\fR command and
wait endlessly for events\&. There is no way one can terminate such thread,
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
to this command increments the reference counter by one (1)\&.
Command returns the value of the reference counter after the increment\&.
If called with the optional thread \fBid\fR, the command preserves
the given thread\&. Otherwise the current thread is preserved\&.
.sp
With reference counting, one can implement controlled access to a
shared Tcl thread\&. By incrementing the reference counter, the
caller signalizes that he/she wishes to use the thread for a longer
period of time\&. By decrementing the counter, caller signalizes that
he/she has finished using the thread\&.
.TP
\fBthread::release\fR ?-wait? ?id?
This command decrements the thread reference counter\&. Each call to
this command decrements the reference counter by one (1)\&.
If called with the optional thread \fBid\fR, the command releases
the given thread\&. Otherwise, the current thread is released\&.
Command returns the value of the reference counter after the decrement\&.
When the reference counter reaches zero (0), the target thread is
marked for termination\&. You should not reference the thread after the
\fBthread::release\fR command returns zero or negative integer\&.
The handle of the thread goes out of scope and should not be used any
more\&. Any following reference to the same thread handle will result
in Tcl error\&.
.sp
Optional flag \fB-wait\fR instructs the caller thread to wait for
the target thread to exit, if the effect of the command would result
in termination of the target thread, i\&.e\&. if the return result would
be zero (0)\&. Without the flag, the caller thread does not wait for
the target thread to exit\&. Care must be taken when using the
\fB-wait\fR, since this may block the caller thread indefinitely\&.
This option has been implemented for some special uses of the extension
and is deprecated for regular use\&. Regular users should create joinable
threads by using the \fB-joinable\fR option of the \fBthread::create\fR
command and the \fBthread::join\fR to wait for thread to exit\&.
.TP
\fBthread::id\fR
This command returns the ID of the current thread\&.
.TP
\fBthread::errorproc\fR ?procname?
This command sets a handler for errors that occur in scripts sent
asynchronously, using the \fB-async\fR flag of the
\fBthread::send\fR command, to other threads\&. If no handler
is specified, the current handler is returned\&. The empty string
resets the handler to default (unspecified) value\&.
An uncaught error in a thread causes an error message to be sent
to the standard error channel\&. This default reporting scheme can
be changed by registering a procedure which is called to report
the error\&. The \fIprocname\fR is called in the interpreter that
invoked the \fBthread::errorproc\fR command\&. The \fIprocname\fR
is called like this:
.CS


    myerrorproc thread_id errorInfo

.CE
.TP
\fBthread::cancel\fR ?-unwind? \fIid\fR ?result?
This command requires Tcl version 8\&.6 or higher\&.
.sp
Cancels the script being evaluated in the thread given by the \fIid\fR
parameter\&. Without the \fB-unwind\fR switch the evaluation stack for
the interpreter is unwound until an enclosing catch command is found or
there are no further invocations of the interpreter left on the call
stack\&. With the \fB-unwind\fR switch the evaluation stack for the
interpreter is unwound without regard to any intervening catch command
until there are no further invocations of the interpreter left on the
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\&.
If your application creates other threads at the C level, they
are not reported by this command\&.
.TP
\fBthread::exists\fR \fIid\fR
Returns true (1) if thread given by the \fIid\fR parameter exists,
false (0) otherwise\&. This applies only for threads that have
been created via \fBthread::create\fR command\&.
.TP
\fBthread::send\fR ?-async? ?-head? \fIid\fR \fIscript\fR ?varname?
This command passes a \fIscript\fR to another thread and, optionally,
waits for the result\&. If the \fB-async\fR flag is specified, the
command does not wait for the result and it returns empty string\&.
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 \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]
    set t2 [thread::create]
    thread::send -async $t1 "set a 1" result
    thread::send -async $t2 "set b 2" result
    for {set i 0} {$i < 2} {incr i} {
        vwait result
    }

.CE
.IP
In the above example, two threads were fed work and both of them were
instructed to signalize the same variable "result" in the calling thread\&.
The caller entered the event loop twice to get both results\&. Note,
however, that the order of the received results may vary, depending on
the current system load, type of work done, etc, etc\&.
.sp
Many threads can simultaneously send scripts to the target thread for
execution\&. All of them are entered into the event queue of the target
thread and executed on the FIFO basis, intermingled with optional other
events pending in the event queue of the target thread\&.
Using the optional ?-head? switch, scripts posted to the thread's
event queue can be placed on the head, instead on the tail of the queue,
thus being executed in the LIFO fashion\&.
.TP
\fBthread::broadcast\fR \fIscript\fR
This command passes a \fIscript\fR to all threads created by the
package for execution\&. It does not wait for response from any of
the threads\&.
.TP
\fBthread::wait\fR
This enters the event loop so a thread can receive messages from
the \fBthread::send\fR command\&. This command should only be used
within the script passed to the \fBthread::create\fR\&. It should
be the very last command in the script\&. If this is not the case,
the exiting thread will continue executing the script lines past
the \fBthread::wait\fR which is usually not what you want and/or
expect\&.
.CS


    set t1 [thread::create {
        #
        # Do some initialization work here
        #
        thread::wait ; # Enter the event loop
    }]

.CE
.TP
\fBthread::eval\fR ?-lock mutex? \fIarg\fR ?arg \&.\&.\&.?
This command concatenates passed arguments and evaluates the
resulting script under the mutex protection\&. If no mutex is
specified by using the ?-lock mutex? optional argument,
the internal static mutex is used\&.
.TP
\fBthread::join\fR \fIid\fR
This command waits for the thread with ID \fIid\fR to exit and
then returns it's exit code\&. Errors will be returned for threads
which are not joinable or already waited upon by another thread\&.
Upon the join the handle of the thread has gone out of scope and
should not be used any more\&.
.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\&.
The command waits until the other thread has incorporated the
channel\&. Because of this it is possible to deadlock the
participating threads by commanding the other through a
synchronous \fBthread::send\fR to transfer a channel to us\&.
This easily extends into longer loops of threads waiting for
each other\&. Other restrictions: the channel in question must
not be shared among multiple interpreters running in the
sending thread\&. This automatically excludes the special channels
for standard input, output and error\&.
.sp
Due to the internal Tcl core implementation and the restriction on
transferring shared channels, one has to take extra measures when
transferring socket channels created by accepting the connection
out of the \fBsocket\fR commands callback procedures:
.CS


    socket -server _Accept 2200
    proc _Accept {s ipaddr port} {
        after idle [list Accept $s $ipaddr $port]
    }
    proc Accept {s ipaddr port} {
        set tid [thread::create]
        thread::transfer $tid $s
    }

.CE
.TP
\fBthread::detach\fR \fIchannel\fR
This detaches the specified \fIchannel\fR from the current thread and
interpreter\&. After that, the current interpreter has no access to the
channel any more\&. The channel is in the parked state until some other
(or the same) thread attaches the channel again with \fBthread::attach\fR\&.
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\&.
They are used to synchronize access from two or more threads to one or
more shared resources\&. This command provides script-level access to
exclusive and/or recursive mutexes\&. Exclusive mutexes can be locked
only once by one thread, while recursive mutexes can be locked many
times by the same thread\&. For recursive mutexes, number of lock and
unlock operations must match, otherwise, the mutex will never be
released, which would lead to various deadlock situations\&.
.sp
Care has to be taken when using mutexes in an multithreading program\&.
Improper use of mutexes may lead to various deadlock situations,
especially when using exclusive mutexes\&.
.sp
The \fBthread::mutex\fR command supports following subcommands and options:
.RS
.TP
\fBthread::mutex\fR \fBcreate\fR ?-recursive?
Creates the mutex and returns it's opaque handle\&. This handle
should be used for any future reference to the newly created mutex\&.
If no optional ?-recursive? argument was specified, the command
creates the exclusive mutex\&. With the ?-recursive? argument,
the command creates a recursive mutex\&.
.TP
\fBthread::mutex\fR \fBdestroy\fR \fImutex\fR
Destroys the \fImutex\fR\&. Mutex should be in unlocked state before
the destroy attempt\&. If the mutex is locked, the command will throw
Tcl error\&.
.TP
\fBthread::mutex\fR \fBlock\fR \fImutex\fR
Locks the \fImutex\fR\&. Locking the exclusive mutex may throw Tcl
error if on attempt to lock the same mutex twice from the same
thread\&. If your program logic forces you to lock the same mutex
twice or more from the same thread (this may happen in recursive
procedure invocations) you should consider using the recursive mutexes\&.
.TP
\fBthread::mutex\fR \fBunlock\fR \fImutex\fR
Unlocks the \fImutex\fR so some other thread may lock it again\&.
Attempt to unlock the already unlocked mutex will throw Tcl error\&.
.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
read-lock while the resource is been modified by the writer thread\&.
Only after the write lock has been released, the resource may be read-locked
again\&.
.sp
The \fBthread::rwmutex\fR command supports following subcommands and options:
.RS
.TP
\fBthread::rwmutex\fR \fBcreate\fR
Creates the reader/writer mutex and returns it's opaque handle\&.
This handle should be used for any future reference to the newly
created mutex\&.
.TP
\fBthread::rwmutex\fR \fBdestroy\fR \fImutex\fR
Destroys the reader/writer \fImutex\fR\&. If the mutex is already locked,
attempt to destroy it will throw Tcl error\&.
.TP
\fBthread::rwmutex\fR \fBrlock\fR \fImutex\fR
Locks the \fImutex\fR for reading\&. More than one thread may read-lock
the same \fImutex\fR at the same time\&.
.TP
\fBthread::rwmutex\fR \fBwlock\fR \fImutex\fR
Locks the \fImutex\fR for writing\&. Only one thread may write-lock
the same \fImutex\fR at the same time\&. Attempt to write-lock same
\fImutex\fR twice from the same thread will throw Tcl error\&.
.TP
\fBthread::rwmutex\fR \fBunlock\fR \fImutex\fR
Unlocks the \fImutex\fR so some other thread may lock it again\&.
Attempt to unlock already unlocked \fImutex\fR will throw Tcl error\&.
.RE
.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\&.
This handle should be used for any future reference to newly
created condition variable\&.
.TP
\fBthread::cond\fR \fBdestroy\fR \fIcond\fR
Destroys condition variable \fIcond\fR\&. Extreme care has to be taken
that nobody is using (i\&.e\&. waiting on) the condition variable,
otherwise unexpected errors may happen\&.
.TP
\fBthread::cond\fR \fBnotify\fR \fIcond\fR
Wakes up all threads waiting on the condition variable \fIcond\fR\&.
.TP
\fBthread::cond\fR \fBwait\fR \fIcond\fR \fImutex\fR ?ms?
This command is used to suspend program execution until the condition
variable \fIcond\fR has been signalled or the optional timer has expired\&.
The exclusive \fImutex\fR must be locked by the calling thread on entrance
to this command\&. If the mutex is not locked, Tcl error is thrown\&.
While waiting on the \fIcond\fR, the command releases \fImutex\fR\&.
Before returning to the calling thread, the command re-acquires the
\fImutex\fR again\&. Unlocking the \fImutex\fR and waiting on the
condition variable \fIcond\fR is done atomically\&.
.sp
The \fBms\fR command option, if given, must be an integer specifying
time interval in milliseconds the command waits to be signalled\&.
Otherwise the command waits on condition notify forever\&.
.sp
In multithreading programs, there are many situations where a thread has
to wait for some event to happen until it is allowed to proceed\&.
This is usually accomplished by repeatedly testing a condition under the
mutex protection and waiting on the condition variable until the condition
evaluates to true:
.CS


    set mutex [thread::mutex create]
    set cond  [thread::cond  create]

    thread::mutex lock $mutex
    while {<some_condition_is_true>} {
        thread::cond wait $cond $mutex
    }
    # Do some work under mutex protection
    thread::mutex unlock $mutex

.CE
.IP
Repeated testing of the condition is needed since the condition variable
may get signalled without the condition being actually changed (spurious
thread wake-ups, for example)\&.
.RE
.PP
.SH DISCUSSION
The fundamental threading model in Tcl is that there can be one or
more Tcl interpreters per thread, but each Tcl interpreter should
only be used by a single thread which created it\&.
A "shared memory" abstraction is awkward to provide in Tcl because
Tcl makes assumptions about variable and data ownership\&. Therefore
this extension supports a simple form of threading where the main
thread can manage several background, or "worker" threads\&.
For example, an event-driven server can pass requests to worker
threads, and then await responses from worker threads or new client
requests\&. Everything goes through the common Tcl event loop, so
message passing between threads works naturally with event-driven I/O,
\fBvwait\fR on variables, and so forth\&. For the transfer of bulk
information it is possible to move channels between the threads\&.
.PP
For advanced multithreading scripts, script-level access to two
basic synchronization primitives, mutex and condition variables,
is also supported\&.
.SH "SEE ALSO"
\fIhttp://www\&.tcl\&.tk/doc/howto/thread_model\&.html\fR, tpool, tsv, ttrace
.SH KEYWORDS
events, message passing, mutex, synchronization, thread
Changes to doc/man/tpool.n.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47









48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
'\" -*- tcl -*- doctools manpage
'\" The definitions below are for supplemental macros used in Tcl/Tk
'\" manual entries.
'\"
'\" .AP type name in/out ?indent?
'\"	Start paragraph describing an argument to a library procedure.
'\"	type is type of argument (int, etc.), in/out is either "in", "out",
'\"	or "in/out" to describe whether procedure reads or modifies arg,
'\"	and indent is equivalent to second arg of .IP (shouldn't ever be
'\"	needed;  use .AS below instead)
'\"
'\" .AS ?type? ?name?
'\"	Give maximum sizes of arguments for setting tab stops.  Type and
'\"	name are examples of largest possible arguments that will be passed
'\"	to .AP later.  If args are omitted, default tab stops are used.
'\"
'\" .BS
'\"	Start box enclosure.  From here until next .BE, everything will be
'\"	enclosed in one large box.
'\"
'\" .BE
'\"	End of box enclosure.
'\"
'\" .CS
'\"	Begin code excerpt.
'\"
'\" .CE
'\"	End code excerpt.







'\"
'\" .VS ?version? ?br?
'\"	Begin vertical sidebar, for use in marking newly-changed parts
'\"	of man pages.  The first argument is ignored and used for recording
'\"	the version when the .VS was added, so that the sidebars can be
'\"	found and removed when they reach a certain age.  If another argument
'\"	is present, then a line break is forced before starting the sidebar.
'\"
'\" .VE
'\"	End of vertical sidebar.
'\"
'\" .DS
'\"	Begin an indented unfilled display.
'\"
'\" .DE
'\"	End of indented unfilled display.









'\"
'\" .SO
'\"	Start of list of standard options for a Tk widget.  The
'\"	options follow on successive lines, in four columns separated
'\"	by tabs.
'\"
'\" .SE
'\"	End of list of standard options for a Tk widget.
'\"
'\" .OP cmdName dbName dbClass
'\"	Start of description of a specific option.  cmdName gives the
'\"	option's name as specified in the class command, dbName gives
'\"	the option's name in the option database, and dbClass gives
'\"	the option's class in the option database.
'\"
'\" .UL arg1 arg2
'\"	Print arg1 underlined, then print arg2 normally.
'\"







'\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
'\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1	\\fI\\$2\\fP	(\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
'\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
'\"	# BS - start boxed text
'\"	# ^y = starting y location
'\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
'\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
'\"	# VS - start vertical sidebar
'\"	# ^Y = starting y location
'\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
'\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
'\"	# Special macro to handle page bottom:  finish off current
'\"	# box/sidebar if in box/sidebar mode, then invoked standard
'\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|



|








|












|









|
|
|









|



















|
|
|






|














|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57








58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
.TH "tpool" n 2\&.8  "Tcl Threading"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
.\"	and indent is equivalent to second arg of .IP (shouldn't ever be
.\"	needed;  use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\"	Give maximum sizes of arguments for setting tab stops.  Type and
.\"	name are examples of largest possible arguments that will be passed
.\"	to .AP later.  If args are omitted, default tab stops are used.
.\"
.\" .BS
.\"	Start box enclosure.  From here until next .BE, everything will be
.\"	enclosed in one large box.
.\"
.\" .BE
.\"	End of box enclosure.
.\"
.\" .CS
.\"	Begin code excerpt.
.\"
.\" .CE
.\"	End code excerpt.
.\"
.\" .VS ?version? ?br?
.\"	Begin vertical sidebar, for use in marking newly-changed parts
.\"	of man pages.  The first argument is ignored and used for recording
.\"	the version when the .VS was added, so that the sidebars can be
.\"	found and removed when they reach a certain age.  If another argument
.\"	is present, then a line break is forced before starting the sidebar.
.\"







.\" .VE
.\"	End of vertical sidebar.
.\"
.\" .DS
.\"	Begin an indented unfilled display.
.\"
.\" .DE
.\"	End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\"	Start of list of standard options for a Tk widget. The manpage
.\"	argument defines where to look up the standard options; if
.\"	omitted, defaults to "options". The options follow on successive
.\"	lines, in three columns separated by tabs.
.\"
.\" .SE
.\"	End of list of standard options for a Tk widget.
.\"








.\" .OP cmdName dbName dbClass
.\"	Start of description of a specific option.  cmdName gives the
.\"	option's name as specified in the class command, dbName gives
.\"	the option's name in the option database, and dbClass gives
.\"	the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\"	Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\"	Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\"	Print an open parenthesis, arg1 in quotes, then arg2 normally
.\"	(for trailing punctuation) and then a closing parenthesis.
.\"
.\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\"	# BS - start boxed text
.\"	# ^y = starting y location
.\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\"	# VS - start vertical sidebar
.\"	# ^Y = starting y location
.\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\"	# Special macro to handle page bottom:  finish off current
.\"	# box/sidebar if in box/sidebar mode, then invoked standard
.\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241



















242

243
244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
'\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
'\"	# DE - end display
.de DE
.fi
.RE
.sp
..
'\"	# SO - start of list of standard options
.de SO


.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
'\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\fBoptions\\fR manual entry for details on the standard options.
..
'\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
'\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
.if t .ft C
..
'\"	# CE - end code excerpt
.de CE
.fi
.if t .ft R
.RE
..

.de UL
\\$1\l'|0\(ul'\\$2
..




















.TH "tpool" n 2.7  "Tcl Threading"

.BS
.SH "NAME"
tpool \-
Part of the Tcl threading extension implementing pools of worker threads.
.SH "SYNOPSIS"
package require \fBTcl  8.4\fR
.sp
package require \fBThread  ?2.7?\fR
.sp
\fBtpool::create\fR ?options?
.sp
\fBtpool::names\fR
.sp
\fBtpool::post\fR ?-detached? ?-nowait? \fItpool\fR \fIscript\fR
.sp
\fBtpool::wait\fR \fItpool\fR \fIjoblist\fR ?varname?
.sp
\fBtpool::cancel\fR \fItpool\fR \fIjoblist\fR ?varname?
.sp
\fBtpool::get\fR \fItpool\fR \fIjob\fR
.sp
\fBtpool::preserve\fR \fItpool\fR
.sp
\fBtpool::release\fR \fItpool\fR
.sp




.BE
.SH "DESCRIPTION"
This package creates and manages pools of worker threads. It allows you
to post jobs to worker threads and wait for their completion. The
threadpool implementation is Tcl event-loop aware. That means that any
time a caller is forced to wait for an event (job being completed or
a worker thread becoming idle or initialized), the implementation will
enter the event loop and allow for servicing of other pending file or
timer (or any other supported) events.
.SH "COMMANDS"
.TP
\fBtpool::create\fR ?options?
This command creates new threadpool. It accepts several options as
key-value pairs. Options are used to tune some threadpool parameters.
The command returns the ID of the newly created threadpool.
.sp
Following options are supported:
.RS
.TP
-minworkers number
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
-maxworkers number
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,
new worker thread will start. The caller will automatically enter the
event loop and wait until the worker thread has initialized. If. however,
the number of available worker threads is equal to the given number,
the caller will enter the event loop and wait for the first worker thread
to get idle, thus ready to run the job.
Default value of this parameter is 4 (four), which means that the
threadpool instance will allow maximum of 4 worker threads running jobs
or being idle waiting for new jobs to get posted to the job queue.
.TP
-idletime seconds
Time in seconds an idle worker thread waits for the job to get posted
to the job queue. If no job arrives during this interval and the time
expires, the worker thread will check the number of currently available
worker threads and if the number is higher than the number set by the
\fBminthreads\fR option, it will exit.
If an \fBexitscript\fR has been defined, the exiting worker thread
will first run the script and then exit. Errors from the exit script,
if any, are ignored.
.sp
The idle worker thread is not servicing the event loop. If you, however,
put the worker thread into the event loop, by evaluating the
\fBvwait\fR or other related Tcl commands, the worker thread
will not be in the idle state, hence the idle timer will not be
taken into account.
Default value for this option is unspecified.
.TP
-initcmd script
Sets a Tcl script used to initialize new worker thread. This is usually
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
\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
-exitcmd script
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
\fBtpool::names\fR
This command returns a list of IDs of threadpools created with the
\fBtpool::create\fR command. If no threadpools were found, the
command will return empty list.
.TP
\fBtpool::post\fR ?-detached? ?-nowait? \fItpool\fR \fIscript\fR
This command sends a \fIscript\fR to the target \fItpool\fR threadpool
for execution. The script will be executed in the first available idle
worker thread. If there are no idle worker threads available, the command
will create new one, enter the event loop and service events until the
newly created thread is initialized. If the current number of worker
threads is equal to the maximum number of worker threads, as defined
during the threadpool creation, the command will enter the event loop and
service events while waiting for one of the worker threads to become idle.
If the optional ?-nowait? argument is given, the command will not wait
for one idle worker. It will just place the job in the pool's job queue
and return immediately.
.sp
The command returns the ID of the posted job. This ID is used for subsequent
\fBtpool::wait\fR, \fBtpool::get\fR and \fBtpool::cancel\fR commands to wait
for and retrieve result of the posted script, or cancel the posted job
respectively. If the optional ?-detached? argument is specified, the
command will post a detached job. A detached job can not be cancelled or
waited upon and is not identified by the job ID.
.sp
If the threadpool \fItpool\fR is not found in the list of active
thread pools, the command will throw error. The error will also be triggered
if the newly created worker thread fails to initialize.
.TP
\fBtpool::wait\fR \fItpool\fR \fIjoblist\fR ?varname?
This command waits for one or many jobs, whose job IDs are given in the
\fIjoblist\fR to get processed by the worker thread(s). If none of the
specified jobs are ready, the command will enter the event loop, service
events and wait for the first job to get ready.
.sp
The command returns the list of completed job IDs. If the optional variable
?varname? is given, it will be set to the list of jobs in the
\fIjoblist\fR which are still pending. If the threadpool \fItpool\fR
is not found in the list of active thread pools, the command will throw error.
.TP
\fBtpool::cancel\fR \fItpool\fR \fIjoblist\fR ?varname?
This command cancels the previously posted jobs given by the \fIjoblist\fR
to the pool \fItpool\fR. Job cancellation succeeds only for job still
waiting to be processed. If the job is already being executed by one of
the worker threads, the job will not be cancelled.
The command returns the list of cancelled job IDs. If the optional variable
?varname? is given, it will be set to the list of jobs in the
\fIjoblist\fR which were not cancelled. If the threadpool \fItpool\fR
is not found in the list of active thread pools, the command will throw error.
.TP
\fBtpool::get\fR \fItpool\fR \fIjob\fR
This command retrieves the result of the previously posted \fIjob\fR.
Only results of jobs waited upon with the \fBtpool::wait\fR command
can be retrieved. If the execution of the script resulted in error,
the command will throw the error and update the \fBerrorInfo\fR and
\fBerrorCode\fR variables correspondingly. If the pool \fItpool\fR
is not found in the list of threadpools, the command will throw error.
If the job \fIjob\fR is not ready for retrieval, because it is currently
being executed by the worker thread, the command will throw error.
.TP
\fBtpool::preserve\fR \fItpool\fR
Each call to this command increments the reference counter of the
threadpool \fItpool\fR by one (1). Command returns the value of the
reference counter after the increment.
By incrementing the reference counter, the caller signalizes that
he/she wishes to use the resource for a longer period of time.
.TP
\fBtpool::release\fR \fItpool\fR
Each call to this command decrements the reference counter of the
threadpool \fItpool\fR by one (1).Command returns the value of the
reference counter after the decrement.
When the reference counter reaches zero (0), the threadpool \fItpool\fR
is marked for termination. You should not reference the threadpool
after the \fBtpool::release\fR command returns zero. The \fItpool\fR
handle goes out of scope and should not be used any more. Any following
reference to the same threadpool handle will result in Tcl error.

















.SH "DISCUSSION"
Threadpool is one of the most common threading paradigm when it comes
to server applications handling a large number of relatively small tasks.
A very simplistic model for building a server application would be to
create a new thread each time a request arrives and service the request
in the new thread. One of the disadvantages of this approach is that
the overhead of creating a new thread for each request is significant;
a server that created a new thread for each request would spend more time
and consume more system resources in creating and destroying threads than
in processing actual user requests. In addition to the overhead of
creating and destroying threads, active threads consume system resources.
Creating too many threads can cause the system to run out of memory or
trash due to excessive memory consumption.
.PP
A thread pool offers a solution to both the problem of thread life-cycle
overhead and the problem of resource trashing. By reusing threads for
multiple tasks, the thread-creation overhead is spread over many tasks.
As a bonus, because the thread already exists when a request arrives,
the delay introduced by thread creation is eliminated. Thus, the request
can be serviced immediately. Furthermore, by properly tuning the number
of threads in the thread pool, resource thrashing may also be eliminated
by forcing any request to wait until a thread is available to process it.
.SH "SEE ALSO"
thread, tsv, ttrace
.SH "KEYWORDS"
thread, threadpool







|





|





|

>
>






|




|

|










|




<

|


<


>



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

|
<
|
|
|

|

















>
>
>
>

|
|
|
|



|
|


|
|
|




|
|


|
|
|

|

|
|

|

|
|


|


|

|

|


|

|
|

|



|
|

|
|

|

|

|

|
|

|

|





|
|



|
|

|


|

|
|

|


|
|
|


|
|



|

|

|

|
|



|
|
|
|

|
|


|

|

|
|

|



|
|

|



|
|

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

|


|



|
|

|


|
|

|
|

|


|

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243

244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
.\"	# DE - end display
.de DE
.fi
.RE
.sp
..
.\"	# SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
.\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i

..
.\"	# CE - end code excerpt
.de CE
.fi

.RE
..
.\"	# UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\"	# QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\"	# PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\"	# QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\"	# MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME

tpool \- Part of the Tcl threading extension implementing pools of worker threads\&.
.SH SYNOPSIS
package require \fBTcl  8\&.4\fR
.sp
package require \fBThread  ?2\&.8?\fR
.sp
\fBtpool::create\fR ?options?
.sp
\fBtpool::names\fR
.sp
\fBtpool::post\fR ?-detached? ?-nowait? \fItpool\fR \fIscript\fR
.sp
\fBtpool::wait\fR \fItpool\fR \fIjoblist\fR ?varname?
.sp
\fBtpool::cancel\fR \fItpool\fR \fIjoblist\fR ?varname?
.sp
\fBtpool::get\fR \fItpool\fR \fIjob\fR
.sp
\fBtpool::preserve\fR \fItpool\fR
.sp
\fBtpool::release\fR \fItpool\fR
.sp
\fBtpool::suspend\fR \fItpool\fR
.sp
\fBtpool::resume\fR \fItpool\fR
.sp
.BE
.SH DESCRIPTION
This package creates and manages pools of worker threads\&. It allows you
to post jobs to worker threads and wait for their completion\&. The
threadpool implementation is Tcl event-loop aware\&. That means that any
time a caller is forced to wait for an event (job being completed or
a worker thread becoming idle or initialized), the implementation will
enter the event loop and allow for servicing of other pending file or
timer (or any other supported) events\&.
.SH COMMANDS
.TP
\fBtpool::create\fR ?options?
This command creates new threadpool\&. It accepts several options as
key-value pairs\&. Options are used to tune some threadpool parameters\&.
The command returns the ID of the newly created threadpool\&.
.sp
Following options are supported:
.RS
.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
of available worker threads is lower than the given number,
new worker thread will start\&. The caller will automatically enter the
event loop and wait until the worker thread has initialized\&. If\&. however,
the number of available worker threads is equal to the given number,
the caller will enter the event loop and wait for the first worker thread
to get idle, thus ready to run the job\&.
Default value of this parameter is 4 (four), which means that the
threadpool instance will allow maximum of 4 worker threads running jobs
or being idle waiting for new jobs to get posted to the job queue\&.
.TP
\fB-idletime\fR \fIseconds\fR
Time in seconds an idle worker thread waits for the job to get posted
to the job queue\&. If no job arrives during this interval and the time
expires, the worker thread will check the number of currently available
worker threads and if the number is higher than the number set by the
\fBminthreads\fR option, it will exit\&.
If an \fBexitscript\fR has been defined, the exiting worker thread
will first run the script and then exit\&. Errors from the exit script,
if any, are ignored\&.
.sp
The idle worker thread is not servicing the event loop\&. If you, however,
put the worker thread into the event loop, by evaluating the
\fBvwait\fR or other related Tcl commands, the worker thread
will not be in the idle state, hence the idle timer will not be
taken into account\&.
Default value for this option is unspecified\&.
.TP
\fB-initcmd\fR \fIscript\fR
Sets a Tcl script used to initialize new worker thread\&. This is usually
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
\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
\fBtpool::names\fR
This command returns a list of IDs of threadpools created with the
\fBtpool::create\fR command\&. If no threadpools were found, the
command will return empty list\&.
.TP
\fBtpool::post\fR ?-detached? ?-nowait? \fItpool\fR \fIscript\fR
This command sends a \fIscript\fR to the target \fItpool\fR threadpool
for execution\&. The script will be executed in the first available idle
worker thread\&. If there are no idle worker threads available, the command
will create new one, enter the event loop and service events until the
newly created thread is initialized\&. If the current number of worker
threads is equal to the maximum number of worker threads, as defined
during the threadpool creation, the command will enter the event loop and
service events while waiting for one of the worker threads to become idle\&.
If the optional ?-nowait? argument is given, the command will not wait
for one idle worker\&. It will just place the job in the pool's job queue
and return immediately\&.
.sp
The command returns the ID of the posted job\&. This ID is used for subsequent
\fBtpool::wait\fR, \fBtpool::get\fR and \fBtpool::cancel\fR commands to wait
for and retrieve result of the posted script, or cancel the posted job
respectively\&. If the optional ?-detached? argument is specified, the
command will post a detached job\&. A detached job can not be cancelled or
waited upon and is not identified by the job ID\&.
.sp
If the threadpool \fItpool\fR is not found in the list of active
thread pools, the command will throw error\&. The error will also be triggered
if the newly created worker thread fails to initialize\&.
.TP
\fBtpool::wait\fR \fItpool\fR \fIjoblist\fR ?varname?
This command waits for one or many jobs, whose job IDs are given in the
\fIjoblist\fR to get processed by the worker thread(s)\&. If none of the
specified jobs are ready, the command will enter the event loop, service
events and wait for the first job to get ready\&.
.sp
The command returns the list of completed job IDs\&. If the optional variable
?varname? is given, it will be set to the list of jobs in the
\fIjoblist\fR which are still pending\&. If the threadpool \fItpool\fR
is not found in the list of active thread pools, the command will throw error\&.
.TP
\fBtpool::cancel\fR \fItpool\fR \fIjoblist\fR ?varname?
This command cancels the previously posted jobs given by the \fIjoblist\fR
to the pool \fItpool\fR\&. Job cancellation succeeds only for job still
waiting to be processed\&. If the job is already being executed by one of
the worker threads, the job will not be cancelled\&.
The command returns the list of cancelled job IDs\&. If the optional variable
?varname? is given, it will be set to the list of jobs in the
\fIjoblist\fR which were not cancelled\&. If the threadpool \fItpool\fR
is not found in the list of active thread pools, the command will throw error\&.
.TP
\fBtpool::get\fR \fItpool\fR \fIjob\fR
This command retrieves the result of the previously posted \fIjob\fR\&.
Only results of jobs waited upon with the \fBtpool::wait\fR command
can be retrieved\&. If the execution of the script resulted in error,
the command will throw the error and update the \fBerrorInfo\fR and
\fBerrorCode\fR variables correspondingly\&. If the pool \fItpool\fR
is not found in the list of threadpools, the command will throw error\&.
If the job \fIjob\fR is not ready for retrieval, because it is currently
being executed by the worker thread, the command will throw error\&.
.TP
\fBtpool::preserve\fR \fItpool\fR
Each call to this command increments the reference counter of the
threadpool \fItpool\fR by one (1)\&. Command returns the value of the
reference counter after the increment\&.
By incrementing the reference counter, the caller signalizes that
he/she wishes to use the resource for a longer period of time\&.
.TP
\fBtpool::release\fR \fItpool\fR
Each call to this command decrements the reference counter of the
threadpool \fItpool\fR by one (1)\&.Command returns the value of the
reference counter after the decrement\&.
When the reference counter reaches zero (0), the threadpool \fItpool\fR
is marked for termination\&. You should not reference the threadpool
after the \fBtpool::release\fR command returns zero\&. The \fItpool\fR
handle goes out of scope and should not be used any more\&. Any following
reference to the same threadpool handle will result in Tcl error\&.
.TP
\fBtpool::suspend\fR \fItpool\fR
Suspends processing work on this queue\&. All pool workers are paused
but additional work can be added to the pool\&. Note that adding the
additional work will not increase the number of workers dynamically
as the pool processing is suspended\&. Number of workers is maintained
to the count that was found prior suspending worker activity\&.
If you need to assure certain number of worker threads, use the
\fBminworkers\fR option of the \fBtpool::create\fR command\&.
.TP
\fBtpool::resume\fR \fItpool\fR
Resume processing work on this queue\&. All paused (suspended)
workers are free to get work from the pool\&. Note that resuming pool
operation will just let already created workers to proceed\&.
It will not create additional worker threads to handle the work
posted to the pool's work queue\&.
.PP
.SH DISCUSSION
Threadpool is one of the most common threading paradigm when it comes
to server applications handling a large number of relatively small tasks\&.
A very simplistic model for building a server application would be to
create a new thread each time a request arrives and service the request
in the new thread\&. One of the disadvantages of this approach is that
the overhead of creating a new thread for each request is significant;
a server that created a new thread for each request would spend more time
and consume more system resources in creating and destroying threads than
in processing actual user requests\&. In addition to the overhead of
creating and destroying threads, active threads consume system resources\&.
Creating too many threads can cause the system to run out of memory or
trash due to excessive memory consumption\&.
.PP
A thread pool offers a solution to both the problem of thread life-cycle
overhead and the problem of resource trashing\&. By reusing threads for
multiple tasks, the thread-creation overhead is spread over many tasks\&.
As a bonus, because the thread already exists when a request arrives,
the delay introduced by thread creation is eliminated\&. Thus, the request
can be serviced immediately\&. Furthermore, by properly tuning the number
of threads in the thread pool, resource thrashing may also be eliminated
by forcing any request to wait until a thread is available to process it\&.
.SH "SEE ALSO"
thread, tsv, ttrace
.SH KEYWORDS
thread, threadpool
Changes to doc/man/tsv.n.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47









48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
'\" -*- tcl -*- doctools manpage
'\" The definitions below are for supplemental macros used in Tcl/Tk
'\" manual entries.
'\"
'\" .AP type name in/out ?indent?
'\"	Start paragraph describing an argument to a library procedure.
'\"	type is type of argument (int, etc.), in/out is either "in", "out",
'\"	or "in/out" to describe whether procedure reads or modifies arg,
'\"	and indent is equivalent to second arg of .IP (shouldn't ever be
'\"	needed;  use .AS below instead)
'\"
'\" .AS ?type? ?name?
'\"	Give maximum sizes of arguments for setting tab stops.  Type and
'\"	name are examples of largest possible arguments that will be passed
'\"	to .AP later.  If args are omitted, default tab stops are used.
'\"
'\" .BS
'\"	Start box enclosure.  From here until next .BE, everything will be
'\"	enclosed in one large box.
'\"
'\" .BE
'\"	End of box enclosure.
'\"
'\" .CS
'\"	Begin code excerpt.
'\"
'\" .CE
'\"	End code excerpt.







'\"
'\" .VS ?version? ?br?
'\"	Begin vertical sidebar, for use in marking newly-changed parts
'\"	of man pages.  The first argument is ignored and used for recording
'\"	the version when the .VS was added, so that the sidebars can be
'\"	found and removed when they reach a certain age.  If another argument
'\"	is present, then a line break is forced before starting the sidebar.
'\"
'\" .VE
'\"	End of vertical sidebar.
'\"
'\" .DS
'\"	Begin an indented unfilled display.
'\"
'\" .DE
'\"	End of indented unfilled display.









'\"
'\" .SO
'\"	Start of list of standard options for a Tk widget.  The
'\"	options follow on successive lines, in four columns separated
'\"	by tabs.
'\"
'\" .SE
'\"	End of list of standard options for a Tk widget.
'\"
'\" .OP cmdName dbName dbClass
'\"	Start of description of a specific option.  cmdName gives the
'\"	option's name as specified in the class command, dbName gives
'\"	the option's name in the option database, and dbClass gives
'\"	the option's class in the option database.
'\"
'\" .UL arg1 arg2
'\"	Print arg1 underlined, then print arg2 normally.
'\"







'\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
'\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1	\\fI\\$2\\fP	(\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
'\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
'\"	# BS - start boxed text
'\"	# ^y = starting y location
'\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
'\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
'\"	# VS - start vertical sidebar
'\"	# ^Y = starting y location
'\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
'\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
'\"	# Special macro to handle page bottom:  finish off current
'\"	# box/sidebar if in box/sidebar mode, then invoked standard
'\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|



|








|












|









|
|
|









|



















|
|
|






|














|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57








58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
.TH "tsv" n 2\&.8  "Tcl Threading"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
.\"	and indent is equivalent to second arg of .IP (shouldn't ever be
.\"	needed;  use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\"	Give maximum sizes of arguments for setting tab stops.  Type and
.\"	name are examples of largest possible arguments that will be passed
.\"	to .AP later.  If args are omitted, default tab stops are used.
.\"
.\" .BS
.\"	Start box enclosure.  From here until next .BE, everything will be
.\"	enclosed in one large box.
.\"
.\" .BE
.\"	End of box enclosure.
.\"
.\" .CS
.\"	Begin code excerpt.
.\"
.\" .CE
.\"	End code excerpt.
.\"
.\" .VS ?version? ?br?
.\"	Begin vertical sidebar, for use in marking newly-changed parts
.\"	of man pages.  The first argument is ignored and used for recording
.\"	the version when the .VS was added, so that the sidebars can be
.\"	found and removed when they reach a certain age.  If another argument
.\"	is present, then a line break is forced before starting the sidebar.
.\"







.\" .VE
.\"	End of vertical sidebar.
.\"
.\" .DS
.\"	Begin an indented unfilled display.
.\"
.\" .DE
.\"	End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\"	Start of list of standard options for a Tk widget. The manpage
.\"	argument defines where to look up the standard options; if
.\"	omitted, defaults to "options". The options follow on successive
.\"	lines, in three columns separated by tabs.
.\"
.\" .SE
.\"	End of list of standard options for a Tk widget.
.\"








.\" .OP cmdName dbName dbClass
.\"	Start of description of a specific option.  cmdName gives the
.\"	option's name as specified in the class command, dbName gives
.\"	the option's name in the option database, and dbClass gives
.\"	the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\"	Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\"	Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\"	Print an open parenthesis, arg1 in quotes, then arg2 normally
.\"	(for trailing punctuation) and then a closing parenthesis.
.\"
.\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\"	# BS - start boxed text
.\"	# ^y = starting y location
.\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\"	# VS - start vertical sidebar
.\"	# ^Y = starting y location
.\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\"	# Special macro to handle page bottom:  finish off current
.\"	# box/sidebar if in box/sidebar mode, then invoked standard
.\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241



















242

243
244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
'\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
'\"	# DE - end display
.de DE
.fi
.RE
.sp
..
'\"	# SO - start of list of standard options
.de SO


.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
'\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\fBoptions\\fR manual entry for details on the standard options.
..
'\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
'\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
.if t .ft C
..
'\"	# CE - end code excerpt
.de CE
.fi
.if t .ft R
.RE
..

.de UL
\\$1\l'|0\(ul'\\$2
..




















.TH "tsv" n 2.7  "Tcl Threading"

.BS
.SH "NAME"
tsv \-
Part of the Tcl threading extension allowing script level
manipulation of data shared between threads.
.SH "SYNOPSIS"
package require \fBTcl  8.4\fR
.sp
package require \fBThread  ?2.7?\fR
.sp
\fBtsv::names\fR ?pattern?
.sp
\fBtsv::object\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::set\fR \fIvarname\fR \fIelement\fR ?value?
.sp
\fBtsv::get\fR \fIvarname\fR \fIelement\fR ?namedvar?
.sp
\fBtsv::unset\fR \fIvarname\fR ?element?
.sp
\fBtsv::exists\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::pop\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::move\fR \fIvarname\fR \fIoldname\fR \fInewname\fR
.sp
\fBtsv::incr\fR \fIvarname\fR \fIelement\fR ?count?
.sp
\fBtsv::append\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value ...?
.sp
\fBtsv::lock\fR \fIvarname\fR \fIarg\fR ?arg ...?
.sp


\fBtsv::lappend\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value ...?
.sp
\fBtsv::linsert\fR \fIvarname\fR \fIelement\fR \fIindex\fR \fIvalue\fR ?value ...?
.sp
\fBtsv::lreplace\fR \fIvarname\fR \fIelement\fR \fIfirst\fR \fIlast\fR ?value ...?
.sp
\fBtsv::llength\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::lindex\fR \fIvarname\fR \fIelement\fR ?index?
.sp
\fBtsv::lrange\fR \fIvarname\fR \fIelement\fR \fIfrom\fR \fIto\fR
.sp
\fBtsv::lsearch\fR \fIvarname\fR \fIelement\fR ?options? \fIpattern\fR
.sp
\fBtsv::lset\fR \fIvarname\fR \fIelement\fR \fIindex\fR ?index ...? \fIvalue\fR
.sp
\fBtsv::lpop\fR \fIvarname\fR \fIelement\fR ?index?
.sp
\fBtsv::lpush\fR \fIvarname\fR \fIelement\fR ?index?
.sp
\fBtsv::array set\fR \fIvarname\fR \fIlist\fR
.sp







|





|





|

>
>






|




|

|










|




<

|


<


>



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

|
<
|
<
|
|

|



















|

|

>
>
|

|

|









|







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243

244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
.\"	# DE - end display
.de DE
.fi
.RE
.sp
..
.\"	# SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
.\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i

..
.\"	# CE - end code excerpt
.de CE
.fi

.RE
..
.\"	# UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\"	# QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\"	# PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\"	# QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\"	# MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME

tsv \- Part of the Tcl threading extension allowing script level manipulation of data shared between threads\&.

.SH SYNOPSIS
package require \fBTcl  8\&.4\fR
.sp
package require \fBThread  ?2\&.8?\fR
.sp
\fBtsv::names\fR ?pattern?
.sp
\fBtsv::object\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::set\fR \fIvarname\fR \fIelement\fR ?value?
.sp
\fBtsv::get\fR \fIvarname\fR \fIelement\fR ?namedvar?
.sp
\fBtsv::unset\fR \fIvarname\fR ?element?
.sp
\fBtsv::exists\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::pop\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::move\fR \fIvarname\fR \fIoldname\fR \fInewname\fR
.sp
\fBtsv::incr\fR \fIvarname\fR \fIelement\fR ?count?
.sp
\fBtsv::append\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value \&.\&.\&.?
.sp
\fBtsv::lock\fR \fIvarname\fR \fIarg\fR ?arg \&.\&.\&.?
.sp
\fBtsv::handlers\fR
.sp
\fBtsv::lappend\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value \&.\&.\&.?
.sp
\fBtsv::linsert\fR \fIvarname\fR \fIelement\fR \fIindex\fR \fIvalue\fR ?value \&.\&.\&.?
.sp
\fBtsv::lreplace\fR \fIvarname\fR \fIelement\fR \fIfirst\fR \fIlast\fR ?value \&.\&.\&.?
.sp
\fBtsv::llength\fR \fIvarname\fR \fIelement\fR
.sp
\fBtsv::lindex\fR \fIvarname\fR \fIelement\fR ?index?
.sp
\fBtsv::lrange\fR \fIvarname\fR \fIelement\fR \fIfrom\fR \fIto\fR
.sp
\fBtsv::lsearch\fR \fIvarname\fR \fIelement\fR ?options? \fIpattern\fR
.sp
\fBtsv::lset\fR \fIvarname\fR \fIelement\fR \fIindex\fR ?index \&.\&.\&.? \fIvalue\fR
.sp
\fBtsv::lpop\fR \fIvarname\fR \fIelement\fR ?index?
.sp
\fBtsv::lpush\fR \fIvarname\fR \fIelement\fR ?index?
.sp
\fBtsv::array set\fR \fIvarname\fR \fIlist\fR
.sp
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346


347
348
349
350
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
.sp
\fBtsv::keyldel\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR
.sp
\fBtsv::keylget\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR ?retvar?
.sp
\fBtsv::keylkeys\fR \fIvarname\fR \fIkeylist\fR ?key?
.sp
\fBtsv::keylset\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR \fIvalue\fR ?key value..?
.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.
.nf


    % tsv::set foo bar "A shared string"
    % set string [tsv::object foo bar]
    % $string append " appended"
    => A shared string appended
.fi

.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
\fBtsv::lock\fR \fIvarname\fR \fIarg\fR ?arg ...?
This command concatenates passed arguments and evaluates the
resulting script under the internal mutex protection. During the
script evaluation, the entire shared variable is locked. For shared
variable commands within the script, internal locking is disabled
so no deadlock can occur. It is also allowed to unset the shared
variable from within the script. The shared variable is automatically
created if it did not exists at the time of the first lock operation.
.nf


    % tsv::lock foo {
        tsv::lappend foo bar 1
        tsv::lappend foo bar 2
        puts stderr [tsv::set foo bar]
        tsv::unset foo
    }
.fi






.SH "LIST COMMANDS"
Those command are similar to the equivalently named Tcl command. The difference
is that they operate on elements of shared arrays.
.TP
\fBtsv::lappend\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value ...?
Similar to standard Tcl \fBlappend\fR command but appends one
or more values to the \fIelement\fR in shared variable \fIvarname\fR
instead of the Tcl variable.
.TP
\fBtsv::linsert\fR \fIvarname\fR \fIelement\fR \fIindex\fR \fIvalue\fR ?value ...?
Similar to standard Tcl \fBlinsert\fR command but inserts one
or more values at the \fIindex\fR list position in the
\fIelement\fR in the shared variable \fIvarname\fR instead of the Tcl variable.
.TP
\fBtsv::lreplace\fR \fIvarname\fR \fIelement\fR \fIfirst\fR \fIlast\fR ?value ...?
Similar to standard Tcl \fBlreplace\fR command but replaces one
or more values between the \fIfirst\fR and \fIlast\fR position
in the \fIelement\fR of the shared variable \fIvarname\fR instead of
the Tcl variable.
.TP
\fBtsv::llength\fR \fIvarname\fR \fIelement\fR
Similar to standard Tcl \fBllength\fR command but returns length
of the \fIelement\fR in the shared variable \fIvarname\fR instead of the Tcl
variable.
.TP
\fBtsv::lindex\fR \fIvarname\fR \fIelement\fR ?index?
Similar to standard Tcl \fBlindex\fR command but returns the value
at the \fIindex\fR list position of the \fIelement\fR from
the shared variable \fIvarname\fR instead of the Tcl variable.
.TP
\fBtsv::lrange\fR \fIvarname\fR \fIelement\fR \fIfrom\fR \fIto\fR
Similar to standard Tcl \fBlrange\fR command but returns values
between \fIfrom\fR and \fIto\fR list positions from the
\fIelement\fR in the shared variable \fIvarname\fR instead of the Tcl variable.
.TP
\fBtsv::lsearch\fR \fIvarname\fR \fIelement\fR ?options? \fIpattern\fR
Similar to standard Tcl \fBlsearch\fR command but searches the \fIelement\fR
in the shared variable \fIvarname\fR instead of the Tcl variable.
.TP
\fBtsv::lset\fR \fIvarname\fR \fIelement\fR \fIindex\fR ?index ...? \fIvalue\fR
Similar to standard Tcl \fBlset\fR command but sets the \fIelement\fR
in the shared variable \fIvarname\fR instead of the Tcl variable.
.TP
\fBtsv::lpop\fR \fIvarname\fR \fIelement\fR ?index?
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.

.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 only persistent option supported is the famous GNU Gdbm
database. This option has 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.
.TP
\fBtsv::array get\fR \fIvarname\fR ?pattern?
Does the same as standard Tcl \fBarray get\fR.
.TP
\fBtsv::array names\fR \fIvarname\fR ?pattern?
Does the same as standard Tcl \fBarray names\fR.
.TP
\fBtsv::array size\fR \fIvarname\fR
Does the same as standard Tcl \fBarray size\fR.
.TP
\fBtsv::array reset\fR \fIvarname\fR \fIlist\fR
Does the same as standard Tcl \fBarray set\fR but it clears
the \fIvarname\fR and sets new values from the list atomically.
.TP
\fBtsv::array bind\fR \fIvarname\fR \fIhandle\fR
Binds the \fIvarname\fR to the persistent storage \fIhandle\fR.
The format of the \fIhandle\fR is <handler>:<address>. For the built-in
GNU Gdbm persistence layer, the format of the handle is "gdbm:<path>"
where <path> is the path to the Gdbm database file.
.TP
\fBtsv::array unbind\fR \fIvarname\fR
Unbinds the shared \fIarray\fR from its bound persistent storage.
.TP
\fBtsv::array isbound\fR \fIvarname\fR
Returns true (1) if the shared \fIvarname\fR is bound to some
persistent storage or zero (0) if not.

.SH "KEYED LIST COMMANDS"
Keyed list commands are borrowed from the TclX package. Keyed lists provide
a structured data type built upon standard Tcl lists. This is a functionality
similar to structs in the C programming language.
.PP
A keyed list is a list in which each element contains a key and value
pair. These element pairs are stored as lists themselves, where the key
is the first element of the list, and the value is the second. The
key-value pairs are referred to as fields.  This is an example of a
keyed list:
.nf


    {{NAME  {Frank  Zappa}} {JOB {musician and composer}}}
.fi

Fields may contain subfields; `.' is the separator character. Subfields
are actually fields  where the value is another keyed list. Thus the
following list has the top level fields ID and NAME, and subfields
NAME.FIRST and NAME.LAST:
.nf


    {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}
.fi

There is no limit to the recursive depth of subfields,
allowing one to build complex data structures. Keyed lists are constructed
and accessed via a number of commands. All  keyed  list management
commands take the name of the variable containing the keyed list as an
argument (i.e. passed by reference), rather than passing the list directly.
.TP
\fBtsv::keyldel\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR
Delete the field specified by \fIkey\fR from the keyed list \fIkeylist\fR
in the shared variable \fIvarname\fR.
This removes both the key and the value from the keyed list.
.TP
\fBtsv::keylget\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR ?retvar?
Return the value associated with \fIkey\fR from the keyed list \fIkeylist\fR
in the shared variable \fIvarname\fR.
If the optional \fIretvar\fR is not specified, then the value will be
returned as the result of the command. In this case, if key is not found
in the list, an error will result.
.sp
If \fIretvar\fR is specified and \fIkey\fR is in the list, then the value
is returned in the variable \fIretvar\fR and the command returns 1 if the
key was present within the list. If \fIkey\fR isn't in the list, the
command will return 0, and \fIretvar\fR will be left unchanged. If {} is
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.

.SH "DISCUSSION"
The current implementation of thread shared variables allows for easy and
convenient access to data shared between different threads.
Internally, the data is stored in Tcl objects and all package commands
operate on internal data representation, thus minimizing shimmering and
improving performance. Special care has been taken to assure that all
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







|


|
|


|


|

|

|

|




|



|

|
|
|
>
>




|
>



|

|


|

|
|

|

|


|
|



|



|



|
|




|

|


|

|

|
|

|
|
|
|
>
>






|
>
>
>
>
>
>

|
|

|


|

|


|

|



|




|




|




|



|

|

|




|

|


|
|
>


|
|
|
|
|
|


|


|


|


|



|


|
|
|
|


|



|
>

|
|
|


|
|
|

|
>
>

|
>
|
|

|
|
>
>

|
>

|
|

|



|
|



|

|
|



|
|


|



|
|

|

|
|
|
|
>
|

|


|

|


|
|
|
|
|

|


|

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
.sp
\fBtsv::keyldel\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR
.sp
\fBtsv::keylget\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR ?retvar?
.sp
\fBtsv::keylkeys\fR \fIvarname\fR \fIkeylist\fR ?key?
.sp
\fBtsv::keylset\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR \fIvalue\fR ?key value\&.\&.?
.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\&.
.CS


    % tsv::set foo bar "A shared string"
    % set string [tsv::object foo bar]
    % $string append " appended"
    => 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
\fBtsv::lock\fR \fIvarname\fR \fIarg\fR ?arg \&.\&.\&.?
This command concatenates passed arguments and evaluates the
resulting script under the internal mutex protection\&. During the
script evaluation, the entire shared variable is locked\&. For shared
variable commands within the script, internal locking is disabled
so no deadlock can occur\&. It is also allowed to unset the shared
variable from within the script\&. The shared variable is automatically
created if it did not exists at the time of the first lock operation\&.
.CS


    % tsv::lock foo {
        tsv::lappend foo bar 1
        tsv::lappend foo bar 2
        puts stderr [tsv::set foo bar]
        tsv::unset foo
    }

.CE
.TP
\fBtsv::handlers\fR
Returns the names of all persistent storage handlers enabled at compile time\&.
See \fBARRAY COMMANDS\fR for details\&.
.PP
.SH "LIST COMMANDS"
Those command are similar to the equivalently named Tcl command\&. The difference
is that they operate on elements of shared arrays\&.
.TP
\fBtsv::lappend\fR \fIvarname\fR \fIelement\fR \fIvalue\fR ?value \&.\&.\&.?
Similar to standard Tcl \fBlappend\fR command but appends one
or more values to the \fIelement\fR in shared variable \fIvarname\fR
instead of the Tcl variable\&.
.TP
\fBtsv::linsert\fR \fIvarname\fR \fIelement\fR \fIindex\fR \fIvalue\fR ?value \&.\&.\&.?
Similar to standard Tcl \fBlinsert\fR command but inserts one
or more values at the \fIindex\fR list position in the
\fIelement\fR in the shared variable \fIvarname\fR instead of the Tcl variable\&.
.TP
\fBtsv::lreplace\fR \fIvarname\fR \fIelement\fR \fIfirst\fR \fIlast\fR ?value \&.\&.\&.?
Similar to standard Tcl \fBlreplace\fR command but replaces one
or more values between the \fIfirst\fR and \fIlast\fR position
in the \fIelement\fR of the shared variable \fIvarname\fR instead of
the Tcl variable\&.
.TP
\fBtsv::llength\fR \fIvarname\fR \fIelement\fR
Similar to standard Tcl \fBllength\fR command but returns length
of the \fIelement\fR in the shared variable \fIvarname\fR instead of the Tcl
variable\&.
.TP
\fBtsv::lindex\fR \fIvarname\fR \fIelement\fR ?index?
Similar to standard Tcl \fBlindex\fR command but returns the value
at the \fIindex\fR list position of the \fIelement\fR from
the shared variable \fIvarname\fR instead of the Tcl variable\&.
.TP
\fBtsv::lrange\fR \fIvarname\fR \fIelement\fR \fIfrom\fR \fIto\fR
Similar to standard Tcl \fBlrange\fR command but returns values
between \fIfrom\fR and \fIto\fR list positions from the
\fIelement\fR in the shared variable \fIvarname\fR instead of the Tcl variable\&.
.TP
\fBtsv::lsearch\fR \fIvarname\fR \fIelement\fR ?options? \fIpattern\fR
Similar to standard Tcl \fBlsearch\fR command but searches the \fIelement\fR
in the shared variable \fIvarname\fR instead of the Tcl variable\&.
.TP
\fBtsv::lset\fR \fIvarname\fR \fIelement\fR \fIindex\fR ?index \&.\&.\&.? \fIvalue\fR
Similar to standard Tcl \fBlset\fR command but sets the \fIelement\fR
in the shared variable \fIvarname\fR instead of the Tcl variable\&.
.TP
\fBtsv::lpop\fR \fIvarname\fR \fIelement\fR ?index?
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\&.
.TP
\fBtsv::array get\fR \fIvarname\fR ?pattern?
Does the same as standard Tcl \fBarray get\fR\&.
.TP
\fBtsv::array names\fR \fIvarname\fR ?pattern?
Does the same as standard Tcl \fBarray names\fR\&.
.TP
\fBtsv::array size\fR \fIvarname\fR
Does the same as standard Tcl \fBarray size\fR\&.
.TP
\fBtsv::array reset\fR \fIvarname\fR \fIlist\fR
Does the same as standard Tcl \fBarray set\fR but it clears
the \fIvarname\fR and sets new values from the list atomically\&.
.TP
\fBtsv::array bind\fR \fIvarname\fR \fIhandle\fR
Binds the \fIvarname\fR to the persistent storage \fIhandle\fR\&.
The format of the \fIhandle\fR is <handler>:<address>, where <handler> is
"gdbm" for GNU Gdbm and "lmdb" for LMDB and <address> is the path to the
database file\&.
.TP
\fBtsv::array unbind\fR \fIvarname\fR
Unbinds the shared \fIarray\fR from its bound persistent storage\&.
.TP
\fBtsv::array isbound\fR \fIvarname\fR
Returns true (1) if the shared \fIvarname\fR is bound to some
persistent storage or zero (0) if not\&.
.PP
.SH "KEYED LIST COMMANDS"
Keyed list commands are borrowed from the TclX package\&. Keyed lists provide
a structured data type built upon standard Tcl lists\&. This is a functionality
similar to structs in the C programming language\&.
.PP
A keyed list is a list in which each element contains a key and value
pair\&. These element pairs are stored as lists themselves, where the key
is the first element of the list, and the value is the second\&. The
key-value pairs are referred to as fields\&.  This is an example of a
keyed list:
.CS


    {{NAME  {Frank  Zappa}} {JOB {musician and composer}}}

.CE
Fields may contain subfields; `\&.' is the separator character\&. Subfields
are actually fields  where the value is another keyed list\&. Thus the
following list has the top level fields ID and NAME, and subfields
NAME\&.FIRST and NAME\&.LAST:
.CS


    {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}

.CE
There is no limit to the recursive depth of subfields,
allowing one to build complex data structures\&. Keyed lists are constructed
and accessed via a number of commands\&. All  keyed  list management
commands take the name of the variable containing the keyed list as an
argument (i\&.e\&. passed by reference), rather than passing the list directly\&.
.TP
\fBtsv::keyldel\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR
Delete the field specified by \fIkey\fR from the keyed list \fIkeylist\fR
in the shared variable \fIvarname\fR\&.
This removes both the key and the value from the keyed list\&.
.TP
\fBtsv::keylget\fR \fIvarname\fR \fIkeylist\fR \fIkey\fR ?retvar?
Return the value associated with \fIkey\fR from the keyed list \fIkeylist\fR
in the shared variable \fIvarname\fR\&.
If the optional \fIretvar\fR is not specified, then the value will be
returned as the result of the command\&. In this case, if key is not found
in the list, an error will result\&.
.sp
If \fIretvar\fR is specified and \fIkey\fR is in the list, then the value
is returned in the variable \fIretvar\fR and the command returns 1 if the
key was present within the list\&. If \fIkey\fR isn't in the list, the
command will return 0, and \fIretvar\fR will be left unchanged\&. If {} is
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\&.
.PP
.SH DISCUSSION
The current implementation of thread shared variables allows for easy and
convenient access to data shared between different threads\&.
Internally, the data is stored in Tcl objects and all package commands
operate on internal data representation, thus minimizing shimmering and
improving performance\&. Special care has been taken to assure that all
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
Changes to doc/man/ttrace.n.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47









48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
'\" -*- tcl -*- doctools manpage
'\" The definitions below are for supplemental macros used in Tcl/Tk
'\" manual entries.
'\"
'\" .AP type name in/out ?indent?
'\"	Start paragraph describing an argument to a library procedure.
'\"	type is type of argument (int, etc.), in/out is either "in", "out",
'\"	or "in/out" to describe whether procedure reads or modifies arg,
'\"	and indent is equivalent to second arg of .IP (shouldn't ever be
'\"	needed;  use .AS below instead)
'\"
'\" .AS ?type? ?name?
'\"	Give maximum sizes of arguments for setting tab stops.  Type and
'\"	name are examples of largest possible arguments that will be passed
'\"	to .AP later.  If args are omitted, default tab stops are used.
'\"
'\" .BS
'\"	Start box enclosure.  From here until next .BE, everything will be
'\"	enclosed in one large box.
'\"
'\" .BE
'\"	End of box enclosure.
'\"
'\" .CS
'\"	Begin code excerpt.
'\"
'\" .CE
'\"	End code excerpt.







'\"
'\" .VS ?version? ?br?
'\"	Begin vertical sidebar, for use in marking newly-changed parts
'\"	of man pages.  The first argument is ignored and used for recording
'\"	the version when the .VS was added, so that the sidebars can be
'\"	found and removed when they reach a certain age.  If another argument
'\"	is present, then a line break is forced before starting the sidebar.
'\"
'\" .VE
'\"	End of vertical sidebar.
'\"
'\" .DS
'\"	Begin an indented unfilled display.
'\"
'\" .DE
'\"	End of indented unfilled display.









'\"
'\" .SO
'\"	Start of list of standard options for a Tk widget.  The
'\"	options follow on successive lines, in four columns separated
'\"	by tabs.
'\"
'\" .SE
'\"	End of list of standard options for a Tk widget.
'\"
'\" .OP cmdName dbName dbClass
'\"	Start of description of a specific option.  cmdName gives the
'\"	option's name as specified in the class command, dbName gives
'\"	the option's name in the option database, and dbClass gives
'\"	the option's class in the option database.
'\"
'\" .UL arg1 arg2
'\"	Print arg1 underlined, then print arg2 normally.
'\"







'\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
'\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1	\\fI\\$2\\fP	(\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
'\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
'\"	# BS - start boxed text
'\"	# ^y = starting y location
'\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
'\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
'\"	# VS - start vertical sidebar
'\"	# ^Y = starting y location
'\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
'\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
'\"	# Special macro to handle page bottom:  finish off current
'\"	# box/sidebar if in box/sidebar mode, then invoked standard
'\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
|
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|



|








|












|









|
|
|









|



















|
|
|






|














|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57








58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'\"
'\" Generated from file '' by tcllib/doctools with format 'nroff'
'\"
.TH "ttrace" n 2\&.8  "Tcl Threading"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
.\"	and indent is equivalent to second arg of .IP (shouldn't ever be
.\"	needed;  use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\"	Give maximum sizes of arguments for setting tab stops.  Type and
.\"	name are examples of largest possible arguments that will be passed
.\"	to .AP later.  If args are omitted, default tab stops are used.
.\"
.\" .BS
.\"	Start box enclosure.  From here until next .BE, everything will be
.\"	enclosed in one large box.
.\"
.\" .BE
.\"	End of box enclosure.
.\"
.\" .CS
.\"	Begin code excerpt.
.\"
.\" .CE
.\"	End code excerpt.
.\"
.\" .VS ?version? ?br?
.\"	Begin vertical sidebar, for use in marking newly-changed parts
.\"	of man pages.  The first argument is ignored and used for recording
.\"	the version when the .VS was added, so that the sidebars can be
.\"	found and removed when they reach a certain age.  If another argument
.\"	is present, then a line break is forced before starting the sidebar.
.\"







.\" .VE
.\"	End of vertical sidebar.
.\"
.\" .DS
.\"	Begin an indented unfilled display.
.\"
.\" .DE
.\"	End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\"	Start of list of standard options for a Tk widget. The manpage
.\"	argument defines where to look up the standard options; if
.\"	omitted, defaults to "options". The options follow on successive
.\"	lines, in three columns separated by tabs.
.\"
.\" .SE
.\"	End of list of standard options for a Tk widget.
.\"








.\" .OP cmdName dbName dbClass
.\"	Start of description of a specific option.  cmdName gives the
.\"	option's name as specified in the class command, dbName gives
.\"	the option's name in the option database, and dbClass gives
.\"	the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\"	Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\"	Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\"	Print an open parenthesis, arg1 in quotes, then arg2 normally
.\"	(for trailing punctuation) and then a closing parenthesis.
.\"
.\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\"	# BS - start boxed text
.\"	# ^y = starting y location
.\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\"	# VS - start vertical sidebar
.\"	# ^Y = starting y location
.\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\"	# Special macro to handle page bottom:  finish off current
.\"	# box/sidebar if in box/sidebar mode, then invoked standard
.\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241



















242

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
'\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
'\"	# DE - end display
.de DE
.fi
.RE
.sp
..
'\"	# SO - start of list of standard options
.de SO


.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
'\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\fBoptions\\fR manual entry for details on the standard options.
..
'\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
'\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
.if t .ft C
..
'\"	# CE - end code excerpt
.de CE
.fi
.if t .ft R
.RE
..

.de UL
\\$1\l'|0\(ul'\\$2
..




















.TH "ttrace" n 2.7  "Tcl Threading"

.BS
.SH "NAME"
ttrace \- Trace-based interpreter initialization
.SH "SYNOPSIS"
package require \fBTcl  8.4\fR
.sp
package require \fBThread  ?2.7?\fR
.sp
\fBttrace::eval\fR \fIarg\fR ?arg ...?
.sp
\fBttrace::enable\fR
.sp
\fBttrace::disable\fR
.sp
\fBttrace::cleanup\fR
.sp







|





|





|

>
>






|




|

|










|




<

|


<


>



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

|

|
|

|

|







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243

244
245
246
247
248
249
250
251
252
253
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
286
287
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
.\"	# DE - end display
.de DE
.fi
.RE
.sp
..
.\"	# SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
.\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i

..
.\"	# CE - end code excerpt
.de CE
.fi

.RE
..
.\"	# UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\"	# QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\"	# PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\"	# QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\"	# MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME
ttrace \- Trace-based interpreter initialization
.SH SYNOPSIS
package require \fBTcl  8\&.4\fR
.sp
package require \fBThread  ?2\&.8?\fR
.sp
\fBttrace::eval\fR \fIarg\fR ?arg \&.\&.\&.?
.sp
\fBttrace::enable\fR
.sp
\fBttrace::disable\fR
.sp
\fBttrace::cleanup\fR
.sp
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299


300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
\fBttrace::getentries\fR \fIcmd\fR ?pattern?
.sp
\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:
.nf



    % package require Ttrace
    2.7.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

    % set t2 [thread::create {package require Ttrace; thread::wait}]
    tid0x1804000

    % thread::send $t2 test
    test-tid0x1804000

.fi

.PP
As seen from above, the \fBttrace::eval\fR and \fBttrace::update\fR
commands are used to create a thread-wide definition of a simple
Tcl procedure and replicate that definition to all, already existing
or later created, threads.
.SH "USER COMMANDS"
This section describes user-level commands. Those commands can be
used by script writers to control the execution of the tracing
framework.
.TP
\fBttrace::eval\fR \fIarg\fR ?arg ...?
This command concatenates given arguments and evaluates the resulting
Tcl command with trace framework enabled. If the command execution
was ok, it takes necessary steps to automatically propagate the
trace epoch change to all threads in the application.
For AOLserver, only newly created threads actually receive the
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.
.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
Deactivates all registered callbacks in the framework
and closes the current trace epoch.
.TP
\fBttrace::cleanup\fR
Used to clean-up all on-demand loaded resources in the interpreter.
It effectively brings Tcl interpreter to its pristine state.
.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.

.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
invoked by the framework in order to gather interpreter state
changes, build in-memory database, perform custom-cleanups and
various other tasks.
.TP
\fBttrace::atenable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated at \fBttrace::enable\fR.
Registered callbacks are activated on FIFO basis. The callback
definition includes the name of the callback, \fIcmd\fR, a list
of callback arguments, \fIarglist\fR and the \fIbody\fR of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl \fBproc\fR command.
.TP
\fBttrace::atdisable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated at \fBttrace::disable\fR.
Registered callbacks are activated on FIFO basis. The callback
definition includes the name of the callback, \fIcmd\fR, a list
of callback arguments, \fIarglist\fR and the \fIbody\fR of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl \fBproc\fR command.
.TP
\fBttrace::addtrace\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated for tracing the Tcl
\fBcmd\fR command. The callback definition includes the name of
the Tcl command to trace, \fIcmd\fR, a list of callback arguments,
\fIarglist\fR and the \fIbody\fR of the callback. Effectively,
this actually resembles the call interface of the standard Tcl
\fBproc\fR command.
.TP
\fBttrace::addscript\fR \fIname\fR \fIbody\fR
Registers Tcl callback to be activated for building a Tcl
script to be passed to other interpreters. This script is
used to set the stage for the Tcl \fBunknown\fR command.
Registered callbacks are activated on FIFO basis.
The callback definition includes the name of the callback,
\fIname\fR and the \fIbody\fR of the callback.
.TP
\fBttrace::addresolver\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated by the overloaded Tcl
\fBunknown\fR command.
Registered callbacks are activated on FIFO basis.
This callback is used to resolve the resource and load the
resource in the current interpreter.
.TP
\fBttrace::addcleanup\fR \fIbody\fR
Registers Tcl callback to be activated by the \fBtrace::cleanup\fR.
Registered callbacks are activated on FIFO basis.
.TP
\fBttrace::addentry\fR \fIcmd\fR \fIvar\fR \fIval\fR
Adds one entry to the named in-memory database.
.TP
\fBttrace::getentry\fR \fIcmd\fR \fIvar\fR
Returns the value of the entry from the named in-memory database.
.TP
\fBttrace::getentries\fR \fIcmd\fR ?pattern?
Returns names of all entries from the named in-memory database.
.TP
\fBttrace::delentry\fR \fIcmd\fR
Deletes an entry from the named in-memory database.
.TP
\fBttrace::preload\fR \fIcmd\fR
Registers the Tcl command to be loaded in the interpreter.
Commands registered this way will always be the part of
the interpreter and not be on-demand loaded by the Tcl
\fBunknown\fR command.

.SH "DISCUSSION"
Common introspective state-replication approaches use a custom Tcl
script to introspect the running interpreter and synthesize another
Tcl script to replicate this state in some other interpreter.
This package, on the contrary, uses Tcl command traces. Command
traces are registered on selected Tcl commands, like \fBproc\fR,
\fBnamespace\fR, \fBload\fR and other standard (and/or user-defined)
Tcl commands. When activated, those traces build an in-memory
database of created resources. This database is used as a resource
repository for the (overloaded) Tcl \fBunknown\fR command which
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.
.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







|

|

|



|



|

>
>

|














|
>




|

|

|

|

|

|

|
|
|


|

|
|



|
|



|


|
|



|
|


|

|

|
>



|
|
|


|


|


|


|
|


|
|


|
|


|
|



|

|

|



|
|
|

|



|
|

|


|
|


|


|


|


|


|


|
>
|


|
|


|
|

|


|


|



|


|

|


|

308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
\fBttrace::getentries\fR \fIcmd\fR ?pattern?
.sp
\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

    % set t2 [thread::create {package require Ttrace; thread::wait}]
    tid0x1804000

    % thread::send $t2 test
    test-tid0x1804000


.CE
.PP
As seen from above, the \fBttrace::eval\fR and \fBttrace::update\fR
commands are used to create a thread-wide definition of a simple
Tcl procedure and replicate that definition to all, already existing
or later created, threads\&.
.SH "USER COMMANDS"
This section describes user-level commands\&. Those commands can be
used by script writers to control the execution of the tracing
framework\&.
.TP
\fBttrace::eval\fR \fIarg\fR ?arg \&.\&.\&.?
This command concatenates given arguments and evaluates the resulting
Tcl command with trace framework enabled\&. If the command execution
was ok, it takes necessary steps to automatically propagate the
trace epoch change to all threads in the application\&.
For AOLserver, only newly created threads actually receive the
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\&.
.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
Deactivates all registered callbacks in the framework
and closes the current trace epoch\&.
.TP
\fBttrace::cleanup\fR
Used to clean-up all on-demand loaded resources in the interpreter\&.
It effectively brings Tcl interpreter to its pristine state\&.
.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
invoked by the framework in order to gather interpreter state
changes, build in-memory database, perform custom-cleanups and
various other tasks\&.
.TP
\fBttrace::atenable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated at \fBttrace::enable\fR\&.
Registered callbacks are activated on FIFO basis\&. The callback
definition includes the name of the callback, \fIcmd\fR, a list
of callback arguments, \fIarglist\fR and the \fIbody\fR of the
callback\&. Effectively, this actually resembles the call interface
of the standard Tcl \fBproc\fR command\&.
.TP
\fBttrace::atdisable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated at \fBttrace::disable\fR\&.
Registered callbacks are activated on FIFO basis\&. The callback
definition includes the name of the callback, \fIcmd\fR, a list
of callback arguments, \fIarglist\fR and the \fIbody\fR of the
callback\&. Effectively, this actually resembles the call interface
of the standard Tcl \fBproc\fR command\&.
.TP
\fBttrace::addtrace\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated for tracing the Tcl
\fBcmd\fR command\&. The callback definition includes the name of
the Tcl command to trace, \fIcmd\fR, a list of callback arguments,
\fIarglist\fR and the \fIbody\fR of the callback\&. Effectively,
this actually resembles the call interface of the standard Tcl
\fBproc\fR command\&.
.TP
\fBttrace::addscript\fR \fIname\fR \fIbody\fR
Registers Tcl callback to be activated for building a Tcl
script to be passed to other interpreters\&. This script is
used to set the stage for the Tcl \fBunknown\fR command\&.
Registered callbacks are activated on FIFO basis\&.
The callback definition includes the name of the callback,
\fIname\fR and the \fIbody\fR of the callback\&.
.TP
\fBttrace::addresolver\fR \fIcmd\fR \fIarglist\fR \fIbody\fR
Registers Tcl callback to be activated by the overloaded Tcl
\fBunknown\fR command\&.
Registered callbacks are activated on FIFO basis\&.
This callback is used to resolve the resource and load the
resource in the current interpreter\&.
.TP
\fBttrace::addcleanup\fR \fIbody\fR
Registers Tcl callback to be activated by the \fBtrace::cleanup\fR\&.
Registered callbacks are activated on FIFO basis\&.
.TP
\fBttrace::addentry\fR \fIcmd\fR \fIvar\fR \fIval\fR
Adds one entry to the named in-memory database\&.
.TP
\fBttrace::getentry\fR \fIcmd\fR \fIvar\fR
Returns the value of the entry from the named in-memory database\&.
.TP
\fBttrace::getentries\fR \fIcmd\fR ?pattern?
Returns names of all entries from the named in-memory database\&.
.TP
\fBttrace::delentry\fR \fIcmd\fR
Deletes an entry from the named in-memory database\&.
.TP
\fBttrace::preload\fR \fIcmd\fR
Registers the Tcl command to be loaded in the interpreter\&.
Commands registered this way will always be the part of
the interpreter and not be on-demand loaded by the Tcl
\fBunknown\fR command\&.
.PP
.SH DISCUSSION
Common introspective state-replication approaches use a custom Tcl
script to introspect the running interpreter and synthesize another
Tcl script to replicate this state in some other interpreter\&.
This package, on the contrary, uses Tcl command traces\&. Command
traces are registered on selected Tcl commands, like \fBproc\fR,
\fBnamespace\fR, \fBload\fR and other standard (and/or user-defined)
Tcl commands\&. When activated, those traces build an in-memory
database of created resources\&. This database is used as a resource
repository for the (overloaded) Tcl \fBunknown\fR command which
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\&.
.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
Changes to doc/thread.man.
1
2
3
4
5
6
7
8
9
10
11
12
13
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin thread n 2.7]
[moddesc {Tcl Threading}]
[titledesc {Extension for script access to Tcl threading}]
[require Tcl 8.4]
[require Thread [opt 2.7]]

[description]
The [package thread] 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 

|



|







1
2
3
4
5
6
7
8
9
10
11
12
13
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin thread n 2.8]
[moddesc {Tcl Threading}]
[titledesc {Extension for script access to Tcl threading}]
[require Tcl 8.4]
[require Thread [opt 2.8]]

[description]
The [package thread] 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 
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
the script passed to newly created thread will continue from the 
[cmd thread::wait] command. If [cmd thread::wait] 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.


[call [cmd thread::exit]]

Use of this command is deprecated in favour of more advanced thread
reservation system implemented with [cmd thread::preserve] and 
[cmd thread::release] commands. Support for [cmd thread::exit] 
command will dissapear in some future major release of the extension.
[para]
This command forces a thread stuck in the [cmd thread::wait]
command to unconditionaly exit. The execution of [cmd thread::exit]

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.

[call [cmd thread::names]]

This command returns a list of thread IDs. These are only for
threads that have been created via [cmd thread::create] command.
If your application creates other threads at the C level, they
are not reported by this command.







|






|
|
>
|
|
|
|
|







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
the script passed to newly created thread will continue from the 
[cmd thread::wait] command. If [cmd thread::wait] 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.


[call [cmd thread::exit] [opt status]]

Use of this command is deprecated in favour of more advanced thread
reservation system implemented with [cmd thread::preserve] and 
[cmd thread::release] commands. Support for [cmd thread::exit] 
command will dissapear in some future major release of the extension.
[para]
This command forces a thread stuck in the [cmd thread::wait] command to
unconditionaly exit. The thread's exit status defaults to 666 and can be
specified using the optional [arg status] argument. The execution of
[cmd thread::exit] 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.

[call [cmd thread::names]]

This command returns a list of thread IDs. These are only for
threads that have been created via [cmd thread::create] command.
If your application creates other threads at the C level, they
are not reported by this command.
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
thread and executed on the FIFO basis, intermingled with optional other
events pending in the event queue of the target thread.
Using the optional [opt -head] switch, scripts posted to the thread's
event queue can be placed on the head, instead on the tail of the queue,
thus being executed in the LIFO fashion.


[call [cmd thread::broadcast] [arg id] [arg script]]

This command passes a [arg script] to all threads created by the
package for execution. It does not wait for response from any of
the threads.

[call [cmd thread::wait]]








|







273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
thread and executed on the FIFO basis, intermingled with optional other
events pending in the event queue of the target thread.
Using the optional [opt -head] switch, scripts posted to the thread's
event queue can be placed on the head, instead on the tail of the queue,
thus being executed in the LIFO fashion.


[call [cmd thread::broadcast] [arg script]]

This command passes a [arg script] to all threads created by the
package for execution. It does not wait for response from any of
the threads.

[call [cmd thread::wait]]

Changes to doc/tpool.man.
1
2
3
4
5
6
7
8
9
10
11
12
13
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin tpool n 2.7]
[moddesc {Tcl Threading}]
[titledesc {Part of the Tcl threading extension implementing pools of worker threads.}]
[require Tcl 8.4]
[require Thread [opt 2.7]]

[description]
This package creates and manages pools of worker threads. It allows you
to post jobs to worker threads and wait for their completion. The 
threadpool implementation is Tcl event-loop aware. That means that any
time a caller is forced to wait for an event (job being completed or 
a worker thread becoming idle or initialized), the implementation will

|



|







1
2
3
4
5
6
7
8
9
10
11
12
13
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin tpool n 2.8]
[moddesc {Tcl Threading}]
[titledesc {Part of the Tcl threading extension implementing pools of worker threads.}]
[require Tcl 8.4]
[require Thread [opt 2.8]]

[description]
This package creates and manages pools of worker threads. It allows you
to post jobs to worker threads and wait for their completion. The 
threadpool implementation is Tcl event-loop aware. That means that any
time a caller is forced to wait for an event (job being completed or 
a worker thread becoming idle or initialized), the implementation will
Changes to doc/tsv.man.
1
2
3
4
5
6
7
8
9
10
11
12
13
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin tsv n 2.7]
[moddesc {Tcl Threading}]
[titledesc {Part of the Tcl threading extension allowing script level manipulation of data shared between threads.}]
[require Tcl 8.4]
[require Thread [opt 2.7]]

[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

|



|







1
2
3
4
5
6
7
8
9
10
11
12
13
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin tsv n 2.8]
[moddesc {Tcl Threading}]
[titledesc {Part of the Tcl threading extension allowing script level manipulation of data shared between threads.}]
[require Tcl 8.4]
[require Thread [opt 2.8]]

[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
110
111
112
113
114
115
116





117
118
119
120
121
122
123
        tsv::lappend foo bar 1
        tsv::lappend foo bar 2
        puts stderr [tsv::set foo bar]
        tsv::unset foo
    }
}]






[list_end]

[section {LIST COMMANDS}]

Those command are similar to the equivalently named Tcl command. The difference
is that they operate on elements of shared arrays.








>
>
>
>
>







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
        tsv::lappend foo bar 1
        tsv::lappend foo bar 2
        puts stderr [tsv::set foo bar]
        tsv::unset foo
    }
}]

[call [cmd tsv::handlers]]

Returns the names of all persistent storage handlers enabled at compile time.
See [sectref {ARRAY COMMANDS}] for details.

[list_end]

[section {LIST COMMANDS}]

Those command are similar to the equivalently named Tcl command. The difference
is that they operate on elements of shared arrays.

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

[list_end]

[section {ARRAY COMMANDS}]

This command supports most of the options of the standard Tcl
[cmd array] command. In addition to those, it allows binding
a shared variable to some persisten storage databases. Currently 
the only persistent option supported is the famous GNU Gdbm 
database. This option has to be selected during the package 
compilation time. The implementation provides hooks for 
defining other persistency layers, if needed.

[list_begin definitions]

[call [cmd {tsv::array set}] [arg varname] [arg list]]

Does the same as standard Tcl [cmd {array set}].








|
|
|
|
|







190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

[list_end]

[section {ARRAY COMMANDS}]

This command supports most of the options of the standard Tcl
[cmd array] 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.

[list_begin definitions]

[call [cmd {tsv::array set}] [arg varname] [arg list]]

Does the same as standard Tcl [cmd {array set}].

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
[call [cmd {tsv::array reset}] [arg varname] [arg list]]

Does the same as standard Tcl [cmd {array set}] but it clears
the [arg varname] and sets new values from the list atomically.

[call [cmd {tsv::array bind}] [arg varname] [arg handle]]
Binds the [arg varname] to the persistent storage [arg handle].
The format of the [arg handle] is <handler>:<address>. For the built-in
GNU Gdbm persistence layer, the format of the handle is "gdbm:<path>"
where <path> is the path to the Gdbm database file.

[call [cmd {tsv::array unbind}] [arg varname]]
Unbinds the shared [arg array] from its bound persistent storage.

[call [cmd {tsv::array isbound}] [arg varname]]
Returns true (1) if the shared [arg varname] is bound to some 
persistent storage or zero (0) if not.







|
|
|







221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
[call [cmd {tsv::array reset}] [arg varname] [arg list]]

Does the same as standard Tcl [cmd {array set}] but it clears
the [arg varname] and sets new values from the list atomically.

[call [cmd {tsv::array bind}] [arg varname] [arg handle]]
Binds the [arg varname] to the persistent storage [arg handle].
The format of the [arg handle] is <handler>:<address>, where <handler> is
"gdbm" for GNU Gdbm and "lmdb" for LMDB and <address> is the path to the
database file.

[call [cmd {tsv::array unbind}] [arg varname]]
Unbinds the shared [arg array] from its bound persistent storage.

[call [cmd {tsv::array isbound}] [arg varname]]
Returns true (1) if the shared [arg varname] is bound to some 
persistent storage or zero (0) if not.
Changes to doc/ttrace.man.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin ttrace n 2.7]
[moddesc {Tcl Threading}]
[titledesc {Trace-based interpreter initialization}]
[require Tcl 8.4]
[require Thread [opt 2.7]]

[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 
[cmd unknown] command and mechanism.
[para]
The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from 
America Online.
[para]
In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:

[example {

    % package require Ttrace
    2.7.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

|



|

















|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin ttrace n 2.8]
[moddesc {Tcl Threading}]
[titledesc {Trace-based interpreter initialization}]
[require Tcl 8.4]
[require Thread [opt 2.8]]

[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 
[cmd unknown] command and mechanism.
[para]
The package requires Tcl threading extension but can be alternatively
used stand-alone within the AOLserver, a scalable webserver from 
America Online.
[para]
In a nutshell, a short sample illustrating the usage of the ttrace
with the Tcl threading extension:

[example {

    % 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
Changes to generic/psGdbm.c.
343
344
345
346
347
348
349

350
351

352
353
354
355
356
357
358
 * Side effects:
 *      Memory gets reclaimed.
 *
 *-----------------------------------------------------------------------------
 */
static void
ps_gdbm_free(

    void   *data)
{

    free(data);
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_gdbm_geterr --







>
|

>







343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
 * Side effects:
 *      Memory gets reclaimed.
 *
 *-----------------------------------------------------------------------------
 */
static void
ps_gdbm_free(
    ClientData handle,
    void        *data)
{
    (void)handle;
    free(data);
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_gdbm_geterr --
Added generic/psLmdb.c.


































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543
544
545
/*
 * This file implements wrappers for persistent lmdb storage for the
 * shared variable arrays.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * ----------------------------------------------------------------------------
 */

#ifdef HAVE_LMDB

#include "threadSvCmd.h"
#include <lmdb.h>

/*
 * Structure keeping the lmdb environment context
 */
typedef struct {
    MDB_env    * env; // Environment
    MDB_txn    * txn; // Last active read transaction
    MDB_cursor * cur; // Cursor used for ps_lmdb_first and ps_lmdb_next
    MDB_dbi      dbi; // Open database (default db)
    int          err; // Last error (used in ps_lmdb_geterr)
} * LmdbCtx;

/*
 * Transaction and DB open mode
 */
enum LmdbOpenMode { LmdbRead, LmdbWrite };

// Initialize or renew a transaction.
static void LmdbTxnGet(LmdbCtx ctx, enum LmdbOpenMode mode);

// Commit a transaction.
static void LmdbTxnCommit(LmdbCtx ctx);

// Abort a transaction
static void LmdbTxnAbort(LmdbCtx ctx);

void LmdbTxnGet(LmdbCtx ctx, enum LmdbOpenMode mode)
{
    // Read transactions are reused, if possible
    if (ctx->txn && mode == LmdbRead)
    {
        ctx->err = mdb_txn_renew(ctx->txn);
        if (ctx->err)
        {
            ctx->txn = NULL;
        }
    }
    else if (ctx->txn && mode == LmdbWrite)
    {
        LmdbTxnAbort(ctx);
    }

    if (ctx->txn == NULL)
    {
        ctx->err = mdb_txn_begin(ctx->env, NULL, 0, &ctx->txn);
    }

    if (ctx->err)
    {
        ctx->txn = NULL;
        return;
    }

    // Given the setup above, and the arguments given, this won't fail.
    mdb_dbi_open(ctx->txn, NULL, 0, &ctx->dbi);
}

void LmdbTxnCommit(LmdbCtx ctx)
{
    ctx->err = mdb_txn_commit(ctx->txn);
    ctx->txn = NULL;
}

void LmdbTxnAbort(LmdbCtx ctx)
{
    mdb_txn_abort(ctx->txn);
    ctx->txn = NULL;
}

/*
 * Functions implementing the persistent store interface
 */

static ps_open_proc   ps_lmdb_open;
static ps_close_proc  ps_lmdb_close;
static ps_get_proc    ps_lmdb_get;
static ps_put_proc    ps_lmdb_put;
static ps_first_proc  ps_lmdb_first;
static ps_next_proc   ps_lmdb_next;
static ps_delete_proc ps_lmdb_delete;
static ps_free_proc   ps_lmdb_free;
static ps_geterr_proc ps_lmdb_geterr;

/*
 * This structure collects all the various pointers
 * to the functions implementing the lmdb store.
 */

const PsStore LmdbStore = {
    "lmdb",
    NULL,
    ps_lmdb_open,
    ps_lmdb_get,
    ps_lmdb_put,
    ps_lmdb_first,
    ps_lmdb_next,
    ps_lmdb_delete,
    ps_lmdb_close,
    ps_lmdb_free,
    ps_lmdb_geterr,
    NULL
};

/*
 *-----------------------------------------------------------------------------
 *
 * Sv_RegisterLmdbStore --
 *
 *      Register the lmdb store with shared variable implementation.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
void
Sv_RegisterLmdbStore(void)
{
    Sv_RegisterPsStore(&LmdbStore);
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_open --
 *
 *      Opens the lmdb-based persistent storage.
 *
 * Results:
 *      Opaque handle for LmdbCtx.
 *
 * Side effects:
 *      The lmdb file might be created if not found.
 *
 *-----------------------------------------------------------------------------
 */
static ClientData
ps_lmdb_open(
    const char *path)
{
    LmdbCtx ctx;

    char *ext;
    Tcl_DString toext;
   
    ctx = ckalloc(sizeof(*ctx));
    if (ctx == NULL)
    {
        return NULL;
    }

    ctx->env = NULL;
    ctx->txn = NULL;
    ctx->cur = NULL;
    ctx->dbi = 0;

    ctx->err = mdb_env_create(&ctx->env);
    if (ctx->err)
    {
        ckfree(ctx);
        return NULL;
    }

    Tcl_DStringInit(&toext);
    ext = Tcl_UtfToExternalDString(NULL, path, strlen(path), &toext);
    ctx->err = mdb_env_open(ctx->env, ext, MDB_NOSUBDIR|MDB_NOLOCK, 0666);
    Tcl_DStringFree(&toext);

    if (ctx->err)
    {
        ckfree(ctx);
        return NULL;
    }

    return (ClientData)ctx;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_close --
 *
 *      Closes the lmdb-based persistent storage.
 *
 * Results:
 *      0 - ok
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static int
ps_lmdb_close(
    ClientData handle)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    if (ctx->cur)
    {
        mdb_cursor_close(ctx->cur);
    }
    if (ctx->txn)
    {
        LmdbTxnAbort(ctx);
    }

    mdb_env_close(ctx->env);
    ckfree(ctx);

    return 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_get --
 *
 *      Retrieves data for the key from the lmdb storage.
 *
 * Results:
 *      1 - no such key
 *      0 - ok
 *
 * Side effects:
 *      Data returned must be copied, then psFree must be called.
 *
 *-----------------------------------------------------------------------------
 */
static int
ps_lmdb_get(
     ClientData  handle,
     const char *keyptr,
     char  **dataptrptr,
     size_t     *lenptr)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    MDB_val key, data;

    LmdbTxnGet(ctx, LmdbRead);
    if (ctx->err)
    {
        return 1;
    }

    key.mv_data = (void *)keyptr;
    key.mv_size = strlen(keyptr) + 1;

    ctx->err = mdb_get(ctx->txn, ctx->dbi, &key, &data);
    if (ctx->err)
    {
        mdb_txn_reset(ctx->txn);
        return 1;
    }

    *dataptrptr = data.mv_data;
    *lenptr = data.mv_size;

    /*
     * Transaction is left open at this point, so that the caller can get ahold
     * of the data and make a copy of it. Afterwards, it will call ps_lmdb_free
     * to free the data, and we'll catch the chance to reset the transaction
     * there.
     */

    return 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_first --
 *
 *      Starts the iterator over the lmdb file and returns the first record.
 *
 * Results:
 *      1 - no more records in the iterator
 *      0 - ok
 *
 * Side effects:
 *      Data returned must be copied, then psFree must be called.
 *
 *-----------------------------------------------------------------------------
 */
static int
ps_lmdb_first(
    ClientData  handle,
    char   **keyptrptr,
    char  **dataptrptr,
    size_t     *lenptr)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    MDB_val key, data;

    LmdbTxnGet(ctx, LmdbRead);
    if (ctx->err)
    {
        return 1;
    }

    ctx->err = mdb_cursor_open(ctx->txn, ctx->dbi, &ctx->cur);
    if (ctx->err)
    {
        return 1;
    }

    ctx->err = mdb_cursor_get(ctx->cur, &key, &data, MDB_FIRST);
    if (ctx->err)
    {
        mdb_txn_reset(ctx->txn);
        mdb_cursor_close(ctx->cur);
        ctx->cur = NULL;
        return 1;
    }

    *dataptrptr = data.mv_data;
    *lenptr = data.mv_size;
    *keyptrptr = key.mv_data;

    return 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_next --
 *
 *      Uses the iterator over the lmdb file and returns the next record.
 *
 * Results:
 *      1 - no more records in the iterator
 *      0 - ok
 *
 * Side effects:
 *      Data returned must be copied, then psFree must be called.
 *
 *-----------------------------------------------------------------------------
 */
static int ps_lmdb_next(
    ClientData  handle,
    char   **keyptrptr,
    char  **dataptrptr,
    size_t     *lenptr)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    MDB_val key, data;

    ctx->err = mdb_cursor_get(ctx->cur, &key, &data, MDB_NEXT);
    if (ctx->err)
    {
        mdb_txn_reset(ctx->txn);
        mdb_cursor_close(ctx->cur);
        ctx->cur = NULL;
        return 1;
    }

    *dataptrptr = data.mv_data;
    *lenptr = data.mv_size;
    *keyptrptr = key.mv_data;

    return 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_put --
 *
 *      Stores used data bound to a key in lmdb storage.
 *
 * Results:
 *      0 - ok
 *     -1 - error; use ps_lmdb_geterr to retrieve the error message
 *
 * Side effects:
 *      If the key is already associated with some user data, this will
 *      be replaced by the new data chunk.
 *
 *-----------------------------------------------------------------------------
 */
static int
ps_lmdb_put(
    ClientData  handle,
    const char *keyptr,
    char      *dataptr,
    size_t         len)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    MDB_val key, data;

    LmdbTxnGet(ctx, LmdbWrite);
    if (ctx->err)
    {
        return -1;
    }

    key.mv_data = (void*)keyptr;
    key.mv_size = strlen(keyptr) + 1;

    data.mv_data = dataptr;
    data.mv_size = len;

    ctx->err = mdb_put(ctx->txn, ctx->dbi, &key, &data, 0);
    if (ctx->err)
    {
        LmdbTxnAbort(ctx);
    }
    else
    {
        LmdbTxnCommit(ctx);
    }

    return ctx->err ? -1 : 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_delete --
 *
 *      Deletes the key and associated data from the lmdb storage.
 *
 * Results:
 *      0 - ok
 *     -1 - error; use ps_lmdb_geterr to retrieve the error message
 *
 * Side effects:
 *      If the key is already associated with some user data, this will
 *      be replaced by the new data chunk.
 *
 *-----------------------------------------------------------------------------
 */
static int
ps_lmdb_delete(
    ClientData  handle,
    const char *keyptr)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    MDB_val key;

    LmdbTxnGet(ctx, LmdbWrite);
    if (ctx->err)
    {
        return -1;
    }

    key.mv_data = (void*)keyptr;
    key.mv_size = strlen(keyptr) + 1;

    ctx->err = mdb_del(ctx->txn, ctx->dbi, &key, NULL);
    if (ctx->err)
    {
        LmdbTxnAbort(ctx);
    }
    else
    {
        LmdbTxnCommit(ctx);
    }

    ctx->txn = NULL;
    return ctx->err ? -1 : 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_free --
 *
 *      This function is called to free data returned by the persistent store
 *      after calls to psFirst, psNext, or psGet. Lmdb doesn't need to free any
 *      data, as the data returned is owned by lmdb. On the other hand, this
 *      method is required to reset the read transaction. This is done only
 *      when iteration is over (ctx->cur == NULL).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Memory gets reclaimed.
 *
 *-----------------------------------------------------------------------------
 */
static void
ps_lmdb_free(
    ClientData handle,
    void        *data)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    (void)data;

    if (ctx->cur == NULL)
    {
        mdb_txn_reset(ctx->txn);
    }
}

/*
 *-----------------------------------------------------------------------------
 *
 * ps_lmdb_geterr --
 *
 *      Retrieves the textual representation of the error caused
 *      by the last lmdb command.
 *
 * Results:
 *      Pointer to the string message.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static const char*
ps_lmdb_geterr(
    ClientData handle)
{
    LmdbCtx ctx = (LmdbCtx)handle;
    return mdb_strerror(ctx->err);
}

#endif  /* HAVE_LMDB */

/* EOF $RCSfile*/

/* Emacs Setup Variables */
/* Local Variables:      */
/* mode: C               */
/* indent-tabs-mode: nil */
/* c-basic-offset: 4     */
/* End:                  */
Added generic/psLmdb.h.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * psLmdb.h --
 *
 * See the file "license.txt" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * ---------------------------------------------------------------------------
 */

#ifndef _PSLMDB_H_
#define _PSLMDB_H_

void Sv_RegisterLmdbStore();

#endif /* _PSLMDB_H_ */

/* EOF $RCSfile */

/* Emacs Setup Variables */
/* Local Variables:      */
/* mode: C               */
/* indent-tabs-mode: nil */
/* c-basic-offset: 4     */
/* End:                  */

Changes to generic/tclThreadInt.h.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
 * Used to tag functions that are only to be visible within the module being
 * built and not outside it (where this is supported by the linker).
 */

#ifndef MODULE_SCOPE
#   ifdef __cplusplus
#	define MODULE_SCOPE extern "C"
#   else
#	define MODULE_SCOPE extern
#   endif
#endif

/*
 * For linking against NaviServer/AOLserver require V4 at least
 */








|

|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
 * Used to tag functions that are only to be visible within the module being
 * built and not outside it (where this is supported by the linker).
 */

#ifndef MODULE_SCOPE
#   ifdef __cplusplus
#       define MODULE_SCOPE extern "C"
#   else
#       define MODULE_SCOPE extern
#   endif
#endif

/*
 * For linking against NaviServer/AOLserver require V4 at least
 */

Changes to generic/tclXkeylist.c.
25
26
27
28
29
30
31



















32
33
34
35
36
37

38
39
40
41
42
43
44
 *-----------------------------------------------------------------------------
 */

#include "tclThreadInt.h"
#include "threadSvCmd.h"
#include "tclXkeylist.h"




















#undef Tcl_RegisterObjType
#define Tcl_RegisterObjType(typePtr) if (threadTclVersion<90) { \
    ((void (*)(const Tcl_ObjType *))((&(tclStubsPtr->tcl_PkgProvideEx))[211]))(typePtr); \
} else { \
    (typePtr)->setFromAnyProc = NULL; \
}


/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*     Stuff copied verbatim from the rest of TclX to avoid dependencies     */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 *-----------------------------------------------------------------------------
 */

#include "tclThreadInt.h"
#include "threadSvCmd.h"
#include "tclXkeylist.h"

#ifdef STATIC_BUILD
#if TCL_MAJOR_VERSION >= 9
/*
 * Static build, Tcl >= 9, compile-time decision to disable T_ROT calls.
 */
#undef Tcl_RegisterObjType
#define Tcl_RegisterObjType(typePtr)    (typePtr)->setFromAnyProc = NULL
#else
/*
 * Static build, Tcl <= 9   --> T_ROT is directly linked, no stubs
 * Nothing needs to be done
 */
#endif
#else /* !STATIC_BUILD */
/*
 * Dynamic build. Assume building with stubs (xx) and make a run-time
 * decision regarding T_ROT.
 * (Ad xx): Should be checked. Without stubs we have to go like static.
 */
#undef Tcl_RegisterObjType
#define Tcl_RegisterObjType(typePtr) if (threadTclVersion<90) { \
    ((void (*)(const Tcl_ObjType *))((&(tclStubsPtr->tcl_PkgProvideEx))[211]))(typePtr); \
} else { \
    (typePtr)->setFromAnyProc = NULL; \
}
#endif /* eof STATIC_BUILD */

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*     Stuff copied verbatim from the rest of TclX to avoid dependencies     */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
 *-----------------------------------------------------------------------------
 */
static int
TclX_IsNullObj (objPtr)
    Tcl_Obj *objPtr;
{
    if (objPtr->typePtr == NULL) {
	return (objPtr->length == 0);
    } else if (objPtr->typePtr == listType) {
	int length;
	Tcl_ListObjLength(NULL, objPtr, &length);
	return (length == 0);
    }
    (void)Tcl_GetString(objPtr);
    return (objPtr->length == 0);
}

/*-----------------------------------------------------------------------------
 * TclX_AppendObjResult --







|

|
|
|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 *-----------------------------------------------------------------------------
 */
static int
TclX_IsNullObj (objPtr)
    Tcl_Obj *objPtr;
{
    if (objPtr->typePtr == NULL) {
        return (objPtr->length == 0);
    } else if (objPtr->typePtr == listType) {
        int length;
        Tcl_ListObjLength(NULL, objPtr, &length);
        return (length == 0);
    }
    (void)Tcl_GetString(objPtr);
    return (objPtr->length == 0);
}

/*-----------------------------------------------------------------------------
 * TclX_AppendObjResult --
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
    Tcl_Obj   **valuePtrPtr;
{
    keylIntObj_t *keylIntPtr;
    const char *nextSubKey;
    int findIdx;

    if (keylPtr->typePtr != &keyedListType) {
	if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
	    return TCL_ERROR;
	}
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;
    KEYL_REP_ASSERT (keylIntPtr);

    findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);

    /*







|
|
|







889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
    Tcl_Obj   **valuePtrPtr;
{
    keylIntObj_t *keylIntPtr;
    const char *nextSubKey;
    int findIdx;

    if (keylPtr->typePtr != &keyedListType) {
        if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;
    KEYL_REP_ASSERT (keylIntPtr);

    findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);

    /*
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
{
    keylIntObj_t *keylIntPtr;
    const char *nextSubKey;
    int findIdx, keyLen, status;
    Tcl_Obj *newKeylPtr;

    if (keylPtr->typePtr != &keyedListType) {
	if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
	    return TCL_ERROR;
	}
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;
    KEYL_REP_ASSERT (keylIntPtr);

    findIdx = FindKeyedListEntry (keylIntPtr, key,
                                  &keyLen, &nextSubKey);








|
|
|







948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
{
    keylIntObj_t *keylIntPtr;
    const char *nextSubKey;
    int findIdx, keyLen, status;
    Tcl_Obj *newKeylPtr;

    if (keylPtr->typePtr != &keyedListType) {
        if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;
    KEYL_REP_ASSERT (keylIntPtr);

    findIdx = FindKeyedListEntry (keylIntPtr, key,
                                  &keyLen, &nextSubKey);

1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
    const char *key;
{
    keylIntObj_t *keylIntPtr, *subKeylIntPtr;
    const char *nextSubKey;
    int findIdx, status;

    if (keylPtr->typePtr != &keyedListType) {
	if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
	    return TCL_ERROR;
	}
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;

    findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);

    /*
     * If not found, return status.







|
|
|







1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
    const char *key;
{
    keylIntObj_t *keylIntPtr, *subKeylIntPtr;
    const char *nextSubKey;
    int findIdx, status;

    if (keylPtr->typePtr != &keyedListType) {
        if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;

    findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);

    /*
     * If not found, return status.
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
{
    keylIntObj_t *keylIntPtr;
    Tcl_Obj *nameObjPtr, *listObjPtr;
    const char *nextSubKey;
    int idx, findIdx;

    if (keylPtr->typePtr != &keyedListType) {
	if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
	    return TCL_ERROR;
	}
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;

    /*
     * If key is not NULL or empty, then recurse down until we go past
     * the end of all of the elements of the key.
     */







|
|
|







1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
{
    keylIntObj_t *keylIntPtr;
    Tcl_Obj *nameObjPtr, *listObjPtr;
    const char *nextSubKey;
    int idx, findIdx;

    if (keylPtr->typePtr != &keyedListType) {
        if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    keylIntPtr = keylPtr->internalRep.twoPtrValue.ptr1;

    /*
     * If key is not NULL or empty, then recurse down until we go past
     * the end of all of the elements of the key.
     */
Changes to generic/threadCmd.c.
17
18
19
20
21
22
23





24
25
26
27
28
29
30
31
32
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * ----------------------------------------------------------------------------
 */

#include "tclThreadInt.h"






#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION "2.7.1"
#endif

/*
 * Check if this is Tcl 8.5 or higher.  In that case, we will have the TIP
 * #143 APIs (i.e. interpreter resource limiting) available.
 */








>
>
>
>
>

|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * ----------------------------------------------------------------------------
 */

#include "tclThreadInt.h"

/*
 * Provide package version in build contexts which do not provide
 * -DPACKAGE_VERSION, like building a shell with the Thread object
 * files built as part of that shell. Example: basekits.
 */
#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION "2.8.0"
#endif

/*
 * Check if this is Tcl 8.5 or higher.  In that case, we will have the TIP
 * #143 APIs (i.e. interpreter resource limiting) available.
 */

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
 * If TIP #285 support is enabled and we are compiling against a pre-Tcl 8.6
 * core, hard-wire the necessary APIs using the "well-known" offsets into the
 * stubs table.
 */

#if defined(TCL_TIP285) && !TCL_MINIMUM_VERSION(8,6)
# if defined(USE_TCL_STUBS)
#  define TCL_CANCEL_UNWIND	0x100000
#  define Tcl_CancelEval ((int (*)(Tcl_Interp *, Tcl_Obj *, ClientData, int)) \
     ((&(tclStubsPtr->tcl_PkgProvideEx))[580]))
#  define Tcl_Canceled ((int (*)(Tcl_Interp *, int)) \
     ((&(tclStubsPtr->tcl_PkgProvideEx))[581]))
# else
#  error "Supporting TIP #285 requires USE_TCL_STUBS before Tcl 8.6"
# endif







|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 * If TIP #285 support is enabled and we are compiling against a pre-Tcl 8.6
 * core, hard-wire the necessary APIs using the "well-known" offsets into the
 * stubs table.
 */

#if defined(TCL_TIP285) && !TCL_MINIMUM_VERSION(8,6)
# if defined(USE_TCL_STUBS)
#  define TCL_CANCEL_UNWIND 0x100000
#  define Tcl_CancelEval ((int (*)(Tcl_Interp *, Tcl_Obj *, ClientData, int)) \
     ((&(tclStubsPtr->tcl_PkgProvideEx))[580]))
#  define Tcl_Canceled ((int (*)(Tcl_Interp *, int)) \
     ((&(tclStubsPtr->tcl_PkgProvideEx))[581]))
# else
#  error "Supporting TIP #285 requires USE_TCL_STUBS before Tcl 8.6"
# endif
261
262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
static Tcl_ThreadId errorThreadId; /* Id of thread to post error message */
static char *errorProcString;      /* Tcl script to run when reporting error */

/*
 * Definition of flags for ThreadSend.
 */

#define THREAD_SEND_WAIT 1<<1
#define THREAD_SEND_HEAD 1<<2


#ifdef BUILD_thread
# undef  TCL_STORAGE_CLASS
# define TCL_STORAGE_CLASS DLLEXPORT
#endif

/*







|
|
>







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
static Tcl_ThreadId errorThreadId; /* Id of thread to post error message */
static char *errorProcString;      /* Tcl script to run when reporting error */

/*
 * Definition of flags for ThreadSend.
 */

#define THREAD_SEND_WAIT (1<<1)
#define THREAD_SEND_HEAD (1<<2)
#define THREAD_SEND_CLBK (1<<3)

#ifdef BUILD_thread
# undef  TCL_STORAGE_CLASS
# define TCL_STORAGE_CLASS DLLEXPORT
#endif

/*
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
#endif

static int
ThreadInit(interp)
    Tcl_Interp *interp; /* The current Tcl interpreter */
{
    if (Tcl_InitStubs(interp, "8.4", 0) == NULL) {
	if ((sizeof(size_t) != sizeof(int)) ||
		!Tcl_InitStubs(interp, "8.4-", 0)) {
	    return TCL_ERROR;
	}
	Tcl_ResetResult(interp);
    }

    if (!threadTclVersion) {

	/*
	 * Check whether we are running threaded Tcl.
	 * Get the current core version to decide whether to use
	 * some lately introduced core features or to back-off.
	 */

	int major, minor;

	Tcl_MutexLock(&threadMutex);
	if (threadMutex == NULL){
	    /* If threadMutex==NULL here, it means that Tcl_MutexLock() is
	     * a dummy function, which is the case in unthreaded Tcl */
	    const char *msg = "Tcl core wasn't compiled for threading";
	    Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1));
	    return TCL_ERROR;
	}
	Tcl_GetVersion(&major, &minor, NULL, NULL);
	threadTclVersion = 10 * major + minor;
	Tcl_MutexUnlock(&threadMutex);
    }

    TCL_CMD(interp, THREAD_CMD_PREFIX"create",    ThreadCreateObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"send",      ThreadSendObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"broadcast", ThreadBroadcastObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"exit",      ThreadExitObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"unwind",    ThreadUnwindObjCmd);







|
|
|
|
|




|
|
|
|
|

|

|
|
|
|
|
|
|
|
|
|
|







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
#endif

static int
ThreadInit(interp)
    Tcl_Interp *interp; /* The current Tcl interpreter */
{
    if (Tcl_InitStubs(interp, "8.4", 0) == NULL) {
        if ((sizeof(size_t) != sizeof(int)) ||
                !Tcl_InitStubs(interp, "8.4-", 0)) {
            return TCL_ERROR;
        }
        Tcl_ResetResult(interp);
    }

    if (!threadTclVersion) {

        /*
         * Check whether we are running threaded Tcl.
         * Get the current core version to decide whether to use
         * some lately introduced core features or to back-off.
         */

        int major, minor;

        Tcl_MutexLock(&threadMutex);
        if (threadMutex == NULL){
            /* If threadMutex==NULL here, it means that Tcl_MutexLock() is
             * a dummy function, which is the case in unthreaded Tcl */
            const char *msg = "Tcl core wasn't compiled for threading";
            Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1));
            return TCL_ERROR;
        }
        Tcl_GetVersion(&major, &minor, NULL, NULL);
        threadTclVersion = 10 * major + minor;
        Tcl_MutexUnlock(&threadMutex);
    }

    TCL_CMD(interp, THREAD_CMD_PREFIX"create",    ThreadCreateObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"send",      ThreadSendObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"broadcast", ThreadBroadcastObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"exit",      ThreadExitObjCmd);
    TCL_CMD(interp, THREAD_CMD_PREFIX"unwind",    ThreadUnwindObjCmd);
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
    if (objc > 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "?-wait? ?threadId?");
        return TCL_ERROR;
    }
    if (objc > 1) {
        if (OPT_CMP(Tcl_GetString(objv[1]), "-wait")) {
            wait = 1;
	    if (objc > 2) {
        	if (ThreadGetId(interp, objv[2], &thrId) != TCL_OK) {
		    return TCL_ERROR;
        	}
	    }
        } else if (ThreadGetId(interp, objv[1], &thrId) != TCL_OK) {
            return TCL_ERROR;
        }
    }

    return ThreadReserve(interp, thrId, THREAD_RELEASE, wait);
}







|
|
|
|
|







709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
    if (objc > 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "?-wait? ?threadId?");
        return TCL_ERROR;
    }
    if (objc > 1) {
        if (OPT_CMP(Tcl_GetString(objv[1]), "-wait")) {
            wait = 1;
            if (objc > 2) {
                if (ThreadGetId(interp, objv[2], &thrId) != TCL_OK) {
                    return TCL_ERROR;
                }
            }
        } else if (ThreadGetId(interp, objv[1], &thrId) != TCL_OK) {
            return TCL_ERROR;
        }
    }

    return ThreadReserve(interp, thrId, THREAD_RELEASE, wait);
}
775
776
777
778
779
780
781

782
783












784
785
786
787
788
789
790
791
792
793
static int
ThreadExitObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int         objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{


    Init(interp);












    ListRemove(NULL);

    Tcl_ExitThread(666);

    return TCL_OK; /* NOT REACHED */
}

/*
 *----------------------------------------------------------------------
 *







>


>
>
>
>
>
>
>
>
>
>
>
>


|







781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
static int
ThreadExitObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int         objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    int status = 666;

    Init(interp);

    if (objc > 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "?status?");
        return TCL_ERROR;
    }

    if (objc == 2) {
        if (Tcl_GetIntFromObj(interp, objv[1], &status) != TCL_OK) {
            return TCL_ERROR;
        }
    }

    ListRemove(NULL);

    Tcl_ExitThread(status);

    return TCL_OK; /* NOT REACHED */
}

/*
 *----------------------------------------------------------------------
 *
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
 *
 *----------------------------------------------------------------------
 */

static void
threadSendFree(ClientData ptr)
{
	ckfree((char *)ptr);
}

static int
ThreadSendObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int         objc;           /* Number of arguments. */







|







924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
 *
 *----------------------------------------------------------------------
 */

static void
threadSendFree(ClientData ptr)
{
    ckfree((char *)ptr);
}

static int
ThreadSendObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int         objc;           /* Number of arguments. */
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
    } else {
        if (errorProcString) {
            ckfree(errorProcString);
        }
        proc = Tcl_GetString(objv[1]);
        len = objv[1]->length;
        if (len == 0) {
	    errorThreadId = NULL;
            errorProcString = NULL;
        } else {
	    errorThreadId = Tcl_GetCurrentThread();
            errorProcString = ckalloc(1+strlen(proc));
            strcpy(errorProcString, proc);
	    Tcl_DeleteThreadExitHandler(ThreadFreeError, NULL);
	    Tcl_CreateThreadExitHandler(ThreadFreeError, NULL);
        }
    }
    Tcl_MutexUnlock(&threadMutex);

    return TCL_OK;
}

static void
ThreadFreeError(clientData)
    ClientData clientData;
{
    Tcl_MutexLock(&threadMutex);
    if (errorThreadId != Tcl_GetCurrentThread()) {
	Tcl_MutexUnlock(&threadMutex);
	return;
    }
    ckfree(errorProcString);
    errorThreadId = NULL;
    errorProcString = NULL;
    Tcl_MutexUnlock(&threadMutex);
}








|


|


|
|













|
|







1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
    } else {
        if (errorProcString) {
            ckfree(errorProcString);
        }
        proc = Tcl_GetString(objv[1]);
        len = objv[1]->length;
        if (len == 0) {
            errorThreadId = NULL;
            errorProcString = NULL;
        } else {
            errorThreadId = Tcl_GetCurrentThread();
            errorProcString = ckalloc(1+strlen(proc));
            strcpy(errorProcString, proc);
            Tcl_DeleteThreadExitHandler(ThreadFreeError, NULL);
            Tcl_CreateThreadExitHandler(ThreadFreeError, NULL);
        }
    }
    Tcl_MutexUnlock(&threadMutex);

    return TCL_OK;
}

static void
ThreadFreeError(clientData)
    ClientData clientData;
{
    Tcl_MutexLock(&threadMutex);
    if (errorThreadId != Tcl_GetCurrentThread()) {
        Tcl_MutexUnlock(&threadMutex);
        return;
    }
    ckfree(errorProcString);
    errorThreadId = NULL;
    errorProcString = NULL;
    Tcl_MutexUnlock(&threadMutex);
}

1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
}

/*
 *----------------------------------------------------------------------
 *
 * ThreadConfigureObjCmd --
 *
 *	This procedure is invoked to process the Tcl "thread::configure"
 *  command. See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *----------------------------------------------------------------------
 */
static int
ThreadConfigureObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int         objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    char *option, *value;
    Tcl_ThreadId thrId;         /* Id of the thread to configure */
    int i;                      /* Iterate over arg-value pairs. */
    Tcl_DString ds;			    /* DString to hold result of
                                 * calling GetThreadOption. */

    if (objc < 2 || (objc % 2 == 1 && objc != 3)) {
        Tcl_WrongNumArgs(interp, 1, objv, "threadlId ?optionName? "
                         "?value? ?optionName value?...");
        return TCL_ERROR;
    }







|



|


|












|







1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
}

/*
 *----------------------------------------------------------------------
 *
 * ThreadConfigureObjCmd --
 *
 *  This procedure is invoked to process the Tcl "thread::configure"
 *  command. See the user documentation for details on what it does.
 *
 * Results:
 *  A standard Tcl result.
 *
 * Side effects:
 *  None.
 *----------------------------------------------------------------------
 */
static int
ThreadConfigureObjCmd(dummy, interp, objc, objv)
    ClientData  dummy;          /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int         objc;           /* Number of arguments. */
    Tcl_Obj    *const objv[];   /* Argument objects. */
{
    char *option, *value;
    Tcl_ThreadId thrId;         /* Id of the thread to configure */
    int i;                      /* Iterate over arg-value pairs. */
    Tcl_DString ds;             /* DString to hold result of
                                 * calling GetThreadOption. */

    if (objc < 2 || (objc % 2 == 1 && objc != 3)) {
        Tcl_WrongNumArgs(interp, 1, objv, "threadlId ?optionName? "
                         "?value? ?optionName value?...");
        return TCL_ERROR;
    }
1622
1623
1624
1625
1626
1627
1628

1629
1630
1631
1632
1633
1634
1635


1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646

1647
1648
1649
1650
1651
1652
1653
    Tcl_Interp *interp;
    ClientData clientData;
{
    ThreadClbkData *clbkPtr = (ThreadClbkData*)clientData;
    const char *var = (const char *)clbkPtr->clientData;
    Tcl_Obj *valObj;
    ThreadEventResult *resultPtr = &clbkPtr->result;


    /*
     * Get the result of the posted command.
     * We will use it to fill-in the result variable.
     */

    valObj = Tcl_NewStringObj(resultPtr->result, -1);


    if (resultPtr->result != threadEmptyResult) {
        ckfree(resultPtr->result);
    }

    /*
     * Set the result variable
     */

    if (Tcl_SetVar2Ex(interp, var, NULL, valObj,
                      TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
        return TCL_ERROR;

    }

    /*
     * In case of error, trigger the bgerror mechansim
     */

    if (resultPtr->code == TCL_ERROR) {







>







>
>










|
>







1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
    Tcl_Interp *interp;
    ClientData clientData;
{
    ThreadClbkData *clbkPtr = (ThreadClbkData*)clientData;
    const char *var = (const char *)clbkPtr->clientData;
    Tcl_Obj *valObj;
    ThreadEventResult *resultPtr = &clbkPtr->result;
    int rc = TCL_OK;

    /*
     * Get the result of the posted command.
     * We will use it to fill-in the result variable.
     */

    valObj = Tcl_NewStringObj(resultPtr->result, -1);
    Tcl_IncrRefCount(valObj);

    if (resultPtr->result != threadEmptyResult) {
        ckfree(resultPtr->result);
    }

    /*
     * Set the result variable
     */

    if (Tcl_SetVar2Ex(interp, var, NULL, valObj,
                      TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
        rc = TCL_ERROR;
        goto cleanup;
    }

    /*
     * In case of error, trigger the bgerror mechansim
     */

    if (resultPtr->code == TCL_ERROR) {
1661
1662
1663
1664
1665
1666
1667


1668
1669
1670
1671
1672
1673
1674
1675
            Tcl_SetVar2Ex(interp, var, NULL, Tcl_NewStringObj(resultPtr->errorInfo, -1), TCL_GLOBAL_ONLY);
            ckfree((char*)resultPtr->errorInfo);
        }
        Tcl_SetObjResult(interp, valObj);
        Tcl_BackgroundError(interp);
    }



    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * ThreadCreate --
 *







>
>
|







1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
            Tcl_SetVar2Ex(interp, var, NULL, Tcl_NewStringObj(resultPtr->errorInfo, -1), TCL_GLOBAL_ONLY);
            ckfree((char*)resultPtr->errorInfo);
        }
        Tcl_SetObjResult(interp, valObj);
        Tcl_BackgroundError(interp);
    }

cleanup:
    Tcl_DecrRefCount(valObj);
    return rc;
}

/*
 *----------------------------------------------------------------------
 *
 * ThreadCreate --
 *
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455

        if (resultPtr->resultMsg) {
            Tcl_AppendResult(interp, resultPtr->resultMsg, NULL);
            ckfree(resultPtr->resultMsg);
        } else {
            Tcl_AppendResult(interp, "for reasons unknown", NULL);
        }
	ckfree((char *)resultPtr);

        return TCL_ERROR;
    }

    if (resultPtr->resultMsg) {
        ckfree(resultPtr->resultMsg);
    }







|







2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480

        if (resultPtr->resultMsg) {
            Tcl_AppendResult(interp, resultPtr->resultMsg, NULL);
            ckfree(resultPtr->resultMsg);
        } else {
            Tcl_AppendResult(interp, "for reasons unknown", NULL);
        }
        ckfree((char *)resultPtr);

        return TCL_ERROR;
    }

    if (resultPtr->resultMsg) {
        ckfree(resultPtr->resultMsg);
    }
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
    /*
     * Short circuit sends to ourself.
     */

    if (thrId == Tcl_GetCurrentThread()) {
        Tcl_MutexUnlock(&threadMutex);
        if ((flags & THREAD_SEND_WAIT)) {
	    int code = (*send->execProc)(interp, (ClientData)send);
	    ThreadFreeProc((ClientData)send);
	    return code;
        } else {
            send->interp = interp;
            Tcl_Preserve((ClientData)send->interp);
            Tcl_DoWhenIdle((Tcl_IdleProc*)ThreadIdleProc, (ClientData)send);
            return TCL_OK;
        }
    }







|
|
|







2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
    /*
     * Short circuit sends to ourself.
     */

    if (thrId == Tcl_GetCurrentThread()) {
        Tcl_MutexUnlock(&threadMutex);
        if ((flags & THREAD_SEND_WAIT)) {
            int code = (*send->execProc)(interp, (ClientData)send);
            ThreadFreeProc((ClientData)send);
            return code;
        } else {
            send->interp = interp;
            Tcl_Preserve((ClientData)send->interp);
            Tcl_DoWhenIdle((Tcl_IdleProc*)ThreadIdleProc, (ClientData)send);
            return TCL_OK;
        }
    }
2743
2744
2745
2746
2747
2748
2749
2750
2751

2752
2753
2754

2755
2756
2757
2758
2759
2760
2761
        Tcl_ThreadQueueEvent(thrId, (Tcl_Event*)eventPtr, TCL_QUEUE_TAIL);
    }
    Tcl_ThreadAlert(thrId);

    if ((flags & THREAD_SEND_WAIT) == 0) {
        /*
         * Might potentially spend some time here, until the
         * worker thread clean's up it's queue a little bit.
         */

        while (tsdPtr->maxEventsCount &&
               tsdPtr->eventsPending > tsdPtr->maxEventsCount) {
            Tcl_ConditionWait(&tsdPtr->doOneEvent, &threadMutex, NULL);

        }
        Tcl_MutexUnlock(&threadMutex);
        return TCL_OK;
    }

    /*
     * Block on the result indefinitely.







|

>
|
|
|
>







2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
        Tcl_ThreadQueueEvent(thrId, (Tcl_Event*)eventPtr, TCL_QUEUE_TAIL);
    }
    Tcl_ThreadAlert(thrId);

    if ((flags & THREAD_SEND_WAIT) == 0) {
        /*
         * Might potentially spend some time here, until the
         * worker thread cleans up its queue a little bit.
         */
        if ((flags & THREAD_SEND_CLBK) == 0) {
            while (tsdPtr->maxEventsCount &&
                   tsdPtr->eventsPending > tsdPtr->maxEventsCount) {
                Tcl_ConditionWait(&tsdPtr->doOneEvent, &threadMutex, NULL);
            }
        }
        Tcl_MutexUnlock(&threadMutex);
        return TCL_OK;
    }

    /*
     * Block on the result indefinitely.
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916

    if (code != TCL_OK) {
        char buf[THREAD_HNDLMAXLEN];
        const char *errorInfo;

        errorInfo = Tcl_GetVar2(tsdPtr->interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
        if (errorInfo == NULL) {
        	errorInfo = Tcl_GetString(Tcl_GetObjResult(tsdPtr->interp));
        }

        ThreadGetHandle(Tcl_GetCurrentThread(), buf);
        Tcl_AppendResult(interp, "Error from thread ", buf, "\n",
                errorInfo, NULL);
    }
#endif







|







2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943

    if (code != TCL_OK) {
        char buf[THREAD_HNDLMAXLEN];
        const char *errorInfo;

        errorInfo = Tcl_GetVar2(tsdPtr->interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
        if (errorInfo == NULL) {
            errorInfo = Tcl_GetString(Tcl_GetObjResult(tsdPtr->interp));
        }

        ThreadGetHandle(Tcl_GetCurrentThread(), buf);
        Tcl_AppendResult(interp, "Error from thread ", buf, "\n",
                errorInfo, NULL);
    }
#endif
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
    interp = (sendPtr && sendPtr->interp) ? sendPtr->interp : tsdPtr->interp;

    if (interp != NULL) {
        Tcl_Preserve((ClientData)interp);

        if (clbkPtr && clbkPtr->threadId == thrId) {
            Tcl_Release((ClientData)interp);
            /* Watch: this thread evaluates it's own callback. */
            interp = clbkPtr->interp;
            Tcl_Preserve((ClientData)interp);
        }

        Tcl_ResetResult(interp);

        if (sendPtr) {







|







3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
    interp = (sendPtr && sendPtr->interp) ? sendPtr->interp : tsdPtr->interp;

    if (interp != NULL) {
        Tcl_Preserve((ClientData)interp);

        if (clbkPtr && clbkPtr->threadId == thrId) {
            Tcl_Release((ClientData)interp);
            /* Watch: this thread evaluates its own callback. */
            interp = clbkPtr->interp;
            Tcl_Preserve((ClientData)interp);
        }

        Tcl_ResetResult(interp);

        if (sendPtr) {
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
         */

        if (code != TCL_OK) {
            ThreadErrorProc(interp);
        }

        ThreadSetResult(interp, code, &clbkPtr->result);
        ThreadSend(interp, clbkPtr->threadId, tmpPtr, NULL, 0);

    } else if (code != TCL_OK) {
        /*
         * Only pass errors onto the registered error handler
         * when we don't have a result target for this event.
         */
        ThreadErrorProc(interp);







|







3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
         */

        if (code != TCL_OK) {
            ThreadErrorProc(interp);
        }

        ThreadSetResult(interp, code, &clbkPtr->result);
        ThreadSend(interp, clbkPtr->threadId, tmpPtr, NULL, THREAD_SEND_CLBK);

    } else if (code != TCL_OK) {
        /*
         * Only pass errors onto the registered error handler
         * when we don't have a result target for this event.
         */
        ThreadErrorProc(interp);
Changes to generic/threadNs.c.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
NsThread_Init (Tcl_Interp *interp, void *cd)
{
    NsThreadInterpData *md = (NsThreadInterpData*)cd;
    int ret = Thread_Init(interp);

    if (ret != TCL_OK) {
        Ns_Log(Warning, "can't load module %s: %s", md->modname,
        		Tcl_GetString(Tcl_GetObjResult(interp)));
        return TCL_ERROR;
    }
    Tcl_SetAssocData(interp, "thread:nsd", NULL, (ClientData)md);

    return TCL_OK;
}








|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
NsThread_Init (Tcl_Interp *interp, void *cd)
{
    NsThreadInterpData *md = (NsThreadInterpData*)cd;
    int ret = Thread_Init(interp);

    if (ret != TCL_OK) {
        Ns_Log(Warning, "can't load module %s: %s", md->modname,
                Tcl_GetString(Tcl_GetObjResult(interp)));
        return TCL_ERROR;
    }
    Tcl_SetAssocData(interp, "thread:nsd", NULL, (ClientData)md);

    return TCL_OK;
}

Changes to generic/threadSpCmd.c.
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 * to pointers of their corresponding synchronization objects.
 */

static int        initOnce;    /* Flag for initializing tables below */
static Tcl_Mutex  initMutex;   /* Controls initialization of primitives */
static SpBucket  muxBuckets[NUMSPBUCKETS];  /* Maps mutex names/handles */
static SpBucket  varBuckets[NUMSPBUCKETS];  /* Maps condition variable
					     * names/handles */

/*
 * Functions implementing Tcl commands
 */

static Tcl_ObjCmdProc ThreadMutexObjCmd;
static Tcl_ObjCmdProc ThreadRWMutexObjCmd;







|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 * to pointers of their corresponding synchronization objects.
 */

static int        initOnce;    /* Flag for initializing tables below */
static Tcl_Mutex  initMutex;   /* Controls initialization of primitives */
static SpBucket  muxBuckets[NUMSPBUCKETS];  /* Maps mutex names/handles */
static SpBucket  varBuckets[NUMSPBUCKETS];  /* Maps condition variable
                                             * names/handles */

/*
 * Functions implementing Tcl commands
 */

static Tcl_ObjCmdProc ThreadMutexObjCmd;
static Tcl_ObjCmdProc ThreadRWMutexObjCmd;
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
 *
 *----------------------------------------------------------------------
 */

static int
SpCondvWait(SpCondv *condvPtr, SpMutex *mutexPtr, int msec)
{
	Sp_AnyMutex **lock = &mutexPtr->lock;
    Sp_ExclusiveMutex_ *emPtr = *(Sp_ExclusiveMutex_**)lock;
    Tcl_Time waitTime, *wt = NULL;
    Tcl_ThreadId threadId = Tcl_GetCurrentThread();

    if (msec > 0) {
        wt = &waitTime;
        wt->sec  = (msec/1000);







|







1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
 *
 *----------------------------------------------------------------------
 */

static int
SpCondvWait(SpCondv *condvPtr, SpMutex *mutexPtr, int msec)
{
    Sp_AnyMutex **lock = &mutexPtr->lock;
    Sp_ExclusiveMutex_ *emPtr = *(Sp_ExclusiveMutex_**)lock;
    Tcl_Time waitTime, *wt = NULL;
    Tcl_ThreadId threadId = Tcl_GetCurrentThread();

    if (msec > 0) {
        wt = &waitTime;
        wt->sec  = (msec/1000);
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
    Tcl_MutexLock(&rmPtr->lock);

    if (rmPtr->owner == thisThread) {
        /*
         * We are already holding the mutex
         * so just count one more lock.
         */
    	rmPtr->lockcount++;
    } else {
    	if (rmPtr->owner == (Tcl_ThreadId)0) {
            /*
             * Nobody holds the mutex, we do now.
             */
    		rmPtr->owner = thisThread;
    		rmPtr->lockcount = 1;
    	} else {
            /*
             * Somebody else holds the mutex; wait.
             */
    		while (1) {
                Tcl_ConditionWait(&rmPtr->cond, &rmPtr->lock, NULL);
    			if (rmPtr->owner == (Tcl_ThreadId)0) {
    				rmPtr->owner = thisThread;
    				rmPtr->lockcount = 1;
    				break;
    			}
    		}
    	}
    }

    Tcl_MutexUnlock(&rmPtr->lock);

    return 1;
}








|

|



|
|
|



|

|
|
|
|
|
|
|







1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
    Tcl_MutexLock(&rmPtr->lock);

    if (rmPtr->owner == thisThread) {
        /*
         * We are already holding the mutex
         * so just count one more lock.
         */
        rmPtr->lockcount++;
    } else {
        if (rmPtr->owner == (Tcl_ThreadId)0) {
            /*
             * Nobody holds the mutex, we do now.
             */
            rmPtr->owner = thisThread;
            rmPtr->lockcount = 1;
        } else {
            /*
             * Somebody else holds the mutex; wait.
             */
            while (1) {
                Tcl_ConditionWait(&rmPtr->cond, &rmPtr->lock, NULL);
                if (rmPtr->owner == (Tcl_ThreadId)0) {
                    rmPtr->owner = thisThread;
                    rmPtr->lockcount = 1;
                    break;
                }
            }
        }
    }

    Tcl_MutexUnlock(&rmPtr->lock);

    return 1;
}

Changes to generic/threadSpCmd.h.
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

typedef struct Sp_ReadWriteMutex_ {
    int lockcount;              /* >0: # of readers, -1: sole writer */
    int numlocks;               /* Number of time the mutex got locked */
    Tcl_Mutex lock;             /* Regular mutex */
    Tcl_ThreadId owner;         /* Current lock owner thread */
    /* --- */
    unsigned int numrd;	        /* # of readers waiting for lock */
    unsigned int numwr;         /* # of writers waiting for lock */
    Tcl_Condition rcond;        /* Reader lockers wait here */
    Tcl_Condition wcond;        /* Writer lockers wait here */
} Sp_ReadWriteMutex_;

typedef Sp_ReadWriteMutex_* Sp_ReadWriteMutex;








|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

typedef struct Sp_ReadWriteMutex_ {
    int lockcount;              /* >0: # of readers, -1: sole writer */
    int numlocks;               /* Number of time the mutex got locked */
    Tcl_Mutex lock;             /* Regular mutex */
    Tcl_ThreadId owner;         /* Current lock owner thread */
    /* --- */
    unsigned int numrd;         /* # of readers waiting for lock */
    unsigned int numwr;         /* # of writers waiting for lock */
    Tcl_Condition rcond;        /* Reader lockers wait here */
    Tcl_Condition wcond;        /* Writer lockers wait here */
} Sp_ReadWriteMutex_;

typedef Sp_ReadWriteMutex_* Sp_ReadWriteMutex;

Changes to generic/threadSvCmd.c.
15
16
17
18
19
20
21















22
23
24
25
26
27
28

#include "tclThreadInt.h"
#include "threadSvCmd.h"

#include "threadSvListCmd.h"    /* Shared variants of list commands */
#include "threadSvKeylistCmd.h" /* Shared variants of list commands */
#include "psGdbm.h"             /* The gdbm persistent store implementation */
















/*
 * Number of buckets to spread shared arrays into. Each bucket is
 * associated with one mutex so locking a bucket locks all arrays
 * in that bucket as well. The number of buckets should be a prime.
 */








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include "tclThreadInt.h"
#include "threadSvCmd.h"

#include "threadSvListCmd.h"    /* Shared variants of list commands */
#include "threadSvKeylistCmd.h" /* Shared variants of list commands */
#include "psGdbm.h"             /* The gdbm persistent store implementation */
#include "psLmdb.h"             /* The lmdb persistent store implementation */

#define SV_FINALIZE

/*
 * Names of registered persistent storage handlers.
 */
static const char * handlers[] = {
#ifdef HAVE_GDBM
    "gdbm",
#endif
#ifdef HAVE_LMDB
    "lmdb",
#endif
};

/*
 * Number of buckets to spread shared arrays into. Each bucket is
 * associated with one mutex so locking a bucket locks all arrays
 * in that bucket as well. The number of buckets should be a prime.
 */

54
55
56
57
58
59
60





61
62
63
64
65
66
67

static char *Sv_tclEmptyStringRep = NULL;

/*
 * Global variables used within this file.
 */






static Bucket*    buckets;      /* Array of buckets. */
static Tcl_Mutex  bucketsMutex; /* Protects the array of buckets */

static SvCmdInfo* svCmdInfo;    /* Linked list of registered commands */
static RegType*   regType;      /* Linked list of registered obj types */
static PsStore*   psStore;      /* Linked list of registered pers. stores */








>
>
>
>
>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

static char *Sv_tclEmptyStringRep = NULL;

/*
 * Global variables used within this file.
 */

#ifdef SV_FINALIZE
static size_t     nofThreads;      /* Number of initialized threads */
static Tcl_Mutex  nofThreadsMutex; /* Protects the nofThreads variable */
#endif /* SV_FINALIZE */

static Bucket*    buckets;      /* Array of buckets. */
static Tcl_Mutex  bucketsMutex; /* Protects the array of buckets */

static SvCmdInfo* svCmdInfo;    /* Linked list of registered commands */
static RegType*   regType;      /* Linked list of registered obj types */
static PsStore*   psStore;      /* Linked list of registered pers. stores */

79
80
81
82
83
84
85

86
87
88
89
90
91
92
static Tcl_ObjCmdProc SvIncrObjCmd;
static Tcl_ObjCmdProc SvSetObjCmd;
static Tcl_ObjCmdProc SvExistsObjCmd;
static Tcl_ObjCmdProc SvGetObjCmd;
static Tcl_ObjCmdProc SvArrayObjCmd;
static Tcl_ObjCmdProc SvUnsetObjCmd;
static Tcl_ObjCmdProc SvNamesObjCmd;


/*
 * New commands added to
 * standard set of nsv_*
 */

static Tcl_ObjCmdProc SvPopObjCmd;







>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
static Tcl_ObjCmdProc SvIncrObjCmd;
static Tcl_ObjCmdProc SvSetObjCmd;
static Tcl_ObjCmdProc SvExistsObjCmd;
static Tcl_ObjCmdProc SvGetObjCmd;
static Tcl_ObjCmdProc SvArrayObjCmd;
static Tcl_ObjCmdProc SvUnsetObjCmd;
static Tcl_ObjCmdProc SvNamesObjCmd;
static Tcl_ObjCmdProc SvHandlersObjCmd;

/*
 * New commands added to
 * standard set of nsv_*
 */

static Tcl_ObjCmdProc SvPopObjCmd;
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
static int DeleteContainer(Container*);
static int FlushArray(Array*);
static int DeleteArray(Array*);

static void SvAllocateContainers(Bucket*);
static void SvRegisterStdCommands(void);

#define SV_FINALIZE
#ifdef SV_FINALIZE
static void SvFinalizeContainers(Bucket*);
static void SvFinalize(ClientData);
#endif /* SV_FINALIZE */

static PsStore* GetPsStore(const char *handle);








<







129
130
131
132
133
134
135

136
137
138
139
140
141
142
static int DeleteContainer(Container*);
static int FlushArray(Array*);
static int DeleteArray(Array*);

static void SvAllocateContainers(Bucket*);
static void SvRegisterStdCommands(void);


#ifdef SV_FINALIZE
static void SvFinalizeContainers(Bucket*);
static void SvFinalize(ClientData);
#endif /* SV_FINALIZE */

static PsStore* GetPsStore(const char *handle);

508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    if (hPtr == NULL) {
        PsStore *psPtr = arrayPtr->psPtr;
        if (psPtr) {
            char *val = NULL;
            size_t len = 0;
            if (psPtr->psGet(psPtr->psHandle, key, &val, &len) == 0) {
                tclObj = Tcl_NewStringObj(val, len);
                psPtr->psFree(val);
            }
        }
        if (!(flags & FLAGS_CREATEVAR) && tclObj == NULL) {
            return NULL;
        }
        if (tclObj == NULL) {
            tclObj = Tcl_NewObj();







|







528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
    if (hPtr == NULL) {
        PsStore *psPtr = arrayPtr->psPtr;
        if (psPtr) {
            char *val = NULL;
            size_t len = 0;
            if (psPtr->psGet(psPtr->psHandle, key, &val, &len) == 0) {
                tclObj = Tcl_NewStringObj(val, len);
                psPtr->psFree(psPtr->psHandle, val);
            }
        }
        if (!(flags & FLAGS_CREATEVAR) && tclObj == NULL) {
            return NULL;
        }
        if (tclObj == NULL) {
            tclObj = Tcl_NewObj();
1314
1315
1316
1317
1318
1319
1320

1321

1322
1323
1324
1325
1326
1327
1328
         *
         * When binding an non-existent array, we open the persistent
         * storage and cache all key/value pairs found there into tne
         * newly created shared array.
         */

        PsStore *psPtr;

        size_t len;

        char *psurl, *key = NULL, *val = NULL;

        if (objc < 4) {
            Tcl_WrongNumArgs(interp, 2, objv, "array handle");
            ret = TCL_ERROR;
            goto cmdExit;
        }







>

>







1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
         *
         * When binding an non-existent array, we open the persistent
         * storage and cache all key/value pairs found there into tne
         * newly created shared array.
         */

        PsStore *psPtr;
        Tcl_HashEntry *hPtr;
        size_t len;
        int new;
        char *psurl, *key = NULL, *val = NULL;

        if (objc < 4) {
            Tcl_WrongNumArgs(interp, 2, objv, "array handle");
            ret = TCL_ERROR;
            goto cmdExit;
        }
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365

1366
1367

1368
1369
1370
1371
1372
1373
1374
            Tcl_AppendResult(interp, "can't open persistent storage on \"",
                             psurl, "\"", NULL);
            ret = TCL_ERROR;
            goto cmdExit;
        }
        if (arrayPtr) {
            Tcl_HashSearch search;
            Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&arrayPtr->vars,&search);
            arrayPtr->psPtr = psPtr;
            arrayPtr->bindAddr = strcpy(ckalloc(len+1), psurl);
            while (hPtr) {
                svObj = Tcl_GetHashValue(hPtr);
                if (ReleaseContainer(interp, svObj, SV_CHANGED) != TCL_OK) {
                    ret = TCL_ERROR;
                    goto cmdExit;
                }
                hPtr = Tcl_NextHashEntry(&search);
            }
        } else {
            arrayPtr = LockArray(interp, arrayName, FLAGS_CREATEARRAY);
            arrayPtr->psPtr = psPtr;
            arrayPtr->bindAddr = strcpy(ckalloc(len+1), psurl);
        }
        if (!psPtr->psFirst(psPtr->psHandle, &key, &val, &len)) {
            do {

                psPtr->psFree(val); /* What a waste! */
                AcquireContainer(arrayPtr, key, FLAGS_CREATEVAR);

            } while (!psPtr->psNext(psPtr->psHandle, &key, &val, &len));
        }

    } else if (index == AUNBIND) {
        if (arrayPtr && arrayPtr->psPtr) {
            PsStore *psPtr = arrayPtr->psPtr;
            if (psPtr->psClose(psPtr->psHandle) == -1) {







|

















>
|
|
>







1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
            Tcl_AppendResult(interp, "can't open persistent storage on \"",
                             psurl, "\"", NULL);
            ret = TCL_ERROR;
            goto cmdExit;
        }
        if (arrayPtr) {
            Tcl_HashSearch search;
            hPtr = Tcl_FirstHashEntry(&arrayPtr->vars,&search);
            arrayPtr->psPtr = psPtr;
            arrayPtr->bindAddr = strcpy(ckalloc(len+1), psurl);
            while (hPtr) {
                svObj = Tcl_GetHashValue(hPtr);
                if (ReleaseContainer(interp, svObj, SV_CHANGED) != TCL_OK) {
                    ret = TCL_ERROR;
                    goto cmdExit;
                }
                hPtr = Tcl_NextHashEntry(&search);
            }
        } else {
            arrayPtr = LockArray(interp, arrayName, FLAGS_CREATEARRAY);
            arrayPtr->psPtr = psPtr;
            arrayPtr->bindAddr = strcpy(ckalloc(len+1), psurl);
        }
        if (!psPtr->psFirst(psPtr->psHandle, &key, &val, &len)) {
            do {
                Tcl_Obj * tclObj = Tcl_NewStringObj(val, len);
                hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, &new);
                Tcl_SetHashValue(hPtr, CreateContainer(arrayPtr, hPtr, tclObj));
                psPtr->psFree(psPtr->psHandle, val);
            } while (!psPtr->psNext(psPtr->psHandle, &key, &val, &len));
        }

    } else if (index == AUNBIND) {
        if (arrayPtr && arrayPtr->psPtr) {
            PsStore *psPtr = arrayPtr->psPtr;
            if (psPtr->psClose(psPtr->psHandle) == -1) {
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
    /*
     * Syntax:
     *
     *     tsv::lock array arg ?arg ...?
     */

    if (objc < 3) {
	Tcl_WrongNumArgs(interp, 1, objv, "array arg ?arg...?");
	return TCL_ERROR;
    }

    arrayPtr  = LockArray(interp, Tcl_GetString(objv[1]), FLAGS_CREATEARRAY);
    bucketPtr = arrayPtr->bucketPtr;

    /*
     * Evaluate passed arguments as Tcl script. Note that







|
|







2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
    /*
     * Syntax:
     *
     *     tsv::lock array arg ?arg ...?
     */

    if (objc < 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "array arg ?arg...?");
        return TCL_ERROR;
    }

    arrayPtr  = LockArray(interp, Tcl_GetString(objv[1]), FLAGS_CREATEARRAY);
    bucketPtr = arrayPtr->bucketPtr;

    /*
     * Evaluate passed arguments as Tcl script. Note that
2051
2052
2053
2054
2055
2056
2057









































2058
2059
2060
2061
2062
2063
2064
     * since it needs the array which may be unset by the script.
     */

    UNLOCK_BUCKET(bucketPtr);

    return ret;
}










































/*
 *-----------------------------------------------------------------------------
 *
 * Sv_RegisterStdCommands --
 *
 *      Register standard shared variable commands







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
     * since it needs the array which may be unset by the script.
     */

    UNLOCK_BUCKET(bucketPtr);

    return ret;
}

/*
 *-----------------------------------------------------------------------------
 *
 * SvHandlersObjCmd --
 *
 *    This procedure is invoked to process "tsv::handlers" Tcl command.
 *    See the user documentation for details on what it does.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static int
SvHandlersObjCmd(
             ClientData arg,                     /* Not used. */
             Tcl_Interp *interp,                 /* Current interpreter. */
             int objc,                           /* Number of arguments. */
             Tcl_Obj *const objv[])              /* Argument objects. */
{
    /*
     * Syntax:
     *
     *     tsv::handlers
     */

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

    Tcl_SetObjResult(interp, Tcl_NewStringObj(
                Tcl_Merge(sizeof(handlers)/sizeof(handlers[0]), handlers), -1));

    return TCL_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Sv_RegisterStdCommands --
 *
 *      Register standard shared variable commands
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095

2096
2097
2098
2099
2100
2101
2102
SvRegisterStdCommands(void)
{
    static int initialized = 0;

    if (initialized == 0) {
        Tcl_MutexLock(&initMutex);
        if (initialized == 0) {
            Sv_RegisterCommand("var",    SvObjObjCmd,    NULL, 1);
            Sv_RegisterCommand("object", SvObjObjCmd,    NULL, 1);
            Sv_RegisterCommand("set",    SvSetObjCmd,    NULL, 0);
            Sv_RegisterCommand("unset",  SvUnsetObjCmd,  NULL, 0);
            Sv_RegisterCommand("get",    SvGetObjCmd,    NULL, 0);
            Sv_RegisterCommand("incr",   SvIncrObjCmd,   NULL, 0);
            Sv_RegisterCommand("exists", SvExistsObjCmd, NULL, 0);
            Sv_RegisterCommand("append", SvAppendObjCmd, NULL, 0);
            Sv_RegisterCommand("array",  SvArrayObjCmd,  NULL, 0);
            Sv_RegisterCommand("names",  SvNamesObjCmd,  NULL, 0);
            Sv_RegisterCommand("pop",    SvPopObjCmd,    NULL, 0);
            Sv_RegisterCommand("move",   SvMoveObjCmd,   NULL, 0);
            Sv_RegisterCommand("lock",   SvLockObjCmd,   NULL, 0);

            initialized = 1;
        }
        Tcl_MutexUnlock(&initMutex);
    }
}

/*







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







2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
SvRegisterStdCommands(void)
{
    static int initialized = 0;

    if (initialized == 0) {
        Tcl_MutexLock(&initMutex);
        if (initialized == 0) {
            Sv_RegisterCommand("var",      SvObjObjCmd,      NULL, 1);
            Sv_RegisterCommand("object",   SvObjObjCmd,      NULL, 1);
            Sv_RegisterCommand("set",      SvSetObjCmd,      NULL, 0);
            Sv_RegisterCommand("unset",    SvUnsetObjCmd,    NULL, 0);
            Sv_RegisterCommand("get",      SvGetObjCmd,      NULL, 0);
            Sv_RegisterCommand("incr",     SvIncrObjCmd,     NULL, 0);
            Sv_RegisterCommand("exists",   SvExistsObjCmd,   NULL, 0);
            Sv_RegisterCommand("append",   SvAppendObjCmd,   NULL, 0);
            Sv_RegisterCommand("array",    SvArrayObjCmd,    NULL, 0);
            Sv_RegisterCommand("names",    SvNamesObjCmd,    NULL, 0);
            Sv_RegisterCommand("pop",      SvPopObjCmd,      NULL, 0);
            Sv_RegisterCommand("move",     SvMoveObjCmd,     NULL, 0);
            Sv_RegisterCommand("lock",     SvLockObjCmd,     NULL, 0);
            Sv_RegisterCommand("handlers", SvHandlersObjCmd, NULL, 0); 
            initialized = 1;
        }
        Tcl_MutexUnlock(&initMutex);
    }
}

/*
2121
2122
2123
2124
2125
2126
2127














2128
2129
2130
2131
2132
2133
2134
{
    int i;
    Bucket *bucketPtr;
    SvCmdInfo *cmdPtr;
    const Tcl_UniChar no[3] = {'n', 'o', 0} ;
    Tcl_Obj *obj;















    /*
     * Add keyed-list datatype
     */

    TclX_KeyedListInit(interp);
    Sv_RegisterKeylistCommands();








>
>
>
>
>
>
>
>
>
>
>
>
>
>







2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
{
    int i;
    Bucket *bucketPtr;
    SvCmdInfo *cmdPtr;
    const Tcl_UniChar no[3] = {'n', 'o', 0} ;
    Tcl_Obj *obj;

#ifdef SV_FINALIZE
    /*
     * Create exit handler for this thread
     */
    Tcl_CreateThreadExitHandler(SvFinalize, NULL);

    /*
     * Increment number of threads
     */
    Tcl_MutexLock(&nofThreadsMutex);
    ++nofThreads;
    Tcl_MutexUnlock(&nofThreadsMutex);
#endif /* SV_FINALIZE */

    /*
     * Add keyed-list datatype
     */

    TclX_KeyedListInit(interp);
    Sv_RegisterKeylistCommands();

2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210

2211
2212



2213
2214
2215
2216
2217
2218
2219
     * Create array of buckets and initialize each bucket
     */

    if (buckets == NULL) {
        Tcl_MutexLock(&bucketsMutex);
        if (buckets == NULL) {
            buckets = (Bucket *)ckalloc(sizeof(Bucket) * NUMBUCKETS);
	    Tcl_CreateExitHandler(SvFinalize, NULL);

            for (i = 0; i < NUMBUCKETS; ++i) {
                bucketPtr = &buckets[i];
                memset(bucketPtr, 0, sizeof(Bucket));
                Tcl_InitHashTable(&bucketPtr->arrays, TCL_STRING_KEYS);
                Tcl_InitHashTable(&bucketPtr->handles, TCL_ONE_WORD_KEYS);
            }

            /*
             * There is no other way to get Sv_tclEmptyStringRep
             * pointer value w/o this trick.
             */

            {
                Tcl_Obj *dummy = Tcl_NewObj();
                Sv_tclEmptyStringRep = dummy->bytes;
                Tcl_DecrRefCount(dummy);
            }

#ifdef HAVE_GDBM
            /*
             * Register persistent store handlers
             */

            Sv_RegisterGdbmStore();
#endif



        }
        Tcl_MutexUnlock(&bucketsMutex);
    }

    return TCL_OK;
}








<



















<



>


>
>
>







2260
2261
2262
2263
2264
2265
2266

2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285

2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
     * Create array of buckets and initialize each bucket
     */

    if (buckets == NULL) {
        Tcl_MutexLock(&bucketsMutex);
        if (buckets == NULL) {
            buckets = (Bucket *)ckalloc(sizeof(Bucket) * NUMBUCKETS);


            for (i = 0; i < NUMBUCKETS; ++i) {
                bucketPtr = &buckets[i];
                memset(bucketPtr, 0, sizeof(Bucket));
                Tcl_InitHashTable(&bucketPtr->arrays, TCL_STRING_KEYS);
                Tcl_InitHashTable(&bucketPtr->handles, TCL_ONE_WORD_KEYS);
            }

            /*
             * There is no other way to get Sv_tclEmptyStringRep
             * pointer value w/o this trick.
             */

            {
                Tcl_Obj *dummy = Tcl_NewObj();
                Sv_tclEmptyStringRep = dummy->bytes;
                Tcl_DecrRefCount(dummy);
            }


            /*
             * Register persistent store handlers
             */
#ifdef HAVE_GDBM
            Sv_RegisterGdbmStore();
#endif
#ifdef HAVE_LMDB
            Sv_RegisterLmdbStore();
#endif
        }
        Tcl_MutexUnlock(&bucketsMutex);
    }

    return TCL_OK;
}

2246
2247
2248
2249
2250
2251
2252












2253
2254
2255
2256
2257
2258
2259
{
    register int i;
    SvCmdInfo *cmdPtr;
    RegType *regPtr;

    Tcl_HashEntry *hashPtr;
    Tcl_HashSearch search;













    /*
     * Reclaim memory for shared arrays
     */

    if (buckets != NULL) {
        Tcl_MutexLock(&bucketsMutex);







>
>
>
>
>
>
>
>
>
>
>
>







2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
{
    register int i;
    SvCmdInfo *cmdPtr;
    RegType *regPtr;

    Tcl_HashEntry *hashPtr;
    Tcl_HashSearch search;

    /*
     * Decrement number of threads. Proceed only if I was the last one. The
     * mutex is unlocked at the end of this function, so new threads that might
     * want to register in the meanwhile will find a clean environment when
     * they eventually succeed acquiring nofThreadsMutex.
     */
    Tcl_MutexLock(&nofThreadsMutex);
    if (nofThreads > 1)
    {
        goto done;
    }

    /*
     * Reclaim memory for shared arrays
     */

    if (buckets != NULL) {
        Tcl_MutexLock(&bucketsMutex);
2307
2308
2309
2310
2311
2312
2313




2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
            ckfree((char*)regPtr);
            regPtr = tmpPtr;
        }
        regType = NULL;
    }

    Tcl_MutexUnlock(&svMutex);




}
#endif /* SV_FINALIZE */

/* EOF $RCSfile: threadSvCmd.c,v $ */

/* Emacs Setup Variables */
/* Local Variables:      */
/* mode: C               */
/* indent-tabs-mode: nil */
/* c-basic-offset: 4     */
/* End:                  */








>
>
>
>












2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
            ckfree((char*)regPtr);
            regPtr = tmpPtr;
        }
        regType = NULL;
    }

    Tcl_MutexUnlock(&svMutex);

done:
    --nofThreads;
    Tcl_MutexUnlock(&nofThreadsMutex);
}
#endif /* SV_FINALIZE */

/* EOF $RCSfile: threadSvCmd.c,v $ */

/* Emacs Setup Variables */
/* Local Variables:      */
/* mode: C               */
/* indent-tabs-mode: nil */
/* c-basic-offset: 4     */
/* End:                  */

Changes to generic/threadSvCmd.h.
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

typedef int (ps_get_proc)   (ClientData, const char*, char**, size_t*);
typedef int (ps_put_proc)   (ClientData, const char*, char*, size_t);
typedef int (ps_first_proc) (ClientData, char**, char**, size_t*);
typedef int (ps_next_proc)  (ClientData, char**, char**, size_t*);
typedef int (ps_delete_proc)(ClientData, const char*);
typedef int (ps_close_proc) (ClientData);
typedef void(ps_free_proc)  (void*);

typedef const char* (ps_geterr_proc)(ClientData);

/*
 * This structure maintains a bunch of pointers to functions implementing
 * the simple persistence layer for the shared variable arrays.
 */







|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

typedef int (ps_get_proc)   (ClientData, const char*, char**, size_t*);
typedef int (ps_put_proc)   (ClientData, const char*, char*, size_t);
typedef int (ps_first_proc) (ClientData, char**, char**, size_t*);
typedef int (ps_next_proc)  (ClientData, char**, char**, size_t*);
typedef int (ps_delete_proc)(ClientData, const char*);
typedef int (ps_close_proc) (ClientData);
typedef void(ps_free_proc)  (ClientData, void*);

typedef const char* (ps_geterr_proc)(ClientData);

/*
 * This structure maintains a bunch of pointers to functions implementing
 * the simple persistence layer for the shared variable arrays.
 */
Changes to generic/threadSvListCmd.c.
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
        Tcl_WrongNumArgs(interp, off, objv, "index ?index...? value");
        goto cmd_err;
    }

    lPtr = svObj->tclObj;
    argc = objc - off - 1;

    if (!SvLsetFlat(interp, lPtr, argc, (Tcl_Obj**)(objv+off),objv[objc-1])) {
        return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, Sv_DuplicateObj(lPtr));

    return Sv_PutContainer(interp, svObj, SV_CHANGED);








|







842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
        Tcl_WrongNumArgs(interp, off, objv, "index ?index...? value");
        goto cmd_err;
    }

    lPtr = svObj->tclObj;
    argc = objc - off - 1;

    if (!SvLsetFlat(interp, lPtr, argc, (Tcl_Obj**)objv+off,objv[objc-1])) {
        return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, Sv_DuplicateObj(lPtr));

    return Sv_PutContainer(interp, svObj, SV_CHANGED);

Changes to lib/ttrace.tcl.
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
        interp alias {} [namespace current]::_set     {} tsv::set
        interp alias {} [namespace current]::_unset   {} tsv::unset
    } else {
        error "requires NaviServer/AOLserver or Tcl threading extension"
    }

    # Keep in sync with the Thread package
    package provide Ttrace 2.7.2

    # Package variables
    variable resolvers ""     ; # List of registered resolvers
    variable tracers   ""     ; # List of registered cmd tracers
    variable scripts   ""     ; # List of registered script makers
    variable enables   ""     ; # List of trace-enable callbacks
    variable disables  ""     ; # List of trace-disable callbacks







|







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
        interp alias {} [namespace current]::_set     {} tsv::set
        interp alias {} [namespace current]::_unset   {} tsv::unset
    } else {
        error "requires NaviServer/AOLserver or Tcl threading extension"
    }

    # Keep in sync with the Thread package
    package provide Ttrace 2.8.0

    # Package variables
    variable resolvers ""     ; # List of registered resolvers
    variable tracers   ""     ; # List of registered cmd tracers
    variable scripts   ""     ; # List of registered script makers
    variable enables   ""     ; # List of trace-enable callbacks
    variable disables  ""     ; # List of trace-disable callbacks
Added tests/French.txt.


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
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
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
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
979
980
981
982
983
984
985
986
987
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
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
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
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
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
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
###########################################################################
#Copyright 1999 The Internet Dictionary Project/Tyler Chambers
#http://www.june29.com/IDP/
#This file is free to use and modify.  Thank you for using the IDP.
#
#Approximately 1340 entries.  9/21/97
#Approximately 1884 entries.  1/7/98
#Appriximately 2160 entries.  3/8/98
#Approximately 3040 entries.  8/18/98
#Approximately 3250 entries. 2/19/99
###########################################################################
a	un(e): ~ book = un livre. 2.(instead of number one) ~ year ago; il y a un an[Article]
a	un, une[Pronoun]
aardvark	orycte/rope[Noun]
aardvark	adverbe[Adverb]
aardvarks	tamanoirs
aback	etre deconcerte[Verb]
aback	to be taken ~ : etre deconcerte,interdit[Adverb]
aback	to be taken ~ :etre stupefait(e)[Adjective]
abacterial	non-bacte/rien[Adjective]
abacus	abaque,boulier (compteur)[Noun]
abacus	abaque[Noun]
abacus	boulier,compteur[Noun]
abacuses	abaques, bouliers
abacuses	abaques[Noun]
abacuses	bouliers, compteurs[Noun]
abaft	sur l'arrie\re, vers l'arrie\re[Adverb]
abalone	ormeau[Noun]
abalones	ormeaux[Noun]
abandon	abandon, laisser-aller[Noun]
abandon	abandonner[Verb]
abandoned	abandonne/[Adjective]
abandoned	abandonnes[Adjective]
abandoned	de/vergonde/[Adjective]
abandonee	abandoned, forsaken[Adjective]
abandoner	abandoneur[Adjective]
abandoning	abandonnant[Verb]
abandonment	abandon[Noun]
abandonment	abandonnement[Noun]
abandonments	abandons[Noun]
abandons	abandonnes[Verb]
abase	abaisser, humilier[Verb]
abased	humilie/
abasement	abaissement, humiliation[Noun]
abasements	abaissement[Noun]
abases	mortifie, humilie, rabaisse[Verb]
abash	confondre, de/concerter[Verb]
abashed	confus(e), embarrasse(e)[Adjective]
abashed	deconcerte, confus, gene[Adjective]
abasing	humiliant[Adjective]
abate	diminuer[Verb]
abate	s'apaiser, se calmer[Verb]
abated	a diminue/[Verb]
abatement	diminution[Noun]
abatement	la suppression[Noun]
abatements	coupures
abates	se calme, s'apaise[Verb]
abating	faiblant[Adjective]
abating	faiblant[Adjective]
abattoir	abattoir[Noun]
abbe	abbe/[Noun]
abberations	aberrations[Noun]
abbey	abbaye (f)[Noun]
abbey	une abbaye[Noun]
abbeys	abbayes[Noun]
abbot	abbe ( d'un monastere)[Noun]
abbot	abbe/
abbot	abbe/[Noun]
abbot	pere superieur[Noun]
abbot	abbe/[Noun]
abbots	abbe/s[Noun]
abbreviate	abbre/ger[Verb]
abbreviate	abre/ger[Verb]
abbreviated	abbre/gé[Verb]
abbreviates	s'abre\ge[Verb]
abbreviating	abre/geant[Verb]
abbreviation	abbre/viation[Noun]
abbreviation	abre/viation[Noun]
abbreviation	abreviation (f)[Noun]
abbreviations	abbre/viations[Noun]
abbreviations	abre/viations[Noun]
abbreviator	abre/viateur[Noun]
abdicable	abdiquable[Adjective]
abdicate	abdiquer[Verb]
abdicate	renoncer[Verb]
abdicated	abdique/[Adjective]
abdication	abdication[Verb]
abdicator	abdicateur[Adjective]
abdomen	abdomen (m)[Noun]
abdomen	abdomen[Noun]
abdomens	abdomens[Noun]
abdominal	abdominal[Adjective]
abdominally	abdominalement[Adverb]
abduct	enlever[Verb]
abducted	enlevé[Verb]
abducting	enlevant[Verb]
abduction	enlèvement[Noun]
abductions	enlèvements[Noun]
abductor	adducteur[Adjective]
abductors	adducteur[Adjective]
abducts	detourner[Verb]
abeam	par le travers[Adverb]
abecedarian	abécédaire[Noun]
abed	au lit[Noun]
aberrance	aberration (f)[Noun]
aberrant	aberrant, egare[Adjective]
aberrantly	de fac,on aberrante[Adverb]
aberration	aberration[Noun]
aberration	anomalie (f)[Noun]
aberrations	erreurs
abet	encourager (au crime)[Verb]
abetment	encouragement[Noun]
abets	secourirs
abetted	encouragea, soutient[Verb]
abetter	complice[Noun]
abetting	provoquant[Adjective]
abettor	aide, complice[Noun]
abeyance	in ~ (law): en desuetude; (matter) en suspens[Noun]
abeyant	en attente[Adjective]
abhor	avoir horreur de[Verb]
abhorred	abhorrait or abhorre/, de/testait or de/teste/[Verb]
abhorrence	aversion extreme, horreur[Noun]
abhorrent	odieux
abhorrently	de fac,on odieuse[Adverb]
abhorring	abhorrant[Adjective]
abhors	abhorrer[Verb]
abide	i can't ~ it/him : je ne peut pas le souffrir &lt;or&gt; supporter; to ~ by : observer, respecter[Verb]
abided	souffri[Adjective]
abides	tole\re, demeure, subsiste[Verb]
abiding	constant, e/ternel[Adjective]
abilities	talents[Noun]
ability	compe/tence
ability	competence (f); capacite (f); (skill) talent (m)[Noun]
abiotic	abiotique[Adjective]
abject	(poverty) sordid; (apology) plat(e)[Adjective]
abjection	abjection(f)[Noun]
abjectly	avec servilite/[Adjective]
abjuration	renoncement, apostasie (rel)[Noun]
abjure	abjurer[Verb]
abjurer	personne qui abjure
ablation	ablation
ablative	ablatif[Adjective]
ablaze	en feu
ablaze	en feu, en flammes[Adjective]
able	compe/tent(e)
able	competent(e); to be ~ to do sth : pouvoir faire qch, etre capable de faire qch[Adjective]
able	capable -to be able to e^tre capable de , pouvoir
able	to be ~: pouvoir[Verb]
abler	plus compétent(e)[Adjective]
ablest	le plus compe/tent[Adjective]
abloom	en fleur
ablution	ablution[Noun]
ablutions	ablutions[Noun]
ably	de fac,on compe/tente, habilement[Adjective]
abnegate	Renouncer à[Verb]
abnegates	renie, re/pudie, rejette[Verb]
abnegation	abne/gation[Noun]
abnormal	abnormal(e)[Adjective]
abnormal	anormal(e)[Adjective]
abnormal	anormal
abnormalities	anomalies, difformite/s[Noun]
abnormality	anomalie, malformation[Noun]
abnormally	anormalement[Adverb]
aboard	a\ bord
abode	of no fixed ~ :sans domicile fixe[Noun]
abodes	demeures, domiciles[Noun]
aboil	en train de bouillir[Verb]
abolish	abolir[Verb]
abolish	abroger, supprimer[Verb]
abolishable	qui peut e^tre aboli
abolished	aboli(es)[Verb]
abolisher	suppresseur[Adjective]
abolishes	abolit, abroge, supprime
abolishment	suppression, abolition, abrogation[Noun]
abolition	abolition
abolition	suppression[Noun]
abolitionism	abolitionnisme[Noun]
abolitionist	abolitionniste[Adjective]
abolitionists	abolitionnistes, antiesclavagistes (hist)[Noun]
abominable	abominable[Adjective]
abominably	abominablement, odieusement[Adverb]
abominate	abhorrer, exe/crer, abominer[Verb]
abomination	abomination[Noun]
abominations	abominations[Noun]
aboriginal	aborige\ne[Noun]
aboriginally	aboriginalement[Adverb]
aborigine	aborigene m/f[Noun]
aborigines	aborigines[Noun]
abort	faire avorter[Verb]
aborted	avorté[Verb]
aborter	avorteur/ avorteuse / faiseuse d'anges[Noun]
aborting	discontinuant[Adjective]
abortion	avortement
abortion	avortement (m)[Noun]
abortionist	avorteur[Noun]
abortionists	avorteurs
abortions	avortements[Noun]
abortive	manque(e)[Adjective]
abortively	en vain[Verb]
aborts	avorte, e/choue, abandonne[Verb]
abound	abonder
abounding	abondant[Adjective]
about	(approximatly)environ, a peu pres[Adjective]
above	au-dessus[Adjective]
aboveboard	re/gulier, correct[Adjective]
aboveground	au-dessus du sol, a\ la surface[Adjective]
abovementioned	ci-haut mentionné
abovementioned	mentionné ci-dessus[Adjective]
abracadabra	abracadabra[Verb]
abradable	qui peut s'e/rafler, qui peut s'e/roder[Adjective]
abrade	user en frottant, e/roder[Verb]
abrasion	frottement, e/corchure, abrasion[Noun]
abrasions	écorchures, égratignures[Noun]
abrasive	abrasif(ive)[Adjective]
abrasives	abrasifs[Noun]
abreast	de front[Adjective]
abridge	abreger[Verb]
abridged	abre/ge/[Verb]
abridgement	re/sume/[Noun]
abridging	raccourc,ant[Adjective]
abroad	a\ l'e/tranger, au loin[Adverb]
abrogate	abroger[Verb]
abrogated	abroge/, aboli[Verb]
abrogation	abrogation[Noun]
abrupt	abrupte[Adjective]
abruptly	brusquement[Adjective]
abruptness	abrupte[Noun]
abscess	abcès[Noun]
abscesses	abce\s[Noun]
abscond	s'enfuir[Verb]
absconded	s'enfuir (from, de)[Verb]
absence	absence[Noun]
absences	absences[Noun]
absent	absent[Adjective]
absentee	absentee[Noun]
absenteeism	absente/isme[Noun]
absentees	absentees[Noun]
absently	distraitement[Adverb]
absentminded	distrait[Adjective]
absentmindedly	d'un air distrait[Adverb]
absentmindedly	distraitement[Adverb]
absentmindedness	distraction[Noun]
absinth	absinthe[Noun]
absinthe	absinthe[Noun]
absolute	absolu[Adjective]
absolutely	absolument[Adverb]
absolution	absolution[Noun]
absolutism	absolutisme[Noun]
absolve	absoudre[Verb]
absolved	exone/re/[Adjective]
absolving	exon/erant[Adjective]
absorb	absorber, retenir, assimiler[Verb]
absorbability	absorbabilite/[Adjective]
absorbable	absorbable[Adjective]
absorbant	absorbant[Adjective]
absorbed	absorb/e[Adjective]
absorbent	absorbant(e)[Adjective]
absorbing	absorbant[Adjective]
absorption	absorbtion[Noun]
abstain	abstenir[Verb]
abstained	absteni[Adjective]
abstemious	frugal(e), sobre[Adjective]
abstention	abstention[Noun]
abstentions	abstentions[Noun]
abstinence	abstinence[Noun]
abstinent	sobre[Adjective]
abstinently	sobrement[Adverb]
abstract	abstrait[Adjective]
abstract	résumé, abrégé[Noun]
abstraction	distraction, abstraction[Noun]
abstruse	abstrus(e)[Adjective]
absurd	absurde[Adjective]
absurdism	absurdisme[Noun]
absurdist	absurdiste[Noun]
absurdities	absurdite/s[Noun]
absurdity	absurdite/[Noun]
abundance	abondance[Noun]
abundant	abondant[Adjective]
abundantly	abondamment[Adjective]
abuse	abus[Noun]
abused	abuse/[Verb]
abused	insulter[Verb]
abusive	abusif[Adjective]
abusively	abusivement[Adverb]
abut	etre contigu(ë) à[Verb]
abuzz	bourdonnant[Adjective]
abysmal	épouvantable, abominable[Noun]
abysmally	abominablement[Adverb]
abyss	abysse[Noun]
abyss	abi^me[Noun]
abysses	abysses[Noun]
abysses	abi^mes[Noun]
acacia	acacia[Noun]
academic	académique[Adjective]
academical	acade/mique[Adjective]
academically	acade/miquement[Adverb]
academician	acade/micien[Noun]
academies	academies[Noun]
academism	academisme[Noun]
academy	académie[Noun]
acanthus	acanthe[Noun]
acanthuses	acanthes[Noun]
accede	acce/der[Verb]
accede	consentir[Verb]
accelerate	acce/le/rer[Verb]
accelerated	accéléré[Adjective]
acceleration	acce/le/ration[Noun]
accelerations	acce/le/rations[Noun]
accelerator	acce/le/rateur[Noun]
accelerators	acce/le/rateurs[Noun]
accelerometer	acce/le/rome\tre[Noun]
accelerometers	acce/le/rome\tres[Noun]
accent	accent[Noun]
accented	accente/[Verb]
accented	accentue/
accents	accents[Noun]
accentual	accentuelle[Adjective]
accentually	accentuellement[Adverb]
accentuate	accentuer[Verb]
accentuated	accentue/[Adjective]
accentuation	accentuation[Noun]
accept	accepter[Verb]
acceptability	acceptabilite/[Noun]
acceptable	acceptable[Adjective]
acceptably	convenablement[Adverb]
acceptance	acceptation[Noun]
accepted	accepte/[Verb]
accepter	accepteur[Noun]
acceptor	accepteur[Noun]
access	acce\s, acce/der (verb)[Noun]
accessed	accesse/[Verb]
accessibility	accessibilite/[Noun]
accessible	accessible[Adjective]
accession	accession[Noun]
accessorial	accessoire[Adjective]
accessories	accessoires[Noun]
accessorize	accessoriser[Verb]
accessors	accesseurs[Noun]
accessory	accessoire[Noun]
accident	accident[Noun]
accidental	accidentel[Adjective]
accidentalism	accidentalisme[Noun]
accidentalist	accidentaliste[Noun]
accidentally	accidentellement[Adverb]
accidently	accidentalement[Adverb]
accidents	accidents[Noun]
acclaim	acclamer[Verb]
acclamation	acclamation[Noun]
acclimate	acclimater[Verb]
acclimated	acclimate/[Adjective]
acclimatize	acclimater[Verb]
accolade	accolade[Noun]
accolades	accolades[Noun]
accommodate	accomoder[Verb]
accommodated	accommode/[Verb]
accommodating	obligeant(e)[Adjective]
accommodation	accomodation[Noun]
accommodations	accommodations[Noun]
accompanied	accompagne/[Adjective]
accompanier	accompagneur[Noun]
accompanies	accompanies[Verb]
accompaniment	accompagnement[Noun]
accompaniments	accompagnements[Noun]
accompanist	accompaniste[Noun]
accompanists	accompanistes[Noun]
accompany	accompagner, accompagner qqn (à)[Verb]
accompanyist	accompagnateur[Noun]
accompanyists	accompagnateurs[Noun]
accomplice	complice[Noun]
accomplices	complices[Noun]
accomplish	accomplir[Verb]
accomplished	accompli(e)[Adjective]
accomplishment	accomplissement[Noun]
accomplishments	accomplissements[Noun]
accord	accord[Noun]
accordance	in accordance with : en conformite/ avec
accorder	accordeur[Noun]
accorders	accordeurs[Noun]
accordion	accorde/on[Noun]
accordionist	accordionist     accordioniste[Noun]
accordionists	accordionists     accordionistes[Noun]
accordions	accorde/ons[Noun]
accords	accords[Noun]
accost	accoster[Verb]
account	compte[Noun]
accountability	responsibilité[Noun]
accountable	responsable[Adjective]
accountant	comptable[Noun]
accounting	comptabilité
accouterment	accoutrement[Noun]
accredited	accredite/[Verb]
accrued	accrue/[Verb]
accruement	accruement[Noun]
accumulated	accumule/[Verb]
acetaminophen	ace/taminophe\n[Noun]
acetify	acetifie/[Verb]
acetone	acetone[Noun]
acid	acide[Noun]
acidic	acidique[Adjective]
admission	aveu[Adverb]
allergen	allergene
allergen	allergene[Noun]
allergic	alergique[Adjective]
allergies	allergies
allergies	allergies[Noun]
allergy	allergie
allergy	allergie[Noun]
alleviate	apaiser, soulager[Verb]
alleviated	apaise/, apaise/e[Adjective]
alley	ruelle, alle/e
alley	ruelle, allée[Noun]
alleys	ruelles, alle/es
alleys	ruelles, alle/es[Noun]
alliance	alliance
alliance	alliance[Noun]
alliances	alliances
alliances	alliances[Noun]
allied	allie/, allie/e
allied	allie/, allie/e[Adjective]
alligator	alligator
alligator	alligator[Noun]
alligators	alligators
alligators	alligators[Noun]
alliteration	allite/ration
alliteration	allite/ration[Noun]
alliterations	allite/rations
alliterations	allite/rations[Noun]
allocate	attribuer[Verb]
allocated	attibue/, attribue/e[Adjective]
allocation	attribution
allocation	attribution[Noun]
allocations	attibutions
allocations	attributions[Noun]
allocution	allocution
allocution	attribution[Noun]
allocution	allocution[Noun]
allot	assigner[Verb]
allow	autoriser, permettre[Verb]
allowable	admissible[Adjective]
allowance	indemnité[Noun]
allowed	autorise/, autorise/e[Adjective]
alloy	alliage[Noun]
alphabet	alphabet[Noun]
alphabetic	alphabetique[Adjective]
alphabetical	alphabetique[Adjective]
alphabetically	alphabetiquement[Adverb]
alphabetization	alphabetisation[Noun]
alphabetizations	alphabetisations[Noun]
alphabetize	alphabetiser[Verb]
alphabetized	alphabetise[Adjective]
alphabetizing	alphabetizer[Verb]
alphabets	alphabets[Noun]
alphamerical	alphanumerique[Adjective]
alphanumeric	alphanumerique[Noun]
alphanumerical	alphanumerique[Adjective]
alphanumerically	alphanumeriquement[Adverb]
alphanumerics	alphanumerique[Adverb]
alpine	alpin[Adjective]
alpinism	alpinisme[Noun]
alpinist	alpiniste[Noun]
already	deja[Conjunction]
also	aussi[Conjunction]
altercation	altercation[Noun]
altercations	altercations[Noun]
alternate	alterner[Verb]
alternated	alterne[Adjective]
alternately	alternativement[Adverb]
among	parmi, entre[Preposition]
amoral	amoral[Adjective]
amorous	amoureux[Adjective]
amorously	amoureusement, avec amour[Adverb]
amorphous	amorphe[Adjective]
amount	s'elever. monter[Verb]
amount	somme, quantite, importance[Noun]
amp	ampere ; amplifier[Noun]
ampere	ampere[Noun]
ampersand	et commercial ; esperluette[Noun]
amphetamine	amphetamine[Noun]
and	et[Preposition]
anyway	En tout cas, de toute facon[Adverb]
anywhere	n'importe ou ; partout[Adverb]
aorta	aorte[Noun]
apace	rapidement[Adverb]
apart	a part, separe[Adjective]
apartheid	apartheid[Noun]
apartment	apartement, chambre[Noun]
apathetic	apathique[Adjective]
apathy	apathie, indifference[Noun]
ape	singe[Noun]
ape	singer[Verb]
aperitif	aperitif[Noun]
aperture	ouverture[Noun]
apex	sommet[Noun]
aphid	aphis; puceron[Noun]
arrive	arrivant[Verb]
aspen	abedul
audit	audit[Noun]
auditorium	auditorium[Noun]
auditoriums	auditoriums[Noun]
audits	audits[Verb]
available	disponible
babble	bavard[Verb]
babbler	bavardent
babies	be/be/s[Noun]
baboon	babouin
baby	be/be/[Noun]
baccalaureate	baccalaure/at[Noun]
baccarat	baccarat[Noun]
bachelor	ce/libataire[Noun]
bachelors	ce/le/bataires[Noun]
bacilli	bacilles (noun masculine)[Noun]
bacillus	bacille (noun masculine)[Noun]
back	en arriere, en retour
backache	douler de dos
backbite	calumnier
backbone	e/pine[Noun]
background	arriere-plan
backgrounds	arriere-plans
backside	derrie\re[Noun]
backslide	re/gresser[Verb]
backup	(computer) sauvegarde (feminine)[Noun]
backward	en arrie\re[Preposition]
bacon	lard
bacteria	bactérie
bad	mauvais, torve
badge	insigne
badged	insignée
badgers	blaireaux
badinage	badinage
bag	sac[Noun]
bagatelle	bagatelle
bagatelles	bagatelles
baggage	effets, colis
bags	fouilles, étuis, sacs
baguette	baguette
bah	bannir[Verb]
bail	caution
bairn	enfant (e/cossais)[Noun]
bait	leurre, eche
bake	faire cuire[Verb]
baker	boulanger
bakeries	boulangeries
bakers	boulangers
bakery	boulangerie
bakeshop	boulangerie[Noun]
balance	balance, équilibrer
balconies	balcons
balcony	balcon
bald	chauve, à ras
baldhead	chauve[Adjective]
baldly	ouvertement[Adverb]
baldness	calvitie[Noun]
bale	ballot
baleen	baleine
balk	déjouer[Verb]
ball	bal (dance), ballon (like for games)
ballad	ballade
ballade	ballade
ballads	ballades
ballast	ballast, lest
ballet	ballet
balletic	balletique
balloon	ballon, aérostat
ballot	ballot
balls	bals[Noun]
ballyhoo	publicite/[Noun]
balm	baume[Noun]
bamboo	enfant (italien)[Noun]
bamboozle	tricher[Verb]
banana	banane
bananas	bananes
band	bande, chapelle
bandage	bandage
bandeau	bandeau
bandit	brigand, forban, bandit
bandits	brigands, forbans, bandits
bandleader	chef d'orchestre[Noun]
bandmaster	chef d'orchestre[Noun]
bane	poison[Noun]
banished	banni[Adjective]
barb	barbe[Noun]
barbarian	barbare[Noun]
barbarians	barbares[Noun]
barbarism	barbarisme[Noun]
barber	barbier[Noun]
barmaid	serveuse[Noun]
barman	serveur[Noun]
barracks	casernes[Noun]
beach	plage[Noun]
bear	ours[Noun]
bear	supporter[Verb]
bed	lit[Noun]
bedroom	chambre (a coucher)[Noun]
believe	croire[Verb]
betray	trahir[Verb]
bibliographic	bibliographique[Adjective]
bibliographies	bibliographies[Noun]
bibliography	bibliographie[Noun]
blackberry	zarzamorra[Noun]
bonjour	hello
book	livre (m)
boomerang	boomerang (m)
boxer	(~ shorts) caleçon[Noun]
boy	garc,on[Noun]
boys	garc,ons[Noun]
brain	cerveaux[Noun]
brood	rimuginare, covare[Verb]
broom	balai
broth	bouillon
build	construire[Verb]
building	ba^timent[Noun]
bye	bon voyage, ciao[Preposition]
cab	taxi[Noun]
cabal	cabale[Noun]
cabala	cabale[Noun]
cabalism	cabalisme[Noun]
cabalist	cabaliste[Noun]
cabalistic	cabalistique[Adjective]
caballero	cavalier[Noun]
cabaret	cabaret[Noun]
cabbage	chou[Noun]
cabbages	choux[Noun]
cabdriver	chauffeur de taxi[Noun]
cabin	cabane[Noun]
cabinet	cabinet[Noun]
cabinetmaker	e/be/niste[Noun]
cabinetmakers	e/be/nistes[Noun]
cabinets	cabinets[Noun]
cabins	cabanes,(ships)cabines[Noun]
cable	ca^ble[Noun]
cabled	cablé[Adjective]
cablegram	ca^blogramme[Noun]
cablegrams	ca^blogrammes[Noun]
cables	ca^bles[Noun]
cabling	cablage[Noun]
cabman	cocher de fiacre[Noun]
caboose	caboose[Noun]
cabotage	cabotage[Noun]
cabriolet	cabriolet[Noun]
cabs	taxis[Noun]
cacao	cacao[Noun]
cacciatore	chasseur[Noun]
cachalot	cachalot[Noun]
cache	cachette[Noun]
cachepot	cachepot[Noun]
caches	cachettes[Noun]
cackle	caquet[Noun]
cackled	caquete/[Verb]
cackles	caquets[Noun]
cackling	caquetant[Verb]
cacophony	cacophonie[Noun]
cacti	cactus(plural)[Noun]
cactus	cactus (singular)[Noun]
cad	mufle[Noun]
cadaver	cadavre
cadaverous	terreux,[Adjective]
caddies	boi^tes a\ the/[Noun]
caddy	boii^te a\ the/[Noun]
cadence	cadence[Noun]
cadet	cadet, younger son[Noun]
cadetship	brevet de cadet[Noun]
cadge	mendier[Verb]
cadger	mendiant[Noun]
cafe	cafe/-restaurant[Noun]
cafes	cafe/-restaurants[Noun]
cafeteria	cafe/taria[Noun]
cafeterias	cafe/tarias[Noun]
caffeine	cafe/ine[Verb]
caftan	caftan[Noun]
cage	cage[Noun]
cages	cages[Noun]
cahier	copy book[Noun]
cairn	cairn[Noun]
cairns	cairns[Noun]
caitiff	la^che[Adjective]
cajole	cajoler[Verb]
cajoler	cajoleur[Noun]
cajolery	cajolerie[Noun]
cake	gâteau[Noun]
cake	ga^teau[Noun]
calcium	calcium[Noun]
calculate	calculer[Verb]
calculated	calculé
calibrate	calibrer[Verb]
calibrated	calibré[Adjective]
calibration	calibration[Noun]
car	voiture[Noun]
carbonization	carbonisation[Noun]
carbonize	carboniser[Verb]
carbonized	carbonise[Adjective]
carbonless	sans carbone[Adjective]
carburetor	carburateur[Noun]
carburetors	carburateurs[Noun]
carburization	carburation[Noun]
carburize	carburer[Verb]
card	carte[Noun]
cardamom	cardamone[Noun]
cardiac	cardiaque[Adjective]
cardigan	cardigan[Noun]
cardioid	cardioide[Noun]
cardioids	cardioides[Noun]
care	attention[Noun]
care	care[Noun]
cared	attentionne[Adjective]
career	cariere[Noun]
careful	attentionne[Adjective]
carefully	avec attention[Adjective]
cat	le chat[Noun]
cell	cellule[Noun]
cellular	cellulaire[Adjective]
chance	hasard
cherries	les cerises[Noun]
church	e/glise[Noun]
churches	e/glises[Noun]
cloak	manteau[Noun]
cloaks	manteaux[Noun]
clock	horloge[Noun]
clockmaker	horloger[Noun]
clockwise	dans le sens des aigu.illes d'une montre
cloister	cloitre[Noun]
clone	clone[Noun]
cloud	le nuage
c'mon	ben voyons
coal	charbon (masc.)[Noun]
computer	ordinateur[Noun]
computers	ordinateurs[Noun]
comrades	amis[Noun]
concealed	e/touffe/
conjuring	escamotage[Verb]
contemplate	contempler[Verb]
contemplate	envisager[Verb]
contemplate	pre/voir[Verb]
contemplates	envisage[Verb]
contemplation	contemplation[Noun]
contemplation	me/ditation[Noun]
contemplation	recueillement[Noun]
contemplations	contemplations[Noun]
contemplations	me/ditations[Noun]
contemplative	contemplatif[Adjective]
contemporaneous	contemporain[Adjective]
contemporaneously	en me^me temps que [Adverb]
contemporaries	contemporains[Noun]
contemporaries	de la me^me ge/ne/ration[Adjective]
contemporary	contemporain[Adjective]
contemporary	de la me^me ge/ne/ration[Adjective]
contempt	de/dain[Noun]
contempt	me/pris[Noun]
contemptible	indigne[Adjective]
contemptible	me/prisable[Adjective]
contemptibly	avec me/pris[Adverb]
contemptuous	de me/pris[Adjective]
contemptuous	me/prisant[Adjective]
contemptuously	avec de/dain[Adverb]
contemptuously	avec me/pris[Adverb]
contend	combattre[Verb]
contend	disputer[Verb]
contend	lutter[Verb]
contender	candidat[Noun]
contender	challenger[Noun]
contender	concurrent[Noun]
contenders	candidats[Noun]
contenders	challengers[Noun]
contenders	concurrents[Noun]
contending	concurrents[Adjective]
contending	oppose/es[Adjective]
contends	dispute[Verb]
contends	lutte[Verb]
content	contenter[Verb]
content	satisfaire[Verb]
content	satisfait[Adjective]
contented	content[Adjective]
contented	satisfait[Adjective]
contentedly	avec contentement[Adverb]
contentedness	contentement[Noun]
contention	dispute[Noun]
contention	e/mulation[Noun]
contention	lutte[Noun]
contentions	disputes[Noun]
contentions	e/mulations[Noun]
contentions	luttes[Noun]
contentious	contentieux[Adjective]
contentious	querelleur[Adjective]
contentment	contentement[Noun]
contents	contenu[Noun]
contents	table de matie\res[Noun]
contest	combat[Noun]
contest	concours[Noun]
contest	contester[Verb]
contestant	combattant[Noun]
contestant	concurrent[Noun]
cot	lit d'enfant[Noun]
coterie	coterie[Noun]
cotillion	cotillon[Noun]
cotillon	cotillon[Noun]
cottage	chaumie\re[Noun]
cottager	paysan[Noun]
couch	couche[Noun]
cough	tousser[Verb]
coughs	toux[Noun]
count	compter[Verb]
countenance	visage[Noun]
counteract	neutraliser[Verb]
counterbalance	contrepoids[Noun]
counterfeit	contrefait[Adjective]
countermand	contremander[Verb]
countermarch	contremarche[Noun]
counterplot	contre-ruse[Noun]
counterpoint	contrepoint[Noun]
counterpoise	contre-balancer[Verb]
counterweight	contrepoids[Noun]
countess	comtesse[Noun]
countless	innombrable[Adjective]
country	pays[Verb]
countryman	concitoyen[Noun]
countrywide	concitoyenne
county	comte/[Noun]
couple	couple[Noun]
couplet	distique[Noun]
coupon	coupon[Noun]
courage	courage[Noun]
courageous	courageux[Adjective]
courageously	cougrageusement[Adjective]
courier	courrier[Noun]
course	cours[Noun]
courser	coursier[Noun]
court	cour[Noun]
courteous	courtois[Adjective]
courteously	courtoisement[Adverb]
courteousness	courtoisie[Noun]
courtesy	courtoisie
courthouse	palais de justice[Noun]
courtier	courtisan[Noun]
courtliness	e/le/gance[Adjective]
courtly	e/le/gant[Adjective]
courtmartial	conseil de guerre[Noun]
courtroom	salle d'audience[Noun]
courtship	cour[Noun]
cousin	cousin[Noun]
cove	anse[Noun]
covenant	convention[Noun]
cover	couvrir[Verb]
covert	cache/[Adjective]
covet	covoiter[Adjective]
covetous	avide[Adjective]
covetously	avidement[Adjective]
cow	la moo cowetta[Noun]
cow	la vache[Noun]
coward	la^che[Noun]
cowardice	la^chete/
cowardly	la^che[Adjective]
cower	se blottir[Verb]
cowherd	vacher[Noun]
cowl	capuchon[Noun]
coxcomb	petit-mai^tre[Noun]
coy	farouche[Adjective]
coyly	modestement[Adverb]
coyness	re/serve[Noun]
coyote	coyote[Noun]
cozily	confortablement[Adverb]
cozy	confortable
crab	cancre[Noun]
crabapple	pomme sauvage[Noun]
crabbed	maussade[Adjective]
crack	fente[Noun]
cracker	pe/tard[Noun]
crackle	craqueter[Verb]
cradle	berceau[Noun]
craft	fourberie[Noun]
craftily	astucieusement[Adverb]
crag	rocher escarpe/[Noun]
cram	fourrer[Verb]
cramp	crampe[Noun]
cranberry	airelle coussinette[Noun]
crane	grue[Noun]
cranium	cra^ne[Noun]
crank	manivelle[Noun]
crankiness	humeur difficile[Noun]
cranky	d'humeur difficile[Adjective]
crape	cre^pe[Noun]
crash	retentir[Verb]
crate	caisse a\ claire-voie[Noun]
crater	crate\re[Noun]
crave	de/sirer ardemment[Verb]
craven	poltron[Adjective]
crawl	ramper[Verb]
crayfish	e/crevisse[Noun]
crayon	pastel[Noun]
craze	manie[Noun]
crazily	follement[Adverb]
crazy	fou[Adjective]
creak	grincer[Verb]
cream	cr\eme[Noun]
creamy	cre/meux[Adjective]
crease	plisser[Verb]
create	cre/er[Verb]
creation	cre/ation
creator	cre/ateur[Noun]
creature	cre/ature[Noun]
credence	croyance[Noun]
credential	certificat[Noun]
credit	cre/dit[Noun]
creditable	estimable
creditably	honourablement[Adverb]
creditor	cre/ancier[Noun]
credo	crois[Verb]
credulity	cre/dulite/[Noun]
credulous	cr/edule[Adjective]
credulously	cr/edulement[Adverb]
creed	credo[Noun]
creek	crique[Noun]
creep	ramper[Verb]
creeps	chair de poule[Noun]
cremate	incin/erer[Verb]
cut	couper[Verb]
cute	mignon, charmant, adorable[Adjective]
cute	mignonne[Adjective]
cuteness	gentillesse, charme[Noun]
cutoffs	limite, seuil[Noun]
cutout	forme a\ de/couper[Noun]
cuts	e/conomies, re/duction de budget[Noun]
cutter	couteau (de de/coupage)[Noun]
cutting	d/ecoupe, de/coupage[Noun]
cyanate	cyanante[Noun]
cyanide	cyanure[Noun]
cybernetic	cyberne/tique[Adjective]
cybernetician	cyberne/ticien[Noun]
cybernetics	cyberne/tique[Noun]
cycle	cycle[Noun]
cyclic	cyclique[Adjective]
cyclical	cyclique[Adjective]
cyclist	cycliste[Noun]
cyclists	cyclistes[Noun]
cycloid	cycloi.de[Noun]
cycloidal	cycloi.dal[Adjective]
cyclone	cyclone[Noun]
cyclonic	cyclonique[Adjective]
cyclopean	cyclope/en[Adjective]
cyclotron	cyclotron[Noun]
cylinder	cylindre[Noun]
dad	papa[Noun]
daddies	papas[Noun]
daddy	papa[Noun]
dads	papas[Noun]
daffy	barjot[Adjective]
daft	barje[Adjective]
dagger	dague (une)[Noun]
dahlia	dahlia[Noun]
dahlias	dahlias[Noun]
daily	quotidien
daintier	plus délicat
dainty	délicat
dairy	laitier
dalmatian	dalmatien[Adjective]
dalmatians	dalmatiens[Noun]
dam	barrage[Noun]
damage	abimer[Verb]
damage	de/ga^t[Noun]
damage	endommagement
damage	endommager[Verb]
damaged	endommage/[Adjective]
damaged	endommagé[Adjective]
damager	endommageur
damagers	endommageurs
damages	de/ga^ts[Noun]
damaging	endommageant
dame	dame
dammit	merde alors
damnation	damnation[Noun]
damned	damne/[Adjective]
damp	humide[Adjective]
dampen	humidifier[Verb]
dampened	humidifie/
dampness	humidite/[Noun]
dams	barrages[Noun]
dance	danse
dancer	danceur
dancers	danceurs
dancing	danser[Verb]
dandelion	pissenlit
dandelions	pissenlits
dandies	dandys[Noun]
dandruff	pelicule[Noun]
danger	danger[Noun]
dangerous	dangereux[Adjective]
d'art	d'art
day	jour[Noun]
daybreak	aube[Noun]
daylight	lumiere du jour[Noun]
days	jours[Noun]
deactivate	de/sactiver[Verb]
deactivation	de/sactivation[Noun]
dead	mort[Adjective]
deaf	sourd[Adjective]
deaths	morts
deep	profond[Adjective]
density	densite/[Noun]
deserve	meriter[Verb]
d'etat	d'état
d'etre	d'être
device	pe/riph/erique[Noun]
device	pe/riphe/rique[Noun]
dictated	dicté[Verb]
dictates	dicte[Verb]
dictation	dictée[Noun]
dictations	dictées[Noun]
dictionaries	dictionnaires[Noun]
dictionary	dictionnaire[Noun]
did	a fait[Verb]
die	mourir[Verb]
died	mort[Verb]
diesel	diesel[Noun]
diet	diète[Noun]
dietetically	diététiquement[Adverb]
diets	diètes[Noun]
different	différent[Adjective]
difficulties	difficultés[Noun]
difficulty	difficulté[Noun]
dig	creuser[Verb]
digested	digéré[Verb]
dignitaries	dignitaires[Noun]
dignities	dignités[Noun]
dignity	dignité[Noun]
dilate	dilater[Verb]
dilated	dilaté[Verb]
doctor	docteur[Noun]
document	document[Noun]
d'oeuvre	d'oeuvre
dog	chien/ne[Noun]
doggie	chien/ne[Noun]
dogs	chiens[Noun]
drink	boire[Verb]
drinks	boissons[Noun]
drive	conduire[Verb]
driver	conducteur[Noun]
drizzle	bruiner[Verb]
dry	se/cher[Verb]
duck	canard[Noun]
each	chaque
eagle	aigle[Noun]
eagles	aigles[Noun]
eaglet	aiglon[Noun]
ear	oreille[Noun]
earache	mal d'oreille[Noun]
eardrum	tympan[Noun]
eardrums	tympans[Noun]
earlobe	lobe d'oreille[Noun]
early	to^t[Adjective]
earn	gagner[Verb]
earning	gagner[Verb]
earphones	e/couteurs[Noun]
earring	boucle d'oreille[Noun]
earrings	boucles d'oreille[Noun]
ears	oreilles[Noun]
earth	terre[Noun]
earthquake	tremblement de terre[Noun]
earthquakes	tremblements de terre[Noun]
earths	terres[Noun]
earthworm	ver de terre[Noun]
earthworms	vers de terre[Noun]
earwax	serumen[Noun]
ease	facilite/[Noun]
easel	chevalet[Noun]
easier	plus facile[Adjective]
easiest	le plus facile[Adjective]
easily	facilement[Adverb]
easiness	facilite/[Noun]
east	est[Noun]
easter	Pa^ques[Noun]
easy	facile[Adjective]
eat	manger[Verb]
eatable	comestible[Adjective]
eats	manger[Verb]
ebony	e/be\ne[Noun]
eccentric	excentrique[Adjective]
eccentricity	excentricite/[Noun]
ecclesiastic	eccle/siastique[Noun]
echo	e/cho[Noun]
eclipse	e/clipse[Noun]
ecologic	e/cologique[Adjective]
ecologist	e/cologiste[Noun]
ecology	e/cologie[Noun]
economics	econom'ica[Noun]
ecru	e/cru[Adjective]
eight	huit[Adverb]
eighteen	dix-huit[Adverb]
eighteenth	dix-huitie\me[Adjective]
eightfold	huit fois[Adverb]
eighth	huitie\me[Adjective]
eighties	les anne/es quatre-vingt (f)[Noun]
eightieth	quatre-vingtie\me[Adjective]
eighty	quatre-vingt[Adverb]
eightyfold	quatre-vingt fois[Adverb]
einsteinium	einsteinium (m)[Noun]
either	soit[Adverb]
ejaculate	e/jaculer[Verb]
ejaculated	e/jacule/[Adjective]
ejaculates	e/jacule[Verb]
ejaculating	e/jaculant[Adjective]
ejaculation	e/jaculation (f)[Noun]
ejaculations	e/jaculations (f)[Noun]
ejaculatory	e/jaculatoire[Adjective]
eject	e/jecter[Verb]
ejectable	e/jectable[Adjective]
ejecting	e/jectant[Verb]
ejection	e/jection (f)[Noun]
ejector	e/jecteur (m)[Noun]
ejectors	e/jecteurs (m)[Noun]
elaborate	e/laborer[Verb]
elaborated	e/labore/ (m), e/labore/e (f)[Adjective]
elaborates	e/labore[Verb]
elaborating	e/laborant[Adjective]
elaboration	e/laboration (f)[Noun]
elaborations	e/laborations (f)[Noun]
elevator	ascenseur[Noun]
eleven	onze[Adjective]
eleventh	onzie\me[Noun]
eliminate	e/liminer[Verb]
end	fin[Noun]
environment	ambiente[Noun]
environmental	ambiental[Adjective]
envoy	enviado[Noun]
envy	envidia[Noun]
envy	envidiar[Verb]
escalator	escalier roulant[Noun]
establish	constater[Verb]
event	occasion[Noun]
eventual	final[Adjective]
eventually	finallement[Adverb]
ever	toujours[Adverb]
evergreen	permanent[Adjective]
everlasting	permanent[Adjective]
every	chaque[Pronoun]
everywhere	partout[Conjunction]
eviction	mise a` l' e'cart[Noun]
evidence	preuve[Noun]
exclaim	s'exclaimer[Verb]
fable	fable[Noun]
fables	fables[Noun]
fabric	tissu[Noun]
fabricant	fabricant[Noun]
fabricate	fabriquer[Verb]
fabricated	fabrique/[Pronoun]
fabricates	fabrique[Verb]
fabricating	fabriquant[Verb]
fabrication	invention(s), fabulation; fait, forge(e) de toutes pieces[Noun]
fabrications	fabrications[Noun]
fabricator	fabricant[Noun]
fabricators	fabricants[Noun]
fabrics	tissus[Noun]
fabulist	fabuliste (m)[Noun]
fabulous	fabuleux(euse);  formidable[Adjective]
facade	fac,ade[Noun]
facades	fac,ades[Noun]
face	visage, figure; expression; (of clock) cadran;  (of building) facade[Noun]
faceless	sans face[Noun]
faceplate	face avant (f),panneau avant (m)[Noun]
facet	facette[Noun]
facetious	facétieux[Adjective]
facetious	plaisant[Adjective]
facetiously	facétieusement[Adverb]
facial	facial[Adjective]
facies	facie\s (m)[Noun]
facile	facile[Adjective]
facilitate	faciliter[Verb]
facilitated	facilite/[Pronoun]
facilitates	facilite[Verb]
facilitating	facilitant[Verb]
facilities	instalations[Noun]
facility	usine (f), e/tablissement (m)[Noun]
facing	face a, en face de[Preposition]
facsimile	(document) telecopie; (machine) telecopieur[Noun]
fact	fait[Noun]
faction	faction[Noun]
factor	facteur[Noun]
factorial	factoriel
factorials	factorielles (f)[Noun]
factories	usines (f)[Noun]
factorization	factorisation[Noun]
factorizations	factorisations[Noun]
factorize	factoriser[Verb]
factorized	factorisé[Adjective]
factors	facteurs (m)[Noun]
factory	usine (m)
facts	faits[Noun]
factual	factuel (m),factuelle (f)[Adjective]
facultative	facultatif[Adjective]
facultatively	facultativement[Adverb]
faculties	faculte/s (f)[Noun]
faculty	faculté
fad	affaiblir, atte/nuer[Verb]
fade	se faner[Verb]
faded	affaibli, atte/nue/[Adjective]
fader	atte/nuateur (m)[Noun]
faders	atte/nuateurs (m)[Noun]
fading	affaiblissement (m), atte/nuation (f)[Noun]
faery	fe/e[Noun]
faience	fai.ence
fail	échouer[Verb]
faint	s'e/vanouir[Verb]
faintly	faiblement[Adverb]
fair	juste[Adjective]
fairly	e/quitablement[Adverb]
fairy	fe/e[Noun]
faith	foi[Noun]
faithful	fide\le[Adjective]
faithfully	fide\lement[Adverb]
fashion	mode[Noun]
fashionable	a\ la mode[Adjective]
fast	rapide[Adjective]
fasten	attacher[Verb]
fastener	fermeture[Noun]
fastidious	exigeant[Adjective]
fat	gros[Adjective]
fatal	mortel[Adjective]
fatalism	fatalisme[Noun]
fatality	victime[Noun]
fatally	mortellement[Adverb]
fate	destin[Noun]
fish	poisson[Noun]
fishable	pêchable[Adverb]
fishbowl	bôl à poisson[Noun]
fished	pêché[Verb]
fisher	pêcheur[Noun]
fisheries	poissonneries[Noun]
fisherman	pêcheur[Noun]
fishermen	pêcheurs[Noun]
fishers	pêcheurs[Noun]
fishery	poissonnerie[Noun]
fishes	poissons[Noun]
fishing	à la pêche
fishtail	queue-de-poisson[Noun]
fission	fision[Noun]
fissionable	fisionable[Adverb]
fissure	fissure[Noun]
fist	poing[Noun]
fisted	poingé[Verb]
fistful	poingée[Noun]
five	cinq[Adverb]
fivefold	cinq fois[Adverb]
fix	re/parer[Verb]
fixation	fixation (f)[Noun]
fixations	fixations (f)[Noun]
fixity	fixite/ (f)[Noun]
fixture	support (m)[Noun]
fixtures	supports (m)[Noun]
fjord	fjord (m)[Noun]
fjords	fjords (m)[Noun]
flight	le vol[Noun]
floppy	disquette[Noun]
fluently	couramment[Adverb]
foot	pied[Noun]
football	football[Noun]
forget	oublier[Verb]
forgetful	oublieux[Adjective]
forgetfulness	manque de me/moire[Noun]
forgivable	excusable[Adjective]
forgivably	de fac,on excusable[Adverb]
forgive	pardonner[Verb]
forgiveness	pardon[Noun]
forgives	pardonne[Verb]
forgiving	indulgent[Adjective]
forgo	renoncer \a[Verb]
forgoes	renonce \a[Verb]
forgotten	oublie/[Adjective]
forlorn	de/laisse/[Adjective]
formaldehyde	formalde/hyde
four	quatre[Adverb]
fox	renard (m)[Noun]
fractal	fractale (f)[Noun]
fractals	fractales (f)[Noun]
fraction	fraction (f)[Noun]
fractionate	fractionner[Verb]
fraternal	fraternel[Adjective]
fraternize	fraterniser[Verb]
friend	ami[Noun]
friendship	Amitie[Noun]
fringe	frange[Noun]
frog	grenouille[Noun]
frogs	grenouilles[Noun]
from	de
front	devant[Preposition]
fucked	foutu[Adjective]
fudge	sucre a` la cre`me[Noun]
fugue	fugue[Noun]
fulgurant	fulgurant[Adjective]
funk	frouse[Noun]
funniest	le plus amusant[Adjective]
funny	amusant[Adjective]
gab	bavarde[Verb]
gabardine	gabardine[Noun]
gabber	comme\re[Noun]
gabbing	bavarder[Verb]
gaberdine	une gabardine[Noun]
gadget	un gadget[Noun]
gaiety	la gaieté[Noun]
gaily	gaiement[Adverb]
gain	le gain[Noun]
gained	gagne/[Verb]
gaining	gagnant[Verb]
gala	un gala[Noun]
galactic	galactique[Adjective]
galaxies	galaxies[Noun]
galaxy	une galaxie[Noun]
gallant	gallant[Adjective]
gallantly	gallamment[Adverb]
gallantry	la gallantrie[Noun]
galleries	galleries[Noun]
gallery	gallerie[Noun]
gallon	un gallon[Noun]
gallop	galloper[Verb]
galloped	gallope/[Verb]
galloping	gallopant[Verb]
galvanic	galvanique[Adjective]
galvanism	le galvanisme[Noun]
galvanize	galvaniser[Verb]
galvanized	galvanise/[Verb]
game	un jeu[Noun]
games	joues[Noun]
garlic	ail[Noun]
gas	essence[Noun]
gasoline	essence[Noun]
ghostwriter	négre[Noun]
gift	cadeau[Noun]
gifts	cadeaux[Noun]
go	aller[Verb]
goal	objectif[Noun]
goals	buts
goat	oie
god	dieu
goddess	de/esse
goddesses	de/esses
godfather	parrain[Noun]
golf	golf
golfer	geolfeur
golfers	golfeurs
golfs	golfs
good	bon;bien
good	bien
goodbye	au revoir
goodnight	bon nuit
gopher	gopher[Noun]
gospel	evangile[Noun]
gown	la robe
grab	saisir (an object)[Verb]
gradient	pente (math.)[Noun]
gradually	graduellement[Adverb]
grammar	grammaire (f)[Noun]
grape	le raisin[Noun]
grapefruit	le pamplemousse[Noun]
habilitated	habilite/[Verb]
habilitation	habilitation[Noun]
habit	coutume[Noun]
habitant	habitant[Noun]
habitants	habitants[Noun]
habitat	habitat[Noun]
habitation	demeure[Noun]
habitual	usuel[Adjective]
had	se faire avoir
hand	main[Noun]
head	tete
heats	series[Noun]
heaven	paradis[Noun]
heavy	lourd[Adjective]
hectare	hectare
hectares	hectares
hectoliter	hectolitre
hedge	haie
hedgehog	he/risson[Noun]
heed	suivre[Verb]
heel	talon
heeler	guerisseur
heels	talons
height	hauteur
heir	he/ritier
held	tenu[Adjective]
helices	helices[Noun]
hell	enfer[Noun]
hello	bonjour[Noun]
helm	barre[Noun]
helmet	casque[Noun]
help	aide[Noun]
hemp	chanvre[Noun]
hen	poule[Noun]
herd	troupeau[Noun]
here	ic,i[Noun]
heron	huron[Noun]
hips	flancs[Noun]
hit	frapper[Verb]
hockey	hockey[Noun]
hogwash	non sense[Noun]
holidayer	vacancier[Noun]
holidays	vacances
home	maison[Noun]
homeless	sans-abri[Noun]
homes	maisons[Noun]
house	maison[Noun]
ice	glace[Noun]
instancing	instanciation[Noun]
internship	stage professionnel en entreprise ou ailleurs
jab	planter[Verb]
jabber	baragouiner[Verb]
jabberwocky	(playing card) valet[Noun]
jabberwocky	Jaseroque, Bredoulocheux, Berdouilleux, Jabberwocheux[Noun]
jack	criq[Noun]
jack	valet[Noun]
jackal	chacal[Noun]
jackals	chacaux[Noun]
jackass	a^ne[Noun]
jackdaw	choucas (m)[Noun]
jacket	blouson[Noun]
jacket	veste[Noun]
jackhammer	marteau piqueur[Noun]
jackknife	canif[Noun]
jackpot	gros lot[Noun]
jade	jade[Noun]
jaded	brime/e(e)[Adjective]
jag	bombe, noce[Noun]
jagged	e'bre'che[Verb]
jail	emprisonner[Verb]
jail	prison
jailbreak	evasion[Noun]
jailed	emprisonne/[Adjective]
jailer	geo^lier[Noun]
jailing	incarce/ration[Noun]
jailor	geo^lier[Noun]
jails	prisons[Noun]
jalopy	guimbarde[Noun]
jam	confiture[Noun]
jam	enfoncer[Verb]
jamb	jambage[Noun]
jammed	coince/[Adjective]
jammed	enraye/[Adjective]
jangle	(bells) faire retenir[Verb]
janitor	agent d'entretien[Noun]
janitor	concierge[Noun]
jar	bocal[Noun]
jar	pot[Noun]
jar	secousse[Noun]
jargon	jargon (m)[Noun]
jasmine	jasmin[Noun]
jaundice	jaunisse[Noun]
jaunt	balade[Noun]
jaunty	désinvolte[Adjective]
jaunty	insouciant(e)[Adjective]
javelin	javelot[Noun]
jaw	ma^choir[Noun]
jaws	ma^choires[Noun]
jay	geai (m)[Noun]
jazz	jazz (m)[Noun]
jazzy	voyant(e)[Adjective]
jealous	jaloux, jalouse[Adjective]
jealousy	jalousie[Noun]
jeer	conspuer[Verb]
jeer	huer[Verb]
jelly	gele/e[Noun]
jellyfish	meduse[Noun]
jeopardize	compromettre[Verb]
jeopardize	mettre en danger[Verb]
jerk	abruti[Noun]
jerk	bousculer[Verb]
jerry	boche[Noun]
jersey	(cloth) jersey[Noun]
jersey	pull (m)[Noun]
jest	plaisanterie (f)[Noun]
jester	bouffon[Noun]
jester	fou[Noun]
jet	gicleur[Noun]
jet	re/acteur[Noun]
jettison	jeter, larguer[Verb]
jetty	jete/e[Noun]
jewel	joyau[Noun]
jeweler	bijoutier[Noun]
jewelery	bijouterie[Noun]
jewelry	bijouterie[Noun]
jewels	joyaux[Noun]
jib	(of crane) flèche (f)[Noun]
jib	(sail) foc (m)[Noun]
jiffy	(in a ~) en un clin d'oeil
jig	gigue (f)[Noun]
jigsaw	(puzzle) puzzle (m)[Noun]
jihad	lutte, combat (Islam)[Noun]
jilt	laisser tomber[Verb]
jingle	(bell) tinter[Verb]
jingle	(song) jingle (m), indicatif (m)[Noun]
jingle	(sound) cliquetis (m)[Noun]
jinx	poisse (f)[Noun]
jitters	trac[Noun]
job	boulot (colloq.)[Noun]
job	emploi (m)[Noun]
job	turbin (slang)[Noun]
jobless	au chômage[Adjective]
jockey	jockey (m)[Noun]
jocular	enjoué(e), jovial(e)[Adjective]
jocund	gai[Adjective]
jodhpur	jodhpur (m)[Noun]
jog	faire du jogging[Verb]
jogging	jogging (m)[Noun]
join	(re)joindre[Verb]
join	raccord (m)[Noun]
joinable	joignable[Adverb]
joined	(re)joint[Adjective]
joiner	menuisier (m)[Noun]
joinery	menuiserie (f)[Noun]
joint	(drugs) joint (m)[Noun]
joint	articulation[Noun]
joint	jointure[Noun]
jointly	conjointement[Adverb]
joke	blague (colloq.)[Noun]
joke	plaisanterie[Noun]
joked	plaisanta (pass.simp.,3rd sing[Verb]
joker	(playing card) joker (m)[Noun]
jokingly	en plaisantant[Adverb]
jokingly	pour rire[Adverb]
jolly	jovial(e), enjoué(e)[Adjective]
jolt	secousse[Noun]
jolt	soubresaut (m)[Noun]
jonquils	jonquilles[Noun]
jostle	bousculer[Verb]
jot	(~ down) noter[Verb]
jot	(of truth) grain(m), brin(m)
journal	(diary) journal (m)[Noun]
journal	(magazine) revue (f)
journalism	journalisme (m)
journalist	journaliste[Noun]
journalists	journalistes[Noun]
journey	periple[Noun]
journey	voyage[Noun]
jovial	jovial(e)[Adjective]
jovially	jovialement[Adverb]
joy	joie[Noun]
joyful	joyeux[Adjective]
joyfully	joyeusement[Adverb]
joyless	sans joie[Adverb]
joyously	joyeusement[Adverb]
jubilant	débordant(e) de joie[Adjective]
jubilee	jubilé (m)[Noun]
judge	juge[Noun]
judge	juger[Verb]
judged	juge/[Adjective]
judgement	jugement[Noun]
judicial	judiciaire[Adjective]
judiciary	magistrature (f)[Noun]
judo	judo (m)[Noun]
jug	broc[Noun]
jug	pichet[Noun]
jug	pot (m)[Noun]
juggernaut	poids (m) lourd[Noun]
juggle	jongler[Verb]
juggler	jongleur (m), jongleusse (f)[Noun]
jugular	jugulaire[Adjective]
juice	jus[Noun]
juiceless	sans jus
juicier	plus juteux
juiciest	le plus juteux
juicy	juteux, juteuse
jukebox	juke-box (m)[Noun]
jumble	mélange (m)[Noun]
jumbo	géant(e)[Adjective]
jump	sauter (inf.)[Verb]
jumped	sauta (pass.simp., 3rd sing.)[Verb]
jumper	robe (f) chasuble[Noun]
jumpy	nerveux(euse)[Adjective]
junction	(rail) embranchement (m)[Noun]
junction	jonction[Noun]
jungle	jungle (f)[Noun]
junior	junior
juniper	genie\vre[Noun]
junk	bric-à-brac (m)[Noun]
junk	camelotte (colloq.)[Noun]
junk	des ordures[Noun]
junk	du toc (colloq.)[Noun]
junkers	casseurs (colloq.)[Noun]
junkers	chiffonniers[Noun]
junkie	accro (colloq.)[Noun]
junkie	drogué(e)[Noun]
junkyard	une casse (automobile)[Noun]
juridic	juridique[Adjective]
jurisdiction	juridiction[Noun]
jurist	juriste[Noun]
juror	jure/[Noun]
jurors	jure/s[Noun]
jury	jury (m)[Noun]
just	juste
justice	justice (f)[Noun]
justified	justifie/(e)[Adjective]
justify	justifier[Verb]
justly	justement[Adverb]
jut	(~ out) avancer[Verb]
juvenile	puéril(e)[Adjective]
juxtapose	juxtaposer[Verb]
kale	chou[Noun]
kaleidescope	kale/idoscope[Noun]
kaleidoscope	kale/idoscope[Noun]
kangaroo	kangourou[Noun]
karat	carat (m)[Noun]
karate	karaté (m)[Noun]
kayak	kayak (m)[Noun]
kebab	brochette[Noun]
keel	la quille (naut.)[Noun]
keep	garder (inf.)[Verb]
keeps	toujours[Noun]
keepsake	souvenir (m)[Noun]
keg	caisson (liq.)[Noun]
keg	tonnelet (m), baril (m)[Noun]
kennel	niche (f)[Noun]
kennel	un chenil[Noun]
kerchief	un foulard[Noun]
kernel	amande (f)[Noun]
kernel	noyau[Noun]
kernel	trognon (fruit)[Noun]
kerosene	kérosène (m)[Noun]
ketchup	ketchup (m)[Noun]
kettle	une bouilloire[Noun]
key	(map) légende
key	une cle/, or clef[Noun]
key	une touche (keyboard)[Noun]
keyboard	un clavier[Noun]
keyhole	le trou de (la) serrure[Noun]
keying	taper (typewriter) (inf.)[Verb]
keynote	note (f) dominante[Noun]
keypad	(keyboard) pavé (m) numérique[Noun]
keypad	un clavier (adding machine)[Noun]
khaki	kaki[Adjective]
kick	(~ out) vider[Verb]
kick	donner (inf.) un coup de pied[Verb]
kickback	un retour de manivelle[Noun]
kicked	frappe/ du pied[Adjective]
kicker	qui donne des coups de pied[Noun]
kickoff	le depart synchronise/[Noun]
kicks	s'amuser
kid	un chevreau, une chevrette[Noun]
kid	un(e) gosse (colloq.)[Noun]
kid	un(e) mioche (colloq.)[Noun]
kiddies	les gamins[Noun]
kiddies	les gosses[Noun]
kiddies	les mioches (colloq.)[Noun]
kidnap	enlever (inf.)[Verb]
kidnap	kidnapper[Verb]
kidnaped	enleve/ (past.part.)[Adjective]
kidnaper	enlèvement (m)[Noun]
kidnaper	un ravisseur[Noun]
kidnapers	les|des ravisseurs[Noun]
kidnaping	un enle\vement[Noun]
kidnapped	enleve/[Verb]
kidnapper	un ravisseur[Noun]
kidnappers	les|des ravisseurs[Noun]
kidnapping	un enle\vement[Noun]
kidnappings	des enle\vements[Noun]
kidney	le rein[Noun]
kidneys	les|des reins[Noun]
kidneys	les rognons (cooking)[Noun]
kids	les|des gamins[Noun]
kids	les|des gosses[Noun]
kill	(fig.) mettre fin à[Verb]
kill	tuer[Verb]
killer	meurtrier (m),meutrière (f)[Noun]
killer	un tueur[Noun]
killers	des tueurs[Noun]
killing	meutre (m)[Noun]
killing	une tuerie[Noun]
killings	des tueries[Noun]
killjoy	rabat-joie (m)[Noun]
kiln	four (m)[Noun]
kiln	un fourneau[Noun]
kilo	kilo (m)[Noun]
kilobytes	kilo-octet (m)[Noun]
kilogram	kilogramme (abbr. kg)[Noun]
kilohertz	kilo-hertz (m)[Noun]
kiloliter	un kilolitre (abbr. kl)[Noun]
kilometer	un kilome\tre (abbr. km)
kilowatt	kilowatt (m)[Noun]
kilt	kilt (m)[Noun]
kin	aparente/ (a\...)[Adjective]
kind	aimable[Adjective]
kind	doux (de caracte\re)[Noun]
kind	gentil(le)[Adjective]
kindergarten	(le) jardin d'enfants[Noun]
kindergarten	(une) cre^che pour enfants[Noun]
kindergarten	(une) garderie (d'enfants)[Noun]
kindergarten	e/cole maternelle[Noun]
kindhearted	bon (de caracte\re, de coeur)[Noun]
kindhearted	tendre de coeur[Adjective]
kindheartedness	(la) tendresse de coeur[Noun]
kindle	(feeling) susciter[Verb]
kindle	(fire) allumer[Verb]
kindlessly	froidement[Adverb]
kindlessly	sans coeur[Adverb]
kindliness	(la) douceur[Noun]
kindliness	(la) gentillesse[Noun]
kindly	avec douceur[Adverb]
kindly	bienveillant(e)[Adverb]
kindly	doucement[Adverb]
kindly	gentiment[Adverb]
kindness	(la) douceur de coeur[Noun]
kindness	(la) gentillesse[Noun]
kindred	aparente/[Adjective]
kindred	semblable, similaire[Adjective]
kinds	des espe\ces[Noun]
kinds	des sortes (de...)[Noun]
king	(le) roi[Noun]
kingdom	(animals/plants) règne (m)[Noun]
kingdom	(le) royaume[Noun]
kingdoms	royaumes[Noun]
kingfisher	(un) martin pe^cheur (bird)[Noun]
kingly	royal[Adjective]
kings	(les) rois[Noun]
kinky	vicieux(ieuse)[Adjective]
kiosk	kiosque[Noun]
kipper	hereng (m) fumé[Noun]
kiss	baiser[Verb]
kiss	baiser[Noun]
kiss	bisou[Noun]
kiss	embrasser[Verb]
kisses	baisers[Noun]
kisses	bisous[Noun]
kit	(set) trousse (f)[Noun]
kit	(to be assembled) kit (m)[Noun]
kitchen	cuisine[Noun]
kitchenette	cuisinette[Noun]
kitchens	cuisines
kitchenware	(la) batterie de cuisine[Noun]
kitchenware	(les) ustensiles de cuisine[Noun]
kite	(un) cerf-volant[Noun]
kith	(~ and kin) parents et amis
kiting	jouer avec un cerf-volant[Verb]
kitten	chaton[Noun]
kittens	chatons[Noun]
kitty	(shared fund) cagnotte (f)[Noun]
kiwi	(bird) kiwi (m), aptéryx[Noun]
kiwi	(fruit) kiwi (m)[Noun]
kleenex	(un) mouchoir en papier[Noun]
kleenex	TM, (R), etc. papier-mouchoir[Noun]
kleptomania	(la) kleptomanie[Noun]
kleptomaniac	un(e) kleptomane[Noun]
knack	_ _ _ _ = avoir la main pour..[Noun]
knack	to have the _ = avoir le chic[Noun]
knapsack	(un) sac-a\-dos[Noun]
knead	pe/trir[Verb]
kneadable	pe/trissable[Adjective]
kneader	pe/trisseur[Noun]
knee	le genou[Noun]
kneecap	la rotule[Noun]
kneel	mettre à genoux[Verb]
kneel	s'agenouiller (inf.reflx.)[Verb]
kneeling	a\ genoux[Adverb]
kneeling	s'agenouillant[Verb]
kneepad	(un) prote\ge-genou[Noun]
knelt	s'agenouilla (pass.simp.)[Verb]
knelt	se mis(e) a\ genoux (pass.simp[Verb]
knickers	pantalon de golf (m)[Noun]
knickknack	(un) bibelot[Noun]
knickknack	(une) babiole[Noun]
knife	(un) canif[Noun]
knife	(un) couteau[Noun]
knifes	des couteaux[Noun]
knifing	une blessure au couteau[Noun]
knight	(chess) cavalier (m)[Noun]
knight	(un) chevalier[Noun]
knighted	e^tre sacre/ chevalier[Adjective]
knighthood	la chevalerie[Noun]
knightly	chevalier[Adjective]
knights	des chevaliers[Noun]
knit	(bones) se souder[Verb]
knit	froncer les sourcils[Verb]
knit	tricotter (inf.)[Verb]
knitted	tricotte/ (past.part.)[Adjective]
knitting	le tricot[Noun]
knitwear	le tricot[Noun]
knives	des couteaux[Noun]
knob	(la) poigne/e[Noun]
knob	(un) bouton (de commande)[Noun]
knobby	noueux, noueuse[Adjective]
knobs	(les) boutons (de commande)[Noun]
knock	frapper (inf.)[Verb]
knock	tapoter[Verb]
knockdown	démolir[Verb]
knockout	knock-out (m)[Noun]
knot	(un) noeud[Noun]
knots	des noeuds[Noun]
knotted	noue/ (past.part.)[Adjective]
knotty	épineux(euse)[Adjective]
knotty	noueux[Adjective]
know	savoir (inf.)[Verb]
knower	celui|celle qui a le savoir[Noun]
knowhow	le savoir-faire[Noun]
knowhow	technique (f)[Noun]
knowing	entendu(e)[Adjective]
knowingly	en connaissance de cause[Adverb]
knowingly	sciemment[Adverb]
knowledge	la connaissance[Noun]
knowledge	le savoir[Noun]
knowledgeable	bien informé(e)[Adjective]
known	connu (past.part.)[Verb]
knows	(il|elle) sait[Verb]
knuckle	(meat) jarret (m)
knuckle	(une) phalange[Noun]
knucklebone	(une) phalange[Noun]
knuckles	(un) poing ame/ricain[Noun]
knuckles	les phalanges[Noun]
koala	koala (m)[Noun]
kosher	cache\re[Adjective]
kosher	kasher[Adjective]
kraut	(la) choucroute[Noun]
kraut	(un) boche (slang)[Noun]
krauts	les chleus (slang.)[Noun]
kudzu	lierre du Japon[Noun]
lab	labo[Noun]
labor	travail (m)[Noun]
laborer	l'ouevrier[Noun]
laborer	l'ouvrier[Noun]
laborers	les ouevriers[Noun]
laborers	les ouvriers[Noun]
laborious	laborieux (m), laborieuse (f)[Noun]
labyrinth	labyrinthe (m)[Noun]
lace	dentelle (f)[Noun]
laceration	lace/ration (f)[Noun]
lacerations	lace/rations (f)[Noun]
lack	manque[Noun]
lackadaisical	nonchalant[Adjective]
lackey	laquais[Noun]
lacking	simplet[Adjective]
lackluster	terne[Adjective]
laconic	laconique[Adjective]
laconically	laconiquement[Adjective]
lacquer	(for wood) vernis (m)[Noun]
lacquer	laque[Noun]
lad	garcon[Noun]
ladies	Mesdames[Noun]
lady	dame[Noun]
ladybird	coccinelle[Noun]
ladybug	coccinelle[Noun]
lake	lac[Noun]
lakes	lacs[Noun]
lamb	veau[Noun]
lambs	veaux[Noun]
laminated	laminé[Verb]
laminates	lamine[Verb]
lamp	lampe[Noun]
language	langue[Noun]
laugh	rire[Verb]
leather	cuir[Noun]
leave	laisser[Verb]
lectern	lutrin[Noun]
lecture	confe/rence[Noun]
lecturer	confe/rencier[Noun]
ledge	rebord[Noun]
ledger	registre[Noun]
lee	co^te/[Noun]
leg	jambe[Noun]
legal	legal[Adjective]
legality	legalite[Noun]
legalize	legaliser[Noun]
legally	legalement[Adverb]
legate	leguer[Verb]
legation	legation[Noun]
legend	legende[Noun]
legendary	lengendaire[Adjective]
legion	legion[Verb]
letter	additive[]
letter	ethnic[]
letter	neighbor[]
like	aimer[Verb]
live	vivre[Verb]
love	aime
love	amour[Noun]
lover	amant[Noun]
luck	chance[Noun]
lunch	le de/jeuner[Noun]
ma	maman[Noun]
ma	Mère[Noun]
ma'am	M'dame[Noun]
ma'am	madame[Noun]
ma'am	mademoiselle[Noun]
macabre	lugubre[Adjective]
macabre	macabre[Adjective]
macabrely	macabrement[Adverb]
macadam	bitume[Noun]
macadam	macadam[Noun]
macadamize	goudronner[Verb]
macadamize	macadamiser[Verb]
macadamized	goudronné[Verb]
macadamized	macadamise/[Adjective]
macadamizes	goudronnes[Verb]
macadamizes	macadamise[Verb]
macadamizing	goudronnant[Verb]
macaque	macaque[Noun]
macaque	macaque[Noun]
macaroni	macaroni[Noun]
macaronies	macaronis[Noun]
macaroon	macaron[Noun]
macaw	l'ara[Noun]
mace	sceptre[Noun]
macerate	mace'rer[Verb]
macerate	maçérer[Verb]
macerated	macere'[Adjective]
maceration	mace'ration[Noun]
macerations	mace'rations[Noun]
maces	masse[Noun]
machete	machette[Noun]
machination	machination[Noun]
machine	machine[Noun]
machined	machine/[Adjective]
machineries	machinerie
machinery	machinerie[Noun]
machines	machines[Noun]
machinist	machiniste[Noun]
machinists	machinistes[Noun]
machismo	machisme[Noun]
macho	macho[Adjective]
macrocephalic	macroce'phale[Adjective]
macrocosm	macrocosme[Noun]
macroeconomics	macroeconomie[Noun]
macroevolution	macroe'volution[Noun]
macroevolutionary	macroe'volutionnaire[Adjective]
macroinstruction	macroinstruction
macromolecular	macromole'culaire[Adjective]
macromolecule	macromole'cule[Noun]
macromolecules	macromole'cules[Noun]
macropathological	macropathologique[Adjective]
macropathology	macropathologie[Noun]
macrophage	macrophage[Noun]
macrophages	macrophages[Noun]
macrophagic	macrophage[Adjective]
macroprocessor	macroprocesseur[Noun]
macroscopic	macroscopic[Adjective]
macrosimulation	macrosimulation[Noun]
macrostructure	macrostructure[Noun]
maculate	maculer[Verb]
maculated	macule'[Adjective]
maculates	macule[Verb]
maculation	maculation[Noun]
maculations	maculations[Noun]
mad	fache'[Adjective]
mad	fou, folle[Adjective]
madam	madame[Noun]
madams	mesdames[Noun]
maddened	rendu fou[Adjective]
made	a fait[Verb]
made	j'adore[Verb]
mademoiselle	mademoiselle[Noun]
mademoiselles	mesdemoiselles[Noun]
maestro	maestro[Noun]
mafioso	mafieux[Noun]
magazine	magazine[Noun]
magic	magique[Adjective]
magician	magicien[Noun]
magicians	magiciens[Noun]
magistral	magistral[Adjective]
magistrally	magistralement[Adverb]
magistrature	magistrature[Noun]
magma	magma[Noun]
me	moi[Pronoun]
mead	le hydromel[Noun]
meadow	le pre/[Noun]
meadow	pre/[Noun]
meadowland	les prairies[Noun]
meadows	pre/s[Noun]
meager	maigre[Adjective]
meagerly	maigrement[Adverb]
meagre	maigre[Adjective]
meal	repas[Noun]
meals	repas[Noun]
mealtime	heure du repas[Noun]
mean	moyenne[Noun]
meander	me/andre[Noun]
meaningless	insense/[Adjective]
meanings	significations[Noun]
means	moyen[Noun]
meanwhile	entretemps[Adverb]
measurable	mesurable[Adjective]
meat	viande[Noun]
meatball	boulette de viande[Noun]
meatballs	boulettes de viande[Noun]
mechanical	me/canique[Adjective]
mechanician	me/canicien[Noun]
mechanism	me/canisme[Noun]
mechanisms	me/canismes[Noun]
medal	me/daille[Noun]
medallion	me/daillon[Noun]
medallions	me/daillons[Noun]
mediatrice	me/diatrice[Noun]
mediatrix	me/diatrice[Noun]
medic	me/decin[Noun]
medical	me/dical[Adjective]
medically	me/dicalement[Conjunction]
medicament	me/dicament[Noun]
medicaments	me/dicaments[Noun]
medicinal	me/dicinal[Adjective]
medicine	me/decine[Noun]
medicines	me/decines[Noun]
medico	medico[Adjective]
medicolegal	me/dicole/gal[Adjective]
medics	me/decins[Noun]
medieval	me/die/val[Adjective]
medievalist	me/die/valiste[Noun]
medievalists	me/die/valistes[Noun]
mediocre	me/diocre[Adjective]
mediocrities	me/diocrite/s[Noun]
mediocrity	me/diocrite/[Noun]
meditate	me/diter[Verb]
meditates	me/dite[Verb]
meditation	me/ditation[Noun]
meditations	me/ditations[Noun]
meditative	me/ditatif[Adjective]
meditatively	d'un air me/ditatif[Adjective]
medium	me/dium[Noun]
medlar	ne\fle[Noun]
medley	me/lange[Noun]
medleys	me/langes[Noun]
medulla	me/dulle[Noun]
medusae	me/duses[Noun]
meek	doux[Adjective]
meekly	avec soumission[Adjective]
meekness	soumission[Noun]
meerschaum	pipe en e/cume de mer[Noun]
megabyte	me/ga-octet[Noun]
megabytes	me/ga-octets[Noun]
megacycle	me/gacycle[Noun]
megahertz	me/gahertz[Noun]
megalith	me/galithe[Noun]
megalithic	me/galitic[Adjective]
megalomania	me/galomanie[Noun]
megalomaniac	me/galomane[Noun]
megalomaniacal	me/galomane[Adjective]
megaphone	me/gaphone[Noun]
melancholia	me/lancolie[Noun]
melancholic	me/lancolique[Adjective]
melange	me/lange[Noun]
melodic	me/lodique[Adjective]
melodies	me/lodies[Noun]
melodious	me/lodieux[Adjective]
melodrama	me/lodrame[Noun]
melodramas	me/lodrames[Noun]
melodramatic	me/lodramatique[Adjective]
melody	me/lodie[Noun]
melon	melon[Noun]
melons	melons[Noun]
melted	fondu[Pronoun]
member	membre[Noun]
members	membres[Noun]
membrane	membrane[Noun]
membranes	membranes[Noun]
membranous	membraneux[Adjective]
memoir	me/moire[Noun]
memoirs	me/moires[Noun]
memorable	me/morable[Adjective]
memories	souvenirs[Noun]
memorize	me/moriser[Verb]
memorized	me/morise/[Pronoun]
memory	me/moire, souvenir[Noun]
men	hommes[Noun]
menace	menace[Noun]
menaced	menace/[Adjective]
menagerie	me/nagerie[Noun]
menageries	me/nageries[Noun]
menhir	menhir[Noun]
meningitis	me/ningite[Noun]
meniscus	me/nisque[Noun]
menopause	me/nopause[Noun]
menstrual	menstruel[Adjective]
menstruation	menstruation[Noun]
menstruations	menstruations[Noun]
mental	mental[Adjective]
mentality	mentalite/[Noun]
menthol	menthol[Noun]
mention	mentionner[Verb]
menu	menu[Noun]
meow	miauler[Verb]
meowing	miauleur[Adjective]
meows	miaule[Verb]
mephitic	me/phitique[Adjective]
mercantile	mercantile[Adjective]
mercantilism	mercantilisme[Noun]
mercenaries	mercenaires[Noun]
mercenary	mercenaire[Noun]
merchant	marchand[Noun]
merchants	marchands[Noun]
mercury	mercure[Noun]
mercy	pitie/[Noun]
meridian	me/ridien[Noun]
meridians	me/ridiens[Noun]
meridional	me/ridional[Adjective]
merino	me/rino[Noun]
merit	me/rite[Noun]
meritocracy	me/ritocratie[Noun]
merits	me/rites[Noun]
mermaid	sire\ne[Noun]
mermaids	sire\nes[Noun]
merry	joyeux[Adjective]
message	message[Noun]
messages	messages[Noun]
messiahs	messie[Noun]
messianic	messianique[Adjective]
moan	gémir[Verb]
moaned	gémit[Verb]
moaning	gémissant[Verb]
moans	gémissements
moat	fossé[Noun]
moats	fosséa[Noun]
mob	foule[Noun]
mock	ridiculiser[Verb]
mocked	ridiculisé[Verb]
mockery	moquerie[Noun]
modal	modal[Adjective]
moon	lune[Noun]
moonlight	clair de lune
moons	lunes
moose	orignal
naive	naif, naive[Adjective]
naively	naivement[Adverb]
naked	nu[Noun]
name	prenom(persons), nom (things)[Noun]
named	nomme[Adjective]
nameless	sans nom[Adjective]
namely	nomement [Adverb]
namely	nommement[Adverb]
names	prenoms, noms (see name)[Noun]
national	national/e[Adjective]
nationalism	nationalisme[Noun]
nationalist	nationaliste[Adjective]
native	habitant/e du pays[Noun]
natural	naturel (le)[Adjective]
naturalize	naturaliser[Verb]
naturally	naturellement[Adverb]
neap	mortes-eaux[Noun]
near	proche[Adjective]
nearby	près[Adverb]
nearer	plus proche[Adjective]
neglect	abandonner[Verb]
neighbor	voisin[Noun]
neighbors	voisins[Noun]
network	réseau
networked	réseauté
networks	réseaux
nine	neuf[Adjective]
nineteen	dix-neuf[Adverb]
nineteenth	dix-neuvie\me[Adjective]
nineties	les anne/es quatre-vingt-dix[Noun]
ninetieth	quatre-vingt-dixie\me[Adjective]
ninety	quatre-vingt-dix[Adverb]
ninetyfold	quatre-vingt-dix fois[Adverb]
ninth	neuvie\me[Adjective]
niobium	niobium (m)[Noun]
nip	te/ton (m)[Noun]
nipple	te/ton[Noun]
nipples	te/tons[Noun]
nips	te/tons (m)[Noun]
nirvana	nirvana[Noun]
nitrate	nitrate[Noun]
nitrated	nitrate/[Noun]
nitrates	nitrates[Noun]
nitrating	nitratant[Adverb]
nitration	nitratation[Noun]
nitrations	nitratations[Noun]
nitric	nitrique[Adjective]
nitride	nitrate (m)[Noun]
nitrogen	azote[Noun]
nitroglycerin	nitroglyce/rine[Noun]
nitroglycerine	nitroglyce/rine[Noun]
noble	noble[Noun]
nobleman	noble[Noun]
noblemen	nobles[Noun]
nobleness	noblesse[Noun]
nobody	personne[Pronoun]
noctambulism	noctambulisme[Noun]
noctambulist	noctambule[Noun]
nocturn	noctune
nocturnally	de nuit[Noun]
now	maintenant[Adverb]
nowadays	de nos jours, aujourd'hui[Adverb]
noway	pas question, pas du tout[Adverb]
noway	pas question, pas du tout[Adverb]
oaf	nigaud[Noun]
oafish	stupide[Adjective]
oafishness	sottise[Noun]
oak	che^ne[Noun]
oar	rame[Noun]
oarsman	rameur[Noun]
oasis	oasis[Noun]
oat	avoine[Noun]
oath	serment[Noun]
oatmeal	farine d'avoine[Noun]
obdurate	inve/te/re/[Adjective]
obedience	obe/issance[Noun]
obedient	soumis[Adjective]
obelisk	obe/lisque[Noun]
obese	obe\se[Adjective]
obesity	obe/site/[Noun]
ocean	ocean[Noun]
one	un[Noun]
orange	orange[Noun]
oranges	oranges[Noun]
orbit	orbite[Noun]
orbital	orbital[Adjective]
orbits	orbites[Noun]
orchestra	orchestre[Noun]
orchestral	orchestral[Adjective]
orchestras	orchestres[Noun]
orchestrated	orchestré[Adjective]
order	commande[Noun]
ordered	ordonné[Adjective]
organism	organisme[Noun]
organisms	organismes[Noun]
organist	organiste[Noun]
organists	organistes[Noun]
organization	organisation[Noun]
organizations	organisations[Noun]
organize	organiser[Verb]
organized	organisé[Adjective]
organs	organes[Noun]
orgasm	orgasme[Noun]
orgasms	orgasmes[Noun]
orgies	orgies[Noun]
orgy	orgie[Noun]
orient	orient[Noun]
orientable	orientable[Adjective]
oriental	oriental[Adjective]
orientation	orientation[Noun]
orientations	orientations[Noun]
oriented	orienté[Adjective]
orients	orients[Noun]
orifice	orifice[Noun]
orifices	orifices[Noun]
orificial	orifique[Adjective]
origin	origine[Noun]
original	original[Adjective]
originality	originalité[Adjective]
originally	originalement[Adverb]
originals	originaux[Adjective]
origins	origines[Noun]
ornamental	ornemental[Adjective]
ornithology	ornithologie[Noun]
orthodox	orthodoxe[Adjective]
orthodoxes	orthodoxes[Adjective]
orthogonal	orthogonal[Adjective]
pace	rhytme[Noun]
pace	vitesse[Noun]
pacemaker	stimulateur cardiaque (m), pacemaker (m)[Noun]
pachyderm	pachyde\rme[Noun]
pacific	pacifique[Noun]
pacifically	pacifiquement[Adverb]
pacification	pacification(f)[Noun]
pacifications	pacificateur / -trice [Noun]
pacificist	pacifiste[Noun]
pacifier	tétine (f), sucette (f)
pacifism	pacifisme[Noun]
pacifist	pacifiste[Noun]
pacify	apaiser[Verb]
pacify	pacifier[Verb]
package	2)paquet(m);3)ballot(m)[Noun]
package	emballage[Noun]
packaged	emballeE[Adverb]
packaged	paquet (m)[Noun]
packed	(~ with) bourré(e) de
packet	1) paquet (m) ;2)ballot(m)[Noun]
packing	emballage (m)[Noun]
pact	pacte, contrat[Noun]
pacts	pcates, contrats[Noun]
pad	coussinet, tampon[Noun]
padding	rembourrage, remplissage[Noun]
paddle	pagaie, palette[Noun]
paddock	enclos, paddock[Noun]
padlock	cadenas[Noun]
padlock	cadenasser[Verb]
padrone	patron /nne[Noun]
paella	paella[Noun]
pagan	pai.en/i.enne)
pagan	payen /nne[Noun]
pagans	pa&iumlen(ne)s[Noun]
pageant	spectacle[Noun]
pageantry	apparat[Noun]
paid	payé(e)[Adjective]
pail	seau[Noun]
pain	douleur[Noun]
pained	peiné(e)[Adjective]
painful	douleureux[Adjective]
painful	pénible[Adjective]
painfully	douloureusement[Adverb]
painstaking	assidu(e)[Adjective]
paint	peindre[Verb]
paint	peinture[Noun]
paintbrush	pinceau[Noun]
painter	peintre[Noun]
painters	peintre[Noun]
painting	peinture[Noun]
paints	couleurs[Noun]
pair	couple[Noun]
pajamas	pyjama[Noun]
pal	copain (m)[Noun]
pal	copine (f)
palace	palais[Noun]
palatable	agréable au goût[Adjective]
palaver	palabres (f)[Noun]
pale	pâle[Adjective]
palette	palette (f)[Noun]
pall	voile (m)[Noun]
pallet	palette (f)[Noun]
pallette	palette (f)[Noun]
pallor	pâleur (f)[Noun]
palm	(~ tree) palmier (m)[Noun]
palm	paume (f)[Noun]
palpable	évident(e)[Adjective]
palpable	manifeste [Adjective]
paltry	dérisoire[Adjective]
pamper	choyer, dorloter[Verb]
pamphlet	brochure (f)[Noun]
pan	casserole (f)[Noun]
panacea	panacée (f)[Noun]
panama	Panama (m)[Noun]
pancake	crêpe (f)[Noun]
panda	panda (m)[Noun]
pandemonium	tohu-bohu (m)[Noun]
pane	vitre (f), carreau (m)[Noun]
panel	invités (m)[Noun]
paneling	lambris (m)[Noun]
panelling	lambris (m)[Noun]
pang	tiraillement (m)[Noun]
panic	panique (f)[Noun]
panic	paniquer[Verb]
panicky	paniqué(e)[Adjective]
panorama	panorama (m)[Noun]
pansy	pensée(f)[Noun]
pant	haleter[Verb]
panther	panthère (f)[Noun]
panties	culotte (f)[Noun]
pantry	garde-manger (m)[Noun]
pants	pantalon (m)[Noun]
papa	papa (m)[Noun]
paper	papier (m)[Noun]
paperback	livre de poche (m)[Noun]
parameter	paramètre[Noun]
passer	passeur[Noun]
passion	passion[Noun]
passionately	passionne/ment[Adverb]
passive	passif[Adjective]
passivity	passivite/[Noun]
password	mot de passe[Noun]
passwords	mots de passe[Noun]
past	passe/[Noun]
pasta	pâtes alimentaires[Noun]
peat	tourbe[Noun]
pectorals	pectoraux[Noun]
peculiar	important
penitence	pe/nitence[Noun]
penitences	pe/nitences[Noun]
penitency	pe/nitence[Noun]
penitent	pe/nitent[Noun]
pentadactyl	pentadactyle[Adjective]
pentadactylism	pentadactylisme[Noun]
pi	pi[Noun]
pianist	pianiste[Noun]
pianistic	pianistique[Adjective]
piano	piano[Noun]
picture	image[Noun]
pictures	images[Noun]
pie	tarte[Noun]
pies	tartes[Noun]
pig	cochon[Noun]
pigeonhole	pigeonnier[Noun]
pigment	pigment[Noun]
pigmentation	pigmentation[Noun]
pigments	pigments[Noun]
pigs	cochons[Noun]
pilot	pilote[Noun]
pilots	pilotes[Noun]
pilule	pilule[Noun]
pine	pin[Noun]
pineapple	ananas[Noun]
pineapples	ananas[Noun]
pines	pins[Noun]
pink	rose[Noun]
pocket	poche[Noun]
pocketbook	livre de poche[Noun]
pocketbooks	livres de poche[Noun]
pockets	poches[Noun]
podium	podium[Noun]
pogrom	pogrom[Noun]
pogroms	pogroms[Noun]
pointer	pointeur[Noun]
pointers	pointeurs[Noun]
pointillism	pointillisme[Noun]
pointillist	pointilliste[Noun]
pointilliste	pointilliste[Noun]
pointillistic	pointilliste[Noun]
poison	poison[Noun]
poisonous	poison[Adjective]
poisonousness	toxicite/[Noun]
poisons	poisons[Noun]
polar	polaire[Adjective]
polarities	polarite/s[Noun]
polarity	polarity[Noun]
polarize	polariser[Verb]
polarized	polarise/[Adjective]
polemical	pole/mique[Adjective]
polemically	de fac,on pole/mique[Adjective]
polemically	de manie\re pole/mique[Adverb]
polemicize	pole/miquer
police	police[Noun]
policeman	agent de police[Noun]
policeman	agent[Noun]
policeman	policier[Noun]
policemen	agents de police[Noun]
policemen	agents[Noun]
policemen	policiers[Noun]
policewoman	contractuelle[Noun]
policewomen	contractuelles[Noun]
policy	politique[Noun]
polish	polonais[Adjective]
polish	Polonais[Noun]
polite	poli[Adjective]
politely	poliment[Adverb]
politeness	politesse[Noun]
political	politique[Adjective]
politically	politiquement[Adverb]
politician	politicien[Noun]
politicians	politiciens[Noun]
politics	politique[Noun]
polka	polka[Noun]
pollutant	polluant[Noun]
pollute	polluer[Verb]
polluted	pollue/[Adjective]
polluter	pollueur[Noun]
pollution	pollution[Noun]
polonium	polonium[Noun]
poltergeist	poltergeist[Noun]
polyandrous	polyandre[Adjective]
polyandry	polyandrie[Noun]
pour	french[Noun]
pout	moue[Noun]
poverty	pauvrete/[Noun]
powder	poudre[Noun]
powdery	poudreux[Adjective]
power	puissance[Noun]
powerful	puissant[Adjective]
powerless	impuissant[Adjective]
powwow	assemble/e[Noun]
practicability	possibilite/[Noun]
practicable	re/alisable[Adjective]
practical	pratique[Adjective]
pray	prier[Verb]
prayer	prie\re[Noun]
prayers	prie\res[Noun]
praying	en prie\res[Adjective]
praying	prie\re[Noun]
preach	pre^cher[Verb]
preach	prononcer[Verb]
preacher	pasteur[Noun]
preacher	pre/dicateur[Noun]
preachers	pasteurs[Noun]
preachers	pre/dicateurs[Noun]
preaches	pre^che[Verb]
preaches	prononce[Verb]
preachify	faire la morale[Verb]
preaching	pre/dication[Noun]
preaching	pre^cheur[Adjective]
preaching	sermons[Noun]
preachy	pre^cheur[Adjective]
preachy	sermonneur[Adjective]
preamplifier	pre/amplificateur[Noun]
preamplifiers	pre/amplificateurs[Noun]
prearrange	arranger au pre/alable[Verb]
prearrange	arranger d'avance[Verb]
prince	prince[Noun]
princedom	principaute/
princely	princier[Adjective]
princes	princes[Verb]
princess	princesse[Noun]
principal	principal[Noun]
principal	directeur[Noun]
principalities	principaute/s[Noun]
principality	principaute/
principally	principalement
principally	surtout
principals	directeurs
principle	principe[Noun]
principles	principes
print	empreinte[Noun]
print	impression[Noun]
print	marque
printable	imprimable[Adjective]
printed	imprime/[Adjective]
printer	imprimeur[Noun]
printer	imprimante[Noun]
priorities	priorite/
priorities	priorite/s
priority	priorite/
prove	prouver[Verb]
pulse	impulsion[Noun]
pulverizable	pulverisable[Adjective]
pulverizables	pulverisables[Adjective]
pulverization	pulverisation[Noun]
pulverize	pulveriser[Verb]
pulverized	pulvérisé[Verb]
pulverizer	pulverisateur[Noun]
pulverizers	pulvérisateurs[Noun]
pulverizes	pulvérises[Verb]
pulverizing	pulve/risant[Verb]
puma	puma[Noun]
pump	pomper[Verb]
push	pousser[Verb]
pushchair	chaise roulante[Noun]
pushing	poussé[Verb]
put	mettre[Verb]
putdown	poser[Verb]
putout	éteindre[Verb]
putrefaction	putréfaction[Noun]
quail	caille[Noun]
quails	cailles[Noun]
quake	trembler[Verb]
quality	qualite/[Noun]
qualm	scrupule[Noun]
qualms	scrupules[Noun]
quantity	quantite/
quarrel	se disputer[Verb]
quarry	carri\ere[Noun]
quarter	quartier[Noun]
quarterdeck	plage arri\ere[Noun]
quarterfinal	quart de finale[Noun]
queen	reine[Noun]
queer	bizarre[Adjective]
quell	re/primer[Verb]
quench	se de/salte/rer
querulous	ronchonneur[Adjective]
query	question[Noun]
quest	que^te[Noun]
question	question[Noun]
queue	queue[Noun]
quibble	chicaner[Verb]
quick	rapide[Adjective]
quicklime	chaux vive[Noun]
quickly	rapidement[Adverb]
quicksand	sables mouvants[Noun]
quicksilver	vif-argent[Noun]
quiet	tranquille[Adjective]
quieten	calmer[Verb]
quietly	doucement[Adverb]
quill	plume d'oie[Noun]
quilt	e/dredon[Noun]
quirk	bizarrerie[Noun]
quit	se rendre[Verb]
quite	assez[Adjective]
quota	quota[Noun]
quotation	citation[Noun]
quote	citer[Verb]
r	r[Noun]
rabbet	feuillure[Noun]
rabbi	rabbin
rabbinic	rabbinique[Adjective]
rabbit	lapin[Noun]
rabbits	lapins[Noun]
rabble	cohue[Noun]
rabid	enrage/[Adjective]
rabies	rage
raccoon	raton-laveur
race	course[Noun]
racehorse	cheval de course
racemic	rece/mique[Adjective]
racer	coureur
races	courses[Noun]
rachis	rachis[Noun]
rachitic	rachitique[Adjective]
rachitis	rachitisme[Noun]
racial	racial[Adjective]
racialism	racisme[Noun]
racialist	raciste[Noun]
racialistic	raciste[Adjective]
racing	de course
racism	racisme[Noun]
racist	raciste[Noun]
rack	e/tag\ere[Noun]
racket	raquette[Noun]
racket	tumulte[Noun]
radar	radar[Noun]
radars	radars[Noun]
radio	radio[Noun]
read	lire[Verb]
readability	lisibilite/[Noun]
readable	lisible[Adjective]
reader	lecteur[Noun]
readers	lecteurs[Noun]
readership	lecteurs[Noun]
readership	lectorat[Noun]
readily	de bonne gra^ce[Adverb]
readily	facilement[Adverb]
readily	volontiers[Adverb]
readiness	empressement[Noun]
readiness	facilite/[Noun]
reading	interpre/tation[Noun]
reading	lecture[Noun]
reading	releve/[Noun]
readings	interpre/tations[Noun]
readings	lectures[Noun]
readjust	rajuster[Verb]
readjust	re/ajuster[Verb]
readjusts	rajuste[Verb]
readjusts	re/ajuste[Verb]
readout	affichage[Noun]
readout	d'affichage[Adjective]
readouts	affichages[Noun]
reads	lit[Verb]
ready	dispose/[Adjective]
ready	pre^t[Adjective]
ready	prompt[Adjective]
reaffirm	affirmer de nouveau[Verb]
reaffirm	re/affirmer[Verb]
reaffirms	affirme de nouveau[Verb]
reaffirms	re/affirme[Verb]
reagent	re/actif[Noun]
real	naturel[Adjective]
real	ve/ritable[Adjective]
real	vrai[Adjective]
realisable	re/alisable[Adjective]
realism	re/alisme[Noun]
realist	re/aliste[Noun]
realistic	plein de re/alisme[Adjective]
realistic	re/aliste[Adjective]
realistically	avec re/alisme[Adverb]
reality	re/alite/[Noun]
realize	re/aliser[Verb]
really	vraiment[Adverb]
reporter	le reporter[Noun]
represent	repre/senter[Verb]
request	demande[Noun]
request	exiger[Verb]
request	reque^te[Noun]
road	rue[Noun]
roads	rues
roadwork	chantier[Noun]
robber	voleur[Noun]
room	chambre
rotting	decay[Verb]
sabbat	un sabbat[Noun]
sabbath	sabbat[Noun]
sabbatic	sabatique[Noun]
sabbatical	sabbatique[Adjective]
saber	sabre[Noun]
sabin	nom de personne qui a decouvert le vaccine contre poliomyltie[Noun]
sable	martre[Noun]
sabot	sabot[Noun]
sabotage	sabotage[Noun]
sabotaged	saboté[Verb]
sabotages	sabotages[Noun]
saboteur	saboteur[Noun]
sabra	citoyen du pays Israel, personne ne en Israel[Noun]
sabras	citoyens du pays Israel, personnese nes en Israel[Noun]
saccade	une saccade[Noun]
saccharin	saccharine[Verb]
saccharine	saccharin[Noun]
saccharose	saccharose[Noun]
sacerdotal	appartient a une chose religieuse[Adjective]
sacerdotally	une acte faite en manière sacrée[Adverb]
sachet	un sachet[Noun]
sack	sac[Noun]
sack	virer[Verb]
sackcloth	une vêtement de deuil[Noun]
sacked	vire/[Adjective]
sackful	une mesure "plein d'un sac"[Adjective]
sacks	piller[Verb]
sacrament	sacrament[Noun]
sacramental	sacremental[Adjective]
sacraments	sacrements[Noun]
sacre	sacre[Noun]
sacred	sacre/[Adjective]
sacrifice	sacrifice[Noun]
sacrificed	sacrifier[Verb]
sacrifices	sacrifices[Noun]
sacrilege	sacrile\ge[Noun]
sad	malheureux[Adjective]
sad	triste[Adjective]
sadden	attrister[Verb]
sadder	plus triste[Adjective]
saddest	plus triste[Adjective]
saddle	selle[Noun]
saddlebag	une sacoche[Noun]
saddlebags	des sacoches
saddlebow	un arçon
saddlecloth	une housse ( de cheval)[Noun]
saddled	sellé[Verb]
saddleless	sans selle
saddler	un sellier[Noun]
saddlery	une sellerie[Noun]
saddles	 une selle
saddletree	un bois de selle[Noun]
saddling	sellant[Verb]
sadism	sadisme[Noun]
sadist	sadiste[Noun]
sadistic	sadique[Adjective]
sadistically	sadiquement[Adverb]
sadists	sadistes[Noun]
sadly	tristement[Adverb]
sadness	tristesse[Noun]
safari	un voyage, en particulier en Afrique[Noun]
safe	coffre-fort[Noun]
safecracker	une personne qui ouvre une caisse illégalement[Noun]
safecracking	l'acte d'ouvrir une caise illégalement[Noun]
safeguard	une sauvegarde[Noun]
safeguarded	sauvegardé[Verb]
safeguards	garde fous[Noun]
safer	pplus sauf[Adjective]
safes	les caisses[Noun]
safest	le plus sauf[Adjective]
safety	le sûreté[Noun]
safflower	le carthame[Noun]
saffron	le safran[Noun]
sag	plier, ployer[Verb]
saga	une saga[Noun]
sagacious	prudent[Adjective]
sagaciously	prudemment[Adverb]
sagaciousness	la sagacité[Noun]
sagacity	la sagacité[Noun]
sage	le sage[Noun]
sagely	sagement[Adjective]
sago	le sagou[Noun]
saguaro	une type de cactus[Noun]
sahib	une forme d'addresser pour un homme des Indes[Noun]
said	dit[Verb]
sail	naviguer[Verb]
sail	voile[Noun]
sailable	navigable[Adjective]
sailboat	un bateau  à voiles[Noun]
sailboater	un marin des bateua à voiles[Noun]
sailcloth	la toile à voiles[Noun]
sailor	marin[Noun]
sailors	les marins[Noun]
sails	les voiles[Noun]
sainfoin	le sainfoin ( bot.)[Noun]
saint	saint(e)[Noun]
sainted	sacré[Adjective]
sainthood	la sainteté[Noun]
saints	saint(e)s[Noun]
saith	dit ( vieux anglais)[Verb]
sake	le vin du riz japonais[Noun]
salability	vendabilite/[Adjective]
salacious	grivois[Adjective]
salad	salade[Noun]
salads	salades[Noun]
salamander	salamandre[Noun]
salami	salami[Noun]
salaried	salarie/[Adjective]
salaries	salaires[Noun]
sale	solde[Noun]
saleable	vendable[Adjective]
saleroom	la salle des ventes[Noun]
sales	les ventes[Noun]
salesclerk	un vendeur
salesgirl	une vendeuse[Noun]
saleslady	une vendeuse[Noun]
salesman	un vendeur[Noun]
salesmanship	l'art de vendre[Noun]
salesmen	les vendeurs[Noun]
salespeople	les vendeurs[Noun]
salesperson	un vendeur[Noun]
salesroom	les salles de ventes[Noun]
saleswoman	une vendeuse
saleswomen	les vendeuses[Noun]
salient	saillant[Adjective]
saline	salin[Adjective]
salinity	la salinité[Noun]
salinometer	un salinomère[Noun]
salinometers	des salinomètres[Noun]
saliva	salive[Noun]
salivary	salivaire[Adjective]
salivate	saliver[Verb]
salivated	 a fait saliver[Verb]
salivates	fait saliver[Verb]
salivating	faisant saliver[Verb]
salivation	;a salivation[Noun]
sallies	les sorties[Noun]
sallow	jaunâtre[Adjective]
sallowish	un peu jaunâtre[Adjective]
salmon	saumon[Noun]
salmons	saumons[Noun]
salon	salon[Noun]
salons	salons[Noun]
salt	sel[Noun]
saltwater	l'eau de mer[Noun]
saltworks	la saline[Noun]
saltwort	le soude ( bot.)[Noun]
salty	sale/[Adjective]
salubrious	salubre[Adjective]
salubrity	la salubrité[Noun]
salvage	le sauvetage[Noun]
salver	le plateau[Noun]
samba	la Samba, une danse originée du Brasil[Noun]
same	même[Adjective]
sampan	un bateua chinois[Noun]
samphire	le passe-pierre  (bot.)[Noun]
samurai	un guerrier ancien japonais[Noun]
samurais	des guerriers anciens japonais[Noun]
sand	le sable[Noun]
sandal	la sandale[Noun]
school	e/cole[Noun]
sea	mer[Noun]
sell	vendre[Verb]
seller	le vendeur[Noun]
sellers	les vendeurs[Noun]
serendipity	le serendiptiy[Verb]
serene	le serene
seven	sept[Adverb]
sevenfold	sept fois[Adverb]
seventeen	dix-sept[Adverb]
seventeenth	dix-septie\me[Adjective]
seventh	septie\me[Adjective]
seventies	les anne/es soixante-dix[Noun]
seventieth	soixante-dixie\me[Adjective]
seventy	soixante-dix[Adverb]
seventyfold	soixante-dix fois[Adverb]
sever	blesser[Verb]
severalfold	plusieurs fois[Adverb]
severe	grave[Adjective]
severed	blesse/ (m), blesse/e (f)[Adjective]
severely	gravement[Adverb]
severities	blessures (f)[Noun]
severity	gravite/ (f)[Noun]
sewage	e/pandage (m)[Noun]
sex	sexe (m)[Noun]
sexagenarian	se/xage/naire[Adjective]
shades	lunettes de soleil (f)[Noun]
shadow	ombre (f)[Noun]
shadows	ombres[Noun]
shagreen	chagrin[Noun]
shah	schah[Noun]
shake	secouer[Verb]
shakedown	lit de fortune[Noun]
shaken	secoue/[Adjective]
shaky	tremblant[Adjective]
shaman	chaman[Noun]
shamanism	chamanisme[Noun]
shampoo	shampooing
shampoos	shampooings
sheep	agnis[Noun]
sheepherder	pastor[Noun]
ship	ba^teau
ship	navire[Noun]
shit	merde[Noun]
sick	malade[Adverb]
silver	argent[Adjective]
sing	chanter[Verb]
singer	chanteur[Noun]
sink	couler[Verb]
six	six[Adverb]
sixteen	seize[Adverb]
sixteenth	seizie\me[Adjective]
sixties	les anne/es soixante (f)[Noun]
sixty	soixante[Adverb]
sixtyfold	soixante fois[Adverb]
size	taille (f), dimension (f)[Noun]
skate	patin (m)[Noun]
skateboard	planche a\ roulettes (f)[Noun]
skateboarder	planchiste (m)[Noun]
skateboarding	faire de la planche a\ roulettes[Verb]
skateboards	planches a\ roulettes (f)[Noun]
skater	patineur (m), patineuse (f)[Noun]
skaters	patineurs (m), patineuses (f)[Noun]
skates	patins (m)[Noun]
skating	patinage (m)[Noun]
skill	Habilete/[Noun]
skilled	habile[Adjective]
skilless	maladroit[Adjective]
skillful	adroit[Adjective]
skills	talents[Noun]
sky	ciel[Noun]
skylark	rossignol[Noun]
skyscraper	gratte-ciel[Noun]
snout	groin[Noun]
snow	neige[Noun]
snowball	boule de neige[Noun]
snowflake	flocon de neige
snowman	bonhomme de neige[Noun]
snowstorm	tempe^te de neige[Noun]
software	logiciel[Noun]
sorrel	oseille[Noun]
sorrily	tristement[Adverb]
sorrow	tristesse[Noun]
sort	trier[Verb]
sorted	trié[Adjective]
sorter	trieur[Noun]
soul	âme[Noun]
spectra	pl de spectrum, spectre[Noun]
spectrum	Phys: spectre; Fig: gamme (de produit)[Noun]
speculate	s'interroger, speculer, conjecturer[Verb]
speculation	meditation; conjectures ; speculation
staple	agrafe[Noun]
stapled	agraf/e[Adjective]
stapler	agrafeuse[Noun]
staplers	agrafeuses[Noun]
staples	agrafes[Noun]
star	e/toile[Noun]
stars	e/toiles[Noun]
start	de/part[Noun]
stifled	e/touffe/
strength	force[Noun]
stupid	Stupide[Adjective]
subject	asignatura[Noun]
succeed	re/ussir[Verb]
success	re/ussite/[Noun]
sun	soleil[Noun]
sunbathe	se bronzer[Verb]
sunny	ensoleille/[Adjective]
suppressed	e/touffe/
suspect	soupc,onner[Verb]
swear	jurer ; preter serment[Verb]
tab	happy[Adjective]
tab	sortir[Verb]
tabernacle	tabernacle[Noun]
table	table[Noun]
tableau	tableau[Noun]
tableaus	tableaux[Noun]
tableaux	tableaux[Noun]
tablecloth	nappe[Noun]
tablecloths	nappes[Noun]
tables	tables[Noun]
tablet	comprime/[Noun]
tablets	comprime/s[Noun]
taboo	tabou[Noun]
taboos	tabous[Noun]
tachometer	tachyme\tre[Noun]
tachometers	tachyme\tres[Noun]
tacit	tacite[Adjective]
tail	queue (of animal)[Noun]
tailor	tailleur[Noun]
tailored	fait sur mesure[Adjective]
tailors	tailleurs[Noun]
tails	queues[Noun]
task	tâche[Noun]
tasks	tâches[Noun]
taste	goût[Noun]
tasted	goûté[Verb]
tasteless	insipide[Adjective]
tear	accroc[Noun]
tear	de/chirer[Verb]
tear	de/chirure[Noun]
tear	larme[Noun]
teardrop	larme[Noun]
tearful	tout en pleurs[Adjective]
tearfully	en pleurant[Adverb]
tearfully	les larmes aux yeux[Adverb]
teargas	gaz lacrymoge\ne[Noun]
tearjerker	me/lo[Noun]
tearless	avec yeux secs[Adjective]
tearless	sans larmes[Adjective]
tearoom	salon de the/[Noun]
tears	de/chirures[Noun]
tears	larmes[Noun]
tearstained	barbouille/ de larmes[Adjective]
tearstained	portant des traces de larmes[Adjective]
tease	taquin[Noun]
tease	taquiner[Verb]
tease	tourmenter[Verb]
teasel	carde\re[Noun]
teaser	question dificile[Noun]
teaser	taquin[Noun]
teases	excite[Verb]
teases	taquine[Verb]
teases	taquins[Noun]
teasing	railleur[Adjective]
teasing	taquinerie[Noun]
teasingly	d'un ton railleur[Adverb]
teasingly	pour taquiner[Verb]
teaspoon	cuille\re a\ cafe/[Noun]
teaspoon	cuiller a\ cafe/[Noun]
teaspoonful	cuillere/e a\ cafe/[Noun]
teat	bout de sein[Noun]
teat	mamelon[Noun]
teat	tette[Noun]
teat	trayon[Noun]
teatime	l'heure du the/[Noun]
teats	mamelons[Noun]
teats	tettes[Noun]
ten	dix[Adverb]
tendencies	tendances (f)[Noun]
tendency	tendance (f)[Noun]
tenderfoot	visage pa^le (m)[Noun]
tenderly	tendrement[Adverb]
thence	de la\[Adverb]
thence	pour cette raison[Adverb]
thenceforth	de\s lors[Adverb]
thenceforward	de\s lors[Adverb]
theocracy	the/ocratie[Noun]
theocratic	the/ocratique[Adjective]
theodolite	the/odolite[Noun]
theologian	the/ologien[Noun]
theologians	the/ologiens
theological	the/ologique[Adjective]
theologically	the/ologiquement[Adverb]
theology	the/ologie[Noun]
theorem	the/ore\me[Noun]
theorems	the/ore\mes[Noun]
theoretical	the/ore/tique[Adjective]
theoretical	the/orique[Adjective]
theoretically	the/oriquement[Adverb]
theoretician	the/oricien[Noun]
theoretician	the/oricienne[Noun]
theoreticians	the/oriciennes[Noun]
theoreticians	the/oriciens[Noun]
theories	the/ories[Noun]
theorist	the/oricien[Noun]
theorist	the/oricienne[Noun]
theorists	the/oriciennes[Noun]
theorists	the/oriciens[Noun]
theorize	the/oriser[Verb]
thief	voleur[Noun]
though	cependant, pourtant[Adverb]
though	quoique , bien que[Conjunction]
thought	pensee,idee[Noun]
thought	reflexion; intention, dessein[Noun]
thoughtful	pensif, meditatif, reveur[Adjective]
thoughtful	serieux, reflechi, prudent[Adjective]
thoughtfully	pensivement[Adverb]
thoughtfulness	meditation, recueillement[Adjective]
thoughtless	irreflechi, etourdi[Adjective]
thoughtlessly	etourdiment, a la legere, sans reflexion[Adverb]
thoughtlessness	irreflexon, etourderie[Noun]
thousand	mille, millier[Noun]
thousandth	millieme[Noun]
thrash	ecraser qn, rouer qn de coup, [Verb]
thrash	se debattre, se demener[Verb]
thread	enfiler (une aiguille); se faufiler ; fileter[Verb]
thread	filament, fil de soie[Noun]
thread	Filet, pas de vis [Noun]
threadbare	elime, rape, use[Adjective]
threat	menace[Noun]
threaten	menacer[Verb]
threatening	menacant[Adjective]
threateningly	d'un ton menacant[Adverb]
three	trois[Article]
tigress	tiger[Noun]
today	au'jour d'hui[Noun]
toe	doigt a pied[Noun]
tradition	tradition[Noun]
traditional	traditional[Adjective]
trail	piste[Noun]
train	train[Noun]
trainee	stagiare[Noun]
trainer	encadreur[Noun]
traitor	trai^tre[Noun]
transaction	transaction[Noun]
transalpine	transalpin[Adjective]
transcribe	transcrire[Verb]
transfer	transfert[Noun]
translate	traduire[Verb]
translation	traduction[Noun]
translator	traducteur[Noun]
transmit	transmettre[Verb]
tree	arbre[Noun]
truck	camion
two	deux[Article]
ubiquitous	omnipre/sent[Adjective]
ubiquity	ubiquite/[Noun]
udder	pis[Noun]
ugh	pouah
uglier	plus laid[Adjective]
ugliest	le plus laid[Adjective]
uglify	enlaidir[Verb]
uglily	laidement
ugliness	laideur[Noun]
ugly	laid[Adjective]
ukulele	guitare hawai.enne[Noun]
ulcer	ulce\re[Noun]
ulcerate	ulce/rer[Verb]
ulcerated	ulce/reux[Adjective]
ulceration	ulce/ration[Noun]
ulcerative	ulce/ratif[Adjective]
ulna	cubitus[Noun]
ultimo	du mois dernier[Adverb]
unemployment	chomage[Noun]
vacant	(room, seat) libre; (stare) vague; (post) vacant[Adjective]
vacate	quitter[Verb]
vacation	vacances[Noun]
vaccinate	vacciner[Verb]
vaccinated	vacciner
vacuum	vide; (vacuum cleaner) aspirateur [masc] [Noun]
vacuumed	passer a\ l'aspirateur[Verb]
vagina	vagin (masc.)[Noun]
vagrant	vagabond(e) [m(f)][Noun]
vague	vague; (outline, photograph) flou; (absent minded) distrait[Adjective]
vain	(hope) vain; (promise) vide; (conceited) vaniteux, [f] -euse  [Adjective]
wacky	drole[Adjective]
wag	remuer[Verb]
walnut	la noix[Noun]
warehouse	entrepot
warehouse	entrepôt[Noun]
warehouses	entrepôts[Noun]
wash	laver[Verb]
weep	pleurer[Verb]
weeping	pleurs[Noun]
weeping	qui pleure[Adjective]
weeps	pleure[Verb]
weepy	larmoyant[Adjective]
weepy	me/lo[Noun]
weevil	charanc,on[Noun]
weft	trame[Noun]
weigh	mesurer[Verb]
weigh	peser[Verb]
weighing	pese/e[Noun]
weighings	pese/es[Noun]
weighs	pe\se[Verb]
weight	poids[Noun]
weighted	leste/[Adjective]
weighted	ponde/re/[Adjective]
weightily	avec force[Adverb]
weightily	puissamment[Adverb]
weighting	lestage[Noun]
weighting	plombage[Noun]
weightless	e/tat d'apesanteur[Adjective]
weightlessness	apesanteur[Noun]
weights	attache un poids a\[Verb]
weights	se/rie de poids[Noun]
weighty	lourd[Adjective]
weighty	pesant[Adjective]
weir	barrage[Noun]
weird	bizarre[Adjective]
weird	e/trange[Adjective]
weirdie	excentrique[Noun]
weirdies	excentriques[Noun]
weirdly	e/trangement[Adverb]
weirdness	caracte\re e/trange[Noun]
weirdness	e/trangete/ inquie/tante[Noun]
weirdo	excentrique[Noun]
weirdos	excentriques[Noun]
weirs	barrages[Noun]
welcome	agre/able[Adjective]
welcome	bienvenu[Adjective]
welcome	bienvenue[Noun]
welcome	souhaiter la bienvenue a\[Verb]
where	ou\[Adjective]
window	la fene^tre[Noun]
window	le guichet  [tickets, etc.]
windowpane	la glace[Noun]
windowpanes	les glaces
windows	les fene^tres
windshield	le parebrise[Noun]
windstorm	la tempe^te[Noun]
windup	remonter[Verb]
wine	le vin[Noun]
wineglass	le verre de vin[Noun]
winegrower	le vigneron[Noun]
winemaker	l'encaveur (m)[Noun]
winemaking	la vinification[Noun]
winepress	le pressoir[Noun]
wines	les vins[Noun]
winey	vineux (-se)[Adjective]
wing	l'aile (f)[Noun]
winglet	l'aileron (m)[Noun]
wink	un clin d'oeil[Noun]
winter	l'hiver (m)
wipe	essuyer
wire	fil me/tallique, fil de fer[Noun]
wire	telegramme[Noun]
wire	telegraphier ; faire l'installation electrique[Verb]
wired	branche , sonorise[Adjective]
wireless	sans fil[Adjective]
wiring	installation electrique[Noun]
wiry	raide, rude ; maigre et nerveux
wisdom	sagesse[Noun]
wise	sage, prudent, savant[Adjective]
wisecrack	astuce, sarcasme[Noun]
wisely	sagement, prudement[Adverb]
wiser	plus sage[Adjective]
wish	desir, souhait[Noun]
wish	desirer, souhaiter qch[Verb]
wishbone	brechet[Noun]
wishes	pl de wish, desir[Noun]
wishful	that's wishfull thinking (on your part ): tu te fais des illusions[Adjective]
witch	sorcie\re[Noun]
witches	sorcie\res[Noun]
with	avec[Conjunction]
withdraw	retirer[Verb]
withdraw	se retirer [intr]
without	sans
witness	te/moigner[Verb]
witness	te/moin[Noun]
witnesses	te/moins[Noun]
wolverine	le carcajou[Noun]
wolverine	le glouton[Noun]
world	le monde[Noun]
worldwide	mondial[Adjective]
xenophobe	xe/nophobe[Noun]
xenophobia	xénophobie[Noun]
xenophobic	xe/nophobe[Adjective]
xerographic	xe/rographique[Adjective]
xylem	xyle\me[Noun]
xylene	xyle\ne[Noun]
xylophone	xylophone[Noun]
xylophones	xylophone[Noun]
xylophonist	joueur (joueuse) de xylophone[Noun]
xylose	xylose[Noun]
xylotomic	xylotomique[Adjective]
yacht	yacht[Noun]
yachting	yachting[Noun]
yachtsman	yachtsman
yak	yack[Noun]
yam	patate douce[Noun]
yank	tirer d'un coup sec[Verb]
yap	japper[Verb]
yard	yard (3 ft)[Noun]
yardstick	mesure[Noun]
yarn	fil[Noun]
yawn	bâillement[Noun]
yawn	bâiller[Verb]
yawning	ba^illement[Noun]
yeah	ouais!
year	année[Noun]
yearbook	annuaire[Noun]
yearlong	annuel[Noun]
yearly	annuel(le)[Adjective]
yearly	annuellement[Adverb]
yearn	désirer[Verb]
yearning	de/sir[Noun]
yearningly	de/sireux[Adjective]
years	années[Noun]
yeast	levure[Noun]
yell	hurlement[Noun]
yell	hurler[Verb]
yellow	jaune[Adjective]
yellowed	jauni[Adjective]
yellowing	jaunissant[Adjective]
yellowish	jauna^tre[Adjective]
yes	oui
yesterday	hier[Noun]
yet	encore[Adverb]
yew	if[Noun]
yield	produire[Verb]
yoga	yoga[Noun]
yoghurt	yaourt[Noun]
yogurt	yaourt[Noun]
yoke	joug[Noun]
yolk	jaune d'oeuf[Noun]
you	(formal and/or plural) vous[Pronoun]
you	(informal and singular) tu[Pronoun]
young	jeune[Adjective]
younger	plus jeune[Adjective]
youngster	jeune[Noun]
youth	jeunesse[Adjective]
youthful	juvénile[Adjective]
yuppie	yuppie[Noun]
zabaglione	sabayon[Noun]
zany	dingue[Adjective]
zazen	zazen[Noun]
zeal	zèle[Noun]
zealous	zélé(e)[Adjective]
zebra	zèbre[Noun]
zenith	zénith[Noun]
zero	zero[Noun]
zero	zéro
zest	piquant[Noun]
zigzag	zigzaguer[Verb]
zinc	zinc[Noun]
zipper	fermeture[Noun]
zodiac	zodiaque[Noun]
zodiac	zondiaque[Noun]
zone	zone[Noun]
zone	zone[Noun]
zoo	zoo[Noun]
zoology	zoologie[Noun]
zoom	aller en trombe[Verb]
zucchini	courgette[Noun]
Added tests/store-load.tcl.












































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env tclsh

lappend auto_path .
package require Thread

if {[llength $argv] != 3} {
    puts "Usage: $argv0 handle path times"
    puts {
    handle
        A persistent storage handle (see [tsv::array bind] manpage).
    path
        The path to file containing lines in the form of "key<tab>val", where
        key is a single-word and val is everyting else.
    times
        The number of times to reload the data from persistent storage.

    This script reads lines of data from <path> and stores them into the
    persistent storage described by <handle>. Values for duplicate keys are
    handled as a lists. The persistent storage engine is then stress-tested by
    reloading the whole store <times> times.
    }
    exit 1
}

lassign $argv handle path times

### Cleanup
set filename [string range $handle [string first : $handle]+1 end]
file delete -force $filename

### Load and store tab-separated values
tsv::array bind a $handle
set fd [open $path r]
set start [clock milliseconds]
set pairs 0
while {[gets $fd line] >  0} {
    if {[string index $line 0] eq {#}} {
        continue
    }
    set tab [string first {	} $line]
    if {$tab == -1} {
        continue
    }

    set k [string range $line 0 $tab-1]
    set v [string range $line $tab+1 end]

    if {![tsv::exists a $k]} {
        incr pairs
    }

    tsv::lappend a $k $v
}
puts "Stored $pairs pairs in [expr {[clock milliseconds]-$start}] milliseconds"

tsv::array unbind a
tsv::unset a

### Reload
set pairs 0
set iter [time {
    tsv::array bind a $handle
    set pairs [tsv::array size a]
    tsv::array unbind a
    tsv::unset a
} $times]
puts "Loaded $pairs pairs $times times at $iter"

## Dump file stats
puts "File $filename is [file size $filename] bytes long"
Changes to tests/thread.test.
142
143
144
145
146
147
148




















149
150
151
152
153
154
155
test thread-7.0 {thread::exit} {
    ThreadReap
    set tid [thread::create -joinable {thread::exit}]
    set c [thread::join $tid]
    ThreadReap
    set c
} {666}





















test thread-8.0 {thread::exists - true} {
    ThreadReap
    set c [thread::exists [thread::create]]
    ThreadReap
    set c
} {1}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
test thread-7.0 {thread::exit} {
    ThreadReap
    set tid [thread::create -joinable {thread::exit}]
    set c [thread::join $tid]
    ThreadReap
    set c
} {666}

test thread-7.1 {thread::exit - # args} {
    set tid [thread::create]
    catch {thread::send $tid {thread::exit 1 0}} msg
    set msg
} {wrong # args: should be "thread::exit ?status?"}

test thread-7.2 {thread::exit - args} {
    set tid [thread::create]
    catch {thread::send $tid {thread::exit foo}} msg
    set msg
} {expected integer but got "foo"}

test thread-7.3 {thread::exit - status} {
    ThreadReap
    set tid [thread::create -joinable {thread::exit 0}]
    set c [thread::join $tid]
    ThreadReap
    set c
} {0}

test thread-8.0 {thread::exists - true} {
    ThreadReap
    set c [thread::exists [thread::create]]
    ThreadReap
    set c
} {1}
Added tests/tkt-84be1b5a73.test.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package require tcltest
namespace import ::tcltest::*
tcltest::loadTestedCommands
package require Thread

# This test used to segfault before commit f4c95731c0.
test tkt-84be1b5a73 {Ticket 84be1b5a73} -body {
    set t [thread::create]
    set resultvar() {}

    trace add variable resultvar() write {
        unset -nocomplain resultvar()
        list}

    proc errorproc {tid einfo} {}
    thread::errorproc errorproc
    thread::send -async $t {
        error ""
    } resultvar()

    after 1000 {
        set forever 1
    }
    vwait forever
} -returnCodes 0
Changes to tests/tsv.test.


























1










































































































return
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package require tcltest
namespace import ::tcltest::*
tcltest::loadTestedCommands
package require Thread

set backends {gdbm lmdb}

foreach b $backends {
    testConstraint have_$b [expr {$b in [tsv::handlers]}]
}

foreach backend $backends {
    set db "data"
    file delete -force $db
    set ::handle $backend:$db

    proc setup {} {
        tsv::array bind a $::handle
    }
    proc cleanup {} {
        tsv::array unbind a
    }

    test tsv-$backend-1.0 {tsv::array isboud} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::array isbound a
    } -cleanup {
        cleanup
    } -result {1}

    test tsv-$backend-1.1 {tsv::array bind - empty} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::array names b
    } -cleanup {
       cleanup 
    } -result {}

    test tsv-$backend-1.2 {tsv::set} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::set a Key Val
    } -cleanup {
        cleanup
    } -result {Val}

    test tsv-$backend-1.3 {tsv::get - previously set was persisted} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::get a Key
    } -cleanup {
        cleanup
    } -result {Val}

    test tsv-$backend-1.4 {tsv::array names - previously set was persisted} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::array names a
    } -cleanup {
        cleanup
    } -result {Key}

    test tsv-$backend-1.5 {tsv::exists - previously set exists} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::exists a Key
    } -cleanup {
        cleanup
    } -result {1}

    test tsv-$backend-1.6 {tsv::pop - get previously set} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::pop a Key
    } -cleanup {
        cleanup
    } -result {Val}

    test tsv-$backend-1.7 {tsv::exists - popped was removed} \
    -constraints have_$backend \
    -setup {
        setup
    } -body {
        tsv::exists a Key
    } -cleanup {
        cleanup
    } -result {0}

    file delete -force $db
}

::tcltest::cleanupTests
Changes to win/makefile.vc.
Changes to win/nmakehlp.c.
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
	/*
	 * Build a list of substutitions from the first filename
	 */

	sp = fopen(substitutions, "rt");
	if (sp != NULL) {
	    while (fgets(szBuffer, cbBuffer, sp) != NULL) {
		char *ks, *ke, *vs, *ve;
		ks = szBuffer;
		while (ks && *ks && isspace(*ks)) ++ks;
		ke = ks;
		while (ke && *ke && !isspace(*ke)) ++ke;
		vs = ke;
		while (vs && *vs && isspace(*vs)) ++vs;
		ve = vs;
		while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
		*ke = 0, *ve = 0;
		list_insert(&substPtr, ks, vs);
	    }
	    fclose(sp);
	}

	/* debug: dump the list */
#ifdef _DEBUG
	{







|
|








|







602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
	/*
	 * Build a list of substutitions from the first filename
	 */

	sp = fopen(substitutions, "rt");
	if (sp != NULL) {
	    while (fgets(szBuffer, cbBuffer, sp) != NULL) {
		unsigned char *ks, *ke, *vs, *ve;
		ks = (unsigned char*)szBuffer;
		while (ks && *ks && isspace(*ks)) ++ks;
		ke = ks;
		while (ke && *ke && !isspace(*ke)) ++ke;
		vs = ke;
		while (vs && *vs && isspace(*vs)) ++vs;
		ve = vs;
		while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
		*ke = 0, *ve = 0;
		list_insert(&substPtr, (char*)ks, (char*)vs);
	    }
	    fclose(sp);
	}

	/* debug: dump the list */
#ifdef _DEBUG
	{
Changes to win/pkg.vc.
1
2
3
4
5
6
# remember to change configure.in as well when these change
# (then re-autoconf)

PACKAGE_MAJOR	= 2
PACKAGE_MINOR	= 7
PACKAGE_VERSION	= "2.7.2"
|



|
|
1
2
3
4
5
6
# remember to change configure.ac as well when these change
# (then re-autoconf)

PACKAGE_MAJOR	= 2
PACKAGE_MINOR	= 8
PACKAGE_VERSION	= "2.8.0"
Changes to win/rules.vc.