Tcl package Thread source code

Check-in [03912025a6]
Login

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

Overview
Comment:Pulling changes from trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | practcl
Files: files | file ages | folders
SHA3-256:03912025a6495064d38cddcdbe9a2c50d9280d2f5f58ad87d2a21b74be890d5b
User & Date: seandeelywoods 2018-03-19 17:24:00
Context
2018-10-01
20:54
Pulling changes from trunk Leaf check-in: d3a0e02691 user: seandeelywoods tags: practcl
2018-03-19
17:24
Pulling changes from trunk check-in: 03912025a6 user: seandeelywoods tags: practcl
2018-02-26
22:17
Merge thread-2-8-branch check-in: 23a8390cd1 user: jan.nijtmans tags: trunk
2016-06-29
22:21
Type removal check-in: 2a36d0a6c3 user: tne tags: practcl
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added .fossil-settings/crlf-glob.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
win/thread-win.dsp
win/thread-win.dsw
win/makefile.vc
win/pkg.vc
win/rules.vc
win/rules-ext.vc
win/targets.vc

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







>
>
1
2
3
4
5
6
7
win/thread-win.dsp
win/thread-win.dsw
win/makefile.vc
win/pkg.vc
win/rules.vc
win/rules-ext.vc
win/targets.vc

Changes to Makefile.in.

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
...
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
pkgdatadir	= $(datadir)/$(PKG_DIR)
pkglibdir	= $(libdir)/$(PKG_DIR)
pkgincludedir	= $(includedir)/$(PKG_DIR)

top_builddir	= .

INSTALL_OPTIONS	=
INSTALL		= @INSTALL@ ${INSTALL_OPTIONS}
INSTALL_DATA_DIR = @INSTALL_DATA_DIR@
INSTALL_DATA	= @INSTALL_DATA@
INSTALL_PROGRAM	= @INSTALL_PROGRAM@
INSTALL_SCRIPT	= @INSTALL_SCRIPT@
INSTALL_LIBRARY	= @INSTALL_LIBRARY@

PACKAGE_NAME	= @PACKAGE_NAME@
................................................................................

# 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
CONFIG_CLEAN_FILES = Makefile pkgIndex.tcl
CLEANFILES	= @CLEANFILES@

CPPFLAGS	= @CPPFLAGS@
LIBS		= @PKG_LIBS@ @LIBS@
................................................................................
#========================================================================

install-doc: doc
	@$(INSTALL_DATA_DIR) $(DESTDIR)$(mandir)/mann
	@echo "Installing documentation in $(DESTDIR)$(mandir)"
	@list='$(srcdir)/doc/man/*.n'; for i in $$list; do \
	    echo "Installing $$i"; \
	    rm -f $(DESTDIR)$(mandir)/mann/`basename $$i`; \
	    $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \
	done

test: binaries libraries
	$(TCLSH) `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS) \
		-load "package ifneeded ${PACKAGE_NAME} ${PACKAGE_VERSION} \
			[list load `@CYGPATH@ $(PKG_LIB_FILE)` $(PACKAGE_NAME)]"

shell: binaries libraries
	@$(TCLSH) $(SCRIPT)

gdb:
	$(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT)
................................................................................
#========================================================================

#COMPRESS	= tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar
COMPRESS	= tar zcvf $(PKG_DIR).tar.gz $(PKG_DIR)
DIST_ROOT	= /tmp/dist
DIST_DIR	= $(DIST_ROOT)/$(PKG_DIR)




dist-clean:
	rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.*

dist: dist-clean
	$(INSTALL_DATA_DIR) $(DIST_DIR)

	cp -p $(srcdir)/ChangeLog $(srcdir)/README* $(srcdir)/license.terms \
	$(srcdir)/aclocal.m4 $(srcdir)/naviserver.m4 $(srcdir)/configure \


        $(srcdir)/*.in $(srcdir)/configure.ac $(DIST_DIR)/
	chmod 664 $(DIST_DIR)/Makefile.in $(DIST_DIR)/aclocal.m4
	chmod 775 $(DIST_DIR)/configure $(DIST_DIR)/configure.ac

	$(INSTALL_DATA_DIR) $(DIST_DIR)/tclconfig

	cp $(srcdir)/tclconfig/install-sh $(srcdir)/tclconfig/tcl.m4 \
		$(DIST_DIR)/tclconfig/
	chmod 664 $(DIST_DIR)/tclconfig/tcl.m4
	chmod +x $(DIST_DIR)/tclconfig/install-sh

	$(INSTALL_DATA_DIR) $(DIST_DIR)/unix
	cp $(srcdir)/unix/README $(srcdir)/unix/CONFIG \
		$(srcdir)/unix/threadUnix.c $(DIST_DIR)/unix/


	$(INSTALL_DATA_DIR) $(DIST_DIR)/win

	cp $(srcdir)/win/README.txt $(srcdir)/win/CONFIG $(srcdir)/win/thread.rc \
		$(srcdir)/win/threadWin.c $(srcdir)/win/makefile.vc \
		$(srcdir)/win/nmakehlp.c $(srcdir)/win/pkg.vc \

		$(srcdir)/win/rules.vc   $(srcdir)/win/thread_win.dsw \
		$(srcdir)/win/thread_win.dsp $(DIST_DIR)/win/


	$(INSTALL_DATA_DIR) $(DIST_DIR)/tcl
	cp $(srcdir)/tcl/README $(DIST_DIR)/tcl/

	list='tests doc doc/man doc/html generic lib tcl/cmdsrv tcl/phttpd tcl/tpool';\
	for p in $$list; do \
	    if test -d $(srcdir)/$$p ; then \
		$(INSTALL_DATA_DIR) $(DIST_DIR)/$$p; \
		cp -p $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \
	    fi; \
	done

	(cd $(DIST_ROOT); $(COMPRESS);)

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







|







 







|







 







<





|







 







>
>
>





>
|
|
>
>
|
<
|


>
|

<
<


|
|
>


>
|


>
|
|
>


|





|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
...
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
...
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
pkgdatadir	= $(datadir)/$(PKG_DIR)
pkglibdir	= $(libdir)/$(PKG_DIR)
pkgincludedir	= $(includedir)/$(PKG_DIR)

top_builddir	= .

INSTALL_OPTIONS	=
INSTALL		= @INSTALL@ $(INSTALL_OPTIONS)
INSTALL_DATA_DIR = @INSTALL_DATA_DIR@
INSTALL_DATA	= @INSTALL_DATA@
INSTALL_PROGRAM	= @INSTALL_PROGRAM@
INSTALL_SCRIPT	= @INSTALL_SCRIPT@
INSTALL_LIBRARY	= @INSTALL_LIBRARY@

PACKAGE_NAME	= @PACKAGE_NAME@
................................................................................

# 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) -DTCL_NO_DEPRECATED=1

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

CPPFLAGS	= @CPPFLAGS@
LIBS		= @PKG_LIBS@ @LIBS@
................................................................................
#========================================================================

install-doc: doc
	@$(INSTALL_DATA_DIR) $(DESTDIR)$(mandir)/mann
	@echo "Installing documentation in $(DESTDIR)$(mandir)"
	@list='$(srcdir)/doc/man/*.n'; for i in $$list; do \
	    echo "Installing $$i"; \

	    $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \
	done

test: binaries libraries
	$(TCLSH) `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS) \
		-load "package ifneeded $(PACKAGE_NAME) $(PACKAGE_VERSION) \
			[list load `@CYGPATH@ $(PKG_LIB_FILE)` $(PACKAGE_NAME)]"

shell: binaries libraries
	@$(TCLSH) $(SCRIPT)

gdb:
	$(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT)
................................................................................
#========================================================================

#COMPRESS	= tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar
COMPRESS	= tar zcvf $(PKG_DIR).tar.gz $(PKG_DIR)
DIST_ROOT	= /tmp/dist
DIST_DIR	= $(DIST_ROOT)/$(PKG_DIR)

DIST_INSTALL_DATA	= CPPROG='cp -p' $(INSTALL) -m 644
DIST_INSTALL_SCRIPT	= CPPROG='cp -p' $(INSTALL) -m 755

dist-clean:
	rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.*

dist: dist-clean
	$(INSTALL_DATA_DIR) $(DIST_DIR)
	$(DIST_INSTALL_DATA) $(srcdir)/license.terms \
		$(srcdir)/ChangeLog $(srcdir)/README \
		$(srcdir)/aclocal.m4 $(srcdir)/configure.ac \
		$(srcdir)/Makefile.in $(srcdir)/pkgIndex.tcl.in \
		$(srcdir)/naviserver.m4 \
		$(DIST_DIR)/

	$(DIST_INSTALL_SCRIPT) $(srcdir)/configure $(DIST_DIR)/

	$(INSTALL_DATA_DIR) $(DIST_DIR)/tclconfig
	$(DIST_INSTALL_DATA) $(srcdir)/tclconfig/README.txt \
		$(srcdir)/tclconfig/tcl.m4 $(srcdir)/tclconfig/install-sh \
		$(DIST_DIR)/tclconfig/



	$(INSTALL_DATA_DIR) $(DIST_DIR)/unix
	$(DIST_INSTALL_DATA) $(srcdir)/unix/README $(srcdir)/unix/CONFIG \
		$(srcdir)/unix/threadUnix.c \
		$(DIST_DIR)/unix/

	$(INSTALL_DATA_DIR) $(DIST_DIR)/win
	$(DIST_INSTALL_DATA) \
		$(srcdir)/win/README.txt $(srcdir)/win/CONFIG $(srcdir)/win/thread.rc \
		$(srcdir)/win/threadWin.c $(srcdir)/win/makefile.vc \
		$(srcdir)/win/nmakehlp.c $(srcdir)/win/pkg.vc \
		$(srcdir)/win/targets.vc $(srcdir)/win/rules-ext.vc \
		$(srcdir)/win/rules.vc $(srcdir)/win/thread_win.dsw \
		$(srcdir)/win/thread_win.dsp \
		$(DIST_DIR)/win/

	$(INSTALL_DATA_DIR) $(DIST_DIR)/tcl
	$(DIST_INSTALL_DATA) $(srcdir)/tcl/README $(DIST_DIR)/tcl/

	list='tests doc doc/man doc/html generic lib tcl/cmdsrv tcl/phttpd tcl/tpool';\
	for p in $$list; do \
	    if test -d $(srcdir)/$$p ; then \
		$(INSTALL_DATA_DIR) $(DIST_DIR)/$$p; \
		$(DIST_INSTALL_DATA) $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \
	    fi; \
	done

	(cd $(DIST_ROOT); $(COMPRESS);)

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

Changes to README.

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

WHAT IS THIS ?
==============

This is the source distribution of the Tcl Thread extension. 
You can use this extension to gain script-level access to Tcl 
threading capabilities.
The extension can be used with Tcl cores starting from Tcl8.4 and later.
Also, this extension supports, i.e. can be used as a loadable module of,
AOLserver 4.x series of the highly-scalable web server from America Online. 

You need to have your Tcl core compiled with "--enable-threads" in order
to turn on internal directives supporting thread-specific details of the
Tcl API. The extension will not load in an Tcl shell built w/o thread
support. 

This extension is a freely available open source package. You can do 
virtually anything you like with it, such as modifying it, redistributing
it, and selling it either in whole or in part.  See the "license.terms"
file in the top-level distribution directory for complete information.


HOW TO COMPILE ?
================

Only Unix-like and Windows platforms are supported at the moment. Depending
on your platform (Unix-like or Windows) go to the appropriate directory 
(unix or win) and start with the README file. Macintosh platform is supported
with the Mac OS X only. The Mac OS 9 (and previous) are not supported.


WHERE IS THE DOCUMENTATION ?
============================

Documentation in Unix man and standard HTML format is available in the
doc/man and doc/html directories respectively. 
Currently, documentation is in reference-style only. The tutorial-style
documentation will be provided with future releases of the extension.
That is, if I ever get time to do that. Everybody is more than welcome
to jump in and help with the docs.


HOW TO GET SUPPORT ?




|
|



|




|

|









|








|







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

WHAT IS THIS ?
==============

This is the source distribution of the Tcl Thread extension.
You can use this extension to gain script-level access to Tcl
threading capabilities.
The extension can be used with Tcl cores starting from Tcl8.4 and later.
Also, this extension supports, i.e. can be used as a loadable module of,
AOLserver 4.x series of the highly-scalable web server from America Online.

You need to have your Tcl core compiled with "--enable-threads" in order
to turn on internal directives supporting thread-specific details of the
Tcl API. The extension will not load in an Tcl shell built w/o thread
support.

This extension is a freely available open source package. You can do
virtually anything you like with it, such as modifying it, redistributing
it, and selling it either in whole or in part.  See the "license.terms"
file in the top-level distribution directory for complete information.


HOW TO COMPILE ?
================

Only Unix-like and Windows platforms are supported at the moment. Depending
on your platform (Unix-like or Windows) go to the appropriate directory
(unix or win) and start with the README file. Macintosh platform is supported
with the Mac OS X only. The Mac OS 9 (and previous) are not supported.


WHERE IS THE DOCUMENTATION ?
============================

Documentation in Unix man and standard HTML format is available in the
doc/man and doc/html directories respectively.
Currently, documentation is in reference-style only. The tutorial-style
documentation will be provided with future releases of the extension.
That is, if I ever get time to do that. Everybody is more than welcome
to jump in and help with the docs.


HOW TO GET SUPPORT ?

Changes to configure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
130
131
132
133
134
135
136

























137
138
139
140
141
142
143
...
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
...
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
...
324
325
326
327
328
329
330








331
332
333
334
335
336
337
...
445
446
447
448
449
450
451




452
453
454
455
456
457
458
...
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
...
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
...
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
...
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
....
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
....
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
....
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
....
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
....
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
....
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
....
1816
1817
1818
1819
1820
1821
1822



































































1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
....
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
....
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
....
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
....
2294
2295
2296
2297
2298
2299
2300

















































2301
2302
2303
2304
2305
2306
2307
....
2313
2314
2315
2316
2317
2318
2319

2320
2321
2322
2323
2324
2325
2326
....
2448
2449
2450
2451
2452
2453
2454

2455
2456
2457
2458
2459
2460
2461
2462
2463
....
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
....
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
....
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
....
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
....
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
....
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
....
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
....
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
....
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
....
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
....
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
....
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
....
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
....
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
....
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
....
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
....
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
....
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
....
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
....
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
....
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
....
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
....
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
....
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
....
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
....
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
....
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117

5118
5119
5120
5121
5122
5123
5124
....
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
....
5612
5613
5614
5615
5616
5617
5618


5619
5620


5621
5622
5623
5624
5625
5626
5627
....
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
....
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
....
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
....
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
....
6797
6798
6799
6800
6801
6802
6803


6804
6805


6806
6807
6808
6809
6810
6811
6812
....
6824
6825
6826
6827
6828
6829
6830


6831
6832


6833
6834
6835
6836
6837
6838
6839
....
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
....
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
....
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
....
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
....
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
....
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
....
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
....
9014
9015
9016
9017
9018
9019
9020


9021
9022
9023
9024
9025
9026
9027
....
9502
9503
9504
9505
9506
9507
9508
9509
9510
9511
9512
9513
9514
9515
9516
9517
9518
9519
9520
9521
9522
9523
9524
9525
....
9571
9572
9573
9574
9575
9576
9577
9578

9579
9580

9581
9582
9583

9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605
9606
....
9613
9614
9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
....
9666
9667
9668
9669
9670
9671
9672
9673
9674
9675
9676
9677
9678
9679
9680
9681
9682
9683
9684
....
9747
9748
9749
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759
9760
9761
....
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793
9794
....
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
9998
9999
10000
10001
10002
.....
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
.....
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
.....
10227
10228
10229
10230
10231
10232
10233

#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.68 for thread 2.8.0.
#
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
# Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
................................................................................
export LC_ALL
LANGUAGE=C
export LANGUAGE

# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH


























if test "x$CONFIG_SHELL" = x; then
  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
  emulate sh
  NULLCMD=:
  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
  # is contrary to our usage.  Disable this feature.
  alias -g '\${1+\"\$@\"}'='\"\$@\"'
................................................................................
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :

else
  exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1"

  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
test \$(( 1 + 1 )) = 2 || exit 1"
  if (eval "$as_required") 2>/dev/null; then :
  as_have_required=yes
................................................................................
	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
  CONFIG_SHELL=$SHELL as_have_required=yes
fi; }
IFS=$as_save_IFS


      if test "x$CONFIG_SHELL" != x; then :

  # We cannot yet assume a decent shell, so we have to provide a
	# neutralization value for shells without unset; and this also
	# works around shells that cannot unset nonexistent variables.
	# Preserve -v and -x to the replacement shell.
	BASH_ENV=/dev/null
	ENV=/dev/null
	(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
	export CONFIG_SHELL
	case $- in # ((((
	  *v*x* | *x*v* ) as_opts=-vx ;;
	  *v* ) as_opts=-v ;;
	  *x* ) as_opts=-x ;;
	  * ) as_opts= ;;
	esac
	exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}




fi

    if test x$as_have_required = xno; then :
  $as_echo "$0: This script requires a shell more modern than all"
  $as_echo "$0: the shells that I found on your system."
  if test x${ZSH_VERSION+set} = xset ; then
    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
................................................................................
      test -d "$as_dir" && break
    done
    test -z "$as_dirs" || eval "mkdir $as_dirs"
  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"


} # as_fn_mkdir_p








# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
................................................................................
      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
      t loop
      s/-\n.*//
    ' >$as_me.lineno &&
  chmod +x "$as_me.lineno" ||
    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }





  # Don't try to exec as it changes $[0], causing all sort of problems
  # (the dirname of $[0] is not the place where we might find the
  # original and so on.  Autoconf is especially sensitive to this).
  . "./$as_me.lineno"
  # Exit status is that of the last command.
  exit
}
................................................................................
fi
if (echo >conf$$.file) 2>/dev/null; then
  if ln -s conf$$.file conf$$ 2>/dev/null; then
    as_ln_s='ln -s'
    # ... but there are two gotchas:
    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
    # In both cases, we have to default to `cp -p'.
    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
      as_ln_s='cp -p'
  elif ln conf$$.file conf$$ 2>/dev/null; then
    as_ln_s=ln
  else
    as_ln_s='cp -p'
  fi
else
  as_ln_s='cp -p'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null

if mkdir -p . 2>/dev/null; then
  as_mkdir_p='mkdir -p "$as_dir"'
else
  test -d ./-p && rmdir ./-p
  as_mkdir_p=false
fi

if test -x / >/dev/null 2>&1; then
  as_test_x='test -x'
else
  if ls -dL / >/dev/null 2>&1; then
    as_ls_L_option=L
  else
    as_ls_L_option=
  fi
  as_test_x='
    eval sh -c '\''
      if test -d "$1"; then
	test -d "$1/.";
      else
	case $1 in #(
	-*)set "./$1";;
	esac;
	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
	???[sx]*):;;*)false;;esac;fi
    '\'' sh
  '
fi
as_executable_p=$as_test_x

# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"

# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"

................................................................................
subdirs=
MFLAGS=
MAKEFLAGS=

# Identity of this package.
PACKAGE_NAME='thread'
PACKAGE_TARNAME='thread'
PACKAGE_VERSION='2.8.0'
PACKAGE_STRING='thread 2.8.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''

# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
................................................................................
AR
STUBS_BUILD
SHARED_BUILD
TCL_THREADS
TCL_INCLUDES
PKG_OBJECTS
PKG_SOURCES
MATH_LIBS
EGREP
GREP
RANLIB
SET_MAKE
INSTALL_LIBRARY
INSTALL_SCRIPT
INSTALL_PROGRAM
INSTALL_DATA
INSTALL_DATA_DIR
INSTALL
CPP
TCL_SHLIB_LD_LIBS
TCL_LD_FLAGS
TCL_EXTRA_CFLAGS
TCL_DEFS
TCL_LIBS
CLEANFILES
................................................................................
TCL_LIB_FILE
TCL_SRC_DIR
TCL_BIN_DIR
TCL_PATCH_LEVEL
TCL_VERSION
CONFIG_CLEAN_FILES
LN_S






PKG_CFLAGS
PKG_LIBS
PKG_INCLUDES
PKG_HEADERS
PKG_TCL_SOURCES
PKG_STUB_OBJECTS
PKG_STUB_SOURCES
PKG_STUB_LIB_FILE
PKG_LIB_FILE
EXEEXT
CYGPATH

target_alias
host_alias
build_alias
LIBS
ECHO_T
ECHO_N
ECHO_C
................................................................................
host=$host_alias
target=$target_alias

# FIXME: To remove some day.
if test "x$host_alias" != x; then
  if test "x$build_alias" = x; then
    cross_compiling=maybe
    $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
    If a cross compiler is detected then cross compile mode will be used" >&2
  elif test "x$build_alias" != "x$host_alias"; then
    cross_compiling=yes
  fi
fi

ac_tool_prefix=
test -n "$host_alias" && ac_tool_prefix=$host_alias-
................................................................................
#
# Report the --help message.
#
if test "$ac_init_help" = "long"; then
  # Omit some internal or obsolete options to make the list less imposing.
  # This message is too long to be a string in the A/UX 3.1 sh.
  cat <<_ACEOF
\`configure' configures thread 2.8.0 to adapt to many kinds of systems.

Usage: $0 [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.
................................................................................

  cat <<\_ACEOF
_ACEOF
fi

if test -n "$ac_init_help"; then
  case $ac_init_help in
     short | recursive ) echo "Configuration of thread 2.8.0:";;
   esac
  cat <<\_ACEOF

Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-threads        build with threads
  --enable-shared         build and link with shared libraries (default: on)
  --enable-stubs          build and link with stub libraries. Always true for
                          shared builds (default: on)
  --enable-64bit          enable 64bit support (default: off)
  --enable-64bit-vis      enable 64bit Sparc VIS support (default: off)
  --disable-rpath         disable rpath support (default: on)
  --enable-wince          enable Win/CE support (where applicable)
................................................................................
    cd "$ac_pwd" || { ac_status=$?; break; }
  done
fi

test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
  cat <<\_ACEOF
thread configure 2.8.0
generated by GNU Autoconf 2.68

Copyright (C) 2010 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
  exit
fi

## ------------------------ ##
................................................................................
  fi
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } && {
	 test -z "$ac_c_werror_flag" ||
	 test ! -s conftest.err
       } && test -s conftest$ac_exeext && {
	 test "$cross_compiling" = yes ||
	 $as_test_x conftest$ac_exeext
       }; then :
  ac_retval=0
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

	ac_retval=1
................................................................................
  # left behind by Apple's compiler.  We do this before executing the actions.
  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
  as_fn_set_status $ac_retval

} # ac_fn_c_try_link

# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
#define $2 innocuous_$2

/* System header to define __stub macros and hopefully few prototypes,
    which can conflict with char $2 (); below.
    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
    <limits.h> exists even on freestanding compilers.  */

#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif

#undef $2

/* Override any GCC internal prototype to avoid an error.
   Use char because int might match the return type of a GCC
   builtin and then its argument prototype would still apply.  */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
    to always fail with ENOSYS.  Some functions are actually named
    something starting with __ and the normal name is an alias.  */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif

int
main ()
{
return $2 ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  eval "$3=yes"
else
  eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno

} # ac_fn_c_check_func

# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists, giving a warning if it cannot be compiled using
# the include files in INCLUDES and setting the cache variable VAR
# accordingly.
ac_fn_c_check_header_mongrel ()
{
................................................................................
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno

} # ac_fn_c_check_header_mongrel



































































cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.

It was created by thread $as_me 2.8.0, which was
generated by GNU Autoconf 2.68.  Invocation command line was

  $ $0 $@

_ACEOF
exec 5>>config.log
{
cat <<_ASUNAME
................................................................................
    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
    sed 's/^/| /' "$ac_site_file" >&5
    . "$ac_site_file" \
      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
See \`config.log' for more details" "$LINENO" 5 ; }
  fi
done

if test -r "$cache_file"; then
  # Some versions of bash will fail to source /dev/null (special files
  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
................................................................................
#--------------------------------------------------------------------
# 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.
#--------------------------------------------------------------------


    # TEA extensions pass this us the version of TEA they think they
    # are compatible with.
    TEA_VERSION="3.10"
    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for correct TEA configuration" >&5
$as_echo_n "checking for correct TEA configuration... " >&6; }
    if test x"${PACKAGE_NAME}" = x ; then
	as_fn_error $? "
The PACKAGE_NAME variable must be defined by your TEA configure.ac" "$LINENO" 5
    fi
    if test x"3.10" = x ; then
	as_fn_error $? "
TEA version not specified." "$LINENO" 5
    elif test "3.10" != "${TEA_VERSION}" ; then
	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: warning: requested TEA version \"3.10\", have \"${TEA_VERSION}\"" >&5
$as_echo "warning: requested TEA version \"3.10\", have \"${TEA_VERSION}\"" >&6; }
    else
	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (TEA ${TEA_VERSION})" >&5
$as_echo "ok (TEA ${TEA_VERSION})" >&6; }
    fi

    # If the user did not set CFLAGS, set it now to keep macros
    # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
    if test "${CFLAGS+set}" != "set" ; then
	CFLAGS=""
    fi


    case "`uname -s`" in
	*win32*|*WIN32*|*MINGW32_*)
	    # Extract the first word of "cygpath", so it can be a program name with args.
set dummy cygpath; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CYGPATH="cygpath -m"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................

























































{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
$as_echo_n "checking whether ln -s works... " >&6; }
LN_S=$as_ln_s
if test "$LN_S" = "ln -s"; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
................................................................................
CONFIG_CLEAN_FILES=
if test ! -d $srcdir/tclconfig ; then
    if test -d $srcdir/../tclconfig ; then
        $LN_S $srcdir/../tclconfig tclconfig
        CONFIG_CLEAN_FILES=tclconfig
    fi
fi


ac_aux_dir=
for ac_dir in tclconfig "$srcdir"/tclconfig; do
  if test -f "$ac_dir/install-sh"; then
    ac_aux_dir=$ac_dir
    ac_install_sh="$ac_aux_dir/install-sh -c"
    break
................................................................................
	    fi

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

			`ls -d /usr/local/lib 2>/dev/null` \
			`ls -d /usr/contrib/lib 2>/dev/null` \
			`ls -d /usr/lib 2>/dev/null` \
			`ls -d /usr/lib64 2>/dev/null` \
			`ls -d /usr/lib/tcl8.6 2>/dev/null` \
			`ls -d /usr/lib/tcl8.5 2>/dev/null` \
			; do
		    if test -f "$i/tclConfig.sh" ; then
			ac_cv_c_tclconfig="`(cd $i; pwd)`"
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CC="${ac_tool_prefix}gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_CC="gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CC="${ac_tool_prefix}cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
  ac_prog_rejected=no
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
       ac_prog_rejected=yes
       continue
     fi
    ac_cv_prog_CC="cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_CC="$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................

fi


test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5 ; }

# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
  { { ac_try="$ac_compiler $ac_option >&5"
................................................................................
$as_echo "no" >&6; }
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
See \`config.log' for more details" "$LINENO" 5 ; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
$as_echo_n "checking for C compiler default output file name... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
................................................................................
    * ) break;;
  esac
done
else
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details" "$LINENO" 5 ; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
$as_echo "$ac_cv_exeext" >&6; }

rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
................................................................................
    if test "$cross_compiling" = maybe; then
	cross_compiling=yes
    else
	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
See \`config.log' for more details" "$LINENO" 5 ; }
    fi
  fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
$as_echo "$cross_compiling" >&6; }

rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
................................................................................
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
See \`config.log' for more details" "$LINENO" 5 ; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
$as_echo "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
................................................................................
else
  ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
     char **p;
     int i;
{
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CYGPATH="cygpath -m"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CC="${ac_tool_prefix}gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_CC="gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CC="${ac_tool_prefix}cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
  ac_prog_rejected=no
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
       ac_prog_rejected=yes
       continue
     fi
    ac_cv_prog_CC="cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_CC="$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................

fi


test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5 ; }

# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
  { { ac_try="$ac_compiler $ac_option >&5"
................................................................................
else
  ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
     char **p;
     int i;
{
................................................................................
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :

else
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
See \`config.log' for more details" "$LINENO" 5 ; }
fi

ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu


    INSTALL='$(SHELL) $(srcdir)/tclconfig/install-sh -c'
    INSTALL_DATA_DIR='${INSTALL} -d -m 755'
    INSTALL_DATA='${INSTALL} -m 644'
    INSTALL_PROGRAM='${INSTALL}'
    INSTALL_SCRIPT='${INSTALL}'
    INSTALL_LIBRARY='${INSTALL_DATA}'








    #--------------------------------------------------------------------
    # Checks to see if the make program sets the $MAKE variable.
    #--------------------------------------------------------------------

    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_RANLIB="ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_prog in grep ggrep; do
    for ac_exec_ext in '' $ac_executable_extensions; do
      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
# Check for GNU ac_path_GREP and select it if it is found.
  # Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
*GNU*)
  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
*)
  ac_count=0
................................................................................
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_prog in egrep; do
    for ac_exec_ext in '' $ac_executable_extensions; do
      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
# Check for GNU ac_path_EGREP and select it if it is found.
  # Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
*GNU*)
  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
*)
  ac_count=0
................................................................................
   universal)

$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h

     ;; #(
   *)
     as_fn_error $? "unknown endianness
 presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5  ;;
 esac

    if test "${TEA_PLATFORM}" = "unix" ; then

    #--------------------------------------------------------------------
    # On a few very rare systems, all of the libm.a stuff is
    # already in libc.a.  Set compiler flags accordingly.
    # Also, Linux requires the "ieee" library for math to work
    # right (and it must appear before "-lm").
    #--------------------------------------------------------------------

    ac_fn_c_check_func "$LINENO" "sin" "ac_cv_func_sin"
if test "x$ac_cv_func_sin" = xyes; then :
  MATH_LIBS=""
else
  MATH_LIBS="-lm"
fi

    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lieee" >&5
$as_echo_n "checking for main in -lieee... " >&6; }
if ${ac_cv_lib_ieee_main+:} false; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lieee  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_ieee_main=yes
else
  ac_cv_lib_ieee_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ieee_main" >&5
$as_echo "$ac_cv_lib_ieee_main" >&6; }
if test "x$ac_cv_lib_ieee_main" = xyes; then :
  MATH_LIBS="-lieee $MATH_LIBS"
fi


    #--------------------------------------------------------------------
    # Interactive UNIX requires -linet instead of -lsocket, plus it
    # needs net/errno.h to define the socket-related error codes.
    #--------------------------------------------------------------------

    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -linet" >&5
$as_echo_n "checking for main in -linet... " >&6; }
if ${ac_cv_lib_inet_main+:} false; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-linet  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_inet_main=yes
else
  ac_cv_lib_inet_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_main" >&5
$as_echo "$ac_cv_lib_inet_main" >&6; }
if test "x$ac_cv_lib_inet_main" = xyes; then :
  LIBS="$LIBS -linet"
fi

    ac_fn_c_check_header_mongrel "$LINENO" "net/errno.h" "ac_cv_header_net_errno_h" "$ac_includes_default"
if test "x$ac_cv_header_net_errno_h" = xyes; then :


$as_echo "#define HAVE_NET_ERRNO_H 1" >>confdefs.h

fi



    #--------------------------------------------------------------------
    #	Check for the existence of the -lsocket and -lnsl libraries.
    #	The order here is important, so that they end up in the right
    #	order in the command line generated by make.  Here are some
    #	special considerations:
    #	1. Use "connect" and "accept" to check for -lsocket, and
    #	   "gethostbyname" to check for -lnsl.
    #	2. Use each function name only once:  can't redo a check because
    #	   autoconf caches the results of the last check and won't redo it.
    #	3. Use -lnsl and -lsocket only if they supply procedures that
    #	   aren't already present in the normal libraries.  This is because
    #	   IRIX 5.2 has libraries, but they aren't needed and they're
    #	   bogus:  they goof up name resolution if used.
    #	4. On some SVR4 systems, can't use -lsocket without -lnsl too.
    #	   To get around this problem, check for both libraries together
    #	   if -lsocket doesn't work by itself.
    #--------------------------------------------------------------------

    tcl_checkBoth=0
    ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect"
if test "x$ac_cv_func_connect" = xyes; then :
  tcl_checkSocket=0
else
  tcl_checkSocket=1
fi

    if test "$tcl_checkSocket" = 1; then
	ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt"
if test "x$ac_cv_func_setsockopt" = xyes; then :

else
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5
$as_echo_n "checking for setsockopt in -lsocket... " >&6; }
if ${ac_cv_lib_socket_setsockopt+:} false; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lsocket  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

/* Override any GCC internal prototype to avoid an error.
   Use char because int might match the return type of a GCC
   builtin and then its argument prototype would still apply.  */
#ifdef __cplusplus
extern "C"
#endif
char setsockopt ();
int
main ()
{
return setsockopt ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_socket_setsockopt=yes
else
  ac_cv_lib_socket_setsockopt=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5
$as_echo "$ac_cv_lib_socket_setsockopt" >&6; }
if test "x$ac_cv_lib_socket_setsockopt" = xyes; then :
  LIBS="$LIBS -lsocket"
else
  tcl_checkBoth=1
fi

fi

    fi
    if test "$tcl_checkBoth" = 1; then
	tk_oldLibs=$LIBS
	LIBS="$LIBS -lsocket -lnsl"
	ac_fn_c_check_func "$LINENO" "accept" "ac_cv_func_accept"
if test "x$ac_cv_func_accept" = xyes; then :
  tcl_checkNsl=0
else
  LIBS=$tk_oldLibs
fi

    fi
    ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname"
if test "x$ac_cv_func_gethostbyname" = xyes; then :

else
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5
$as_echo_n "checking for gethostbyname in -lnsl... " >&6; }
if ${ac_cv_lib_nsl_gethostbyname+:} false; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lnsl  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

/* Override any GCC internal prototype to avoid an error.
   Use char because int might match the return type of a GCC
   builtin and then its argument prototype would still apply.  */
#ifdef __cplusplus
extern "C"
#endif
char gethostbyname ();
int
main ()
{
return gethostbyname ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_nsl_gethostbyname=yes
else
  ac_cv_lib_nsl_gethostbyname=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5
$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; }
if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then :
  LIBS="$LIBS -lnsl"
fi

fi


    # TEA specific: Don't perform the eval of the libraries here because
    # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS

    TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'





    { $as_echo "$as_me:${as_lineno-$LINENO}: checking dirent.h" >&5
$as_echo_n "checking dirent.h... " >&6; }
if ${tcl_cv_dirent_h+:} false; then :
  $as_echo_n "(cached) " >&6
else

................................................................................
    ac_fn_c_check_header_mongrel "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default"
if test "x$ac_cv_header_errno_h" = xyes; then :

else

$as_echo "#define NO_ERRNO_H 1" >>confdefs.h

fi


    ac_fn_c_check_header_mongrel "$LINENO" "float.h" "ac_cv_header_float_h" "$ac_includes_default"
if test "x$ac_cv_header_float_h" = xyes; then :

else

$as_echo "#define NO_FLOAT_H 1" >>confdefs.h

fi


    ac_fn_c_check_header_mongrel "$LINENO" "values.h" "ac_cv_header_values_h" "$ac_includes_default"
if test "x$ac_cv_header_values_h" = xyes; then :

else
................................................................................
    done



    vars="${GDBM_LIBS} ${LMDB_LIBS} ${NS_LIBS}"
    for i in $vars; do
	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then


	    # Convert foo.lib to -lfoo for GCC.  No-op if not *.lib
	    i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'`


	fi
	PKG_LIBS="$PKG_LIBS $i"
    done



    PKG_CFLAGS="$PKG_CFLAGS ${GDBM_CFLAGS} ${LMDB_CFLAGS}"
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_RANLIB="ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_AR="${ac_tool_prefix}ar"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_AR="ar"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
	        case "x`echo \${VisualStudioVersion}`" in
	            x1[4-9]*)
		        lflags="${lflags} -nodefaultlib:libucrt.lib"

    vars="ucrt.lib"
    for i in $vars; do
	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then


	    # Convert foo.lib to -lfoo for GCC.  No-op if not *.lib
	    i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'`


	fi
	PKG_LIBS="$PKG_LIBS $i"
    done


	            ;;
	            *)
................................................................................
		    CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
		    # Avoid 'unresolved external symbol __security_cookie'
		    # errors, c.f. http://support.microsoft.com/?id=894573

    vars="bufferoverflowU.lib"
    for i in $vars; do
	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then


	    # Convert foo.lib to -lfoo for GCC.  No-op if not *.lib
	    i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'`


	fi
	PKG_LIBS="$PKG_LIBS $i"
    done


		elif test "$doWince" != "no" ; then
		    CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_RC="${ac_tool_prefix}windres"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
    ac_cv_prog_ac_ct_RC="windres"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
  RC="$ac_cv_prog_RC"
fi

		CFLAGS_DEBUG="-g"
		CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
		SHLIB_LD='${CC} -shared'
		UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
  	PRACTCL_UNSHARED_LIB_SUFFIX='.a'

		LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
		LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"

		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cross-compile version of gcc" >&5
$as_echo_n "checking for cross-compile version of gcc... " >&6; }
if ${ac_cv_cross+:} false; then :
................................................................................
	    CC_SEARCH_FLAGS=""
	    LD_SEARCH_FLAGS=""
	    ;;
	Haiku*)
	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
	    SHLIB_CFLAGS="-fPIC"
	    SHLIB_SUFFIX=".so"
	    SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}'
	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnetwork" >&5
$as_echo_n "checking for inet_ntoa in -lnetwork... " >&6; }
if ${ac_cv_lib_network_inet_ntoa+:} false; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lnetwork  $LIBS"
................................................................................
	    SHLIB_CFLAGS="-fPIC"
	    SHLIB_SUFFIX=".so"

	    # TEA specific:
	    CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"

	    # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
	    SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}'
	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
	    if test $doRpath = yes; then :

		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
	    if test "`uname -m`" = "alpha"; then :
................................................................................
		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
		LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    ;;
	OpenBSD-*)
	    arch=`arch -s`
	    case "$arch" in
	    vax)
		SHLIB_SUFFIX=""
		SHARED_LIB_SUFFIX=""
		LDFLAGS=""
		;;
	    *)
		case "$arch" in
		alpha|sparc64)
		    SHLIB_CFLAGS="-fPIC"
		    ;;
		*)
		    SHLIB_CFLAGS="-fpic"
		    ;;
		esac
		SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
		SHLIB_SUFFIX=".so"
		if test $doRpath = yes; then :

		    CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
		SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so${SHLIB_VERSION}'
		LDFLAGS="-Wl,-export-dynamic"
		;;
	    esac
	    case "$arch" in
	    vax)
		CFLAGS_OPTIMIZE="-O1"
		;;
	    *)
		CFLAGS_OPTIMIZE="-O2"
		;;
	    esac
	    if test "${TCL_THREADS}" = "1"; then :

		# On OpenBSD:	Compile with -pthread
		#		Don't link with -lpthread
		LIBS=`echo $LIBS | sed s/-lpthread//`
		CFLAGS="$CFLAGS -pthread"

................................................................................
	    # OpenBSD doesn't do version numbers with dots.
	    UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
	    TCL_LIB_VERSIONS_OK=nodots
	    ;;
	NetBSD-*)
	    # NetBSD has ELF and can use 'cc -shared' to build shared libs
	    SHLIB_CFLAGS="-fPIC"
	    SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
	    SHLIB_SUFFIX=".so"
	    LDFLAGS="$LDFLAGS -export-dynamic"
	    if test $doRpath = yes; then :

		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
................................................................................
	    fi
		else
      if test -f "${TCL_BIN_DIR}/Makefile" ; then
        # tclConfig.sh is in Tcl build directory
        if test "${TEA_PLATFORM}" = "windows"; then
          if test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"


          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}s${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}s${EXEEXT}"
          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}t${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}t${EXEEXT}"
          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}st${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}st${EXEEXT}"
          fi
................................................................................
fi
if (echo >conf$$.file) 2>/dev/null; then
  if ln -s conf$$.file conf$$ 2>/dev/null; then
    as_ln_s='ln -s'
    # ... but there are two gotchas:
    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
    # In both cases, we have to default to `cp -p'.
    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
      as_ln_s='cp -p'
  elif ln conf$$.file conf$$ 2>/dev/null; then
    as_ln_s=ln
  else
    as_ln_s='cp -p'
  fi
else
  as_ln_s='cp -p'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null


# as_fn_mkdir_p
# -------------
................................................................................
if mkdir -p . 2>/dev/null; then
  as_mkdir_p='mkdir -p "$as_dir"'
else
  test -d ./-p && rmdir ./-p
  as_mkdir_p=false
fi

if test -x / >/dev/null 2>&1; then

  as_test_x='test -x'
else

  if ls -dL / >/dev/null 2>&1; then
    as_ls_L_option=L
  else

    as_ls_L_option=
  fi
  as_test_x='
    eval sh -c '\''
      if test -d "$1"; then
	test -d "$1/.";
      else
	case $1 in #(
	-*)set "./$1";;
	esac;
	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
	???[sx]*):;;*)false;;esac;fi
    '\'' sh
  '
fi
as_executable_p=$as_test_x

# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"

# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"

................................................................................
test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1

cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# Save the log message, to keep $0 and so on meaningful, and to
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by thread $as_me 2.8.0, which was
generated by GNU Autoconf 2.68.  Invocation command line was

  CONFIG_FILES    = $CONFIG_FILES
  CONFIG_HEADERS  = $CONFIG_HEADERS
  CONFIG_LINKS    = $CONFIG_LINKS
  CONFIG_COMMANDS = $CONFIG_COMMANDS
  $ $0 $@

................................................................................

Report bugs to the package provider."

_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
thread config.status 2.8.0
configured by $0, generated by GNU Autoconf 2.68,
  with options \\"\$ac_cs_config\\"

Copyright (C) 2010 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."

ac_pwd='$ac_pwd'
srcdir='$srcdir'
test -n "\$AWK" || AWK=awk
_ACEOF
................................................................................
  exec 6>/dev/null
  ac_configure_extra_args="$ac_configure_extra_args --silent"
fi

_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
  shift
  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
  CONFIG_SHELL='$SHELL'
  export CONFIG_SHELL
  exec "\$@"
fi

................................................................................
for ac_config_target in $ac_config_targets
do
  case $ac_config_target in
    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
    "pkgIndex.tcl") CONFIG_FILES="$CONFIG_FILES pkgIndex.tcl" ;;
    "config.tcl") CONFIG_FILES="$CONFIG_FILES config.tcl:tclconfig/config.tcl.in" ;;

  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;;
  esac
done


# If the user did not use the arguments to specify the items to instantiate,
# then the envvar interface is used.  Set only those that are not.
# We use the long form for the default assignment because of an extremely
................................................................................
for ac_tag
do
  case $ac_tag in
  :[FHLC]) ac_mode=$ac_tag; continue;;
  esac
  case $ac_mode$ac_tag in
  :[FHL]*:*);;
  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;;
  :[FH]-) ac_tag=-:-;;
  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
  esac
  ac_save_IFS=$IFS
  IFS=:
  set x $ac_tag
  IFS=$ac_save_IFS
................................................................................
	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
	 # because $ac_f cannot contain `:'.
	 test -f "$ac_f" ||
	   case $ac_f in
	   [\\/$]*) false;;
	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
	   esac ||
	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;;
      esac
      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
      as_fn_append ac_file_inputs " '$ac_f'"
    done

    # Let's still pretend it is `configure' which instantiates (i.e., don't
    # use $as_me), people would be surprised to read:
................................................................................
       ac_sed_conf_input=`$as_echo "$configure_input" |
       sed 's/[\\\\&|]/\\\\&/g'`;; #(
    *) ac_sed_conf_input=$configure_input;;
    esac

    case $ac_tag in
    *:-:* | *:-) cat >"$ac_tmp/stdin" \
      || as_fn_error $? "could not create $ac_file" "$LINENO" 5  ;;
    esac
    ;;
  esac

  ac_dir=`$as_dirname -- "$ac_file" ||
$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
	 X"$ac_file" : 'X\(//\)[^/]' \| \
................................................................................
  # would make configure fail if this is the last instruction.
  $ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi



|


|
<
<







 







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







 







|
>







 







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







 







>
>
>
>
>
>
>
>







 







>
>
>
>







 







|

|



|


|











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







 







|
|







 







<




<
<
<
<
<
<







 







>
>
>
>
>
>











>







 







<
<







 







|







 







|







|







 







|
|

|







 







|







 







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







 







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




|
|







 







|







 







<
<
|
|
|




<
<
<
<
<
<
<
|

<






>







 







|







 







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







 







>







 







>

|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|
<







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|
<







 







|







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







 







|







 







|







 







|







 







|







 







|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>







 







<
<
<
<
<
<
<
<
<
<







 







>
>
|
|
>
>







 







|







 







|







 







|







 







|







 







>
>
|
|
>
>







 







>
>
|
|
>
>







 







|







 







|







 







|







 







|







 







|







 







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

|

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







 







|







 







>
>







 







|

|



|


|







 







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







 







|
|







 







|
|


|







 







|







 







|







 







|







 







|







 







|







 







>
1
2
3
4
5
6


7
8
9
10
11
12
13
...
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
...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
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
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
...
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546

547



















548
549
550
551
552
553
554
555
...
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
...
653
654
655
656
657
658
659

660
661
662
663






664
665
666
667
668
669
670
...
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
....
1221
1222
1223
1224
1225
1226
1227


1228
1229
1230
1231
1232
1233
1234
....
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
....
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
....
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
....
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
....
1681
1682
1683
1684
1685
1686
1687



































































1688
1689
1690
1691
1692
1693
1694
....
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
....
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
....
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
....
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
....
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
....
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
....
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
....
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
....
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
....
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
....
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
....
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
....
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
....
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
....
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
....
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
....
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
....
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
....
3259
3260
3261
3262
3263
3264
3265
3266

3267
3268
3269
3270
3271
3272
3273
....
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
....
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
....
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
....
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
....
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
....
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
....
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
....
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
....
4020
4021
4022
4023
4024
4025
4026
4027

4028
4029
4030
4031
4032
4033
4034
....
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247














4248
4249
4250
4251
4252
4253
4254
....
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
....
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
....
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
....
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
....
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
















































































































































































































































4922
4923
4924
4925
4926
4927
4928
4929
....
4978
4979
4980
4981
4982
4983
4984










4985
4986
4987
4988
4989
4990
4991
....
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
....
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
....
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
....
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
....
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
....
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
6614
6615
....
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
....
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
....
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
....
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
....
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
....
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
....
7317
7318
7319
7320
7321
7322
7323







7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339







7340


7341
7342
7343
7344
7345
7346
7347
....
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
....
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
....
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
9309
9310
9311
9312
9313
9314
9315
9316
9317
9318
....
9364
9365
9366
9367
9368
9369
9370

9371
9372

9373
9374
9375

9376
9377
9378
9379












9380
9381
9382
9383
9384
9385
9386
9387
....
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
....
9447
9448
9449
9450
9451
9452
9453
9454
9455
9456
9457
9458
9459
9460
9461
9462
9463
9464
9465
....
9528
9529
9530
9531
9532
9533
9534
9535
9536
9537
9538
9539
9540
9541
9542
....
9561
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
....
9769
9770
9771
9772
9773
9774
9775
9776
9777
9778
9779
9780
9781
9782
9783
....
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806
9807
9808
9809
9810
9811
....
9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838
.....
10008
10009
10010
10011
10012
10013
10014
10015
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for thread 2.9a1.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.


#
#
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
................................................................................
export LC_ALL
LANGUAGE=C
export LANGUAGE

# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH

# Use a proper internal environment variable to ensure we don't fall
  # into an infinite loop, continuously re-executing ourselves.
  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
    _as_can_reexec=no; export _as_can_reexec;
    # We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
# Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
case $- in # ((((
  *v*x* | *x*v* ) as_opts=-vx ;;
  *v* ) as_opts=-v ;;
  *x* ) as_opts=-x ;;
  * ) as_opts= ;;
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
as_fn_exit 255
  fi
  # We don't want this to propagate to other subprocesses.
          { _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
  emulate sh
  NULLCMD=:
  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
  # is contrary to our usage.  Disable this feature.
  alias -g '\${1+\"\$@\"}'='\"\$@\"'
................................................................................
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :

else
  exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1
test -x / || exit 1"
  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
test \$(( 1 + 1 )) = 2 || exit 1"
  if (eval "$as_required") 2>/dev/null; then :
  as_have_required=yes
................................................................................
	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
  CONFIG_SHELL=$SHELL as_have_required=yes
fi; }
IFS=$as_save_IFS


      if test "x$CONFIG_SHELL" != x; then :
  export CONFIG_SHELL
             # We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
# Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV

case $- in # ((((
  *v*x* | *x*v* ) as_opts=-vx ;;
  *v* ) as_opts=-v ;;
  *x* ) as_opts=-x ;;
  * ) as_opts= ;;
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi

    if test x$as_have_required = xno; then :
  $as_echo "$0: This script requires a shell more modern than all"
  $as_echo "$0: the shells that I found on your system."
  if test x${ZSH_VERSION+set} = xset ; then
    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
................................................................................
      test -d "$as_dir" && break
    done
    test -z "$as_dirs" || eval "mkdir $as_dirs"
  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"


} # as_fn_mkdir_p

# as_fn_executable_p FILE
# -----------------------
# Test if FILE is an executable regular file.
as_fn_executable_p ()
{
  test -f "$1" && test -x "$1"
} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
................................................................................
      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
      t loop
      s/-\n.*//
    ' >$as_me.lineno &&
  chmod +x "$as_me.lineno" ||
    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }

  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
  # already done that, so ensure we don't try to do so again and fall
  # in an infinite loop.  This has already happened in practice.
  _as_can_reexec=no; export _as_can_reexec
  # Don't try to exec as it changes $[0], causing all sort of problems
  # (the dirname of $[0] is not the place where we might find the
  # original and so on.  Autoconf is especially sensitive to this).
  . "./$as_me.lineno"
  # Exit status is that of the last command.
  exit
}
................................................................................
fi
if (echo >conf$$.file) 2>/dev/null; then
  if ln -s conf$$.file conf$$ 2>/dev/null; then
    as_ln_s='ln -s'
    # ... but there are two gotchas:
    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
    # In both cases, we have to default to `cp -pR'.
    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
      as_ln_s='cp -pR'
  elif ln conf$$.file conf$$ 2>/dev/null; then
    as_ln_s=ln
  else
    as_ln_s='cp -pR'
  fi
else
  as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null

if mkdir -p . 2>/dev/null; then
  as_mkdir_p='mkdir -p "$as_dir"'
else
  test -d ./-p && rmdir ./-p
  as_mkdir_p=false
fi


as_test_x='test -x'



















as_executable_p=as_fn_executable_p

# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"

# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"

................................................................................
subdirs=
MFLAGS=
MAKEFLAGS=

# Identity of this package.
PACKAGE_NAME='thread'
PACKAGE_TARNAME='thread'
PACKAGE_VERSION='2.9a1'
PACKAGE_STRING='thread 2.9a1'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''

# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
................................................................................
AR
STUBS_BUILD
SHARED_BUILD
TCL_THREADS
TCL_INCLUDES
PKG_OBJECTS
PKG_SOURCES

EGREP
GREP
RANLIB
SET_MAKE






CPP
TCL_SHLIB_LD_LIBS
TCL_LD_FLAGS
TCL_EXTRA_CFLAGS
TCL_DEFS
TCL_LIBS
CLEANFILES
................................................................................
TCL_LIB_FILE
TCL_SRC_DIR
TCL_BIN_DIR
TCL_PATCH_LEVEL
TCL_VERSION
CONFIG_CLEAN_FILES
LN_S
INSTALL_LIBRARY
INSTALL_SCRIPT
INSTALL_PROGRAM
INSTALL_DATA
INSTALL_DATA_DIR
INSTALL
PKG_CFLAGS
PKG_LIBS
PKG_INCLUDES
PKG_HEADERS
PKG_TCL_SOURCES
PKG_STUB_OBJECTS
PKG_STUB_SOURCES
PKG_STUB_LIB_FILE
PKG_LIB_FILE
EXEEXT
CYGPATH
TEA_TK_EXTENSION
target_alias
host_alias
build_alias
LIBS
ECHO_T
ECHO_N
ECHO_C
................................................................................
host=$host_alias
target=$target_alias

# FIXME: To remove some day.
if test "x$host_alias" != x; then
  if test "x$build_alias" = x; then
    cross_compiling=maybe


  elif test "x$build_alias" != "x$host_alias"; then
    cross_compiling=yes
  fi
fi

ac_tool_prefix=
test -n "$host_alias" && ac_tool_prefix=$host_alias-
................................................................................
#
# Report the --help message.
#
if test "$ac_init_help" = "long"; then
  # Omit some internal or obsolete options to make the list less imposing.
  # This message is too long to be a string in the A/UX 3.1 sh.
  cat <<_ACEOF
\`configure' configures thread 2.9a1 to adapt to many kinds of systems.

Usage: $0 [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.
................................................................................

  cat <<\_ACEOF
_ACEOF
fi

if test -n "$ac_init_help"; then
  case $ac_init_help in
     short | recursive ) echo "Configuration of thread 2.9a1:";;
   esac
  cat <<\_ACEOF

Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-threads        build with threads (default: on)
  --enable-shared         build and link with shared libraries (default: on)
  --enable-stubs          build and link with stub libraries. Always true for
                          shared builds (default: on)
  --enable-64bit          enable 64bit support (default: off)
  --enable-64bit-vis      enable 64bit Sparc VIS support (default: off)
  --disable-rpath         disable rpath support (default: on)
  --enable-wince          enable Win/CE support (where applicable)
................................................................................
    cd "$ac_pwd" || { ac_status=$?; break; }
  done
fi

test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
  cat <<\_ACEOF
thread configure 2.9a1
generated by GNU Autoconf 2.69

Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
  exit
fi

## ------------------------ ##
................................................................................
  fi
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } && {
	 test -z "$ac_c_werror_flag" ||
	 test ! -s conftest.err
       } && test -s conftest$ac_exeext && {
	 test "$cross_compiling" = yes ||
	 test -x conftest$ac_exeext
       }; then :
  ac_retval=0
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

	ac_retval=1
................................................................................
  # left behind by Apple's compiler.  We do this before executing the actions.
  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
  as_fn_set_status $ac_retval

} # ac_fn_c_try_link




































































# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists, giving a warning if it cannot be compiled using
# the include files in INCLUDES and setting the cache variable VAR
# accordingly.
ac_fn_c_check_header_mongrel ()
{
................................................................................
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno

} # ac_fn_c_check_header_mongrel

# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
#define $2 innocuous_$2

/* System header to define __stub macros and hopefully few prototypes,
    which can conflict with char $2 (); below.
    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
    <limits.h> exists even on freestanding compilers.  */

#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif

#undef $2

/* Override any GCC internal prototype to avoid an error.
   Use char because int might match the return type of a GCC
   builtin and then its argument prototype would still apply.  */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
    to always fail with ENOSYS.  Some functions are actually named
    something starting with __ and the normal name is an alias.  */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif

int
main ()
{
return $2 ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  eval "$3=yes"
else
  eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno

} # ac_fn_c_check_func
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.

It was created by thread $as_me 2.9a1, which was
generated by GNU Autoconf 2.69.  Invocation command line was

  $ $0 $@

_ACEOF
exec 5>>config.log
{
cat <<_ASUNAME
................................................................................
    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
    sed 's/^/| /' "$ac_site_file" >&5
    . "$ac_site_file" \
      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
See \`config.log' for more details" "$LINENO" 5; }
  fi
done

if test -r "$cache_file"; then
  # Some versions of bash will fail to source /dev/null (special files
  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
................................................................................
#--------------------------------------------------------------------
# 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.
#--------------------------------------------------------------------




    TEA_VERSION="4.0"
    { $as_echo "$as_me:${as_lineno-$LINENO}: checking TEA configuration" >&5
$as_echo_n "checking TEA configuration... " >&6; }
    if test x"${PACKAGE_NAME}" = x ; then
	as_fn_error $? "
The PACKAGE_NAME variable must be defined by your TEA configure.ac" "$LINENO" 5
    fi







    { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (TEA ${TEA_VERSION})" >&5
$as_echo "ok (TEA ${TEA_VERSION})" >&6; }


    # If the user did not set CFLAGS, set it now to keep macros
    # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
    if test "${CFLAGS+set}" != "set" ; then
	CFLAGS=""
    fi
		TEA_TK_EXTENSION=0

    case "`uname -s`" in
	*win32*|*WIN32*|*MINGW32_*)
	    # Extract the first word of "cygpath", so it can be a program name with args.
set dummy cygpath; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CYGPATH="cygpath -m"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................








    # Configure the installer.

    INSTALL='$(SHELL) $(srcdir)/tclconfig/install-sh -c'
    INSTALL_DATA_DIR='${INSTALL} -d -m 755'
    INSTALL_DATA='${INSTALL} -m 644'
    INSTALL_PROGRAM='${INSTALL} -m 755'
    INSTALL_SCRIPT='${INSTALL} -m 755'


    { $as_echo "$as_me:${as_lineno-$LINENO}: checking system version" >&5
$as_echo_n "checking system version... " >&6; }
if ${tcl_cv_sys_version+:} false; then :
  $as_echo_n "(cached) " >&6
else

	# TEA specific:
	if test "${TEA_PLATFORM}" = "windows" ; then
	    tcl_cv_sys_version=windows
	else
	    tcl_cv_sys_version=`uname -s`-`uname -r`
	    if test "$?" -ne 0 ; then
		{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: can't find uname command" >&5
$as_echo "$as_me: WARNING: can't find uname command" >&2;}
		tcl_cv_sys_version=unknown
	    else
		if test "`uname -s`" = "AIX" ; then
		    tcl_cv_sys_version=AIX-`uname -v`.`uname -r`
		fi
	    fi
	fi

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_sys_version" >&5
$as_echo "$tcl_cv_sys_version" >&6; }
    system=$tcl_cv_sys_version

    case $system in
	HP-UX-*) INSTALL_LIBRARY='${INSTALL} -m 755' ;;
	      *) INSTALL_LIBRARY='${INSTALL} -m 644' ;;
    esac









{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
$as_echo_n "checking whether ln -s works... " >&6; }
LN_S=$as_ln_s
if test "$LN_S" = "ln -s"; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
................................................................................
CONFIG_CLEAN_FILES=
if test ! -d $srcdir/tclconfig ; then
    if test -d $srcdir/../tclconfig ; then
        $LN_S $srcdir/../tclconfig tclconfig
        CONFIG_CLEAN_FILES=tclconfig
    fi
fi


ac_aux_dir=
for ac_dir in tclconfig "$srcdir"/tclconfig; do
  if test -f "$ac_dir/install-sh"; then
    ac_aux_dir=$ac_dir
    ac_install_sh="$ac_aux_dir/install-sh -c"
    break
................................................................................
	    fi

	    # check in a few common install locations
	    if test x"${ac_cv_c_tclconfig}" = x ; then
		for i in `ls -d ${libdir} 2>/dev/null` \
			`ls -d ${exec_prefix}/lib 2>/dev/null` \
			`ls -d ${prefix}/lib 2>/dev/null` \
			`ls -d /usr/contrib/lib 2>/dev/null` \
			`ls -d /usr/local/lib 2>/dev/null` \
			`ls -d /usr/pkg/lib 2>/dev/null` \
			`ls -d /usr/lib 2>/dev/null` \
			`ls -d /usr/lib64 2>/dev/null` \
			`ls -d /usr/lib/tcl8.6 2>/dev/null` \
			`ls -d /usr/lib/tcl8.5 2>/dev/null` \
			; do
		    if test -f "$i/tclConfig.sh" ; then
			ac_cv_c_tclconfig="`(cd $i; pwd)`"
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CC="${ac_tool_prefix}gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_CC="gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CC="${ac_tool_prefix}cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
  ac_prog_rejected=no
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
       ac_prog_rejected=yes
       continue
     fi
    ac_cv_prog_CC="cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_CC="$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................

fi


test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5; }

# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
  { { ac_try="$ac_compiler $ac_option >&5"
................................................................................
$as_echo "no" >&6; }
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
See \`config.log' for more details" "$LINENO" 5; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
$as_echo_n "checking for C compiler default output file name... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
................................................................................
    * ) break;;
  esac
done
else
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
$as_echo "$ac_cv_exeext" >&6; }

rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
................................................................................
    if test "$cross_compiling" = maybe; then
	cross_compiling=yes
    else
	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
See \`config.log' for more details" "$LINENO" 5; }
    fi
  fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
$as_echo "$cross_compiling" >&6; }

rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
................................................................................
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
$as_echo "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
................................................................................
else
  ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdarg.h>
#include <stdio.h>
struct stat;

/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
     char **p;
     int i;
{
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CYGPATH="cygpath -m"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CC="${ac_tool_prefix}gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_CC="gcc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CC="${ac_tool_prefix}cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
  ac_prog_rejected=no
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
       ac_prog_rejected=yes
       continue
     fi
    ac_cv_prog_CC="cc"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_CC="$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................

fi


test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5; }

# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
  { { ac_try="$ac_compiler $ac_option >&5"
................................................................................
else
  ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdarg.h>
#include <stdio.h>
struct stat;

/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
     char **p;
     int i;
{
................................................................................
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :

else
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
See \`config.log' for more details" "$LINENO" 5; }
fi

ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
















    #--------------------------------------------------------------------
    # Checks to see if the make program sets the $MAKE variable.
    #--------------------------------------------------------------------

    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_RANLIB="ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_prog in grep ggrep; do
    for ac_exec_ext in '' $ac_executable_extensions; do
      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
      as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
  # Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
*GNU*)
  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
*)
  ac_count=0
................................................................................
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_prog in egrep; do
    for ac_exec_ext in '' $ac_executable_extensions; do
      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
      as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
  # Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
*GNU*)
  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
*)
  ac_count=0
................................................................................
   universal)

$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h

     ;; #(
   *)
     as_fn_error $? "unknown endianness
 presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
 esac

    if test "${TEA_PLATFORM}" = "unix" ; then
















































































































































































































































	TEA_TCL_LINK_LIBS

    { $as_echo "$as_me:${as_lineno-$LINENO}: checking dirent.h" >&5
$as_echo_n "checking dirent.h... " >&6; }
if ${tcl_cv_dirent_h+:} false; then :
  $as_echo_n "(cached) " >&6
else

................................................................................
    ac_fn_c_check_header_mongrel "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default"
if test "x$ac_cv_header_errno_h" = xyes; then :

else

$as_echo "#define NO_ERRNO_H 1" >>confdefs.h











fi


    ac_fn_c_check_header_mongrel "$LINENO" "values.h" "ac_cv_header_values_h" "$ac_includes_default"
if test "x$ac_cv_header_values_h" = xyes; then :

else
................................................................................
    done



    vars="${GDBM_LIBS} ${LMDB_LIBS} ${NS_LIBS}"
    for i in $vars; do
	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
          case $i in
            *.lib)
              # Convert foo.lib to -lfoo for GCC
                i=-l`echo "$i" | sed -e 's/\.[^.]*$//' -e 's/\.lib.*//'`
            ;;
          esac
	fi
	PKG_LIBS="$PKG_LIBS $i"
    done



    PKG_CFLAGS="$PKG_CFLAGS ${GDBM_CFLAGS} ${LMDB_CFLAGS}"
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_RANLIB="ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_AR="${ac_tool_prefix}ar"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_AR="ar"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
	        case "x`echo \${VisualStudioVersion}`" in
	            x1[4-9]*)
		        lflags="${lflags} -nodefaultlib:libucrt.lib"

    vars="ucrt.lib"
    for i in $vars; do
	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
          case $i in
            *.lib)
              # Convert foo.lib to -lfoo for GCC
                i=-l`echo "$i" | sed -e 's/\.[^.]*$//' -e 's/\.lib.*//'`
            ;;
          esac
	fi
	PKG_LIBS="$PKG_LIBS $i"
    done


	            ;;
	            *)
................................................................................
		    CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
		    # Avoid 'unresolved external symbol __security_cookie'
		    # errors, c.f. http://support.microsoft.com/?id=894573

    vars="bufferoverflowU.lib"
    for i in $vars; do
	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
          case $i in
            *.lib)
              # Convert foo.lib to -lfoo for GCC
                i=-l`echo "$i" | sed -e 's/\.[^.]*$//' -e 's/\.lib.*//'`
            ;;
          esac
	fi
	PKG_LIBS="$PKG_LIBS $i"
    done


		elif test "$doWince" != "no" ; then
		    CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_RC="${ac_tool_prefix}windres"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
    for ac_exec_ext in '' $ac_executable_extensions; do
  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    ac_cv_prog_ac_ct_RC="windres"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS
................................................................................
  RC="$ac_cv_prog_RC"
fi

		CFLAGS_DEBUG="-g"
		CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
		SHLIB_LD='${CC} -shared'
		UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
		PRACTCL_UNSHARED_LIB_SUFFIX='.a'

		LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
		LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"

		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cross-compile version of gcc" >&5
$as_echo_n "checking for cross-compile version of gcc... " >&6; }
if ${ac_cv_cross+:} false; then :
................................................................................
	    CC_SEARCH_FLAGS=""
	    LD_SEARCH_FLAGS=""
	    ;;
	Haiku*)
	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
	    SHLIB_CFLAGS="-fPIC"
	    SHLIB_SUFFIX=".so"
	    SHLIB_LD='${CC} ${CFLAGS} ${LDFLAGS} -shared'
	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnetwork" >&5
$as_echo_n "checking for inet_ntoa in -lnetwork... " >&6; }
if ${ac_cv_lib_network_inet_ntoa+:} false; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lnetwork  $LIBS"
................................................................................
	    SHLIB_CFLAGS="-fPIC"
	    SHLIB_SUFFIX=".so"

	    # TEA specific:
	    CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"

	    # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
	    SHLIB_LD='${CC} ${CFLAGS} ${LDFLAGS_DEFAULT} -shared'
	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
	    if test $doRpath = yes; then :

		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
	    if test "`uname -m`" = "alpha"; then :
................................................................................
		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
		LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    ;;
	OpenBSD-*)
	    arch=`arch -s`
	    case "$arch" in







	    alpha|sparc64)
		SHLIB_CFLAGS="-fPIC"
		;;
	    *)
		SHLIB_CFLAGS="-fpic"
		;;
	    esac
	    SHLIB_LD='${CC} ${SHLIB_CFLAGS} -shared'
	    SHLIB_SUFFIX=".so"
	    if test $doRpath = yes; then :

		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so${SHLIB_VERSION}'
	    LDFLAGS="-Wl,-export-dynamic"







	    CFLAGS_OPTIMIZE="-O2"


	    if test "${TCL_THREADS}" = "1"; then :

		# On OpenBSD:	Compile with -pthread
		#		Don't link with -lpthread
		LIBS=`echo $LIBS | sed s/-lpthread//`
		CFLAGS="$CFLAGS -pthread"

................................................................................
	    # OpenBSD doesn't do version numbers with dots.
	    UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
	    TCL_LIB_VERSIONS_OK=nodots
	    ;;
	NetBSD-*)
	    # NetBSD has ELF and can use 'cc -shared' to build shared libs
	    SHLIB_CFLAGS="-fPIC"
	    SHLIB_LD='${CC} ${SHLIB_CFLAGS} -shared'
	    SHLIB_SUFFIX=".so"
	    LDFLAGS="$LDFLAGS -export-dynamic"
	    if test $doRpath = yes; then :

		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
fi
	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
................................................................................
	    fi
		else
      if test -f "${TCL_BIN_DIR}/Makefile" ; then
        # tclConfig.sh is in Tcl build directory
        if test "${TEA_PLATFORM}" = "windows"; then
          if test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}sg${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}sg${EXEEXT}"
          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}s${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}s${EXEEXT}"
          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}t${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}t${EXEEXT}"
          elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}st${EXEEXT}" ; then
            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}st${EXEEXT}"
          fi
................................................................................
fi
if (echo >conf$$.file) 2>/dev/null; then
  if ln -s conf$$.file conf$$ 2>/dev/null; then
    as_ln_s='ln -s'
    # ... but there are two gotchas:
    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
    # In both cases, we have to default to `cp -pR'.
    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
      as_ln_s='cp -pR'
  elif ln conf$$.file conf$$ 2>/dev/null; then
    as_ln_s=ln
  else
    as_ln_s='cp -pR'
  fi
else
  as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null


# as_fn_mkdir_p
# -------------
................................................................................
if mkdir -p . 2>/dev/null; then
  as_mkdir_p='mkdir -p "$as_dir"'
else
  test -d ./-p && rmdir ./-p
  as_mkdir_p=false
fi



# as_fn_executable_p FILE

# -----------------------
# Test if FILE is an executable regular file.
as_fn_executable_p ()

{
  test -f "$1" && test -x "$1"
} # as_fn_executable_p
as_test_x='test -x'












as_executable_p=as_fn_executable_p

# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"

# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"

................................................................................
test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1

cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# Save the log message, to keep $0 and so on meaningful, and to
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by thread $as_me 2.9a1, which was
generated by GNU Autoconf 2.69.  Invocation command line was

  CONFIG_FILES    = $CONFIG_FILES
  CONFIG_HEADERS  = $CONFIG_HEADERS
  CONFIG_LINKS    = $CONFIG_LINKS
  CONFIG_COMMANDS = $CONFIG_COMMANDS
  $ $0 $@

................................................................................

Report bugs to the package provider."

_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
thread config.status 2.9a1
configured by $0, generated by GNU Autoconf 2.69,
  with options \\"\$ac_cs_config\\"

Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."

ac_pwd='$ac_pwd'
srcdir='$srcdir'
test -n "\$AWK" || AWK=awk
_ACEOF
................................................................................
  exec 6>/dev/null
  ac_configure_extra_args="$ac_configure_extra_args --silent"
fi

_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
  shift
  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
  CONFIG_SHELL='$SHELL'
  export CONFIG_SHELL
  exec "\$@"
fi

................................................................................
for ac_config_target in $ac_config_targets
do
  case $ac_config_target in
    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
    "pkgIndex.tcl") CONFIG_FILES="$CONFIG_FILES pkgIndex.tcl" ;;
    "config.tcl") CONFIG_FILES="$CONFIG_FILES config.tcl:tclconfig/config.tcl.in" ;;

  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
  esac
done


# If the user did not use the arguments to specify the items to instantiate,
# then the envvar interface is used.  Set only those that are not.
# We use the long form for the default assignment because of an extremely
................................................................................
for ac_tag
do
  case $ac_tag in
  :[FHLC]) ac_mode=$ac_tag; continue;;
  esac
  case $ac_mode$ac_tag in
  :[FHL]*:*);;
  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
  :[FH]-) ac_tag=-:-;;
  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
  esac
  ac_save_IFS=$IFS
  IFS=:
  set x $ac_tag
  IFS=$ac_save_IFS
................................................................................
	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
	 # because $ac_f cannot contain `:'.
	 test -f "$ac_f" ||
	   case $ac_f in
	   [\\/$]*) false;;
	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
	   esac ||
	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
      esac
      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
      as_fn_append ac_file_inputs " '$ac_f'"
    done

    # Let's still pretend it is `configure' which instantiates (i.e., don't
    # use $as_me), people would be surprised to read:
................................................................................
       ac_sed_conf_input=`$as_echo "$configure_input" |
       sed 's/[\\\\&|]/\\\\&/g'`;; #(
    *) ac_sed_conf_input=$configure_input;;
    esac

    case $ac_tag in
    *:-:* | *:-) cat >"$ac_tmp/stdin" \
      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
    esac
    ;;
  esac

  ac_dir=`$as_dirname -- "$ac_file" ||
$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
	 X"$ac_file" : 'X\(//\)[^/]' \| \
................................................................................
  # would make configure fail if this is the last instruction.
  $ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi

Changes to configure.ac.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
31
32
33
34
35
36
37

38
39
40
41
42
43
44
# 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.
#--------------------------------------------------------------------

................................................................................
if test ! -d $srcdir/tclconfig ; then
    if test -d $srcdir/../tclconfig ; then
        $LN_S $srcdir/../tclconfig tclconfig
        CONFIG_CLEAN_FILES=tclconfig
    fi
fi
AC_SUBST(CONFIG_CLEAN_FILES)

AC_CONFIG_AUX_DIR(tclconfig)

#--------------------------------------------------------------------
# Load the tclConfig.sh file
#--------------------------------------------------------------------

TEA_PATH_TCLCONFIG







|







 







>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 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.9a1])

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

................................................................................
if test ! -d $srcdir/tclconfig ; then
    if test -d $srcdir/../tclconfig ; then
        $LN_S $srcdir/../tclconfig tclconfig
        CONFIG_CLEAN_FILES=tclconfig
    fi
fi
AC_SUBST(CONFIG_CLEAN_FILES)

AC_CONFIG_AUX_DIR(tclconfig)

#--------------------------------------------------------------------
# Load the tclConfig.sh file
#--------------------------------------------------------------------

TEA_PATH_TCLCONFIG

Changes to doc/html/thread.html.

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







|


|







|











|
|
|


|
|


|

|
|
|
|




|
|

|











|
|


|
|

|
|
|
|
|
|



|
|


|
|

|


|
|



|

|

|

|
|

|
|








|
|
|







 







|
|


|






|
|







 







|




|

|
|
|




|

|



|

|






|
|

|
|










|







 







|











|


|
|


|


|
|




|
|




|
|

|
|
|


|







 







|







|







|
|
|
|
|
|
|


|











|


|

|
|







|
|
|













|







 







|
|
|

|





|


|
|








|
|
|


|







 







|
|











|
|

|
|







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

Changes to doc/html/tpool.html.

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
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
<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>
................................................................................
<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>








|

|







 







|


|







|
|
|



|
|





|


|









|




|

|
|
|








|
|










|
|







|
|




|







|




|
|
|

|









|


|

|
|






|
|
|
|


|



|
|

|

|











130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
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
<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>
................................................................................
<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.

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
...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
...
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
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
...
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
<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>
................................................................................
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
................................................................................
</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>
................................................................................
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>
................................................................................
<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>








|



|

|


|





|



|











|

|




|

|
|
|







 







|



|







 







|



|



|
|


|







 







|
|







 







|







|

|





|
|
|






|
|









|
|

|

|





|
|



|

|








|


|


|



|










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
...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
...
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
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
...
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
<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>
................................................................................
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
................................................................................
</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>
................................................................................
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>
................................................................................
<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.

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








|


|





|











|












|


|




|









|


|











|

|
|


|







 







|
|
|
|
|












|







 







|








|
|



|
|
|
|


|







|










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
...
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
<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.2
    % set t1 [thread::create {package require Ttrace; thread::wait}]
    tid0x1802800
    % ttrace::eval {proc test args {return test-[thread::id]}}
    % thread::send $t1 test
    test-tid0x1802800
    % 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">
................................................................................
<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/thread.man.

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
...
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
...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
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
...
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
...
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
...
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
...
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
[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 
synchronization primitives, like mutexes and condition variables.

[section COMMANDS]
This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation.



[list_begin definitions]

[call [cmd thread::create] [opt -joinable] [opt -preserved] [opt script]]

This command creates a thread that contains a Tcl interpreter. 
The Tcl interpreter either evaluates the optional [option script], if
specified, or it waits in the event loop for scripts that arrive via
the [cmd thread::send] command. The result, if any, of the
optional [option script] is never returned to the caller.
The result of [cmd thread::create] 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 [cmd thread::release] command below).

[para]

If the optional [option script] argument contains the [cmd thread::wait]
command the thread will enter into the event loop. If such command is not
found  in the [option script] the thread will run the [option script] 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.

[para]

Using flag [option -joinable] it is possible to create a joinable
thread, i.e. one upon whose exit can be waited upon by using 
[cmd thread::join] command. 
Note that failure to join a thread created with [option -joinable] flag
results in resource and memory leaks. 


[para]

Threads created by the [cmd thread::create] cannot be destroyed 
forcefully. Consequently, there is no corresponding thread destroy
command. A thread may only be released using the [cmd thread::release] 
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 [option script] argument, following the [cmd thread::wait]
command. If this was the last command in the script, as usualy the
case, the thread will exit.

[para]

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 [cmd thread::wait] 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:

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

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

[para]

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 
[cmd thread::preserve] and decremented by a call to [cmd thread::release]
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 [option -preserved] of the 
[cmd thread::create] command. Threads created with this flag have the 
initial value of the reference counter of 1 (one), and are thus 
initially marked reserved. 


[call [cmd thread::preserve] [opt 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 [option id], the command preserves
the given thread. Otherwise the current thread is preserved.

[para]

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.

[call [cmd thread::release] [opt -wait] [opt 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 [option id], 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
[cmd thread::release] 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.

[para]

Optional flag [option -wait] 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 
[option -wait], 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 [option -joinable] option of the [cmd thread::create]
command and the [cmd thread::join] to wait for thread to exit. 

[call [cmd thread::id]]

This command returns the ID of the current thread.

[call [cmd thread::errorproc] [opt procname]]

This command sets a handler for errors that occur in scripts sent 
asynchronously, using the [option -async] flag of the 
[cmd thread::send] 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 [arg procname] is called in the interpreter that
invoked the [cmd thread::errorproc] command. The [arg procname]
................................................................................
until there are no further invocations of the interpreter left on the
call stack. If [arg result] is present, it will be used as the error
message string; otherwise, a default error message string will be used.

[call [cmd thread::unwind]]

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::unwind] 
command will dissapear in some future major release of the extension.
[para]
This command stops a prior [cmd thread::wait] command. Execution of
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.


[call [cmd thread::exists] [arg id]]

Returns true (1) if thread given by the [arg id] parameter exists, 
false (0) otherwise. This applies only for threads that have
been created via [cmd thread::create] command.


[call [cmd thread::send] [opt -async] [opt -head] [arg id] [arg script] [opt varname]]

This command passes a [arg script] to another thread and, optionally,
waits for the result. If the [option -async] 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 [cmd thread::wait] or any other relevant Tcl/Tk
command, like [cmd update], [cmd vwait], etc. 

[para]

Optional [option varname] specifies name of the variable to store
the result of the [arg script]. Without the [option -async] flag,
the command returns the evaluation code, similarily to the standard 
Tcl [cmd catch] command. If, however, the [option -async] flag is
specified, the command returns immediately and caller can later 
[cmd vwait] on [opt varname] to get the result of the passed [arg script]

[example {
    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
    }
}]

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.

[para]

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


................................................................................

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

This enters the event loop so a thread can receive messages from 
the [cmd thread::send] command. This command should only be used
within the script passed to the [cmd thread::create]. 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 [cmd thread::wait] which is usually not what you want and/or
expect.

................................................................................
        #
        thread::wait ; # Enter the event loop
    }]
}]

[call [cmd thread::eval] [opt {-lock mutex}] [arg arg] [opt {arg ...}]]

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


[call [cmd thread::join] [arg id]]

................................................................................
Upon the join the handle of the thread has gone out of scope and
should not be used any more.


[call [cmd thread::configure] [arg id] [opt option] [opt value] [opt ...]]

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

[para]

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

[para]

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


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

This moves the specified [arg channel] from the current thread 
and interpreter to the main interpreter of the thread with the 
given [arg id]. After the move the current interpreter has no
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 [cmd thread::send] 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.

[para]

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 [cmd socket] commands callback procedures:

[example {
    socket -server _Accept 2200
    proc _Accept {s ipaddr port} {
................................................................................
        set tid [thread::create]
        thread::transfer $tid $s
    }
}]

[call [cmd thread::detach] [arg channel]]

This detaches the specified [arg channel] 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 [cmd thread::attach].
Restrictions: same as for transferring shared channels with the
[cmd thread::transfer] command.

[call [cmd thread::attach] [arg channel]]

This attaches the previously detached [arg channel] 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 [arg channel] cannot be
found in the list of detached channels and/or in the current
interpreter.

[call [cmd thread::mutex]]

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.
[para]
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.

[para]

The [cmd thread::mutex] command supports following subcommands and options:

[list_begin definitions]
................................................................................
If no optional [opt -recursive] argument was specified, the command
creates the exclusive mutex. With the [opt -recursive] argument,
the command creates a recursive mutex.

[call [cmd thread::mutex] [method destroy] [arg mutex]]

Destroys the [arg mutex]. Mutex should be in unlocked state before
the destroy attempt. If the mutex is locked, the command will throw 
Tcl error. 

[call [cmd thread::mutex] [method lock] [arg mutex]]

Locks the [arg mutex]. 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. 

[call [cmd thread::mutex] [method unlock] [arg mutex]]

Unlocks the [arg mutex] so some other thread may lock it again.
Attempt to unlock the already unlocked mutex will throw Tcl error.

[list_end]

[para]

[call [cmd thread::rwmutex]]

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. 
[para]
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. 

[para]

The [cmd thread::rwmutex] command supports following subcommands and options:

[list_begin definitions]

[call [cmd thread::rwmutex] [method create]]

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.

[call [cmd thread::rwmutex] [method destroy] [arg mutex]]

Destroys the reader/writer [arg mutex]. If the mutex is already locked,
attempt to destroy it will throw Tcl error.

................................................................................
[list_end]

[para]

[call [cmd thread::cond]]

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.

[para]

The command supports following subcommands and options:

[list_begin definitions]

[call [cmd thread::cond] [method create]]

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

[call [cmd thread::cond] [method destroy] [arg cond]]

Destroys condition variable [arg cond]. Extreme care has to be taken 
that nobody is using (i.e. waiting on) the condition variable, 
otherwise unexpected errors may happen.

[call [cmd thread::cond] [method notify] [arg cond]]

Wakes up all threads waiting on the condition variable [arg cond].

[call [cmd thread::cond] [method wait] [arg cond] [arg mutex] [opt ms]]

This command is used to suspend program execution until the condition
variable [arg cond] has been signalled or the optional timer has expired.
The exclusive [arg mutex] 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 [arg cond], the command releases [arg mutex]. 
Before returning to the calling thread, the command re-acquires the 
[arg mutex] again. Unlocking the [arg mutex] and waiting on the 
condition variable [arg cond] is done atomically.

[para]

The [option ms] 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.

[para]

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
................................................................................
    while {<some_condition_is_true>} {
        thread::cond wait $cond $mutex
    }
    # Do some work under mutex protection
    thread::mutex unlock $mutex
}]

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

[list_end]

[list_end]

[section 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, 
[cmd vwait] on variables, and so forth. For the transfer of bulk
information it is possible to move channels between the threads.

[para]

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







|



|












|







 







|
|
|





|
|

|




|

|
|
|
|

|





|
|

|













|
|





|
|

|
|
|
|
|
|
|





|
|





|
|

|




|
|



|

|

|




|
|

|
|




|







|
|
|







 







|
|
|


|









|
|
|


|


|







 







|







|

|
|
|

|





|

|




|

|







|
|




|
|







 







|







 







|







 







|





|
|


|





|
|







|
|




|
|

|
|
|





|







 







|









|

|







|
|
|
|
|
|
|



|







 







|
|



|

|
|
|













|
|
|
|








|










|







 







|
|
|
|
|











|




|
|












|
|
|





|







 







|
|













|
|

|
|







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
...
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
...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
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
...
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
...
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
...
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
...
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
[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.

Additionally, it provides script-level access to basic thread
synchronization primitives, like mutexes and condition variables.

[section COMMANDS]
This section describes commands for creating and destroying threads
and sending scripts to threads for evaluation.



[list_begin definitions]

[call [cmd thread::create] [opt -joinable] [opt -preserved] [opt script]]

This command creates a thread that contains a Tcl interpreter.
The Tcl interpreter either evaluates the optional [option script], if
specified, or it waits in the event loop for scripts that arrive via
the [cmd thread::send] command. The result, if any, of the
optional [option script] is never returned to the caller.
The result of [cmd thread::create] 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 [cmd thread::release] command below).

[para]

If the optional [option script] argument contains the [cmd thread::wait]
command the thread will enter into the event loop. If such command is not
found  in the [option script] the thread will run the [option script] 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.

[para]

Using flag [option -joinable] it is possible to create a joinable
thread, i.e. one upon whose exit can be waited upon by using
[cmd thread::join] command.
Note that failure to join a thread created with [option -joinable] flag
results in resource and memory leaks.


[para]

Threads created by the [cmd thread::create] cannot be destroyed
forcefully. Consequently, there is no corresponding thread destroy
command. A thread may only be released using the [cmd thread::release]
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 [option script] argument, following the [cmd thread::wait]
command. If this was the last command in the script, as usually the
case, the thread will exit.

[para]

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 [cmd thread::wait] 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:

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

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

[para]

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
[cmd thread::preserve] and decremented by a call to [cmd thread::release]
command. These two commands implement simple but effective thread
reservation system and offer predictable and controllable thread
termination capabilities. It is however possible to create initially
preserved threads by using flag [option -preserved] of the
[cmd thread::create] command. Threads created with this flag have the
initial value of the reference counter of 1 (one), and are thus
initially marked reserved.


[call [cmd thread::preserve] [opt 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 [option id], the command preserves
the given thread. Otherwise the current thread is preserved.

[para]

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.

[call [cmd thread::release] [opt -wait] [opt 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 [option id], 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
[cmd thread::release] 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.

[para]

Optional flag [option -wait] 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
[option -wait], 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 [option -joinable] option of the [cmd thread::create]
command and the [cmd thread::join] to wait for thread to exit.

[call [cmd thread::id]]

This command returns the ID of the current thread.

[call [cmd thread::errorproc] [opt procname]]

This command sets a handler for errors that occur in scripts sent
asynchronously, using the [option -async] flag of the
[cmd thread::send] 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 [arg procname] is called in the interpreter that
invoked the [cmd thread::errorproc] command. The [arg procname]
................................................................................
until there are no further invocations of the interpreter left on the
call stack. If [arg result] is present, it will be used as the error
message string; otherwise, a default error message string will be used.

[call [cmd thread::unwind]]

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::unwind]
command will disappear in some future major release of the extension.
[para]
This command stops a prior [cmd thread::wait] command. Execution of
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 disappear in some future major release of the extension.
[para]
This command forces a thread stuck in the [cmd thread::wait] command to
unconditionally 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
inconsistent state, produce memory leaks and otherwise affect other subsystem(s)
of the Tcl application in an unpredictable manner. The command returns empty
result but may trigger Tcl error with the message "target thread died" in some
situations.

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


[call [cmd thread::exists] [arg id]]

Returns true (1) if thread given by the [arg id] parameter exists,
false (0) otherwise. This applies only for threads that have
been created via [cmd thread::create] command.


[call [cmd thread::send] [opt -async] [opt -head] [arg id] [arg script] [opt varname]]

This command passes a [arg script] to another thread and, optionally,
waits for the result. If the [option -async] 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 [cmd thread::wait] or any other relevant Tcl/Tk
command, like [cmd update], [cmd vwait], etc.

[para]

Optional [option varname] specifies name of the variable to store
the result of the [arg script]. Without the [option -async] flag,
the command returns the evaluation code, similarly to the standard
Tcl [cmd catch] command. If, however, the [option -async] flag is
specified, the command returns immediately and caller can later
[cmd vwait] on [opt varname] to get the result of the passed [arg script]

[example {
    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
    }
}]

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.

[para]

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


................................................................................

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

This enters the event loop so a thread can receive messages from
the [cmd thread::send] command. This command should only be used
within the script passed to the [cmd thread::create]. 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 [cmd thread::wait] which is usually not what you want and/or
expect.

................................................................................
        #
        thread::wait ; # Enter the event loop
    }]
}]

[call [cmd thread::eval] [opt {-lock mutex}] [arg arg] [opt {arg ...}]]

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


[call [cmd thread::join] [arg id]]

................................................................................
Upon the join the handle of the thread has gone out of scope and
should not be used any more.


[call [cmd thread::configure] [arg id] [opt option] [opt value] [opt ...]]

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

[para]

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

[para]

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


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

This moves the specified [arg channel] from the current thread
and interpreter to the main interpreter of the thread with the
given [arg id]. After the move the current interpreter has no
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 [cmd thread::send] 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.

[para]

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 [cmd socket] commands callback procedures:

[example {
    socket -server _Accept 2200
    proc _Accept {s ipaddr port} {
................................................................................
        set tid [thread::create]
        thread::transfer $tid $s
    }
}]

[call [cmd thread::detach] [arg channel]]

This detaches the specified [arg channel] 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 [cmd thread::attach].
Restrictions: same as for transferring shared channels with the
[cmd thread::transfer] command.

[call [cmd thread::attach] [arg channel]]

This attaches the previously detached [arg channel] in the
current thread/interpreter. For already existing channels,
the command does nothing, i.e. it is not an error to attach the
same channel more than once. The first operation will actually
perform the operation, while all subsequent operation will just
do nothing. Command throws error if the [arg channel] cannot be
found in the list of detached channels and/or in the current
interpreter.

[call [cmd thread::mutex]]

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.
[para]
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.

[para]

The [cmd thread::mutex] command supports following subcommands and options:

[list_begin definitions]
................................................................................
If no optional [opt -recursive] argument was specified, the command
creates the exclusive mutex. With the [opt -recursive] argument,
the command creates a recursive mutex.

[call [cmd thread::mutex] [method destroy] [arg mutex]]

Destroys the [arg mutex]. Mutex should be in unlocked state before
the destroy attempt. If the mutex is locked, the command will throw
Tcl error.

[call [cmd thread::mutex] [method lock] [arg mutex]]

Locks the [arg mutex]. 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.

[call [cmd thread::mutex] [method unlock] [arg mutex]]

Unlocks the [arg mutex] so some other thread may lock it again.
Attempt to unlock the already unlocked mutex will throw Tcl error.

[list_end]

[para]

[call [cmd thread::rwmutex]]

This command creates many-readers/single-writer mutexes. Reader/writer
mutexes allow you to serialize access to a shared resource more optimally.
In situations where a shared resource gets mostly read and seldom modified,
you might gain some performance by using reader/writer mutexes instead of
exclusive or recursive mutexes.
[para]
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.

[para]

The [cmd thread::rwmutex] command supports following subcommands and options:

[list_begin definitions]

[call [cmd thread::rwmutex] [method create]]

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.

[call [cmd thread::rwmutex] [method destroy] [arg mutex]]

Destroys the reader/writer [arg mutex]. If the mutex is already locked,
attempt to destroy it will throw Tcl error.

................................................................................
[list_end]

[para]

[call [cmd thread::cond]]

This command provides script-level access to condition variables.
A condition variable creates a safe environment for the program
to test some condition, sleep on it when false and be awakened
when it might have become true. A condition variable is always
used in the conjunction with an exclusive mutex. If you attempt
to use other type of mutex in conjunction with the condition
variable, a Tcl error will be thrown.

[para]

The command supports following subcommands and options:

[list_begin definitions]

[call [cmd thread::cond] [method create]]

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

[call [cmd thread::cond] [method destroy] [arg cond]]

Destroys condition variable [arg cond]. Extreme care has to be taken
that nobody is using (i.e. waiting on) the condition variable,
otherwise unexpected errors may happen.

[call [cmd thread::cond] [method notify] [arg cond]]

Wakes up all threads waiting on the condition variable [arg cond].

[call [cmd thread::cond] [method wait] [arg cond] [arg mutex] [opt ms]]

This command is used to suspend program execution until the condition
variable [arg cond] has been signalled or the optional timer has expired.
The exclusive [arg mutex] 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 [arg cond], the command releases [arg mutex].
Before returning to the calling thread, the command re-acquires the
[arg mutex] again. Unlocking the [arg mutex] and waiting on the
condition variable [arg cond] is done atomically.

[para]

The [option ms] 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.

[para]

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
................................................................................
    while {<some_condition_is_true>} {
        thread::cond wait $cond $mutex
    }
    # Do some work under mutex protection
    thread::mutex unlock $mutex
}]

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

[list_end]

[list_end]

[section 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,
[cmd vwait] on variables, and so forth. For the transfer of bulk
information it is possible to move channels between the threads.

[para]

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

Changes to doc/tpool.man.

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
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
[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
enter the event loop and allow for servicing of other pending file or
timer (or any other supported) events.

[section COMMANDS]

[list_begin definitions]
................................................................................

[opt_def -minworkers [arg 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 [cmd tpool::post] command
and posting jobs to the job queue.

[opt_def -maxworkers [arg 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.

[opt_def -idletime [arg 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
[option minthreads] option, it will exit. 
If an [option exitscript] has been defined, the exiting worker thread 
will first run the script and then exit. Errors from the exit script, 
if any, are ignored.
[para]
The idle worker thread is not servicing the event loop. If you, however,
put the worker thread into the event loop, by evaluating the
[cmd vwait] 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.

[opt_def -initcmd [arg 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
[cmd tpool::create] or [cmd tpool::post]) 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.

[opt_def -exitcmd [arg 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.

[list_end]

[para] 

[call [cmd tpool::names]]

This command returns a list of IDs of threadpools created with the 
[cmd tpool::create] command. If no threadpools were found, the
command will return empty list.

[call [cmd tpool::post] [opt -detached] [opt -nowait] [arg tpool] [arg script]]

This command sends a [arg script] to the target [arg tpool] 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 [opt -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.
[para]
The command returns the ID of the posted job. This ID is used for subsequent
[cmd tpool::wait], [cmd tpool::get] and [cmd tpool::cancel] commands to wait
for and retrieve result of the posted script, or cancel the posted job
respectively. If the optional [opt -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.
[para]
If the threadpool [arg tpool] 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.

[call [cmd tpool::wait] [arg tpool] [arg joblist] [opt varname]]
................................................................................

This command waits for one or many jobs, whose job IDs are given in the
[arg joblist] 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.
[para]
The command returns the list of completed job IDs. If the optional variable
[opt varname] is given, it will be set to the list of jobs in the 
[arg joblist] which are still pending. If the threadpool [arg tpool] 
is not found in the list of active thread pools, the command will throw error.

[call [cmd tpool::cancel] [arg tpool] [arg joblist] [opt varname]]

This command cancels the previously posted jobs given by the [arg joblist]
to the pool [arg tpool]. 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
[opt varname] is given, it will be set to the list of jobs in the 
[arg joblist] which were not cancelled. If the threadpool [arg tpool] 
is not found in the list of active thread pools, the command will throw error.

[call [cmd tpool::get] [arg tpool] [arg job]]

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

[call [cmd tpool::preserve] [arg tpool]]

Each call to this command increments the reference counter of the
threadpool [arg tpool] 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.

[call [cmd tpool::release] [arg tpool]]

Each call to this command decrements the reference counter of the 
threadpool [arg tpool] by one (1).Command returns the value of the 
reference counter after the decrement. 
When the reference counter reaches zero (0), the threadpool [arg tpool]
is marked for termination. You should not reference the threadpool 
after the [cmd tpool::release] command returns zero. The [arg tpool]
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.

[call [cmd tpool::suspend] [arg tpool]]

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 
[option minworkers] option of the [cmd tpool::create] command.

[call [cmd tpool::resume] [arg tpool]]

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.

[list_end]


[section 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.
[para]
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.

[see_also tsv ttrace thread]

[keywords thread threadpool]

[manpage_end]







|

|







 







|










|


|








|
|
|




|
|






|


|



|







|



|






|

|
|
|









|
|







 







|
|









|
|






|









|






|
|
|

|











|




|

|
|









|
|
|
|


|




|
|

|

|








3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
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
[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
enter the event loop and allow for servicing of other pending file or
timer (or any other supported) events.

[section COMMANDS]

[list_begin definitions]
................................................................................

[opt_def -minworkers [arg 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 initially. All worker threads
will be started on demand by callers running [cmd tpool::post] command
and posting jobs to the job queue.

[opt_def -maxworkers [arg 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.

[opt_def -idletime [arg 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
[option minthreads] option, it will exit.
If an [option exitscript] has been defined, the exiting worker thread
will first run the script and then exit. Errors from the exit script,
if any, are ignored.
[para]
The idle worker thread is not servicing the event loop. If you, however,
put the worker thread into the event loop, by evaluating the
[cmd vwait] 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.

[opt_def -initcmd [arg 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
[cmd tpool::create] or [cmd tpool::post]) 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.

[opt_def -exitcmd [arg script]]
Sets a Tcl script run when the idle worker thread exits. This is normally
used to cleanup the state of the worker thread, release reserved resources,
cleanup memory and such.
Default value for this option is unspecified, thus no Tcl script will run
on the worker thread exit.

[list_end]

[para]

[call [cmd tpool::names]]

This command returns a list of IDs of threadpools created with the
[cmd tpool::create] command. If no threadpools were found, the
command will return empty list.

[call [cmd tpool::post] [opt -detached] [opt -nowait] [arg tpool] [arg script]]

This command sends a [arg script] to the target [arg tpool] 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 [opt -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.
[para]
The command returns the ID of the posted job. This ID is used for subsequent
[cmd tpool::wait], [cmd tpool::get] and [cmd tpool::cancel] commands to wait
for and retrieve result of the posted script, or cancel the posted job
respectively. If the optional [opt -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.
[para]
If the threadpool [arg tpool] 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.

[call [cmd tpool::wait] [arg tpool] [arg joblist] [opt varname]]
................................................................................

This command waits for one or many jobs, whose job IDs are given in the
[arg joblist] 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.
[para]
The command returns the list of completed job IDs. If the optional variable
[opt varname] is given, it will be set to the list of jobs in the
[arg joblist] which are still pending. If the threadpool [arg tpool]
is not found in the list of active thread pools, the command will throw error.

[call [cmd tpool::cancel] [arg tpool] [arg joblist] [opt varname]]

This command cancels the previously posted jobs given by the [arg joblist]
to the pool [arg tpool]. 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
[opt varname] is given, it will be set to the list of jobs in the
[arg joblist] which were not cancelled. If the threadpool [arg tpool]
is not found in the list of active thread pools, the command will throw error.

[call [cmd tpool::get] [arg tpool] [arg job]]

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

[call [cmd tpool::preserve] [arg tpool]]

Each call to this command increments the reference counter of the
threadpool [arg tpool] 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.

[call [cmd tpool::release] [arg tpool]]

Each call to this command decrements the reference counter of the
threadpool [arg tpool] by one (1).Command returns the value of the
reference counter after the decrement.
When the reference counter reaches zero (0), the threadpool [arg tpool]
is marked for termination. You should not reference the threadpool
after the [cmd tpool::release] command returns zero. The [arg tpool]
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.

[call [cmd tpool::suspend] [arg tpool]]

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
[option minworkers] option of the [cmd tpool::create] command.

[call [cmd tpool::resume] [arg tpool]]

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.

[list_end]


[section 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.
[para]
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.

[see_also tsv ttrace thread]

[keywords thread threadpool]

[manpage_end]

Changes to doc/tsv.man.

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
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
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
...
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
...
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
[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
protected by a mutex so script programmer does not have to take care 
about locking the variable himself.
[para]
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.

[section {ELEMENT COMMANDS}]

[list_begin definitions]

[call [cmd tsv::names] [opt pattern]]

Returns names of shared variables matching optional [opt pattern] 
or all known variables if pattern is ommited.

[call [cmd tsv::object] [arg varname] [arg element]]

Creates object accessor command for the [arg element] in the
shared variable [arg varname]. 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.

[example {
    % tsv::set foo bar "A shared string"
    % set string [tsv::object foo bar]
    % $string append " appended"
    => A shared string appended
}]

[call [cmd tsv::set] [arg varname] [arg element] [opt value]]

Sets the value of the [arg element] in the shared variable [arg varname] 
to [arg value] and returns the value to caller. The [arg value]
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.

[call [cmd tsv::get] [arg varname] [arg element] [opt namedvar]]

Retrieves the value of the [arg element] from the shared variable [arg varname].
If the optional argument [arg namedvar] is given, the value is
stored in the named variable. Return value of the command depends 
of the existence of the optional argument [arg namedvar].
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.

[call [cmd tsv::unset] [arg varname] [opt element]]

Unsets the [arg element] from the shared variable [arg varname].
If the optional element is not given, it deletes the variable.

[call [cmd tsv::exists] [arg varname] [arg element]]

Checks wether the [arg element] exists in the shared variable [arg varname]
and returns true (1) if it does or false (0) if it doesn't.

[call [cmd tsv::pop] [arg varname] [arg element]]

Returns value of the [arg element] in the shared variable [arg varname]
and unsets the element, all in one atomic operation.

................................................................................
Renames the element [arg oldname] to the [arg newname] in the
shared variable [arg varname]. This effectively performs an get/unset/set
sequence of operations but all in one atomic step.

[call [cmd tsv::incr] [arg varname] [arg element] [opt count]]

Similar to standard Tcl [cmd incr] command but increments the value
of the [arg element] in shared variaboe [arg varname] instead of 
the Tcl variable.

[call [cmd tsv::append] [arg varname] [arg element] [arg value] [opt {value ...}]]

Similar to standard Tcl [cmd append] command but appends one or more
values to the [arg element] in shared variable [arg varname] instead of the 
Tcl variable.

[call [cmd tsv::lock] [arg varname] [arg arg] [opt {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
................................................................................
is that they operate on elements of shared arrays.

[list_begin definitions]

[call [cmd tsv::lappend] [arg varname] [arg element] [arg value] [opt {value ...}]]

Similar to standard Tcl [cmd lappend] command but appends one
or more values to the [arg element] in shared variable [arg varname] 
instead of the Tcl variable.

[call [cmd tsv::linsert] [arg varname] [arg element] [arg index] [arg value] [opt {value ...}]]

Similar to standard Tcl [cmd linsert] command but inserts one
or more values at the [arg index] list position in the 
[arg element] in the shared variable [arg varname] instead of the Tcl variable.

[call [cmd tsv::lreplace] [arg varname] [arg element] [arg first] [arg last] [opt {value ...}]]

Similar to standard Tcl [cmd lreplace] command but replaces one
or more values between the [arg first] and [arg last] position 
in the [arg element] of the shared variable [arg varname] instead of 
the Tcl variable.

[call [cmd tsv::llength] [arg varname] [arg element]]

Similar to standard Tcl [cmd llength] command but returns length 
of the [arg element] in the shared variable [arg varname] instead of the Tcl
variable.

[call [cmd tsv::lindex] [arg varname] [arg element] [opt index]]

Similar to standard Tcl [cmd lindex] command but returns the value
at the [arg index] list position of the [arg element] from
................................................................................
Similar to standard Tcl [cmd lset] command but sets the [arg element]
in the shared variable [arg varname] instead of the Tcl variable.

[call [cmd tsv::lpop] [arg varname] [arg element] [opt index]]

Similar to the standard Tcl [cmd lindex] command but in addition to
returning, it also splices the value out of the [arg element]
from the shared variable [arg varname] in one atomic operation. 
In contrast to the Tcl [cmd lindex] command, this command returns 
no value to the caller.

[call [cmd tsv::lpush] [arg varname] [arg element] [opt index]]

This command performes the opposite of the [cmd tsv::lpop] command.
As its counterpart, it returns no value to the caller.

[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]

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


[list_end]

[section {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.
[para]
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:

[example {
    {{NAME  {Frank  Zappa}} {JOB {musician and composer}}}
}]

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:

[example {
    {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}
}]

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.

[list_begin definitions]

[call [cmd tsv::keyldel] [arg varname] [arg keylist] [arg key]]

Delete the field specified by [arg key] from the keyed list [arg keylist]
................................................................................
in the shared variable [arg varname].
This removes both the key and the value from the keyed list.

[call [cmd tsv::keylget] [arg varname] [arg keylist] [arg key] [opt retvar]]

Return the value associated with [arg key] from the keyed list [arg keylist]
in the shared variable [arg varname].
If the optional [arg retvar] 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.
[para]
If [arg retvar] is specified and [arg key] is in the list, then the value 
is returned in the variable [arg retvar] and the command returns 1 if the
key was present within the list. If [arg key] isn't in the list, the 
command will return 0, and [arg retvar] will be left unchanged. If {} is
specified for [arg retvar], the value is not returned, allowing the Tcl
programmer to determine if a [arg key] is present in a keyed list without
setting a variable as a side-effect.

[call [cmd tsv::keylkeys] [arg varname] [arg keylist] [opt key]]
Return  the a list of the keys in the keyed list [arg keylist] in the 
shared variable [arg varname]. If [arg key] is specified, then it is 
the name of a key field who's subfield keys are to be retrieved.


[call [cmd tsv::keylset] [arg varname] [arg keylist] [arg key] [arg value] [opt {key value..}]]
Set the value associated with [arg key], in the keyed list [arg keylist]
to [arg value]. If the [arg keylist] does not exists, it is created. 
If [arg key] is not currently in the list, it will be added. If it already
exists, [arg value] replaces the existing value. Multiple keywords and 
values may be specified, if desired.

[list_end]


[section 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.
[para]
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.

[section CREDITS]
Thread shared variables are inspired by the nsv interface found in 
AOLserver, a highly scalable Web server from America Online.

[see_also tpool ttrace thread]

[keywords threads synchronization locking {thread shared data}]

[manpage_end]







|


|
|


|


|








|
|




|













|

|






|

|
|
|









|







 







|





|







 







|





|





|
|




|







 







|
|




|








|







 







|











|

|







|
|
|








|
|







 







|
|


|

|






|
|
|




|

|










|



|


|
|


|







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
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
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
...
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
...
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
[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 internally
protected by a mutex so script programmer does not have to take care
about locking the variable himself.
[para]
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.

[section {ELEMENT COMMANDS}]

[list_begin definitions]

[call [cmd tsv::names] [opt pattern]]

Returns names of shared variables matching optional [opt pattern]
or all known variables if pattern is omitted.

[call [cmd tsv::object] [arg varname] [arg element]]

Creates object accessor command for the [arg element] in the
shared variable [arg varname]. 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.

[example {
    % tsv::set foo bar "A shared string"
    % set string [tsv::object foo bar]
    % $string append " appended"
    => A shared string appended
}]

[call [cmd tsv::set] [arg varname] [arg element] [opt value]]

Sets the value of the [arg element] in the shared variable [arg varname]
to [arg value] and returns the value to caller. The [arg value]
may be omitted, in which case the command will return the current
value of the element. If the element cannot be found, error is triggered.

[call [cmd tsv::get] [arg varname] [arg element] [opt namedvar]]

Retrieves the value of the [arg element] from the shared variable [arg varname].
If the optional argument [arg namedvar] is given, the value is
stored in the named variable. Return value of the command depends
of the existence of the optional argument [arg namedvar].
If the argument is omitted and the requested element cannot be found
in the shared array, the command triggers error. If, however, the
optional argument is given on the command line, the command returns
true (1) if the element is found or false (0) if the element is not found.

[call [cmd tsv::unset] [arg varname] [opt element]]

Unsets the [arg element] from the shared variable [arg varname].
If the optional element is not given, it deletes the variable.

[call [cmd tsv::exists] [arg varname] [arg element]]

Checks whether the [arg element] exists in the shared variable [arg varname]
and returns true (1) if it does or false (0) if it doesn't.

[call [cmd tsv::pop] [arg varname] [arg element]]

Returns value of the [arg element] in the shared variable [arg varname]
and unsets the element, all in one atomic operation.

................................................................................
Renames the element [arg oldname] to the [arg newname] in the
shared variable [arg varname]. This effectively performs an get/unset/set
sequence of operations but all in one atomic step.

[call [cmd tsv::incr] [arg varname] [arg element] [opt count]]

Similar to standard Tcl [cmd incr] command but increments the value
of the [arg element] in shared variable [arg varname] instead of
the Tcl variable.

[call [cmd tsv::append] [arg varname] [arg element] [arg value] [opt {value ...}]]

Similar to standard Tcl [cmd append] command but appends one or more
values to the [arg element] in shared variable [arg varname] instead of the
Tcl variable.

[call [cmd tsv::lock] [arg varname] [arg arg] [opt {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
................................................................................
is that they operate on elements of shared arrays.

[list_begin definitions]

[call [cmd tsv::lappend] [arg varname] [arg element] [arg value] [opt {value ...}]]

Similar to standard Tcl [cmd lappend] command but appends one
or more values to the [arg element] in shared variable [arg varname]
instead of the Tcl variable.

[call [cmd tsv::linsert] [arg varname] [arg element] [arg index] [arg value] [opt {value ...}]]

Similar to standard Tcl [cmd linsert] command but inserts one
or more values at the [arg index] list position in the
[arg element] in the shared variable [arg varname] instead of the Tcl variable.

[call [cmd tsv::lreplace] [arg varname] [arg element] [arg first] [arg last] [opt {value ...}]]

Similar to standard Tcl [cmd lreplace] command but replaces one
or more values between the [arg first] and [arg last] position
in the [arg element] of the shared variable [arg varname] instead of
the Tcl variable.

[call [cmd tsv::llength] [arg varname] [arg element]]

Similar to standard Tcl [cmd llength] command but returns length
of the [arg element] in the shared variable [arg varname] instead of the Tcl
variable.

[call [cmd tsv::lindex] [arg varname] [arg element] [opt index]]

Similar to standard Tcl [cmd lindex] command but returns the value
at the [arg index] list position of the [arg element] from
................................................................................
Similar to standard Tcl [cmd lset] command but sets the [arg element]
in the shared variable [arg varname] instead of the Tcl variable.

[call [cmd tsv::lpop] [arg varname] [arg element] [opt index]]

Similar to the standard Tcl [cmd lindex] command but in addition to
returning, it also splices the value out of the [arg element]
from the shared variable [arg varname] in one atomic operation.
In contrast to the Tcl [cmd lindex] command, this command returns
no value to the caller.

[call [cmd tsv::lpush] [arg varname] [arg element] [opt index]]

This command performs the opposite of the [cmd tsv::lpop] command.
As its counterpart, it returns no value to the caller.

[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 persistent storage databases. Currently the persistent
options supported are the famous GNU Gdbm and LMDB. These options have to be
selected during the package compilation time.
The implementation provides hooks for defining other persistency layers, if
needed.

[list_begin definitions]

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


[list_end]

[section {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.
[para]
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:

[example {
    {{NAME  {Frank  Zappa}} {JOB {musician and composer}}}
}]

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:

[example {
    {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}
}]

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.

[list_begin definitions]

[call [cmd tsv::keyldel] [arg varname] [arg keylist] [arg key]]

Delete the field specified by [arg key] from the keyed list [arg keylist]
................................................................................
in the shared variable [arg varname].
This removes both the key and the value from the keyed list.

[call [cmd tsv::keylget] [arg varname] [arg keylist] [arg key] [opt retvar]]

Return the value associated with [arg key] from the keyed list [arg keylist]
in the shared variable [arg varname].
If the optional [arg retvar] 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.
[para]
If [arg retvar] is specified and [arg key] is in the list, then the value
is returned in the variable [arg retvar] and the command returns 1 if the
key was present within the list. If [arg key] isn't in the list, the
command will return 0, and [arg retvar] will be left unchanged. If {} is
specified for [arg retvar], the value is not returned, allowing the Tcl
programmer to determine if a [arg key] is present in a keyed list without
setting a variable as a side-effect.

[call [cmd tsv::keylkeys] [arg varname] [arg keylist] [opt key]]
Return  the a list of the keys in the keyed list [arg keylist] in the
shared variable [arg varname]. If [arg key] is specified, then it is
the name of a key field whose subfield keys are to be retrieved.


[call [cmd tsv::keylset] [arg varname] [arg keylist] [arg key] [arg value] [opt {key value..}]]
Set the value associated with [arg key], in the keyed list [arg keylist]
to [arg value]. If the [arg keylist] does not exists, it is created.
If [arg key] is not currently in the list, it will be added. If it already
exists, [arg value] replaces the existing value. Multiple keywords and
values may be specified, if desired.

[list_end]


[section 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.
[para]
Due to the internal design of the Tcl core, there is no provision of full
integration of shared variables within the Tcl syntax, unfortunately. All
access to shared data must be performed with the supplied package commands.
Also, variable traces are not supported. But even so, benefits of easy,
simple and safe shared data manipulation outweighs imposed limitations.

[section CREDITS]
Thread shared variables are inspired by the nsv interface found in
AOLserver, a highly scalable Web server from America Online.

[see_also tpool ttrace thread]

[keywords threads synchronization locking {thread shared data}]

[manpage_end]

Changes to doc/ttrace.man.

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
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
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
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
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
[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
................................................................................

    % thread::send $t2 test
    test-tid0x1804000

}]
[para]
As seen from above, the [cmd ttrace::eval] and [cmd ttrace::update]
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.

[section {USER COMMANDS}]
This section describes user-level commands. Those commands can be
used by script writers to control the execution of the tracing
framework.
................................................................................
[list_begin definitions]

[call [cmd ttrace::eval] [arg arg] [opt {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.
[para]
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.

[call [cmd ttrace::enable]]

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.

................................................................................
[call [cmd ttrace::disable]]

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

[call [cmd ttrace::cleanup]]

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

[call [cmd ttrace::update] [opt epoch]]

Used to refresh the state of the interpreter to match the optional 
trace [opt epoch]. If the optional [opt epoch] is not given, it takes
the most recent trace epoch.

[call [cmd ttrace::getscript]]

Returns a synthetized Tcl script which may be sourced in any interpreter.
This script sets the stage for the Tcl [cmd unknown] command so it can
load traced resources from the in-memory database. Normally, this command
is automatically invoked by other higher-level commands like
[cmd ttrace::eval] and [cmd ttrace::update].

[list_end]

[section {CALLBACK COMMANDS}]
A word upfront: the package already includes callbacks for tracing 
following Tcl commands: [cmd proc], [cmd namespace], [cmd variable],
[cmd load], and [cmd rename]. 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.
[para]
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.
................................................................................
of callback arguments, [arg arglist] and the [arg body] of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl [cmd proc] command.


[call [cmd ttrace::addtrace] [arg cmd] [arg arglist] [arg body]]

Registers Tcl callback to be activated for tracing the Tcl 
[cmd cmd] command. The callback definition includes the name of 
the Tcl command to trace, [arg cmd], a list of callback arguments, 
[arg arglist] and the [arg body] of the callback. Effectively, 
this actually resembles the call interface of the standard Tcl 
[cmd proc] command.


[call [cmd ttrace::addscript] [arg name] [arg body]]

Registers Tcl callback to be activated for building a Tcl
script to be passed to other interpreters. This script is
................................................................................
[arg name] and the [arg body] of the callback.

[call [cmd ttrace::addresolver] [arg cmd] [arg arglist] [arg body]]

Registers Tcl callback to be activated by the overloaded Tcl
[cmd unknown] command.
Registered callbacks are activated on FIFO basis.
This callback is used to resolve the resource and load the 
resource in the current interpreter.

[call [cmd ttrace::addcleanup] [arg body]]

Registers Tcl callback to be activated by the [cmd trace::cleanup].
Registered callbacks are activated on FIFO basis.

[call [cmd ttrace::addentry] [arg cmd] [arg var] [arg val]]

Adds one entry to the named in-memory database.

[call [cmd ttrace::getentry] [arg cmd] [arg var]]

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

[call [cmd ttrace::getentries] [arg cmd] [opt pattern]]

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

[call [cmd ttrace::delentry] [arg cmd]]

Deletes an entry from the named in-memory database.

[call [cmd ttrace::preload] [arg cmd]]

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
[cmd unknown] command.

[list_end]

[section 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 [cmd proc], 
[cmd namespace], [cmd load] 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 [cmd unknown] 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.
[para]
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.

[see_also tsv tpool thread]

[keywords {command tracing} introspection]

[manpage_end]







|
|



|








|







 







|







 







|


|





|
|







 







|




|





|








|

|
|


|







 







|
|
|
|
|







 







|













|












|









|
|



|
|
|
|



|

|






|







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
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
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
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
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
[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 across 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.2

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

    % ttrace::eval {proc test args {return test-[thread::id]}}
    % thread::send $t1 test
    test-tid0x1802800
................................................................................

    % thread::send $t2 test
    test-tid0x1804000

}]
[para]
As seen from above, the [cmd ttrace::eval] and [cmd ttrace::update]
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.

[section {USER COMMANDS}]
This section describes user-level commands. Those commands can be
used by script writers to control the execution of the tracing
framework.
................................................................................
[list_begin definitions]

[call [cmd ttrace::eval] [arg arg] [opt {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.
[para]
This is the most important user-level command of the package as
it wraps most of the commands described below. This greatly
simplifies things, because user need to learn just this (one)
command in order to effectively use the package. Other commands,
as described below, are included mostly for the sake of completeness.

[call [cmd ttrace::enable]]

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.

................................................................................
[call [cmd ttrace::disable]]

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

[call [cmd ttrace::cleanup]]

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

[call [cmd ttrace::update] [opt epoch]]

Used to refresh the state of the interpreter to match the optional
trace [opt epoch]. If the optional [opt epoch] is not given, it takes
the most recent trace epoch.

[call [cmd ttrace::getscript]]

Returns a synthesized Tcl script which may be sourced in any interpreter.
This script sets the stage for the Tcl [cmd unknown] command so it can
load traced resources from the in-memory database. Normally, this command
is automatically invoked by other higher-level commands like
[cmd ttrace::eval] and [cmd ttrace::update].

[list_end]

[section {CALLBACK COMMANDS}]
A word upfront: the package already includes callbacks for tracing
following Tcl commands: [cmd proc], [cmd namespace], [cmd variable],
[cmd load], and [cmd rename]. Additionally, a set of callbacks for
tracing resources (object, classes) for the XOTcl v1.3.8+, an
OO-extension to Tcl, is also provided.
This gives a solid base for solving most of the real-life needs and
serves as an example for people wanting to customize the package
to cover their specific needs.
[para]
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.
................................................................................
of callback arguments, [arg arglist] and the [arg body] of the
callback. Effectively, this actually resembles the call interface
of the standard Tcl [cmd proc] command.


[call [cmd ttrace::addtrace] [arg cmd] [arg arglist] [arg body]]

Registers Tcl callback to be activated for tracing the Tcl
[cmd cmd] command. The callback definition includes the name of
the Tcl command to trace, [arg cmd], a list of callback arguments,
[arg arglist] and the [arg body] of the callback. Effectively,
this actually resembles the call interface of the standard Tcl
[cmd proc] command.


[call [cmd ttrace::addscript] [arg name] [arg body]]

Registers Tcl callback to be activated for building a Tcl
script to be passed to other interpreters. This script is
................................................................................
[arg name] and the [arg body] of the callback.

[call [cmd ttrace::addresolver] [arg cmd] [arg arglist] [arg body]]

Registers Tcl callback to be activated by the overloaded Tcl
[cmd unknown] command.
Registered callbacks are activated on FIFO basis.
This callback is used to resolve the resource and load the
resource in the current interpreter.

[call [cmd ttrace::addcleanup] [arg body]]

Registers Tcl callback to be activated by the [cmd trace::cleanup].
Registered callbacks are activated on FIFO basis.

[call [cmd ttrace::addentry] [arg cmd] [arg var] [arg val]]

Adds one entry to the named in-memory database.

[call [cmd ttrace::getentry] [arg cmd] [arg var]]

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

[call [cmd ttrace::getentries] [arg cmd] [opt pattern]]

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

[call [cmd ttrace::delentry] [arg cmd]]

Deletes an entry from the named in-memory database.

[call [cmd ttrace::preload] [arg cmd]]

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
[cmd unknown] command.

[list_end]

[section 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 [cmd proc],
[cmd namespace], [cmd load] 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 [cmd unknown] 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.
[para]
Immediate benefit of such approach is the much smaller memory footprint
of the application and much faster thread creation. By not actually
loading all necessary procedures (and other resources) in every thread
at the thread initialization time, but by deferring this to the time the
resource is actually referenced, significant improvements in both
memory consumption and thread initialization time can be achieved. Some
tests have shown that memory footprint of an multithreading Tcl application
went down more than three times and thread startup time was reduced for
about 50 times. Note that your mileage may vary.

Other benefits include much finer control about what (and when) gets
replicated from the master to other Tcl thread/interpreters.

[see_also tsv tpool thread]

[keywords {command tracing} introspection]

[manpage_end]

Changes to generic/psLmdb.c.

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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;







|







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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;

Changes to generic/tclThreadInt.h.

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

typedef struct {
    void *unused1;
    void *unused2;
    int errorLine;
} tclInterpType;

#if defined(TCL_TIP285)
# undef Tcl_GetErrorLine
# if defined(USE_TCL_STUBS)
#   define Tcl_GetErrorLine(interp) ((threadTclVersion>85)? \
    ((int (*)(Tcl_Interp *))((&(tclStubsPtr->tcl_PkgProvideEx))[605]))(interp): \
    (((tclInterpType *)(interp))->errorLine))

#   undef Tcl_AddErrorInfo
#   define Tcl_AddErrorInfo(interp, msg) ((threadTclVersion>85)? \
    ((void (*)(Tcl_Interp *, Tcl_Obj *))((&(tclStubsPtr->tcl_PkgProvideEx))[574]))(interp, Tcl_NewStringObj(msg, -1)): \
    ((void (*)(Tcl_Interp *, const char *))((&(tclStubsPtr->tcl_PkgProvideEx))[66]))(interp, msg))

#   undef Tcl_BackgroundError
#   define Tcl_BackgroundError(interp) ((threadTclVersion>85)? \
    ((void (*)(Tcl_Interp *, int))((&(tclStubsPtr->tcl_PkgProvideEx))[609]))(interp, TCL_ERROR): \
    ((void (*)(Tcl_Interp *))((&(tclStubsPtr->tcl_PkgProvideEx))[76]))(interp))
# else


#   define Tcl_GetErrorLine(interp) (((tclInterpType *)(interp))->errorLine)
# endif
#endif


/* 8.5, 8.4, or less - Emulate access to the error-line information
 * This is TIP 336, unrelated to 285 (async cancellation).  When doing
 * a static link of the thread package (use case: basekits, tclkits,
 * ...)  and the core Tcl is < 8.6 we cannot use TCL_TIP285 to get
 * things done, because USE_TCL_STUBS is not set for static builds,
 * causing the check in threadCmd.c to bomb.
 */



#ifndef TCL_TIP285
# if !TCL_MINIMUM_VERSION(8,6)
#   define Tcl_GetErrorLine(interp) (((tclInterpType *)(interp))->errorLine)
# endif



#endif

#endif /* _TCL_THREAD_INT_H_ */







|

<
|


>
|
|


>
|
|


<
>
>
|
<


<
|
|
|
|
|
<

<
>
>
|
|
|
|
>
>
>



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

typedef struct {
    void *unused1;
    void *unused2;
    int errorLine;
} tclInterpType;

#if defined(TCL_TIP285) && defined(USE_TCL_STUBS)
# undef Tcl_GetErrorLine

# define Tcl_GetErrorLine(interp) ((threadTclVersion>85)? \
    ((int (*)(Tcl_Interp *))((&(tclStubsPtr->tcl_PkgProvideEx))[605]))(interp): \
    (((tclInterpType *)(interp))->errorLine))
/* TIP #270 */
# undef Tcl_AddErrorInfo
# define Tcl_AddErrorInfo(interp, msg) ((threadTclVersion>85)? \
    ((void (*)(Tcl_Interp *, Tcl_Obj *))((&(tclStubsPtr->tcl_PkgProvideEx))[574]))(interp, Tcl_NewStringObj(msg, -1)): \
    ((void (*)(Tcl_Interp *, const char *))((&(tclStubsPtr->tcl_PkgProvideEx))[66]))(interp, msg))
/* TIP #337 */
# undef Tcl_BackgroundError
# define Tcl_BackgroundError(interp) ((threadTclVersion>85)? \
    ((void (*)(Tcl_Interp *, int))((&(tclStubsPtr->tcl_PkgProvideEx))[609]))(interp, TCL_ERROR): \
    ((void (*)(Tcl_Interp *))((&(tclStubsPtr->tcl_PkgProvideEx))[76]))(interp))

#elif !TCL_MINIMUM_VERSION(8,6)
  /* 8.5, 8.4, or less - Emulate access to the error-line information */
# define Tcl_GetErrorLine(interp) (((tclInterpType *)(interp))->errorLine)

#endif


/* When running on Tcl >= 8.7, make sure that Thread still runs when Tcl is compiled
 * with -DTCL_NO_DEPRECATED=1. Stub entries for Tcl_SetIntObj/Tcl_NewIntObj are NULL then.
 * Just use Tcl_SetWideIntObj/Tcl_NewWideIntObj in stead. We don't simply want to use
 * Tcl_SetWideIntObj/Tcl_NewWideIntObj always, since extensions might not expect to
 * get an actual "wideInt".

 */

#if defined(USE_TCL_STUBS)
# undef Tcl_SetIntObj
# define Tcl_SetIntObj(objPtr, value) ((threadTclVersion>86)? \
  ((void (*)(Tcl_Obj *, Tcl_WideInt))((&(tclStubsPtr->tcl_PkgProvideEx))[489]))(objPtr, (int)(value)): \
  ((void (*)(Tcl_Obj *, int))((&(tclStubsPtr->tcl_PkgProvideEx))[61]))(objPtr, value))
# undef Tcl_NewIntObj
# define Tcl_NewIntObj(value) ((threadTclVersion>86)? \
  ((Tcl_Obj * (*)(Tcl_WideInt))((&(tclStubsPtr->tcl_PkgProvideEx))[488]))((int)(value)): \
  ((Tcl_Obj * (*)(int))((&(tclStubsPtr->tcl_PkgProvideEx))[52]))(value))
#endif

#endif /* _TCL_THREAD_INT_H_ */

Changes to generic/tclXkeylist.c.

24
25
26
27
28
29
30

31
32
33
34
35
36
37
..
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
...
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
....
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
....
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
....
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
....
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
....
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
....
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
 * For any questions, contant Zoran Vasiljevic (zoran@archiware.com)
 *-----------------------------------------------------------------------------
 */

#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 TclX_Assert(expr) ((expr) ? (void)0 : \
                            panic("TclX assertion failure: %s:%d \"%s\"\n",\
                            __FILE__, __LINE__, "expr"))
#else
# define TclX_Assert(expr)
#endif

#define TRUE  1
#define FALSE 0

/*
 * Macro that behaves like strdup, only uses ckalloc.  Also macro that does the
 * same with a string that might contain zero bytes,
 */

#define ckstrdup(sourceStr) \
  (strcpy (ckalloc (strlen (sourceStr) + 1), sourceStr))
................................................................................
 *
 *   Check if an object is {}, either in list or zero-lemngth string form, with
 * out forcing a conversion.
 *
 * Parameters:
 *   o objPtr - Object to check.
 * Returns:
 *   True if NULL, FALSE if not.
 *-----------------------------------------------------------------------------
 */
static int
TclX_IsNullObj (objPtr)
    Tcl_Obj *objPtr;
{
    if (objPtr->typePtr == NULL) {
................................................................................
{
    int idx;

    TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
    TclX_Assert (keylIntPtr->arraySize >= 0);
    TclX_Assert (keylIntPtr->numEntries >= 0);
    TclX_Assert ((keylIntPtr->arraySize > 0) ?
                 (keylIntPtr->entries != NULL) : TRUE);
    TclX_Assert ((keylIntPtr->numEntries > 0) ?
                 (keylIntPtr->entries != NULL) : TRUE);

    for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
        keylEntry_t *entryPtr = &(keylIntPtr->entries [idx]);
        TclX_Assert (entryPtr->key != NULL);
        TclX_Assert (entryPtr->valuePtr->refCount >= 1);
        if (entryPtr->valuePtr->typePtr == &keyedListType) {
            ValidateKeyedList (entryPtr->valuePtr->internalRep.twoPtrValue.ptr1);
................................................................................
 * ValidateKey --
 *   Check that a key or keypath string is a valid value.
 *
 * Parameters:
 *   o interp - Used to return error messages.
 *   o key - Key string to check.
 *   o keyLen - Length of the string, used to check for binary data.
 *   o isPath - TRUE if this is a key path, FALSE if its a simple key and
 *     thus "." is illegal.
 * Returns:
 *    TCL_OK or TCL_ERROR.
 *-----------------------------------------------------------------------------
 */
static int
ValidateKey(interp, key, keyLen, isPath)
................................................................................
                                "element list, found \"",
                                Tcl_GetString(objPtr),
                                "\"", (char *) NULL);
        return TCL_ERROR;
    }

    key = Tcl_GetString(objv[0]);
    if (ValidateKey(interp, key, objv[0]->length, FALSE) == TCL_ERROR) {
        return TCL_ERROR;
    }

    entryPtr->key = ckstrdup(key);
    entryPtr->valuePtr = Tcl_DuplicateObj(objv [1]);
    Tcl_IncrRefCount(entryPtr->valuePtr);

................................................................................
        return TCL_ERROR;
    }

    /*
     * Handle retrieving a value for a specified key.
     */
    key = Tcl_GetString(objv[2]);
    if (ValidateKey(interp, key, objv[2]->length, TRUE) == TCL_ERROR) {
        return TCL_ERROR;
    }

    status = TclX_KeyedListGet (interp, keylPtr, key, &valuePtr);
    if (status == TCL_ERROR)
        return TCL_ERROR;

................................................................................
        if (objc == 3) {
            TclX_AppendObjResult (interp, "key \"",  key,
                                  "\" not found in keyed list",
                                  (char *) NULL);
            return TCL_ERROR;
        } else {
            Tcl_ResetResult(interp);
            Tcl_SetBooleanObj (Tcl_GetObjResult (interp), FALSE);
            return TCL_OK;
        }
    }

    /*
     * No variable specified, so return value in the result.
     */
................................................................................
     */
    if (!TclX_IsNullObj(objv [3])) {
        if (Tcl_ObjSetVar2(interp, objv[3], NULL,
                          valuePtr, TCL_LEAVE_ERR_MSG) == NULL)
            return TCL_ERROR;
    }
    Tcl_ResetResult(interp);
    Tcl_SetBooleanObj (Tcl_GetObjResult (interp), TRUE);
    return TCL_OK;
}
 
/*-----------------------------------------------------------------------------
 * Tcl_KeylsetObjCmd --
 *     Implements the TCL keylset command:
 *         keylset listvar key value ?key value...?
................................................................................
        newVarObj = keylVarPtr;
    } else {
        newVarObj = NULL;
    }

    for (idx = 2; idx < objc; idx += 2) {
        key = Tcl_GetString(objv[idx]);
        if (ValidateKey(interp, key, objv[idx]->length, TRUE) == TCL_ERROR) {
            goto errorExit;
        }
        if (TclX_KeyedListSet (interp, keylVarPtr, key, objv [idx+1]) != TCL_OK) {
            goto errorExit;
        }
    }

................................................................................
            Tcl_DecrRefCount (keylPtr);
        }
    }
    keylPtr = keylVarPtr;

    for (idx = 2; idx < objc; idx++) {
        key = Tcl_GetString(objv[idx]);
        if (ValidateKey(interp, key, objv[idx]->length, TRUE) == TCL_ERROR) {
            return TCL_ERROR;
        }

        status = TclX_KeyedListDelete (interp, keylPtr, key);
        switch (status) {
          case TCL_BREAK:
            TclX_AppendObjResult (interp, "key not found: \"",
................................................................................
     * If key argument is not specified, then objv [2] is NULL or empty,
     * meaning get top level keys.
     */
    if (objc < 3) {
        key = NULL;
    } else {
        key = Tcl_GetString(objv[2]);
        if (ValidateKey(interp, key, objv[2]->length, TRUE) == TCL_ERROR) {
            return TCL_ERROR;
        }
    }

    status = TclX_KeyedListGetKeys (interp, keylPtr, key, &listObjPtr);
    switch (status) {
      case TCL_BREAK:







>







 







<
<
<







 







|







 







|

|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
72
73
74
75
76
77
78



79
80
81
82
83
84
85
..
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
...
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
....
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
....
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
....
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
....
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
....
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
....
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
 * For any questions, contant Zoran Vasiljevic (zoran@archiware.com)
 *-----------------------------------------------------------------------------
 */

#include "tclThreadInt.h"
#include "threadSvCmd.h"
#include "tclXkeylist.h"
#include <stdarg.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 TclX_Assert(expr) ((expr) ? (void)0 : \
                            panic("TclX assertion failure: %s:%d \"%s\"\n",\
                            __FILE__, __LINE__, "expr"))
#else
# define TclX_Assert(expr)
#endif




/*
 * Macro that behaves like strdup, only uses ckalloc.  Also macro that does the
 * same with a string that might contain zero bytes,
 */

#define ckstrdup(sourceStr) \
  (strcpy (ckalloc (strlen (sourceStr) + 1), sourceStr))
................................................................................
 *
 *   Check if an object is {}, either in list or zero-lemngth string form, with
 * out forcing a conversion.
 *
 * Parameters:
 *   o objPtr - Object to check.
 * Returns:
 *   1 if NULL, 0 if not.
 *-----------------------------------------------------------------------------
 */
static int
TclX_IsNullObj (objPtr)
    Tcl_Obj *objPtr;
{
    if (objPtr->typePtr == NULL) {
................................................................................
{
    int idx;

    TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
    TclX_Assert (keylIntPtr->arraySize >= 0);
    TclX_Assert (keylIntPtr->numEntries >= 0);
    TclX_Assert ((keylIntPtr->arraySize > 0) ?
                 (keylIntPtr->entries != NULL) : 1);
    TclX_Assert ((keylIntPtr->numEntries > 0) ?
                 (keylIntPtr->entries != NULL) : 1);

    for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
        keylEntry_t *entryPtr = &(keylIntPtr->entries [idx]);
        TclX_Assert (entryPtr->key != NULL);
        TclX_Assert (entryPtr->valuePtr->refCount >= 1);
        if (entryPtr->valuePtr->typePtr == &keyedListType) {
            ValidateKeyedList (entryPtr->valuePtr->internalRep.twoPtrValue.ptr1);
................................................................................
 * ValidateKey --
 *   Check that a key or keypath string is a valid value.
 *
 * Parameters:
 *   o interp - Used to return error messages.
 *   o key - Key string to check.
 *   o keyLen - Length of the string, used to check for binary data.
 *   o isPath - 1 if this is a key path, 0 if its a simple key and
 *     thus "." is illegal.
 * Returns:
 *    TCL_OK or TCL_ERROR.
 *-----------------------------------------------------------------------------
 */
static int
ValidateKey(interp, key, keyLen, isPath)
................................................................................
                                "element list, found \"",
                                Tcl_GetString(objPtr),
                                "\"", (char *) NULL);
        return TCL_ERROR;
    }

    key = Tcl_GetString(objv[0]);
    if (ValidateKey(interp, key, objv[0]->length, 0) == TCL_ERROR) {
        return TCL_ERROR;
    }

    entryPtr->key = ckstrdup(key);
    entryPtr->valuePtr = Tcl_DuplicateObj(objv [1]);
    Tcl_IncrRefCount(entryPtr->valuePtr);

................................................................................
        return TCL_ERROR;
    }

    /*
     * Handle retrieving a value for a specified key.
     */
    key = Tcl_GetString(objv[2]);
    if (ValidateKey(interp, key, objv[2]->length, 1) == TCL_ERROR) {
        return TCL_ERROR;
    }

    status = TclX_KeyedListGet (interp, keylPtr, key, &valuePtr);
    if (status == TCL_ERROR)
        return TCL_ERROR;

................................................................................
        if (objc == 3) {
            TclX_AppendObjResult (interp, "key \"",  key,
                                  "\" not found in keyed list",
                                  (char *) NULL);
            return TCL_ERROR;
        } else {
            Tcl_ResetResult(interp);
            Tcl_SetIntObj(Tcl_GetObjResult (interp), 0);
            return TCL_OK;
        }
    }

    /*
     * No variable specified, so return value in the result.
     */
................................................................................
     */
    if (!TclX_IsNullObj(objv [3])) {
        if (Tcl_ObjSetVar2(interp, objv[3], NULL,
                          valuePtr, TCL_LEAVE_ERR_MSG) == NULL)
            return TCL_ERROR;
    }
    Tcl_ResetResult(interp);
    Tcl_SetIntObj(Tcl_GetObjResult (interp), 1);
    return TCL_OK;
}
 
/*-----------------------------------------------------------------------------
 * Tcl_KeylsetObjCmd --
 *     Implements the TCL keylset command:
 *         keylset listvar key value ?key value...?
................................................................................
        newVarObj = keylVarPtr;
    } else {
        newVarObj = NULL;
    }

    for (idx = 2; idx < objc; idx += 2) {
        key = Tcl_GetString(objv[idx]);
        if (ValidateKey(interp, key, objv[idx]->length, 1) == TCL_ERROR) {
            goto errorExit;
        }
        if (TclX_KeyedListSet (interp, keylVarPtr, key, objv [idx+1]) != TCL_OK) {
            goto errorExit;
        }
    }

................................................................................
            Tcl_DecrRefCount (keylPtr);
        }
    }
    keylPtr = keylVarPtr;

    for (idx = 2; idx < objc; idx++) {
        key = Tcl_GetString(objv[idx]);
        if (ValidateKey(interp, key, objv[idx]->length, 1) == TCL_ERROR) {
            return TCL_ERROR;
        }

        status = TclX_KeyedListDelete (interp, keylPtr, key);
        switch (status) {
          case TCL_BREAK:
            TclX_AppendObjResult (interp, "key not found: \"",
................................................................................
     * If key argument is not specified, then objv [2] is NULL or empty,
     * meaning get top level keys.
     */
    if (objc < 3) {
        key = NULL;
    } else {
        key = Tcl_GetString(objv[2]);
        if (ValidateKey(interp, key, objv[2]->length, 1) == TCL_ERROR) {
            return TCL_ERROR;
        }
    }

    status = TclX_KeyedListGetKeys (interp, keylPtr, key, &listObjPtr);
    switch (status) {
      case TCL_BREAK:

Changes to generic/threadCmd.c.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
431
432
433
434
435
436
437
438



439



440
441
442
443
444
445
446

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

................................................................................
#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) {








|







 







|
>
>
>
|
>
>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

/*
 * 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.9a1"
#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.
 */

................................................................................
#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))
#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 7 && defined(TCL_NO_DEPRECATED)
                /* As long as Tcl 8.7 is not final, this allows the Thread extension */
                /* to be loadable on Tcl 9.0, provided it is compiled against Tcl 8.7+ headers */
                || !(Tcl_InitStubs)(interp, "8.4-",
                (TCL_MAJOR_VERSION<<8)|(TCL_MINOR_VERSION<<16), TCL_STUB_MAGIC)
#endif
            ) {
            return TCL_ERROR;
        }
        Tcl_ResetResult(interp);
    }

    if (!threadTclVersion) {

Changes to generic/threadPoolCmd.c.

359
360
361
362
363
364
365








366
367
368
369
370
371
372
...
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
....
1224
1225
1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
....
1647
1648
1649
1650
1651
1652
1653
1654
1655

1656
1657
1658
1659
1660
1661
1662
            detached  = 1;
        } else if (OPT_CMP(opt, "-nowait")) {
            nowait = 1;
        } else {
            goto usage;
        }
    }









    tpoolName = Tcl_GetString(objv[ii]);
    script    = Tcl_GetString(objv[ii+1]);
    len = objv[ii+1]->length;
    tpoolPtr  = GetTpool(tpoolName);
    if (tpoolPtr == NULL) {
        Tcl_AppendResult(interp, "can not find threadpool \"", tpoolName,
................................................................................
    char *tpoolName;
    Tcl_Obj *listVar = NULL;
    Tcl_Obj *doneList, *waitList, **wObjv;
    ThreadPool *tpoolPtr;
    TpoolResult *rPtr;

    /*
     * Syntax: tpool::wait tpoolId jobIdList ?listVar?
     */

    if (objc < 3 || objc > 4) {
        Tcl_WrongNumArgs(interp, 1, objv, "tpoolId jobIdList ?listVar");
        return TCL_ERROR;
    }
    if (objc == 4) {
................................................................................
            }
        } else if (tpoolPtr->tearDown) {
            PushWork(rPtr, tpoolPtr);
            break; /* Kill worker because pool is going down */
        }
        Tcl_MutexUnlock(&tpoolPtr->mutex);
        TpoolEval(interp, rPtr->script, rPtr->scriptLen, rPtr);
        Tcl_MutexLock(&tpoolPtr->mutex);
        ckfree(rPtr->script);

        if (!rPtr->detached) {
            int new;
            Tcl_SetHashValue(Tcl_CreateHashEntry(&tpoolPtr->jobsDone,
                                                 (void *)(size_t)rPtr->jobId, &new),
                             (ClientData)rPtr);

        } else {
            ckfree((char*)rPtr);
        }
    }

    /*
     * Tear down the worker
................................................................................
    SpliceOut(tpoolPtr, tpoolList);
    InitWaiter();

    /*
     * Signal and wait for all workers to die.
     */

    tpoolPtr->tearDown = 1;
    Tcl_MutexLock(&tpoolPtr->mutex);

    while (tpoolPtr->numWorkers > 0) {
        PushWaiter(tpoolPtr);
        Tcl_ConditionNotify(&tpoolPtr->cond);
        Tcl_MutexUnlock(&tpoolPtr->mutex);
        tsdPtr->stop = -1;
        while(tsdPtr->stop == -1) {
            Tcl_DoOneEvent(TCL_ALL_EVENTS);







>
>
>
>
>
>
>
>







 







|







 







<

>





>







 







<

>







359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
....
1232
1233
1234
1235
1236
1237
1238

1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
....
1656
1657
1658
1659
1660
1661
1662

1663
1664
1665
1666
1667
1668
1669
1670
1671
            detached  = 1;
        } else if (OPT_CMP(opt, "-nowait")) {
            nowait = 1;
        } else {
            goto usage;
        }
    }

    /*
     * We expect exactly two arguments remaining after options
     */
    if (objc - ii != 2)
    {
        goto usage;
    }

    tpoolName = Tcl_GetString(objv[ii]);
    script    = Tcl_GetString(objv[ii+1]);
    len = objv[ii+1]->length;
    tpoolPtr  = GetTpool(tpoolName);
    if (tpoolPtr == NULL) {
        Tcl_AppendResult(interp, "can not find threadpool \"", tpoolName,
................................................................................
    char *tpoolName;
    Tcl_Obj *listVar = NULL;
    Tcl_Obj *doneList, *waitList, **wObjv;
    ThreadPool *tpoolPtr;
    TpoolResult *rPtr;

    /*
     * Syntax: tpool::cancel tpoolId jobIdList ?listVar?
     */

    if (objc < 3 || objc > 4) {
        Tcl_WrongNumArgs(interp, 1, objv, "tpoolId jobIdList ?listVar");
        return TCL_ERROR;
    }
    if (objc == 4) {
................................................................................
            }
        } else if (tpoolPtr->tearDown) {
            PushWork(rPtr, tpoolPtr);
            break; /* Kill worker because pool is going down */
        }
        Tcl_MutexUnlock(&tpoolPtr->mutex);
        TpoolEval(interp, rPtr->script, rPtr->scriptLen, rPtr);

        ckfree(rPtr->script);
        Tcl_MutexLock(&tpoolPtr->mutex);
        if (!rPtr->detached) {
            int new;
            Tcl_SetHashValue(Tcl_CreateHashEntry(&tpoolPtr->jobsDone,
                                                 (void *)(size_t)rPtr->jobId, &new),
                             (ClientData)rPtr);
            SignalWaiter(tpoolPtr);
        } else {
            ckfree((char*)rPtr);
        }
    }

    /*
     * Tear down the worker
................................................................................
    SpliceOut(tpoolPtr, tpoolList);
    InitWaiter();

    /*
     * Signal and wait for all workers to die.
     */


    Tcl_MutexLock(&tpoolPtr->mutex);
    tpoolPtr->tearDown = 1;
    while (tpoolPtr->numWorkers > 0) {
        PushWaiter(tpoolPtr);
        Tcl_ConditionNotify(&tpoolPtr->cond);
        Tcl_MutexUnlock(&tpoolPtr->mutex);
        tsdPtr->stop = -1;
        while(tsdPtr->stop == -1) {
            Tcl_DoOneEvent(TCL_ALL_EVENTS);

Changes to generic/threadSvCmd.c.

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
..
55
56
57
58
59
60
61

62
63
64
65
66
67
68
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
...
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
....
1004
1005
1006
1007
1008
1009
1010

1011
1012
1013
1014
1015
1016
1017
....
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
....
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
....
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
....
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
....
2238
2239
2240
2241
2242
2243
2244




2245
2246
2247
2248
2249
2250
2251
....
2354
2355
2356
2357
2358
2359
2360



2361
2362
2363
2364
2365
2366
2367
2368
#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.
 */

#define NUMBUCKETS 31
................................................................................
 * Those are referenced read-only, thus no mutex protection.
 */

static const Tcl_ObjType* booleanObjTypePtr;
static const Tcl_ObjType* byteArrayObjTypePtr;
static const Tcl_ObjType* doubleObjTypePtr;
static const Tcl_ObjType* intObjTypePtr;

static const Tcl_ObjType* stringObjTypePtr;

/*
 * In order to be fully stub enabled, a small
 * hack is needed to query the tclEmptyStringRep
 * global symbol defined by Tcl. See Sv_Init.
 */
................................................................................

static Array* CreateArray(Bucket*, const char*);
static Array* LockArray(Tcl_Interp*, const char*, int);

static int ReleaseContainer(Tcl_Interp*, Container*, int);
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);
................................................................................
 * Side effects:
 *      Memory gets reclaimed.
 *
 *-----------------------------------------------------------------------------
 */

static int
DeleteArray(Array *arrayPtr)






















{
    if (FlushArray(arrayPtr) == -1) {
        return TCL_ERROR;
    }
    if (arrayPtr->psPtr) {
        PsStore *psPtr = arrayPtr->psPtr;
        if (psPtr->psClose(psPtr->psHandle) == -1) {
            return TCL_ERROR;
        }
        ckfree((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL;
    }
    if (arrayPtr->bindAddr) {
        ckfree(arrayPtr->bindAddr);
    }
    if (arrayPtr->entryPtr) {
        Tcl_DeleteHashEntry(arrayPtr->entryPtr);
    }

    Tcl_DeleteHashTable(&arrayPtr->vars);
    ckfree((char*)arrayPtr);
................................................................................
            dupPtr->typePtr = objPtr->typePtr;
            Tcl_InvalidateStringRep(dupPtr);
        } else {
            if (   objPtr->typePtr == booleanObjTypePtr    \
                || objPtr->typePtr == byteArrayObjTypePtr  \
                || objPtr->typePtr == doubleObjTypePtr     \
                || objPtr->typePtr == intObjTypePtr        \

                || objPtr->typePtr == stringObjTypePtr) {
               /*
                * Cover all "safe" obj types (see header comment)
                */
              (*objPtr->typePtr->dupIntRepProc)(objPtr, dupPtr);
              Tcl_InvalidateStringRep(dupPtr);
            } else {
................................................................................
                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) {
                const char *err = psPtr->psError(psPtr->psHandle);
                Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1));
                ret = TCL_ERROR;
                goto cmdExit;
            }
            ckfree((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL;
        } else {
            Tcl_AppendResult(interp, "shared variable is not bound", NULL);
            ret = TCL_ERROR;
            goto cmdExit;




        }
    }

 cmdExit:
    if (arrayPtr) {
        UnlockArray(arrayPtr);
    }
................................................................................
    arrayPtr  = LockArray(interp, arrayName, 0);

    if (arrayPtr == NULL) {
        return TCL_ERROR;
    }
    if (objc == 2) {
        UnlockArray(arrayPtr);
        if (DeleteArray(arrayPtr) != TCL_OK) {
            return TCL_ERROR;
        }
    } else {
        for (ii = 2; ii < objc; ii++) {
            const char *key = Tcl_GetString(objv[ii]);
            Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key);
            if (hPtr) {
................................................................................
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_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);
    }
}
 
/*
................................................................................
    obj = Tcl_NewDoubleObj(0.0);
    doubleObjTypePtr    = obj->typePtr;
    Tcl_DecrRefCount(obj);

    obj = Tcl_NewIntObj(0);
    intObjTypePtr       = obj->typePtr;
    Tcl_DecrRefCount(obj);





    /*
     * Plug-in registered commands in current interpreter
     */

    for (cmdPtr = svCmdInfo; cmdPtr; cmdPtr = cmdPtr->nextPtr) {
        Tcl_CreateObjCommand(interp, cmdPtr->cmdName, cmdPtr->objProcPtr,
................................................................................
        if (buckets != NULL) {
            for (i = 0; i < NUMBUCKETS; ++i) {
                Bucket *bucketPtr = &buckets[i];
                hashPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search);
                while (hashPtr != NULL) {
                    Array *arrayPtr = (Array*)Tcl_GetHashValue(hashPtr);
                    UnlockArray(arrayPtr);



                    DeleteArray(arrayPtr);
                    hashPtr = Tcl_NextHashEntry(&search);
                }
                if (bucketPtr->lock) {
                    Sp_RecursiveMutexFinalize(&bucketPtr->lock);
                }
                SvFinalizeContainers(bucketPtr);
                Tcl_DeleteHashTable(&bucketPtr->handles);







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







 







>







 







|







 







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





|
<

|
<
<
<
<







 







>







 







|
<
<
<
<
<
<
<
<
<



>
>
>
>







 







|







 







>
>











|
|
>
>
>
>







 







|







 







>
>
>
>







 







>
>
>
|







19
20
21
22
23
24
25












26
27
28
29
30
31
32
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
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
....
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
....
1396
1397
1398
1399
1400
1401
1402
1403









1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
....
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
....
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
....
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
....
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
....
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
#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













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

#define NUMBUCKETS 31
................................................................................
 * Those are referenced read-only, thus no mutex protection.
 */

static const Tcl_ObjType* booleanObjTypePtr;
static const Tcl_ObjType* byteArrayObjTypePtr;
static const Tcl_ObjType* doubleObjTypePtr;
static const Tcl_ObjType* intObjTypePtr;
static const Tcl_ObjType* wideIntObjTypePtr;
static const Tcl_ObjType* stringObjTypePtr;

/*
 * In order to be fully stub enabled, a small
 * hack is needed to query the tclEmptyStringRep
 * global symbol defined by Tcl. See Sv_Init.
 */
................................................................................

static Array* CreateArray(Bucket*, const char*);
static Array* LockArray(Tcl_Interp*, const char*, int);

static int ReleaseContainer(Tcl_Interp*, Container*, int);
static int DeleteContainer(Container*);
static int FlushArray(Array*);
static int DeleteArray(Tcl_Interp *, Array*);

static void SvAllocateContainers(Bucket*);
static void SvRegisterStdCommands(void);

#ifdef SV_FINALIZE
static void SvFinalizeContainers(Bucket*);
static void SvFinalize(ClientData);
................................................................................
 * Side effects:
 *      Memory gets reclaimed.
 *
 *-----------------------------------------------------------------------------
 */

static int
UnbindArray(Tcl_Interp *interp, Array *arrayPtr)
{
    PsStore *psPtr = arrayPtr->psPtr;
    if (arrayPtr->bindAddr) {
        ckfree(arrayPtr->bindAddr);
        arrayPtr->bindAddr = NULL;
    }
    if (psPtr) {
        if (psPtr->psClose(psPtr->psHandle) == -1) {
            if (interp) {
                const char *err = psPtr->psError(psPtr->psHandle);
                Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1));
            }
            return TCL_ERROR;
        }
        ckfree((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL;
        arrayPtr->psPtr = NULL;
    }
    return TCL_OK;
}

static int
DeleteArray(Tcl_Interp *interp, Array *arrayPtr)
{
    if (FlushArray(arrayPtr) == -1) {
        return TCL_ERROR;
    }
    if (arrayPtr->psPtr) {
        if (UnbindArray(interp, arrayPtr) != TCL_OK) {

            return TCL_ERROR;
        };




    }
    if (arrayPtr->entryPtr) {
        Tcl_DeleteHashEntry(arrayPtr->entryPtr);
    }

    Tcl_DeleteHashTable(&arrayPtr->vars);
    ckfree((char*)arrayPtr);
................................................................................
            dupPtr->typePtr = objPtr->typePtr;
            Tcl_InvalidateStringRep(dupPtr);
        } else {
            if (   objPtr->typePtr == booleanObjTypePtr    \
                || objPtr->typePtr == byteArrayObjTypePtr  \
                || objPtr->typePtr == doubleObjTypePtr     \
                || objPtr->typePtr == intObjTypePtr        \
                || objPtr->typePtr == wideIntObjTypePtr    \
                || objPtr->typePtr == stringObjTypePtr) {
               /*
                * Cover all "safe" obj types (see header comment)
                */
              (*objPtr->typePtr->dupIntRepProc)(objPtr, dupPtr);
              Tcl_InvalidateStringRep(dupPtr);
            } else {
................................................................................
                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) {









            Tcl_AppendResult(interp, "shared variable is not bound", NULL);
            ret = TCL_ERROR;
            goto cmdExit;
        }
        if (UnbindArray(interp, arrayPtr) != TCL_OK) {
            ret = TCL_ERROR;
            goto cmdExit;
        }
    }

 cmdExit:
    if (arrayPtr) {
        UnlockArray(arrayPtr);
    }
................................................................................
    arrayPtr  = LockArray(interp, arrayName, 0);

    if (arrayPtr == NULL) {
        return TCL_ERROR;
    }
    if (objc == 2) {
        UnlockArray(arrayPtr);
        if (DeleteArray(interp, arrayPtr) != TCL_OK) {
            return TCL_ERROR;
        }
    } else {
        for (ii = 2; ii < objc; ii++) {
            const char *key = Tcl_GetString(objv[ii]);
            Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key);
            if (hPtr) {
................................................................................
static int
SvHandlersObjCmd(
             ClientData arg,                     /* Not used. */
             Tcl_Interp *interp,                 /* Current interpreter. */
             int objc,                           /* Number of arguments. */
             Tcl_Obj *const objv[])              /* Argument objects. */
{
    PsStore *tmpPtr = NULL;

    /*
     * Syntax:
     *
     *     tsv::handlers
     */

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

    Tcl_ResetResult(interp);
    Tcl_MutexLock(&svMutex);
    for (tmpPtr = psStore; tmpPtr; tmpPtr = tmpPtr->nextPtr) {
        Tcl_AppendElement(interp, tmpPtr->type);
    }
    Tcl_MutexUnlock(&svMutex);

    return TCL_OK;
}

 
/*
 *-----------------------------------------------------------------------------
................................................................................
            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);
    }
}
 
/*
................................................................................
    obj = Tcl_NewDoubleObj(0.0);
    doubleObjTypePtr    = obj->typePtr;
    Tcl_DecrRefCount(obj);

    obj = Tcl_NewIntObj(0);
    intObjTypePtr       = obj->typePtr;
    Tcl_DecrRefCount(obj);

    obj = Tcl_NewWideIntObj(((Tcl_WideInt)1)<<35);
    wideIntObjTypePtr       = obj->typePtr;
    Tcl_DecrRefCount(obj);

    /*
     * Plug-in registered commands in current interpreter
     */

    for (cmdPtr = svCmdInfo; cmdPtr; cmdPtr = cmdPtr->nextPtr) {
        Tcl_CreateObjCommand(interp, cmdPtr->cmdName, cmdPtr->objProcPtr,
................................................................................
        if (buckets != NULL) {
            for (i = 0; i < NUMBUCKETS; ++i) {
                Bucket *bucketPtr = &buckets[i];
                hashPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search);
                while (hashPtr != NULL) {
                    Array *arrayPtr = (Array*)Tcl_GetHashValue(hashPtr);
                    UnlockArray(arrayPtr);
                    /* unbind array before delete (avoid flush of persistent storage) */
                    UnbindArray(NULL, arrayPtr);
                    /* flush, delete etc. */
                    DeleteArray(NULL, arrayPtr);
                    hashPtr = Tcl_NextHashEntry(&search);
                }
                if (bucketPtr->lock) {
                    Sp_RecursiveMutexFinalize(&bucketPtr->lock);
                }
                SvFinalizeContainers(bucketPtr);
                Tcl_DeleteHashTable(&bucketPtr->handles);

Changes to lib/ttrace.tcl.

1
2
3
4
5
6
7
8
9
10
11
12
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
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
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
...
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
...
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
#
# ttrace.tcl --
#
# Copyright (C) 2003 Zoran Vasiljevic, Archiware GmbH. All Rights Reserved.
# 
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
# ----------------------------------------------------------------------------
#
# User level commands:
#
#   ttrace::eval           top-level wrapper (ttrace-savvy eval)
................................................................................
        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
................................................................................

    proc isenabled {} {
        variable enabled
        expr {$enabled > 0}
    }

    proc update {{from -1}} {
        if {$from == -1} { 
            variable epoch [_set ttrace lastepoch]
        } else {
            if {[lsearch [_set ttrace epochlist] $from] == -1} {
                error "no such epoch: $from"
            }
            variable epoch $from
        }
        uplevel [getscript]
    } 

    proc getscript {} {
        variable preloads
        variable epoch
        variable scripts
        append script [_serializensp] \n
        append script "::namespace eval [namespace current] {" \n
................................................................................
        if {[lsearch $enables $cmd] == -1} {
            lappend enables $cmd
            set cmd [namespace current]::enable::_$cmd
            proc $cmd $arglist $body
            return $cmd
        }
    }
    
    proc atdisable {cmd arglist body} {
        variable disables
        if {[lsearch $disables $cmd] == -1} {
            lappend disables $cmd
            set cmd [namespace current]::disable::_$cmd
            proc $cmd $arglist $body
            return $cmd
        }
    }
     
    proc addtrace {cmd arglist body} {
        variable tracers
        if {[lsearch $tracers $cmd] == -1} {
            lappend tracers $cmd
            set tracer [namespace current]::trace::_$cmd
            proc $tracer $arglist $body
            if {[isenabled]} {
................................................................................
                _unset ttrace $epoch
            }
        }
        _set ttrace epochlist $elist
    }

    proc _dropepoch {epoch threads} {
        set self [_getthread] 
        foreach tid [_set ttrace $epoch] {
            if {$tid != $self && [lsearch $threads $tid] >= 0} {
                lappend alive $tid
            }
        }
        if {[info exists alive]} {
            _set ttrace $epoch $alive
................................................................................
            set cns [namespace qual $entry]
            set var [namespace tail $entry]
            append res "::namespace eval $cns {" \n
            append res "::variable $var"
            if {[array exists $entry]} {
                append res "\n::array set $var [list [array get $entry]]" \n
            } elseif {[info exists $entry]} {
                append res " [list [set $entry]]" \n 
            } else {
                append res \n
            }
            append res "}" \n
        }
        return $res
    }
................................................................................
                return $result
            }
        }
        ::xotcl::Class instmixin ::xotcl::_creator
    }

    ttrace::atdisable XOTclDisabler {args} {
        if {   [info commands ::xotcl::Class] == "" 
            || [info commands ::xotcl::_creator] == ""} {
            return
        }
        ::xotcl::Class instmixin ""
        ::xotcl::_creator destroy
    }

................................................................................
            return {::xotcl::Class proc __unknown name {$resolver \$name}}
        }
    }]

    #
    # Register callback to be called on cleanup. This will trash lazily loaded
    # procs which have changed since.
    # 

    ttrace::addcleanup {
        variable resolveproc
        foreach cmd [array names resolveproc] {
            set def [ttrace::getentry proc $cmd]
            if {$def != ""} {
                set new [lindex $def 0]




|







 







|







 







|








|







 







|









|







 







|







 







|







 







|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
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
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
...
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
...
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
#
# ttrace.tcl --
#
# Copyright (C) 2003 Zoran Vasiljevic, Archiware GmbH. All Rights Reserved.
#
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
# ----------------------------------------------------------------------------
#
# User level commands:
#
#   ttrace::eval           top-level wrapper (ttrace-savvy eval)
................................................................................
        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.9a1

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

    proc isenabled {} {
        variable enabled
        expr {$enabled > 0}
    }

    proc update {{from -1}} {
        if {$from == -1} {
            variable epoch [_set ttrace lastepoch]
        } else {
            if {[lsearch [_set ttrace epochlist] $from] == -1} {
                error "no such epoch: $from"
            }
            variable epoch $from
        }
        uplevel [getscript]
    }

    proc getscript {} {
        variable preloads
        variable epoch
        variable scripts
        append script [_serializensp] \n
        append script "::namespace eval [namespace current] {" \n
................................................................................
        if {[lsearch $enables $cmd] == -1} {
            lappend enables $cmd
            set cmd [namespace current]::enable::_$cmd
            proc $cmd $arglist $body
            return $cmd
        }
    }

    proc atdisable {cmd arglist body} {
        variable disables
        if {[lsearch $disables $cmd] == -1} {
            lappend disables $cmd
            set cmd [namespace current]::disable::_$cmd
            proc $cmd $arglist $body
            return $cmd
        }
    }

    proc addtrace {cmd arglist body} {
        variable tracers
        if {[lsearch $tracers $cmd] == -1} {
            lappend tracers $cmd
            set tracer [namespace current]::trace::_$cmd
            proc $tracer $arglist $body
            if {[isenabled]} {
................................................................................
                _unset ttrace $epoch
            }
        }
        _set ttrace epochlist $elist
    }

    proc _dropepoch {epoch threads} {
        set self [_getthread]
        foreach tid [_set ttrace $epoch] {
            if {$tid != $self && [lsearch $threads $tid] >= 0} {
                lappend alive $tid
            }
        }
        if {[info exists alive]} {
            _set ttrace $epoch $alive
................................................................................
            set cns [namespace qual $entry]
            set var [namespace tail $entry]
            append res "::namespace eval $cns {" \n
            append res "::variable $var"
            if {[array exists $entry]} {
                append res "\n::array set $var [list [array get $entry]]" \n
            } elseif {[info exists $entry]} {
                append res " [list [set $entry]]" \n
            } else {
                append res \n
            }
            append res "}" \n
        }
        return $res
    }
................................................................................
                return $result
            }
        }
        ::xotcl::Class instmixin ::xotcl::_creator
    }

    ttrace::atdisable XOTclDisabler {args} {
        if {   [info commands ::xotcl::Class] == ""
            || [info commands ::xotcl::_creator] == ""} {
            return
        }
        ::xotcl::Class instmixin ""
        ::xotcl::_creator destroy
    }

................................................................................
            return {::xotcl::Class proc __unknown name {$resolver \$name}}
        }
    }]

    #
    # Register callback to be called on cleanup. This will trash lazily loaded
    # procs which have changed since.
    #

    ttrace::addcleanup {
        variable resolveproc
        foreach cmd [array names resolveproc] {
            set def [ttrace::getentry proc $cmd]
            if {$def != ""} {
                set new [lindex $def 0]

Changes to license.terms.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.

GOVERNMENT USE: If you are acquiring this software on behalf of the
U.S. government, the Government shall have only "Restricted Rights"
in the software and related documentation as defined in the Federal 
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
are acquiring the software on behalf of the Department of Defense, the
software shall be classified as "Commercial Computer Software" and the
Government shall have only "Restricted Rights" as defined in Clause
252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
authors grant the U.S. Government and others acting in its behalf
permission to use and distribute the software in accordance with the
terms specified in this license. 







|







|
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.

GOVERNMENT USE: If you are acquiring this software on behalf of the
U.S. government, the Government shall have only "Restricted Rights"
in the software and related documentation as defined in the Federal
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
are acquiring the software on behalf of the Department of Defense, the
software shall be classified as "Commercial Computer Software" and the
Government shall have only "Restricted Rights" as defined in Clause
252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
authors grant the U.S. Government and others acting in its behalf
permission to use and distribute the software in accordance with the
terms specified in this license.

Added project.shed.















































































































































































































































































































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

7bce708b4197acccd1db4644fdbe5ee249f3b5dd {
  entity {
    name: thread
    shed_class: project
    version: {}
  }
  meta {
    class: generic
    description: {The Tcl Thread package}
    distribution: fossil
    generated: {2015-10-06 10:32:03 UTC}
    release: trunk
  }
  release/ {
    ccc83b2283498a2ec86d00bf8593e7de0cf8a456
  }
  distribution/ {
    e47b6a79a963c99b7de15a4817b3010fbb3dd693
  }
  package/ {
    41c2f28a24688f0ad5c1431fbd22f1621b6b0d6e
  }
  module/ {
    6dbf64aeadfe9d6fb88c0d84562a98a4b33d374b
    893421a5151dd42645a1437b6e41ed1943dc4ed6
    cf5303e1f3f79bc837e9b1a18e5e2e3f4892c58c
    037601b1d656568d18dd3aaaaa2c1bd760b8c16d
    41b815e9db25ad951c6b7800c02cd3706468e263
  }
  file/ {
    032fb91c412db8ae1a282ae54d353fe33e45ff6a
  }
}
ccc83b2283498a2ec86d00bf8593e7de0cf8a456 {
  entity {
    name: trunk
    shed_class: release
    version: {}
  }
  meta {
    checkout: trunk
    class: generic
    timestamp: {2015-10-06 10:32:03 UTC}
  }
}
e47b6a79a963c99b7de15a4817b3010fbb3dd693 {
  entity {
    name: fossil
    shed_class: distribution
    version: {}
  }
  meta {
    class: generic
    format: fossil
    project: 7bce708b4197acccd1db4644fdbe5ee249f3b5dd
    project-name: {Tcl package Thread source code}
    project-short-name: thread
    release: trunk
    url: http://fossil.etoyoc.com/fossil/thread
  }
}
41c2f28a24688f0ad5c1431fbd22f1621b6b0d6e {
  entity {
    name: thread
    shed_class: package
    version: 2.7.2
  }
  meta {
    build: tea
    class: binary
  }
}
6dbf64aeadfe9d6fb88c0d84562a98a4b33d374b {
  entity {
    name: lib
    shed_class: module
    version: {}
  }
  meta {
    class: source
    package-provide: {}
    package-require: {}
    path: lib
  }
}
893421a5151dd42645a1437b6e41ed1943dc4ed6 {
  entity {
    name: tcl
    shed_class: module
    version: {}
  }
  meta {
    class: source
    package-provide: {}
    package-require: {{Tcl 8.4} {Thread 2.5}}
    path: tcl
  }
}
cf5303e1f3f79bc837e9b1a18e5e2e3f4892c58c {
  entity {
    name: generic
    shed_class: module
    version: {}
  }
  meta {
    class: source
    package-provide: {}
    package-require: {}
    path: generic
  }
}
037601b1d656568d18dd3aaaaa2c1bd760b8c16d {
  entity {
    name: win
    shed_class: module
    version: {}
  }
  meta {
    class: source
    package-provide: {}
    package-require: {}
    path: win
  }
}
41b815e9db25ad951c6b7800c02cd3706468e263 {
  entity {
    name: unix
    shed_class: module
    version: {}
  }
  meta {
    class: source
    package-provide: {}
    package-require: {}
    path: unix
  }
}
032fb91c412db8ae1a282ae54d353fe33e45ff6a {
  entity {
    name: ttrace.tcl
    shed_class: file
    version: {}
  }
  meta {
    class: source
    file: lib/ttrace.tcl
    format: tcl
    package-require: {}
  }
}

Changes to tcl/README.

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
Software here is provided as example of making some interesting
things and applications using the Tcl threading extension.

Currently, following packages are supplied:

   tpool/    Example Tcl-only implementation of thread pools.
             The threading extension includes an efficient
             threadpool implementation in C. This file is 
             provided as a fully functional example on how this
             functionality could be implemented in Tcl alone.

   phttpd/   MT-enabled httpd server. It uses threadpool to
             distribute incoming requests among several worker 
             threads in the threadpool. This way blocking 
             requests may be handled much better, w/o halting
             the event loop of the main responder thread.
             In this directory you will also find the uhttpd.
             This is the same web-server but operating in the 
             event-loop mode alone, no threadpool support. 
             This is good for comparison purposes.

   cmdsrv/   Socket command-line server. Each new connection 
             gets new thread, thus allowing multiple outstanding
             blocking calls without halting the event loop.

To play around with above packages, change to the corresponding 
directory and source files in the Tcl8.4 (or later) Tcl shell. 
Be sure to have the latest Tcl threading extension installed in 
your package path.

- EOF







|




|
|



|
|


|



|
|
|



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
Software here is provided as example of making some interesting
things and applications using the Tcl threading extension.

Currently, following packages are supplied:

   tpool/    Example Tcl-only implementation of thread pools.
             The threading extension includes an efficient
             threadpool implementation in C. This file is
             provided as a fully functional example on how this
             functionality could be implemented in Tcl alone.

   phttpd/   MT-enabled httpd server. It uses threadpool to
             distribute incoming requests among several worker
             threads in the threadpool. This way blocking
             requests may be handled much better, w/o halting
             the event loop of the main responder thread.
             In this directory you will also find the uhttpd.
             This is the same web-server but operating in the
             event-loop mode alone, no threadpool support.
             This is good for comparison purposes.

   cmdsrv/   Socket command-line server. Each new connection
             gets new thread, thus allowing multiple outstanding
             blocking calls without halting the event loop.

To play around with above packages, change to the corresponding
directory and source files in the Tcl8.4 (or later) Tcl shell.
Be sure to have the latest Tcl threading extension installed in
your package path.

- EOF

Changes to tcl/cmdsrv/cmdsrv.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
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
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
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#
# cmdsrv.tcl --
#
# Simple socket command server. Supports many simultaneous sessions.
# Works in thread mode with each new connection receiving a new thread.
#  
# Usage:
#    cmdsrv::create port ?-idletime value? ?-initcmd cmd?
# 
#    port         Tcp port where the server listens
#    -idletime    # of sec to idle before tearing down socket (def: 300 sec)
#    -initcmd     script to initialize new worker thread (def: empty)
#
# Example:
#
#    # tclsh8.4
#    % source cmdsrv.tcl
#    % cmdsrv::create 5000 -idletime 60
#    % vwait forever
#
#    Starts the server on the port 5000, sets idle timer to 1 minute. 
#    You can now use "telnet" utility to connect.
#
# Copyright (c) 2002 by Zoran Vasiljevic.
#
# See the file "license.terms" for information on usage and
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -----------------------------------------------------------------------------
................................................................................
    # Setup default pool data.
    #

    array set data {
        -idletime 300000
        -initcmd  {source cmdsrv.tcl}
    }
    
    #
    # Override with user-supplied data
    #

    foreach {arg val} $args {
        switch -- $arg {
            -idletime {set data($arg) [expr {$val*1000}]}
................................................................................
    }

    #
    # Start the server on the given port. Note that we wrap
    # the actual accept with a helper after/idle callback.
    # This is a workaround for a well-known Tcl bug.
    #
    
    socket -server [namespace current]::_Accept -myaddr 127.0.0.1 $port
}
 
#
# cmdsrv::_Accept --
#
#	Helper procedure to solve Tcl shared channel bug when responding
................................................................................
# Results:
#	None.
#

proc cmdsrv::Accept {s ipaddr port} {

    variable data
    
    #
    # Configure socket for sane operation
    #
    
    fconfigure $s -blocking 0 -buffering none -translation {auto crlf}
    
    #
    # Emit the prompt
    #
    
    puts -nonewline $s "% "

    #
    # Create worker thread and transfer socket ownership
    #
 
    set tid [thread::create [append data(-initcmd) \n thread::wait]]
    thread::transfer $tid $s ; # This flushes the socket as well

    #
    # Start event-loop processing in the remote thread
    #

................................................................................
        [namespace current]::StartIdleTimer $s
    }]
}
 
#
# cmdsrv::Read --
#
#	Event loop procedure to read data from socket and collect the 
#   command to execute. If the command read from socket is complete
#   it executes the command are prints the result back. 
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	None.
#
................................................................................
    }
    if {$line == "\n" || $line == ""} {
        if {[catch {puts -nonewline $s "% "}]} {
            return [SockDone $s]
        }
        return [StartIdleTimer $s]
    }
    
    #
    # Construct command line to eval
    #

    append data(cmd) $line
    if {[info complete $data(cmd)] == 0} {
        if {[catch {puts -nonewline $s "> "}]} {
................................................................................
    StartIdleTimer $s
}
 
#
# cmdsrv::SockDone --
#
#	Tears down the thread and closes the socket if the remote peer has
#   closed his side of the comm channel. 
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	Worker thread gets released.
#
................................................................................
    catch {close $s}
    thread::release
}
 
#
# cmdsrv::StopIdleTimer --
#
#	Cancel the connection idle timer. 
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	After event gets cancelled.
#
................................................................................
        unset data(idleevent)
    }
}
 
#
# cmdsrv::StartIdleTimer --
#
#	Initiates the connection idle timer. 
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	After event gets posted.
#





|


|











|







 







|







 







|







 







|



|

|



|





|







 







|

|







 







|







 







|







 







|







 







|







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
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
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
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#
# cmdsrv.tcl --
#
# Simple socket command server. Supports many simultaneous sessions.
# Works in thread mode with each new connection receiving a new thread.
#
# Usage:
#    cmdsrv::create port ?-idletime value? ?-initcmd cmd?
#
#    port         Tcp port where the server listens
#    -idletime    # of sec to idle before tearing down socket (def: 300 sec)
#    -initcmd     script to initialize new worker thread (def: empty)
#
# Example:
#
#    # tclsh8.4
#    % source cmdsrv.tcl
#    % cmdsrv::create 5000 -idletime 60
#    % vwait forever
#
#    Starts the server on the port 5000, sets idle timer to 1 minute.
#    You can now use "telnet" utility to connect.
#
# Copyright (c) 2002 by Zoran Vasiljevic.
#
# See the file "license.terms" for information on usage and
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -----------------------------------------------------------------------------
................................................................................
    # Setup default pool data.
    #

    array set data {
        -idletime 300000
        -initcmd  {source cmdsrv.tcl}
    }

    #
    # Override with user-supplied data
    #

    foreach {arg val} $args {
        switch -- $arg {
            -idletime {set data($arg) [expr {$val*1000}]}
................................................................................
    }

    #
    # Start the server on the given port. Note that we wrap
    # the actual accept with a helper after/idle callback.
    # This is a workaround for a well-known Tcl bug.
    #

    socket -server [namespace current]::_Accept -myaddr 127.0.0.1 $port
}
 
#
# cmdsrv::_Accept --
#
#	Helper procedure to solve Tcl shared channel bug when responding
................................................................................
# Results:
#	None.
#

proc cmdsrv::Accept {s ipaddr port} {

    variable data

    #
    # Configure socket for sane operation
    #

    fconfigure $s -blocking 0 -buffering none -translation {auto crlf}

    #
    # Emit the prompt
    #

    puts -nonewline $s "% "

    #
    # Create worker thread and transfer socket ownership
    #

    set tid [thread::create [append data(-initcmd) \n thread::wait]]
    thread::transfer $tid $s ; # This flushes the socket as well

    #
    # Start event-loop processing in the remote thread
    #

................................................................................
        [namespace current]::StartIdleTimer $s
    }]
}
 
#
# cmdsrv::Read --
#
#	Event loop procedure to read data from socket and collect the
#   command to execute. If the command read from socket is complete
#   it executes the command are prints the result back.
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	None.
#
................................................................................
    }
    if {$line == "\n" || $line == ""} {
        if {[catch {puts -nonewline $s "% "}]} {
            return [SockDone $s]
        }
        return [StartIdleTimer $s]
    }

    #
    # Construct command line to eval
    #

    append data(cmd) $line
    if {[info complete $data(cmd)] == 0} {
        if {[catch {puts -nonewline $s "> "}]} {
................................................................................
    StartIdleTimer $s
}
 
#
# cmdsrv::SockDone --
#
#	Tears down the thread and closes the socket if the remote peer has
#   closed his side of the comm channel.
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	Worker thread gets released.
#
................................................................................
    catch {close $s}
    thread::release
}
 
#
# cmdsrv::StopIdleTimer --
#
#	Cancel the connection idle timer.
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	After event gets cancelled.
#
................................................................................
        unset data(idleevent)
    }
}
 
#
# cmdsrv::StartIdleTimer --
#
#	Initiates the connection idle timer.
#
# Arguments:
#   s      incoming socket
#
# Side Effects:
#	After event gets posted.
#

Changes to tcl/phttpd/phttpd.tcl.

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
...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
# Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems.
#
# Modified to use namespaces, direct url-to-procedure access
# and thread pool package. Grown little larger since ;)
#
# Usage:
#    phttpd::create port
# 
#    port         Tcp port where the server listens
#
# Example:
#
#    # tclsh8.4
#    % source phttpd.tcl
#    % phttpd::create 5000
#    % vwait forever
#
#    Starts the server on the port 5000. Also, look at the Httpd array
#    definition in the "phttpd" namespace declaration to find out 
#    about other options you may put on the command line.
#
#    You can use: http://localhost:5000/monitor URL to test the
#    server functionality.
#
# Copyright (c) 2002 by Zoran Vasiljevic.
#
................................................................................
    set Httpd(tpid) [tpool::create -maxworkers 8 -initcmd $initcmd]

    #
    # Start the server on the given port. Note that we wrap
    # the actual accept with a helper after/idle callback.
    # This is a workaround for a well-known Tcl bug.
    #
    
    socket -server [namespace current]::_Accept $port
}
 
#
# phttpd::_Accept --
#
#	Helper procedure to solve Tcl shared-channel bug when responding
................................................................................
    # One of the worker threads will attach it again.
    #

    thread::detach $sock

    #
    # Send the work ticket to threadpool.
    # 

    tpool::post -detached $Httpd(tpid) [list [namespace current]::Ticket $sock]
}
 
#
# phttpd::Ticket --
#
................................................................................
#	None..
#
# Results:
#	None.
#

proc phttpd::Ticket {sock} {
    
    thread::attach $sock
    fileevent $sock readable [list [namespace current]::Read $sock]
    
    #
    # End of processing is signalized here.
    # This will release the worker thread.
    #
    
    vwait [namespace current]::done
}

 
#
# phttpd::Read --
#
................................................................................
                continue
            } else {
                Log error "bad request line: (%s)" $line
                Error 400
                return [Done]
            }
        }
        
        # string compare $readCount 0 maps -1 to -1, 0 to 0, and > 0 to 1
        
        set state [string compare $readCount 0],$data(state),$data(proto)
        switch -- $state {
            "0,mime,GET" - "0,query,POST" {
                Respond
                return [Done]
            }
            "0,mime,POST" {
................................................................................

proc phttpd::Done {} {

    variable done
    variable data

    close $data(sock)
    
    if {[info exists data]} {
        unset data
    }

    set done 1 ; # Releases the request thread (See Ticket procedure)
}
 
................................................................................

    # @c Convert the file suffix into a mime type.

    variable MimeTypes

    set type "text/plain"
    catch {set type $MimeTypes([file extension $path])}
    
    return $type
}
 
#
# phttpd::Error --
#
#	Emit error page
................................................................................
#	None..
#
# Results:
#	None.
#

proc phttpd::QueryMap {query} {
    
    set res [list]

    regsub -all {[&=]} $query { }    query
    regsub -all {  }   $query { {} } query; # Othewise we lose empty values

    foreach {key val} $query {
        lappend res [CgiMap $key] [CgiMap $val]







|










|







 







|







 







|







 







|


|




|







 







|

|







 







|







 







|







 







|







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
...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
# Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems.
#
# Modified to use namespaces, direct url-to-procedure access
# and thread pool package. Grown little larger since ;)
#
# Usage:
#    phttpd::create port
#
#    port         Tcp port where the server listens
#
# Example:
#
#    # tclsh8.4
#    % source phttpd.tcl
#    % phttpd::create 5000
#    % vwait forever
#
#    Starts the server on the port 5000. Also, look at the Httpd array
#    definition in the "phttpd" namespace declaration to find out
#    about other options you may put on the command line.
#
#    You can use: http://localhost:5000/monitor URL to test the
#    server functionality.
#
# Copyright (c) 2002 by Zoran Vasiljevic.
#
................................................................................
    set Httpd(tpid) [tpool::create -maxworkers 8 -initcmd $initcmd]

    #
    # Start the server on the given port. Note that we wrap
    # the actual accept with a helper after/idle callback.
    # This is a workaround for a well-known Tcl bug.
    #

    socket -server [namespace current]::_Accept $port
}
 
#
# phttpd::_Accept --
#
#	Helper procedure to solve Tcl shared-channel bug when responding
................................................................................
    # One of the worker threads will attach it again.
    #

    thread::detach $sock

    #
    # Send the work ticket to threadpool.
    #

    tpool::post -detached $Httpd(tpid) [list [namespace current]::Ticket $sock]
}
 
#
# phttpd::Ticket --
#
................................................................................
#	None..
#
# Results:
#	None.
#

proc phttpd::Ticket {sock} {

    thread::attach $sock
    fileevent $sock readable [list [namespace current]::Read $sock]

    #
    # End of processing is signalized here.
    # This will release the worker thread.
    #

    vwait [namespace current]::done
}

 
#
# phttpd::Read --
#
................................................................................
                continue
            } else {
                Log error "bad request line: (%s)" $line
                Error 400
                return [Done]
            }
        }

        # string compare $readCount 0 maps -1 to -1, 0 to 0, and > 0 to 1

        set state [string compare $readCount 0],$data(state),$data(proto)
        switch -- $state {
            "0,mime,GET" - "0,query,POST" {
                Respond
                return [Done]
            }
            "0,mime,POST" {
................................................................................

proc phttpd::Done {} {

    variable done
    variable data

    close $data(sock)

    if {[info exists data]} {
        unset data
    }

    set done 1 ; # Releases the request thread (See Ticket procedure)
}
 
................................................................................

    # @c Convert the file suffix into a mime type.

    variable MimeTypes

    set type "text/plain"
    catch {set type $MimeTypes([file extension $path])}

    return $type
}
 
#
# phttpd::Error --
#
#	Emit error page
................................................................................
#	None..
#
# Results:
#	None.
#

proc phttpd::QueryMap {query} {

    set res [list]

    regsub -all {[&=]} $query { }    query
    regsub -all {  }   $query { {} } query; # Othewise we lose empty values

    foreach {key val} $query {
        lappend res [CgiMap $key] [CgiMap $val]

Changes to tcl/phttpd/uhttpd.tcl.

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
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
..
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
...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems.
#
# Modified to use namespaces and direct url-to-procedure access (zv).
# Eh, due to this, and nicer indenting, it's now 150 lines longer :-)
#
# Usage:
#    phttpd::create port
# 
#    port         Tcp port where the server listens
#
# Example:
#
#    # tclsh8.4
#    % source uhttpd.tcl
#    % uhttpd::create 5000
#    % vwait forever
#
#    Starts the server on the port 5000. Also, look at the Httpd array
#    definition in the "uhttpd" namespace declaration to find out 
#    about other options you may put on the command line.
#
#    You can use: http://localhost:5000/monitor URL to test the
#    server functionality.
#
# Copyright (c) Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems.
# Copyright (c) 2002 by Zoran Vasiljevic.
................................................................................
    }
}

proc uhttpd::create {port args} {

    # @c Start the server by listening for connections on the desired port.

    variable Httpd 
    set arglen [llength $args]

    if {$arglen} {
        if {$arglen % 2} {
            error "wrong \# arguments, should be: key1 val1 key2 val2..."
        }
        set opts [array names Httpd]
................................................................................
    set Httpd(port) $port
    set Httpd(host) [info hostname]

    socket -server [namespace current]::Accept $port
}

proc uhttpd::respond {s status contype data {length 0}} {
    
    puts $s "HTTP/1.0 $status"
    puts $s "Date: [Date]"
    puts $s "Content-Type: $contype"

    if {$length} {
        puts $s "Content-Length: $length" 
    } else {
        puts $s "Content-Length: [string length $data]"
    }

    puts $s ""
    puts $s $data 
}
	
proc uhttpd::Accept {newsock ipaddr port} {

    # @c Accept a new connection from the client.

    variable Httpd
    upvar \#0 [namespace current]::Httpd$newsock data

................................................................................

    # @c Convert the file suffix into a mime type.

    variable MimeTypes

    set type "text/plain"
    catch {set type $MimeTypes([file extension $path])}
    
    return $type
}

proc uhttpd::Error {s code} {

    # @c Emit error page.

................................................................................
    if {$seconds == 0} {
        set seconds [clock seconds]
    }
    clock format $seconds -format {%a, %d %b %Y %T %Z} -gmt 1
}

proc uhttpd::Log {reason format args} {
    
    # @c Log an httpd transaction.

    set messg [eval format [list $format] $args]
    set stamp [clock format [clock seconds] -format "%d/%b/%Y:%H:%M:%S"]

    puts stderr "\[$stamp\] $reason: $messg"
}
................................................................................

    return [subst $data]
}

proc uhttpd::QueryMap {query} {

    # @c Decode url-encoded query into key/value pairs
    
    set res [list]

    regsub -all {[&=]} $query { }    query
    regsub -all {  }   $query { {} } query; # Othewise we lose empty values

    foreach {key val} $query {
        lappend res [CgiMap $key] [CgiMap $val]







|










|







 







|







 







|





|





|

|







 







|







 







|







 







|







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
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
..
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
...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems.
#
# Modified to use namespaces and direct url-to-procedure access (zv).
# Eh, due to this, and nicer indenting, it's now 150 lines longer :-)
#
# Usage:
#    phttpd::create port
#
#    port         Tcp port where the server listens
#
# Example:
#
#    # tclsh8.4
#    % source uhttpd.tcl
#    % uhttpd::create 5000
#    % vwait forever
#
#    Starts the server on the port 5000. Also, look at the Httpd array
#    definition in the "uhttpd" namespace declaration to find out
#    about other options you may put on the command line.
#
#    You can use: http://localhost:5000/monitor URL to test the
#    server functionality.
#
# Copyright (c) Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems.
# Copyright (c) 2002 by Zoran Vasiljevic.
................................................................................
    }
}

proc uhttpd::create {port args} {

    # @c Start the server by listening for connections on the desired port.

    variable Httpd
    set arglen [llength $args]

    if {$arglen} {
        if {$arglen % 2} {
            error "wrong \# arguments, should be: key1 val1 key2 val2..."
        }
        set opts [array names Httpd]
................................................................................
    set Httpd(port) $port
    set Httpd(host) [info hostname]

    socket -server [namespace current]::Accept $port
}

proc uhttpd::respond {s status contype data {length 0}} {

    puts $s "HTTP/1.0 $status"
    puts $s "Date: [Date]"
    puts $s "Content-Type: $contype"

    if {$length} {
        puts $s "Content-Length: $length"
    } else {
        puts $s "Content-Length: [string length $data]"
    }

    puts $s ""
    puts $s $data
}

proc uhttpd::Accept {newsock ipaddr port} {

    # @c Accept a new connection from the client.

    variable Httpd
    upvar \#0 [namespace current]::Httpd$newsock data

................................................................................

    # @c Convert the file suffix into a mime type.

    variable MimeTypes

    set type "text/plain"
    catch {set type $MimeTypes([file extension $path])}

    return $type
}

proc uhttpd::Error {s code} {

    # @c Emit error page.

................................................................................
    if {$seconds == 0} {
        set seconds [clock seconds]
    }
    clock format $seconds -format {%a, %d %b %Y %T %Z} -gmt 1
}

proc uhttpd::Log {reason format args} {

    # @c Log an httpd transaction.

    set messg [eval format [list $format] $args]
    set stamp [clock format [clock seconds] -format "%d/%b/%Y:%H:%M:%S"]

    puts stderr "\[$stamp\] $reason: $messg"
}
................................................................................

    return [subst $data]
}

proc uhttpd::QueryMap {query} {

    # @c Decode url-encoded query into key/value pairs

    set res [list]

    regsub -all {[&=]} $query { }    query
    regsub -all {  }   $query { {} } query; # Othewise we lose empty values

    foreach {key val} $query {
        lappend res [CgiMap $key] [CgiMap $val]

Changes to tcl/tpool/tpool.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
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
...
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
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
...
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
#
# tpool.tcl --
#
# Tcl implementation of a threadpool paradigm in pure Tcl using
# the Tcl threading extension 2.5 (or higher). 
#
# This file is for example purposes only. The efficient C-level
# threadpool implementation is already a part of the threading
# extension starting with 2.5 version. Both implementations have
# the same Tcl API so both can be used interchangeably. Goal of 
# this implementation is to serve as an example of using the Tcl
# extension to implement some very common threading paradigms.
#
# Beware: with time, as improvements are made to the C-level
# implementation, this Tcl one might lag behind.
# Please consider this code as a working example only.
# 
# 
#
# Copyright (c) 2002 by Zoran Vasiljevic.
#
# See the file "license.terms" for information on usage and
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -----------------------------------------------------------------------------

................................................................................
#        -initcmd     script used to initialize new worker thread
#        -exitcmd     script run at worker thread exit
#
# Side Effects:
#   Might create many new threads if "-minworkers" option is > 0.
#
# Results:
#   The id of the newly created thread pool. This id must be used 
#   in all other tpool::* commands.
#

proc tpool::create {args} {

    variable thisScript

................................................................................
 
#
# tpool::post --
#
#   Submits the new job to the thread pool. The caller might pass
#   the job in two modes: synchronous and asynchronous.
#   For the synchronous mode, the pool implementation will retain
#   the result of the passed script until the caller collects it 
#   using the "thread::get" command.
#   For the asynchronous mode, the result of the script is ignored.
#
# Arguments:
#   args   Variable # of arguments with the following syntax:
#          tpool::post ?-detached? tpid script
#
#          -detached  flag to turn the async operation (ignore result)
#          tpid       the id of the thread pool 
#          script     script to pass to the worker thread for execution
#
# Side Effects:
#   Depends on the passed script.
#
# Results:
#   The id of the posted job. This id is used later on to collect
................................................................................
#

proc tpool::post {args} {

    #
    # Parse command arguments.
    #
    
    set ns [namespace current]
    set usage "wrong \# args: should be \"[lindex [info level 1] 0]\
               ?-detached? tpoolId script\""

    if {[llength $args] == 2} {
        set detached 0
        set tpid [lindex $args 0]
................................................................................
        set cmd  [lindex $args 1]
    } elseif {[llength $args] == 3} {
        if {[lindex $args 0] != "-detached"} {
            error $usage
        }
        set detached 1
        set tpid [lindex $args 1]
        set cmd  [lindex $args 2]            
    } else {
        error $usage
    }

    #
    # Find idle (or create new) worker thread. This is relatively
    # a complex issue, since we must honour the limits about number 
    # of allowed worker threads imposed to us by the caller.
    #

    set tid ""

    while {$tid == ""} {
        tsv::lock $tpid {
................................................................................
#   None.
#

proc tpool::Timer {tpid} {

    tsv::lock $tpid {
        if {[tsv::set $tpid  numworkers] > [tsv::set $tpid -minworkers]} {
            
            #
            # We have more workers than needed, so kill this one.
            # We first splice ourselves from the list of active
            # workers, adjust the number of workers and release 
            # this thread, which may exit eventually.
            #

            set x [tsv::lsearch $tpid thrworkers [thread::id]]
            if {$x >= 0} {
                tsv::lreplace $tpid thrworkers $x $x
                tsv::incr $tpid numworkers -1
................................................................................
    # Cancel the idle timer callback, if any.
    #

    variable afterevent
    if {$afterevent != ""} {
        after cancel $afterevent
    }
    
    #
    # Evaluate passed command and build the result list.
    #

    set code [catch {uplevel \#0 $cmd} ret]
    if {$code == 0} {
        set res [list $jid 0 $ret]
................................................................................
        set res [list $jid $code $ret $::errorInfo $::errorCode]
    }

    #
    # Check to see if any caller is waiting to be serviced.
    # If yes, kick it out of the waiting state.
    #
    
    set ns [namespace current]

    tsv::lock $tpid {
        tsv::lpush $tpid thrworkers [thread::id]
        set waiter [tsv::lpop $tpid thrwaiters]
        if {$waiter != ""} {
            thread::send -async $waiter [subst {
                set ${ns}::waiter 1
            }]
        }
    }

    #
    # Release the thread. If this turns out to be 
    # the last refcount held, don't bother to do
    # any more work, since thread will soon exit.
    #

    if {[thread::release] <= 0} {
        return $res
    }




|




|






|
|







 







|







 







|








|







 







|







 







|






|







 







|



|







 







|







 







|













|







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
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
...
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
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
...
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
#
# tpool.tcl --
#
# Tcl implementation of a threadpool paradigm in pure Tcl using
# the Tcl threading extension 2.5 (or higher).
#
# This file is for example purposes only. The efficient C-level
# threadpool implementation is already a part of the threading
# extension starting with 2.5 version. Both implementations have
# the same Tcl API so both can be used interchangeably. Goal of
# this implementation is to serve as an example of using the Tcl
# extension to implement some very common threading paradigms.
#
# Beware: with time, as improvements are made to the C-level
# implementation, this Tcl one might lag behind.
# Please consider this code as a working example only.
#
#
#
# Copyright (c) 2002 by Zoran Vasiljevic.
#
# See the file "license.terms" for information on usage and
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -----------------------------------------------------------------------------

................................................................................
#        -initcmd     script used to initialize new worker thread
#        -exitcmd     script run at worker thread exit
#
# Side Effects:
#   Might create many new threads if "-minworkers" option is > 0.
#
# Results:
#   The id of the newly created thread pool. This id must be used
#   in all other tpool::* commands.
#

proc tpool::create {args} {

    variable thisScript

................................................................................
 
#
# tpool::post --
#
#   Submits the new job to the thread pool. The caller might pass
#   the job in two modes: synchronous and asynchronous.
#   For the synchronous mode, the pool implementation will retain
#   the result of the passed script until the caller collects it
#   using the "thread::get" command.
#   For the asynchronous mode, the result of the script is ignored.
#
# Arguments:
#   args   Variable # of arguments with the following syntax:
#          tpool::post ?-detached? tpid script
#
#          -detached  flag to turn the async operation (ignore result)
#          tpid       the id of the thread pool
#          script     script to pass to the worker thread for execution
#
# Side Effects:
#   Depends on the passed script.
#
# Results:
#   The id of the posted job. This id is used later on to collect
................................................................................
#

proc tpool::post {args} {

    #
    # Parse command arguments.
    #

    set ns [namespace current]
    set usage "wrong \# args: should be \"[lindex [info level 1] 0]\
               ?-detached? tpoolId script\""

    if {[llength $args] == 2} {
        set detached 0
        set tpid [lindex $args 0]
................................................................................
        set cmd  [lindex $args 1]
    } elseif {[llength $args] == 3} {
        if {[lindex $args 0] != "-detached"} {
            error $usage
        }
        set detached 1
        set tpid [lindex $args 1]
        set cmd  [lindex $args 2]
    } else {
        error $usage
    }

    #
    # Find idle (or create new) worker thread. This is relatively
    # a complex issue, since we must honour the limits about number
    # of allowed worker threads imposed to us by the caller.
    #

    set tid ""

    while {$tid == ""} {
        tsv::lock $tpid {
................................................................................
#   None.
#

proc tpool::Timer {tpid} {

    tsv::lock $tpid {
        if {[tsv::set $tpid  numworkers] > [tsv::set $tpid -minworkers]} {

            #
            # We have more workers than needed, so kill this one.
            # We first splice ourselves from the list of active
            # workers, adjust the number of workers and release
            # this thread, which may exit eventually.
            #

            set x [tsv::lsearch $tpid thrworkers [thread::id]]
            if {$x >= 0} {
                tsv::lreplace $tpid thrworkers $x $x
                tsv::incr $tpid numworkers -1
................................................................................
    # Cancel the idle timer callback, if any.
    #

    variable afterevent
    if {$afterevent != ""} {
        after cancel $afterevent
    }

    #
    # Evaluate passed command and build the result list.
    #

    set code [catch {uplevel \#0 $cmd} ret]
    if {$code == 0} {
        set res [list $jid 0 $ret]
................................................................................
        set res [list $jid $code $ret $::errorInfo $::errorCode]
    }

    #
    # Check to see if any caller is waiting to be serviced.
    # If yes, kick it out of the waiting state.
    #

    set ns [namespace current]

    tsv::lock $tpid {
        tsv::lpush $tpid thrworkers [thread::id]
        set waiter [tsv::lpop $tpid thrwaiters]
        if {$waiter != ""} {
            thread::send -async $waiter [subst {
                set ${ns}::waiter 1
            }]
        }
    }

    #
    # Release the thread. If this turns out to be
    # the last refcount held, don't bother to do
    # any more work, since thread will soon exit.
    #

    if {[thread::release] <= 0} {
        return $res
    }

Changes to tests/thread.test.

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
....
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
    close $file
    ThreadReap
    set msg
} {transfer failed: target thread died}

test thread-17.3 {thread::transfer - clearing of fileevents} {chanTransfer} {
    proc _HandleIt_ {} {
        global gotEvents tid file    
        if {$gotEvents == 0} {
            thread::transfer $tid $file
            # From now on no events should be delivered anymore,
            # restricting the end value to 1
        }
        incr gotEvents
    }
................................................................................
    thread::rwmutex destroy $rwmutex
    list $x $msg
} {1 {write-locking the same read-write mutex twice from the same thread}}

test thread-20.15 {thread::mutex - read-lock write-locked mutex} {
    set rwmutex [thread::rwmutex create]
    thread::rwmutex wlock $rwmutex
    set x [catch {thread::rwmutex rlock $rwmutex} msg]  
    thread::rwmutex unlock $rwmutex 
    thread::rwmutex destroy $rwmutex
    list $x $msg
} {1 {read-locking already write-locked mutex from the same thread}}

test thread-20.16 {thread::mutex - unlock not locked mutex} {
    set rwmutex [thread::rwmutex create]
    set x [catch {thread::rwmutex unlock $rwmutex} msg]







|







 







|
|







488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
....
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
    close $file
    ThreadReap
    set msg
} {transfer failed: target thread died}

test thread-17.3 {thread::transfer - clearing of fileevents} {chanTransfer} {
    proc _HandleIt_ {} {
        global gotEvents tid file
        if {$gotEvents == 0} {
            thread::transfer $tid $file
            # From now on no events should be delivered anymore,
            # restricting the end value to 1
        }
        incr gotEvents
    }
................................................................................
    thread::rwmutex destroy $rwmutex
    list $x $msg
} {1 {write-locking the same read-write mutex twice from the same thread}}

test thread-20.15 {thread::mutex - read-lock write-locked mutex} {
    set rwmutex [thread::rwmutex create]
    thread::rwmutex wlock $rwmutex
    set x [catch {thread::rwmutex rlock $rwmutex} msg]
    thread::rwmutex unlock $rwmutex
    thread::rwmutex destroy $rwmutex
    list $x $msg
} {1 {read-locking already write-locked mutex from the same thread}}

test thread-20.16 {thread::mutex - unlock not locked mutex} {
    set rwmutex [thread::rwmutex create]
    set x [catch {thread::rwmutex unlock $rwmutex} msg]

Changes to tests/tsv.test.

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    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 {







|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    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 {

Changes to unix/CONFIG.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ../configure --enable-threads
#
# As of 2.6, the threading extension supports persistent
# shared variables. As an working example of this, there
# is a simple wrapper for the popular Gdbm library.
# Uncomment the following line if you like to compile the
# Gdbm wrapper for persistent shared variables.
# 
# ../configure --enable-threads --with-gdbm
#
# If your Gdbm library is not installed in one of the
# default system locations (/usr/lib, /usr/local/lib ...)
# please use following directive. Note that both library
# file *and* includes should be located in "/my/gdbm". 
# Of course, you have to replace the "/my/gdbm" below
# with the exact location, as found in your system:
# 
# ../configure --enable-threads --with-gdbm=/my/gdbm
#
#
# AOLserver 4.X; Uses public Tcl library.
# ----------------------------------------------------
# nsdir="/usr/local/naviserver"
# ../configure --enable-threads \







|





|


|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ../configure --enable-threads
#
# As of 2.6, the threading extension supports persistent
# shared variables. As an working example of this, there
# is a simple wrapper for the popular Gdbm library.
# Uncomment the following line if you like to compile the
# Gdbm wrapper for persistent shared variables.
#
# ../configure --enable-threads --with-gdbm
#
# If your Gdbm library is not installed in one of the
# default system locations (/usr/lib, /usr/local/lib ...)
# please use following directive. Note that both library
# file *and* includes should be located in "/my/gdbm".
# Of course, you have to replace the "/my/gdbm" below
# with the exact location, as found in your system:
#
# ../configure --enable-threads --with-gdbm=/my/gdbm
#
#
# AOLserver 4.X; Uses public Tcl library.
# ----------------------------------------------------
# nsdir="/usr/local/naviserver"
# ../configure --enable-threads \

Changes to unix/README.

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
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
checked some versions of Solaris, Linux and Darwin, but the extension
should compile without problems on any Unix-like operating system
with a proper pthreads library implementation.

To build on Unix-like operating systems, start with the CONFIG script
and see if there is already a combination of the "configure" options
which may satisfy your needs. If not, you can run the configure script
located in the root of the distribution directory with a choice of 
supported options yourself.  If yes, you can uncomment corresponding
lines from the CONFIG script and do:

    % sh CONFIG

Either way, this will create a Makefile which you use to run "make" and
"make install".
You can use "make clean" to clean the directory from temporary compilation
files and/or "make distclean" to additionaly remove local config files.
You might want to do "make test" before doing the "make install" in order
to run the regression tests on the package. 

To explore other building options, look into the CONFIG file for more
information.


Note for NaviServer/AOLserver users
------------------------
................................................................................
So, uncomment the line, recompile and there you go.


II. Building optional support libraries
=======================================

As of 2.6 release, this extension supports persistent shared variables.
To use this functionality, you might need to download and compile some 
other supporting libraries. Currently, there is a simple implementation
of shared variable persistency built atop of popular GNU Gdbm package.
You can obtain the latest version of the Gdbm package from the GNU 
website at: http://www.gnu.org/software/gdbm/gdbm.html
To compile with GNU Gdbm support you must configure with --with-gdbm
switch. This option, if used, will try to locate the Gdbm library on
your system at couple of standard locations. You might override this
behaviour by giving --with-gdbm=/some/dir. Note that both library file
and the include file must then reside in this directory.

-EOF-







|










|







 







|


|








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
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
checked some versions of Solaris, Linux and Darwin, but the extension
should compile without problems on any Unix-like operating system
with a proper pthreads library implementation.

To build on Unix-like operating systems, start with the CONFIG script
and see if there is already a combination of the "configure" options
which may satisfy your needs. If not, you can run the configure script
located in the root of the distribution directory with a choice of
supported options yourself.  If yes, you can uncomment corresponding
lines from the CONFIG script and do:

    % sh CONFIG

Either way, this will create a Makefile which you use to run "make" and
"make install".
You can use "make clean" to clean the directory from temporary compilation
files and/or "make distclean" to additionaly remove local config files.
You might want to do "make test" before doing the "make install" in order
to run the regression tests on the package.

To explore other building options, look into the CONFIG file for more
information.


Note for NaviServer/AOLserver users
------------------------
................................................................................
So, uncomment the line, recompile and there you go.


II. Building optional support libraries
=======================================

As of 2.6 release, this extension supports persistent shared variables.
To use this functionality, you might need to download and compile some
other supporting libraries. Currently, there is a simple implementation
of shared variable persistency built atop of popular GNU Gdbm package.
You can obtain the latest version of the Gdbm package from the GNU
website at: http://www.gnu.org/software/gdbm/gdbm.html
To compile with GNU Gdbm support you must configure with --with-gdbm
switch. This option, if used, will try to locate the Gdbm library on
your system at couple of standard locations. You might override this
behaviour by giving --with-gdbm=/some/dir. Note that both library file
and the include file must then reside in this directory.

-EOF-

Changes to win/CONFIG.

10
11
12
13
14
15
16
17
18
19
20
21
# from in those directories with the appropriate flags.
#
# Note the CC=gcc must be set *before* the "configure" is ran.
# This is really needed, otherwise configure will not be able
# to compile the small test file which checks the presence
# of the MinGW build environment. It is *not* enough to use
# "--enable-gcc" configure option; you *need* to define CC.
# 

export CC=gcc
sh ../configure --enable-threads --with-tcl=e:/tcl/win








|




10
11
12
13
14
15
16
17
18
19
20
21
# from in those directories with the appropriate flags.
#
# Note the CC=gcc must be set *before* the "configure" is ran.
# This is really needed, otherwise configure will not be able
# to compile the small test file which checks the presence
# of the MinGW build environment. It is *not* enough to use
# "--enable-gcc" configure option; you *need* to define CC.
#

export CC=gcc
sh ../configure --enable-threads --with-tcl=e:/tcl/win

Changes to win/README.txt.

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

Files in this directory may be useful if you have not set up
your TEA (i.e., MinGW) environment and you're using the MSVC++
from Micro$oft.

To build the extension invoke the following command:

    nmake -f makefile.vc TCLDIR=<path>

You would need to give the <path> of the Tcl distribution where
tcl.h and other needed Tcl files are located.




Please look into the makefile.vc file for more information.

Alternatively, you can open the extension workspace and project files
(thread_win.dsw and thread_win.dsp) from within the MSVC++ and press
the F7 key to build the extension under the control of the MSVC IDE.



II. Building optional support libraries
=======================================

As of 2.6 release, this extension supports persistent shared 
variables. To use this functionality, you might need to download 
and compile some other supporting libraries. Currently, there is 
a simple implementation of shared variable persistency built atop
of popular GNU Gdbm package. You can obtain the latest version of
the Gdbm from: http://www.gnu.org/software/gdbm/gdbm.html.

For the impatient, there are Windows ports of GNU Gdbm found on
various places on the Internet. The easiest way to start is to go 
to the GnuWin32 project: http://sourceforge.net/projects/gnuwin32
and fetch yourself a compiled GNU Gdbm DLL. 

-EOF-







|

|
|
>
>
>
>
|




|
>




|
|
|





|

|


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

Files in this directory may be useful if you have not set up
your TEA (i.e., MinGW) environment and you're using the MSVC++
from Micro$oft.

To build the extension invoke the following command:

    nmake -f makefile.vc INSTALLDIR=<path-to-installed-tcl>

INSTALLDIR is the path of the Tcl distribution where
tcl.h and other needed Tcl files are installed.
To build against a Tcl source build instead,

    nmake -f makefile.vc TCLDIR=<path-to-tcl-sources>

Please look into the makefile.vc file for more options etc.

Alternatively, you can open the extension workspace and project files
(thread_win.dsw and thread_win.dsp) from within the MSVC++ and press
the F7 key to build the extension under the control of the MSVC IDE.
NOTE: it is likely that the .dsw and .dsp files are out of date. At
least Visual Studio 2017 was not able to open those files.

II. Building optional support libraries
=======================================

As of 2.6 release, this extension supports persistent shared
variables. To use this functionality, you might need to download
and compile some other supporting libraries. Currently, there is
a simple implementation of shared variable persistency built atop
of popular GNU Gdbm package. You can obtain the latest version of
the Gdbm from: http://www.gnu.org/software/gdbm/gdbm.html.

For the impatient, there are Windows ports of GNU Gdbm found on
various places on the Internet. The easiest way to start is to go
to the GnuWin32 project: http://sourceforge.net/projects/gnuwin32
and fetch yourself a compiled GNU Gdbm DLL.

-EOF-

Changes to win/makefile.vc.

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
#------------------------------------------------------------- -*- makefile -*-




# makefile.vc --


#
#	Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)



#







# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# Copyright (c) 1995-1996 Sun Microsystems, Inc.
# Copyright (c) 1998-2000 Ajuba Solutions.
# Copyright (c) 2001-2005 ActiveState Corporation.
# Copyright (c) 2001-2004 David Gravereaux.
# Copyright (c) 2003-2008 Pat Thoyts.
#------------------------------------------------------------------------------

# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)
!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
MSG = ^
You need to run vcvars32.bat from Developer Studio or setenv.bat from the^
Platform SDK first to setup the environment.  Jump to this line to read^
the build instructions.
!error $(MSG)
!endif

#------------------------------------------------------------------------------
# HOW TO USE this makefile:
#
# 1)  It is now necessary to have MSVCDir, MSDevDir or MSSDK set in the
#     environment.  This is used as a check to see if vcvars32.bat had been
#     run prior to running nmake or during the installation of Microsoft
#     Visual C++, MSVCDir had been set globally and the PATH adjusted.
#     Either way is valid.
#
#     You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin
#     directory to setup the proper environment, if needed, for your
#     current setup.  This is a needed bootstrap requirement and allows the
#     swapping of different environments to be easier.
#
# 2)  To use the Platform SDK (not expressly needed), run setenv.bat after
#     vcvars32.bat according to the instructions for it.  This can also
#     turn on the 64-bit compiler, if your SDK has it.
#
# 3)  Targets are:
#	all       -- Builds everything.
#	<project> -- Builds the project (eg: nmake sample)
#	test      -- Builds and runs the test suite.
#	install   -- Installs the built binaries and libraries to $(INSTALLDIR)
#		     in an appropriate subdirectory.
#	clean/realclean/distclean -- varying levels of cleaning.
#
# 4)  Macros usable on the commandline:
#	INSTALLDIR=<path>
#		Sets where to install Tcl from the built binaries.
#		C:\Progra~1\Tcl is assumed when not specified.
#
#	OPTS=static,msvcrt,staticpkg,nothreads,symbols,profile,loimpact,none
#		Sets special options for the core.  The default is for none.
#		Any combination of the above may be used (comma separated).
#		'none' will over-ride everything to nothing.
#
#		static  =  Builds a static library of the core instead of a
#			   dll.  The shell will be static (and large), as well.
#		msvcrt  =  Affects the static option only to switch it from
#			   using libcmt(d) as the C runtime [by default] to
#			   msvcrt(d). This is useful for static embedding
#			   support.
#		staticpkg = Affects the static option only to switch
#			   tclshXX.exe to have the dde and reg extension linked
#			   inside it.
#		nothreads = Turns off multithreading support (not supported).
#		thrdalloc = Use the thread allocator (shared global free pool)
#			   This is the default on threaded builds.
#		symbols  = Debug build. Links to the debug C runtime, disables
#			   optimizations and creates pdb symbols files.
#		profile  = Adds profiling hooks.  Map file is assumed.
#		loimpact = Adds a flag for how NT treats the heap to keep memory
#			   in use, low.  This is said to impact alloc performance.
#
#	STATS=memdbg,compdbg,none
#		Sets optional memory and bytecode compiler debugging code added
#		to the core.  The default is for none.  Any combination of the
#		above may be used (comma separated).  'none' will over-ride
#		everything to nothing.
#
#		memdbg   = Enables the debugging memory allocator.
#		compdbg  = Enables byte compilation logging.
#
#	MACHINE=(ARM|AMD64|IA64|X86)
#		Set the machine type used for the compiler, linker, and
#		resource compiler.  This hook is needed to tell the tools
#		when alternate platforms are requested.  IX86 is the default
#		when not specified. If the CPU environment variable has been
#		set (ie: recent Platform SDK) then MACHINE is set from CPU.
#
#	TMP_DIR=<path>
#	OUT_DIR=<path>
#		Hooks to allow the intermediate and output directories to be
#		changed.  $(OUT_DIR) is assumed to be
#		$(BINROOT)\(Release|Debug) based on if symbols are requested.
#		$(TMP_DIR) will de $(OUT_DIR)\<buildtype> by default.
#
#	TESTPAT=<file>
#		Reads the tests requested to be run from this file.
#
#	CFG_ENCODING=encoding
#		name of encoding for configuration information. Defaults
#		to cp1252
#
# 5)  Examples:
#
#	Basic syntax of calling nmake looks like this:
#	nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]]
#
#                        Standard (no frills)
#       c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
#       Setting environment for using Microsoft Visual C++ tools.
#       c:\tcl_src\win\>nmake -f makefile.vc all
#       c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl
#
#                         Building for Win64
#       c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
#       Setting environment for using Microsoft Visual C++ tools.
#       c:\tcl_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL
#       Targeting Windows pre64 RETAIL
#       c:\tcl_src\win\>nmake -f makefile.vc MACHINE=IA64
#
#------------------------------------------------------------------------------
#==============================================================================
#------------------------------------------------------------------------------

!if !exist("makefile.vc")
MSG = ^
You must run this makefile only from the directory it is in.^
Please `cd` to its location first.
!error $(MSG)
!endif

#-------------------------------------------------------------------------
# Project specific information (EDIT)
#
# You should edit this with the name and version of your project. This
# information is used to generate the name of the package library and
# it's install location.
#
# For example, the sample extension is  going to build sample05.dll and
# would install it into $(INSTALLDIR)\lib\sample05
#
# You need to specify the object files that need to be linked into your
# binary here.
#
#-------------------------------------------------------------------------

PROJECT = thread



# Uncomment the following line if this is a Tk extension.
#PROJECT_REQUIRES_TK=1
!include "rules.vc"




!include "pkg.vc"

DOTVERSION      = $(PACKAGE_VERSION:"=) #"
VERSION         = $(PACKAGE_MAJOR)$(PACKAGE_MINOR)
STUBPREFIX      = $(PROJECT)stub

DLLOBJS = \
	$(TMP_DIR)\threadNs.obj \
	$(TMP_DIR)\threadCmd.obj \
	$(TMP_DIR)\threadSvCmd.obj \
	$(TMP_DIR)\threadSpCmd.obj \
	$(TMP_DIR)\threadPoolCmd.obj \
	$(TMP_DIR)\psGdbm.obj \

	$(TMP_DIR)\threadSvListCmd.obj \
	$(TMP_DIR)\threadSvKeylistCmd.obj \
	$(TMP_DIR)\tclXkeylist.obj \
	$(TMP_DIR)\threadWin.obj \
!if !$(STATIC_BUILD)
	$(TMP_DIR)\thread.res
!endif

PRJHEADERS =


#-------------------------------------------------------------------------
# Target names and paths ( shouldn't need changing )
#-------------------------------------------------------------------------



BINROOT		= $(MAKEDIR)
ROOT            = $(MAKEDIR)\..

PRJIMPLIB	= $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
PRJLIBNAME	= $(PROJECT)$(VERSION)$(SUFX).$(EXT)
PRJLIB		= $(OUT_DIR)\$(PRJLIBNAME)

PRJSTUBLIBNAME	= $(STUBPREFIX)$(VERSION).lib
PRJSTUBLIB	= $(OUT_DIR)\$(PRJSTUBLIBNAME)

### Make sure we use backslash only.
PRJ_INSTALL_DIR         = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
LIB_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
BIN_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
DOC_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
SCRIPT_INSTALL_DIR	= $(PRJ_INSTALL_DIR)
INCLUDE_INSTALL_DIR	= $(_TCLDIR)\include

### The following paths CANNOT have spaces in them.
GENERICDIR	= $(ROOT)\generic
WINDIR		= $(ROOT)\win
LIBDIR          = $(ROOT)\lib
DOCDIR		= $(ROOT)\doc
TOOLSDIR	= $(ROOT)\tools
COMPATDIR	= $(ROOT)\compat

#---------------------------------------------------------------------
# Compile flags
#---------------------------------------------------------------------

!if !$(DEBUG)
!if $(OPTIMIZING)
### This cranks the optimization level to maximize speed
cdebug	= $(OPTIMIZATIONS)
!else
cdebug	=
!endif
!else if "$(MACHINE)" == "IA64"
### Warnings are too many, can't support warnings into errors.
cdebug	= -Zi -Od $(DEBUGFLAGS)
!else
cdebug	= -Zi -W3 $(DEBUGFLAGS)
!endif

### Common compiler options that are architecture specific
!if "$(MACHINE)" == "ARM"
carch = -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
!else
carch =
!endif

### Declarations common to all compiler options
cwarn = $(WARNINGS) -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE
cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\

!if $(MSVCRT)
!if $(DEBUG) && !$(UNCHECKED)
crt = -MDd
!else
crt = -MD
!endif
!else
!if $(DEBUG) && !$(UNCHECKED)
crt = -MTd
!else
crt = -MT
!endif
!endif

cflags = $(cflags) -DMODULE_SCOPE=extern -DUSE_TCL_STUBS
cflags = $(cflags) -DTCL_TIP143 -DTCL_TIP285
!if defined(TKSTUBLIB)
cflags = $(cflags) -DUSE_TK_STUBS
!endif

INCLUDES	= $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)"
BASE_CFLAGS	= $(cflags) $(cdebug) $(crt) $(INCLUDES)
CON_CFLAGS	= $(cflags) $(cdebug) $(crt) -DCONSOLE
TCL_CFLAGS	= -DPACKAGE_NAME="\"$(PROJECT)\"" \
		  -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \
		  -DBUILD_$(PROJECT) \
		  $(BASE_CFLAGS) $(OPTDEFINES)

### Stubs files should not be compiled with -GL
STUB_CFLAGS     = $(cflags) $(cdebug:-GL=) #$(TK_DEFINES)

#---------------------------------------------------------------------
# Link flags
#---------------------------------------------------------------------

!if $(DEBUG)
ldebug	= -debug
!if $(MSVCRT)
ldebug = $(ldebug) -nodefaultlib:msvcrt
!endif
!else
ldebug	= -release -opt:ref -opt:icf,3
!endif

### Declarations common to all linker options
lflags	= -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)

!if $(PROFILE)
lflags	= $(lflags) -profile
!endif

!if $(ALIGN98_HACK) && !$(STATIC_BUILD)
### Align sections for PE size savings.
lflags	= $(lflags) -opt:nowin98
!else if !$(ALIGN98_HACK) && $(STATIC_BUILD)
### Align sections for speed in loading by choosing the virtual page size.
lflags	= $(lflags) -align:4096
!endif

!if $(LOIMPACT)
lflags	= $(lflags) -ws:aggressive
!endif

dlllflags = $(lflags) -dll
conlflags = $(lflags) -subsystem:console
guilflags = $(lflags) -subsystem:windows
!if !$(STATIC_BUILD)
baselibs  = $(TCLSTUBLIB)
!if defined(TKSTUBLIB)
baselibs  = $(baselibs) $(TKSTUBLIB)
!endif
!endif

# Avoid 'unresolved external symbol __security_cookie' errors.
# c.f. http://support.microsoft.com/?id=894573
!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500
baselibs   = $(baselibs) bufferoverflowU.lib
!endif
!endif

#---------------------------------------------------------------------
# TclTest flags
#---------------------------------------------------------------------

!if "$(TESTPAT)" != ""
TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
!endif

#---------------------------------------------------------------------
# Project specific targets (EDIT)
#---------------------------------------------------------------------

all:	    setup $(PROJECT)
$(PROJECT): setup pkgIndex $(PRJLIB)
install:    install-binaries install-libraries install-docs
pkgIndex:   $(OUT_DIR)\pkgIndex.tcl

test: setup $(PROJECT)
	@set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
	@set TCLLIBPATH=$(OUT_DIR_PATH:\=/)
	@$(CPY) $(LIBDIR)\*.tcl $(OUT_DIR)
!if $(TCLINSTALL)
	@set PATH=$(_TCLDIR)\bin;$(PATH)
!else
	@set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
!endif
	$(DEBUGGER) $(TCLSH) "$(ROOT)/tests/all.tcl" $(TESTFLAGS)

shell: setup $(PROJECT)
	@set VLERQ_LIBRARY=$(LIBDIR:\=/)
	@set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
	@set TCLLIBPATH=$(OUT_DIR:\=/)
	@$(CPY) $(LIBDIR)\*.tcl $(OUT_DIR)
!if $(TCLINSTALL)
	@set PATH=$(_TCLDIR)\bin;$(PATH)
!else
	@set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
!endif
	$(DEBUGGER) $(TCLSH) $(SCRIPT)

setup:
	@if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
	@if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)

# See <tcl>/win/coffbase.txt for extension base addresses.
$(PRJLIB): $(DLLOBJS)
!if $(STATIC_BUILD)
	$(lib32) -nologo -out:$@ @<<
$**
<<
!else
	$(link32) $(dlllflags) -base:0x10C80000 -out:$@ $(baselibs) @<<
$**
<<
	$(_VC_MANIFEST_EMBED_DLL)
	-@del $*.exp
!endif

$(PRJSTUBLIB): $(PRJSTUBOBJS)
	$(lib32) -nologo -nodefaultlib -out:$@ $(PRJSTUBOBJS)

#---------------------------------------------------------------------
# Implicit rules
#---------------------------------------------------------------------

{$(WINDIR)}.c{$(TMP_DIR)}.obj::
    $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
$<
<<

{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
    $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
$<
<<

{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
    $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
$<
<<

{$(WINDIR)}.rc{$(TMP_DIR)}.res:
	$(rc32) -fo $@ -r -i "$(GENERICDIR)" $(TCL_INCLUDES) \
		-D_WIN32 -D__WIN32__ \
		-DCOMMAVERSION=$(DOTVERSION:.=,),0 \
		-DDOTVERSION=\"$(DOTVERSION)\" \
		-DVERSION=\"$(VERSION)$(SUFX)\" \
		-DDEBUG=$(DEBUG) \
		-DPACKAGE_MAJOR=$(PACKAGE_MAJOR) \
		-DPACKAGE_MINOR=$(PACKAGE_MINOR) \
		-DPACKAGE_VERSION=\"$(PACKAGE_VERSION)\" \
!if $(DEBUG)
	-d DEBUG \
!endif
!if $(TCL_THREADS)
	-d TCL_THREADS \
!endif
!if $(STATIC_BUILD)
	-d STATIC_BUILD \
!endif
	$<

.SUFFIXES:
.SUFFIXES:.c .rc

#-------------------------------------------------------------------------
# Explicit dependency rules
#
#-------------------------------------------------------------------------

#{$(WINDIR)}.c{$(TMP_DIR)}.obj ::
$(GENERICDIR)\psGdbm.c: $(GENERICDIR)\psGdbm.h


$(GENERICDIR)\threadSpCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSvCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadPoolCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSvListCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSvKeylistCmd.c : $(GENERICDIR)\tclThreadInt.h

.PHONY: $(OUT_DIR)\pkgIndex.tcl

$(OUT_DIR)\pkgIndex.tcl: $(ROOT)\pkgIndex.tcl.in
	@nmakehlp -s << $** > $@
@PACKAGE_NAME@       thread
@PACKAGE_VERSION@    $(DOTVERSION)
@PKG_LIB_FILE@       $(PRJLIBNAME)
<<

#---------------------------------------------------------------------
# Installation. (EDIT)
#
# You may need to modify this section to reflect the final distribution
# of your files and possibly to generate documentation.
#
#---------------------------------------------------------------------

install-binaries:
	@echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
	@if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
	@$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL

install-libraries:
	@echo Installing library files to '$(SCRIPT_INSTALL_DIR)'
	@if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
	@$(CPY) $(OUT_DIR)\pkgIndex.tcl "$(SCRIPT_INSTALL_DIR)"

install-docs:
	@echo Installing documentation files to '$(DOC_INSTALL_DIR)'
	@if exist $(DOCDIR)\man $(CPY) $(DOCDIR)\man\*.n "$(DOC_INSTALL_DIR)"

#---------------------------------------------------------------------
# Clean up
#---------------------------------------------------------------------

clean:
	@if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
	@if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc
	@if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i
	@if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x
	@if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch

realclean: clean
	@if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)

distclean: realclean
	@if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
	@if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj

>
>
>
>
|
>
>

<
>
>
>

>
>
>
>
>
>
>



<
<
<
<
<


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

>
>

<
<
<
>
>
>

|

<
<
<
<
|






>



|
<
<
<

<
>

<
<
<
>
>

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

<
<
<
<

>
>






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
















































#------------------------------------------------------------- -*- makefile -*-
#
# Makefile for thread extension
#
# Basic build, test and install
#   nmake /f makefile.vc INSTALLDIR=c:\tcl
#   nmake /f makefile.vc INSTALLDIR=c:\tcl test
#   nmake /f makefile.vc INSTALLDIR=c:\tcl install
#

# For other build options (debug, static etc.),
# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
# detailed documentation.
#
# In addition to the command line macros described there the following
# may also be defined.
#  ADDOPTDEFINES - addition compiler options
#  ADDLINKOPTS - addition link options
# E.g.
#   nmake -nologo -f makefile.vc TCLDIR=%TCLDIR% ... ADDOPTDEFINES="-I%LMDBDIR%" ADDLINKOPTS="%LMDBDIR%\Release\lmdb.lib"
# 
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#





#------------------------------------------------------------------------------











































































































































PROJECT = thread
RCFILE = thread.rc
DOCDIR          = $(ROOT)\doc\html




PRJ_DEFINES = -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
PRJ_DEFINES = $(PRJ_DEFINES) -DTCL_TIP143 -DTCL_TIP285 -DTCL_NO_DEPRECATED=1 $(ADDOPTDEFINES)
PRJ_LIBS = $(ADDLINKOPTS)

!include "rules-ext.vc"





PRJ_OBJS = \
	$(TMP_DIR)\threadNs.obj \
	$(TMP_DIR)\threadCmd.obj \
	$(TMP_DIR)\threadSvCmd.obj \
	$(TMP_DIR)\threadSpCmd.obj \
	$(TMP_DIR)\threadPoolCmd.obj \
	$(TMP_DIR)\psGdbm.obj \
	$(TMP_DIR)\psLmdb.obj \
	$(TMP_DIR)\threadSvListCmd.obj \
	$(TMP_DIR)\threadSvKeylistCmd.obj \
	$(TMP_DIR)\tclXkeylist.obj \
	$(TMP_DIR)\threadWin.obj





!include "$(_RULESDIR)\targets.vc"




install:    default-install-docs-html
pkgindex:   default-pkgindex-tea


















































































































































































































































# Explicit dependency rules




$(GENERICDIR)\psGdbm.c: $(GENERICDIR)\psGdbm.h
$(GENERICDIR)\psLmdb.c: $(GENERICDIR)\psLmdb.h
$(GENERICDIR)\threadCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSpCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSvCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadPoolCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSvListCmd.c : $(GENERICDIR)\tclThreadInt.h
$(GENERICDIR)\threadSvKeylistCmd.c : $(GENERICDIR)\tclThreadInt.h

















































Changes to win/nmakehlp.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
..
70
71
72
73
74
75
76

77
78
79
80
81
82
83
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
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
...
309
310
311
312
313
314
315
316

317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
...
364
365
366
367
368
369
370


371


372
373
374
375
376
377
378
...
429
430
431
432
433
434
435
436


437
438
439
440
441
442
443
...
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
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * ----------------------------------------------------------------------------
 */

#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#define NO_SHLWAPI_GDI
#define NO_SHLWAPI_STREAM
#define NO_SHLWAPI_REG
#include <shlwapi.h>
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "kernel32.lib")
#pragma comment (lib, "shlwapi.lib")
#include <stdio.h>
#include <math.h>

/*
 * This library is required for x64 builds with _some_ versions of MSVC
 */
#if defined(_M_IA64) || defined(_M_AMD64)
................................................................................

/* ISO hack for dumb VC++ */
#ifdef _MSC_VER
#define   snprintf	_snprintf
#endif



/* protos */

static int CheckForCompilerFeature(const char *option);
static int CheckForLinkerFeature(const char *option);
static int IsIn(const char *string, const char *substring);
static int SubstituteFile(const char *substs, const char *filename);
static int QualifyPath(const char *path);

static const char *GetVersionFromFile(const char *filename, const char *match, int numdots);
static DWORD WINAPI ReadFromPipe(LPVOID args);

/* globals */

#define CHUNK	25
#define STATICBUFFERSIZE    1000
................................................................................
main(
    int argc,
    char *argv[])
{
    char msg[300];
    DWORD dwWritten;
    int chars;


    /*
     * Make sure children (cl.exe and link.exe) are kept quiet.
     */

    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

................................................................................
			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
			&dwWritten, NULL);
		return 2;
	    }
	    return CheckForCompilerFeature(argv[2]);
	case 'l':
	    if (argc != 3) {
		chars = snprintf(msg, sizeof(msg) - 1,
	       		"usage: %s -l <linker option>\n"
			"Tests for whether link.exe supports an option\n"
			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
			&dwWritten, NULL);
		return 2;
	    }
	    return CheckForLinkerFeature(argv[2]);
	case 'f':
	    if (argc == 2) {
		chars = snprintf(msg, sizeof(msg) - 1,
			"usage: %s -f <string> <substring>\n"
			"Find a substring within another\n"
			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
................................................................................
		    "Extract a version from a file:\n"
		    "eg: pkgIndex.tcl \"package ifneeded http\"",
		    argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
		    &dwWritten, NULL);
		return 0;
	    }
	    printf("%s\n", GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0'));


	    return 0;



	case 'Q':
	    if (argc != 3) {
		chars = snprintf(msg, sizeof(msg) - 1,
		    "usage: %s -Q path\n"
		    "Emit the fully qualified path\n"
		    "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
		    &dwWritten, NULL);
		return 2;
	    }
	    return QualifyPath(argv[2]);












	}
    }
    chars = snprintf(msg, sizeof(msg) - 1,
	    "usage: %s -c|-f|-l|-Q|-s|-V ...\n"
	    "This is a little helper app to equalize shell differences between WinNT and\n"
	    "Win9x and get nmake.exe to accomplish its job.\n",
	    argv[0]);
................................................................................
             || strstr(Err.buffer, "D9002") != NULL
             || strstr(Out.buffer, "D2021") != NULL
             || strstr(Err.buffer, "D2021") != NULL);
}
 
static int
CheckForLinkerFeature(
    const char *option)

{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;
    DWORD threadID;
    char msg[300];
    BOOL ok;
    HANDLE hProcess, h, pipeThreads[2];

    char cmdline[100];

    hProcess = GetCurrentProcess();

    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags   = STARTF_USESTDHANDLES;
................................................................................

    lstrcpy(cmdline, "link.exe -nologo ");

    /*
     * Append our option for testing.
     */



    lstrcat(cmdline, option);



    ok = CreateProcess(
	    NULL,	    /* Module name. */
	    cmdline,	    /* Command line. */
	    NULL,	    /* Process handle not inheritable. */
	    NULL,	    /* Thread handle not inheritable. */
	    TRUE,	    /* yes, inherit handles. */
................................................................................
    /*
     * Look for the commandline warning code in the stderr stream.
     */

    return !(strstr(Out.buffer, "LNK1117") != NULL ||
	    strstr(Err.buffer, "LNK1117") != NULL ||
	    strstr(Out.buffer, "LNK4044") != NULL ||
	    strstr(Err.buffer, "LNK4044") != NULL);


}
 
static DWORD WINAPI
ReadFromPipe(
    LPVOID args)
{
    pipeinfo *pi = (pipeinfo *) args;
................................................................................

	list_free(&substPtr);
    }
    fclose(fp);
    return 0;
}
 











/*
 * QualifyPath --
 *
 *	This composes the current working directory with a provided path
 *	and returns the fully qualified and normalized path.
 *	Mostly needed to setup paths for testing.
 */

static int
QualifyPath(
    const char *szPath)
{
    char szCwd[MAX_PATH + 1];
    char szTmp[MAX_PATH + 1];

    char *p;
    GetCurrentDirectory(MAX_PATH, szCwd);
    while ((p = strchr(szPath, '/')) && *p)
	*p = '\\';
    PathCombine(szTmp, szCwd, szPath);
    PathCanonicalize(szCwd, szTmp);
    printf("%s\n", szCwd);
    return 0;
}

/*



























































































 * Local variables:
 *   mode: c
 *   c-basic-offset: 4
 *   fill-column: 78
 *   indent-tabs-mode: t
 *   tab-width: 8
 * End:
 */







<
<
<
<


<







 







<



|



>







 







>







 







|

|






|







 







|
>
>
|
>
>
>











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







 







|
>








>
|







 







>
>
|
>
>







 







|
>
>







 







>
>
>
>
>
>
>
>
>
>
>













<
>
|
<
<
<
<
<





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








10
11
12
13
14
15
16




17
18

19
20
21
22
23
24
25
..
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
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
...
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
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
...
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
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * ----------------------------------------------------------------------------
 */

#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>




#pragma comment (lib, "user32.lib")
#pragma comment (lib, "kernel32.lib")

#include <stdio.h>
#include <math.h>

/*
 * This library is required for x64 builds with _some_ versions of MSVC
 */
#if defined(_M_IA64) || defined(_M_AMD64)
................................................................................

/* ISO hack for dumb VC++ */
#ifdef _MSC_VER
#define   snprintf	_snprintf
#endif



/* protos */

static int CheckForCompilerFeature(const char *option);
static int CheckForLinkerFeature(const char **options, int count);
static int IsIn(const char *string, const char *substring);
static int SubstituteFile(const char *substs, const char *filename);
static int QualifyPath(const char *path);
static int LocateDependency(const char *keyfile);
static const char *GetVersionFromFile(const char *filename, const char *match, int numdots);
static DWORD WINAPI ReadFromPipe(LPVOID args);

/* globals */

#define CHUNK	25
#define STATICBUFFERSIZE    1000
................................................................................
main(
    int argc,
    char *argv[])
{
    char msg[300];
    DWORD dwWritten;
    int chars;
    const char *s;

    /*
     * Make sure children (cl.exe and link.exe) are kept quiet.
     */

    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

................................................................................
			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
			&dwWritten, NULL);
		return 2;
	    }
	    return CheckForCompilerFeature(argv[2]);
	case 'l':
	    if (argc < 3) {
		chars = snprintf(msg, sizeof(msg) - 1,
	       		"usage: %s -l <linker option> ?<mandatory option> ...?\n"
			"Tests for whether link.exe supports an option\n"
			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
			&dwWritten, NULL);
		return 2;
	    }
	    return CheckForLinkerFeature(&argv[2], argc-2);
	case 'f':
	    if (argc == 2) {
		chars = snprintf(msg, sizeof(msg) - 1,
			"usage: %s -f <string> <substring>\n"
			"Find a substring within another\n"
			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
................................................................................
		    "Extract a version from a file:\n"
		    "eg: pkgIndex.tcl \"package ifneeded http\"",
		    argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
		    &dwWritten, NULL);
		return 0;
	    }
	    s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0');
	    if (s && *s) {
		printf("%s\n", s);
		return 0;
	    } else
		return 1; /* Version not found. Return non-0 exit code */

	case 'Q':
	    if (argc != 3) {
		chars = snprintf(msg, sizeof(msg) - 1,
		    "usage: %s -Q path\n"
		    "Emit the fully qualified path\n"
		    "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
		    &dwWritten, NULL);
		return 2;
	    }
	    return QualifyPath(argv[2]);

	case 'L':
	    if (argc != 3) {
		chars = snprintf(msg, sizeof(msg) - 1,
		    "usage: %s -L keypath\n"
		    "Emit the fully qualified path of directory containing keypath\n"
		    "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]);
		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
		    &dwWritten, NULL);
		return 2;
	    }
	    return LocateDependency(argv[2]);
	}
    }
    chars = snprintf(msg, sizeof(msg) - 1,
	    "usage: %s -c|-f|-l|-Q|-s|-V ...\n"
	    "This is a little helper app to equalize shell differences between WinNT and\n"
	    "Win9x and get nmake.exe to accomplish its job.\n",
	    argv[0]);
................................................................................
             || strstr(Err.buffer, "D9002") != NULL
             || strstr(Out.buffer, "D2021") != NULL
             || strstr(Err.buffer, "D2021") != NULL);
}
 
static int
CheckForLinkerFeature(
    const char **options,
    int count)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;
    DWORD threadID;
    char msg[300];
    BOOL ok;
    HANDLE hProcess, h, pipeThreads[2];
    int i;
    char cmdline[255];

    hProcess = GetCurrentProcess();

    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags   = STARTF_USESTDHANDLES;
................................................................................

    lstrcpy(cmdline, "link.exe -nologo ");

    /*
     * Append our option for testing.
     */

    for (i = 0; i < count; i++) {
	lstrcat(cmdline, " \"");
	lstrcat(cmdline, options[i]);
	lstrcat(cmdline, "\"");
    }

    ok = CreateProcess(
	    NULL,	    /* Module name. */
	    cmdline,	    /* Command line. */
	    NULL,	    /* Process handle not inheritable. */
	    NULL,	    /* Thread handle not inheritable. */
	    TRUE,	    /* yes, inherit handles. */
................................................................................
    /*
     * Look for the commandline warning code in the stderr stream.
     */

    return !(strstr(Out.buffer, "LNK1117") != NULL ||
	    strstr(Err.buffer, "LNK1117") != NULL ||
	    strstr(Out.buffer, "LNK4044") != NULL ||
	    strstr(Err.buffer, "LNK4044") != NULL ||
	    strstr(Out.buffer, "LNK4224") != NULL ||
	    strstr(Err.buffer, "LNK4224") != NULL);
}
 
static DWORD WINAPI
ReadFromPipe(
    LPVOID args)
{
    pipeinfo *pi = (pipeinfo *) args;
................................................................................

	list_free(&substPtr);
    }
    fclose(fp);
    return 0;
}
 
BOOL FileExists(LPCTSTR szPath)
{
#ifndef INVALID_FILE_ATTRIBUTES
    #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 
#endif
    DWORD pathAttr = GetFileAttributes(szPath);
    return (pathAttr != INVALID_FILE_ATTRIBUTES && 
	    !(pathAttr & FILE_ATTRIBUTE_DIRECTORY));
}
 

/*
 * QualifyPath --
 *
 *	This composes the current working directory with a provided path
 *	and returns the fully qualified and normalized path.
 *	Mostly needed to setup paths for testing.
 */

static int
QualifyPath(
    const char *szPath)
{
    char szCwd[MAX_PATH + 1];


	GetFullPathName(szPath, sizeof(szCwd)-1, szCwd, NULL);





    printf("%s\n", szCwd);
    return 0;
}

/*
 * Implements LocateDependency for a single directory. See that command
 * for an explanation.
 * Returns 0 if found after printing the directory.
 * Returns 1 if not found but no errors.
 * Returns 2 on any kind of error
 * Basically, these are used as exit codes for the process.
 */
static int LocateDependencyHelper(const char *dir, const char *keypath)
{
    HANDLE hSearch;
    char path[MAX_PATH+1];
    int dirlen, keylen, ret;
    WIN32_FIND_DATA finfo;

    if (dir == NULL || keypath == NULL)
	return 2; /* Have no real error reporting mechanism into nmake */
    dirlen = strlen(dir);
    if ((dirlen + 3) > sizeof(path))
	return 2;
    strncpy(path, dir, dirlen);
    strncpy(path+dirlen, "\\*", 3);	/* Including terminating \0 */
    keylen = strlen(keypath);

#if 0 /* This function is not available in Visual C++ 6 */
    /*
     * Use numerics 0 -> FindExInfoStandard,
     * 1 -> FindExSearchLimitToDirectories, 
     * as these are not defined in Visual C++ 6
     */
    hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0);
#else
    hSearch = FindFirstFile(path, &finfo);
#endif
    if (hSearch == INVALID_HANDLE_VALUE)
	return 1; /* Not found */

    /* Loop through all subdirs checking if the keypath is under there */
    ret = 1; /* Assume not found */
    do {
	int sublen;
	/*
	 * We need to check it is a directory despite the 
	 * FindExSearchLimitToDirectories in the above call. See SDK docs
	 */
	if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
	    continue;
	sublen = strlen(finfo.cFileName);
	if ((dirlen+1+sublen+1+keylen+1) > sizeof(path))
	    continue;		/* Path does not fit, assume not matched */
	strncpy(path+dirlen+1, finfo.cFileName, sublen);
	path[dirlen+1+sublen] = '\\';
	strncpy(path+dirlen+1+sublen+1, keypath, keylen+1);
	if (FileExists(path)) {
	    /* Found a match, print to stdout */
	    path[dirlen+1+sublen] = '\0';
	    QualifyPath(path);
	    ret = 0;
	    break;
	}
    } while (FindNextFile(hSearch, &finfo));
    FindClose(hSearch);
    return ret;
}

/*
 * LocateDependency --
 *
 *	Locates a dependency for a package.
 *        keypath - a relative path within the package directory
 *          that is used to confirm it is the correct directory.
 *	The search path for the package directory is currently only
 *      the parent and grandparent of the current working directory.
 *      If found, the command prints 
 *         name_DIRPATH=<full path of located directory>
 *      and returns 0. If not found, does not print anything and returns 1.
 */
static int LocateDependency(const char *keypath)
{
    int i, ret;
    static char *paths[] = {"..", "..\\..", "..\\..\\.."};
    
    for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) {
	ret = LocateDependencyHelper(paths[i], keypath);
	if (ret == 0)
	    return ret;
    }
    return ret;
}


/*
 * Local variables:
 *   mode: c
 *   c-basic-offset: 4
 *   fill-column: 78
 *   indent-tabs-mode: t
 *   tab-width: 8
 * End:
 */

Changes to win/pkg.vc.

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"




|
|
1
2
3
4
5
6
# remember to change configure.ac as well when these change
# (then re-autoconf)

PACKAGE_MAJOR	= 2
PACKAGE_MINOR	= 9
PACKAGE_VERSION	= "2.9a1"

Added win/rules-ext.vc.













































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# This file should only be included in makefiles for Tcl extensions,
# NOT in the makefile for Tcl itself.

!ifndef _RULES_EXT_VC

# We need to run from the directory the parent makefile is located in.
# nmake does not tell us what makefile was used to invoke it so parent
# makefile has to set the MAKEFILEVC macro or we just make a guess and
# warn if we think that is not the case.
!if "$(MAKEFILEVC)" == ""

!if exist("$(PROJECT).vc")
MAKEFILEVC = $(PROJECT).vc
!elseif exist("makefile.vc")
MAKEFILEVC = makefile.vc
!endif
!endif # "$(MAKEFILEVC)" == ""

!if !exist("$(MAKEFILEVC)")
MSG = ^
You must run nmake from the directory containing the project makefile.^
If you are doing that and getting this message, set the MAKEFILEVC^
macro to the name of the project makefile.
!message WARNING: $(MSG)
!endif

!if "$(PROJECT)" == "tcl"
!error The rules-ext.vc file is not intended for Tcl itself.
!endif

# We extract version numbers using the nmakehlp program. For now use
# the local copy of nmakehlp. Once we locate Tcl, we will use that
# one if it is newer.
!if [$(CC) -nologo "nmakehlp.c" -link -subsystem:console > nul]
!endif

# First locate the Tcl directory that we are working with.
!if "$(TCLDIR)" != ""

_RULESDIR = $(TCLDIR:/=\)

!else

# If an installation path is specified, that is also the Tcl directory.
# Also Tk never builds against an installed Tcl, it needs Tcl sources
!if defined(INSTALLDIR) && "$(PROJECT)" != "tk"
_RULESDIR=$(INSTALLDIR:/=\)
!else
# Locate Tcl sources
!if [echo _RULESDIR = \> nmakehlp.out] \
   || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
_RULESDIR = ..\..\tcl
!else
!include nmakehlp.out
!endif

!endif # defined(INSTALLDIR)....

!endif # ifndef TCLDIR

# Now look for the targets.vc file under the Tcl root. Note we check this
# file and not rules.vc because the latter also exists on older systems.
!if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl
_RULESDIR = $(_RULESDIR)\lib\nmake
!elseif exist("$(_RULESDIR)\win\targets.vc")   # Building against Tcl sources
_RULESDIR = $(_RULESDIR)\win
!else
# If we have not located Tcl's targets file, most likely we are compiling
# against an older version of Tcl and so must use our own support files.
_RULESDIR = .
!endif

!if "$(_RULESDIR)" != "."
# Potentially using Tcl's support files. If this extension has its own
# nmake support files, need to compare the versions and pick newer.

!if exist("rules.vc") # The extension has its own copy

!if [echo TCL_RULES_MAJOR = \> versions.vc] \
   && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc]
!endif
!if [echo TCL_RULES_MINOR = \>> versions.vc] \
   && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc]
!endif

!if [echo OUR_RULES_MAJOR = \>> versions.vc] \
   && [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc]
!endif
!if [echo OUR_RULES_MINOR = \>> versions.vc] \
   && [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc]
!endif
!include versions.vc
# We have a newer version of the support files, use them
!if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR))
_RULESDIR = .
!endif

!endif # if exist("rules.vc")

!endif # if $(_RULESDIR) != "."

# Let rules.vc know what copy of nmakehlp.c to use.
NMAKEHLPC = $(_RULESDIR)\nmakehlp.c

# Get rid of our internal defines before calling rules.vc
!undef TCL_RULES_MAJOR
!undef TCL_RULES_MINOR
!undef OUR_RULES_MAJOR
!undef OUR_RULES_MINOR

!if exist("$(_RULESDIR)\rules.vc")
!message *** Using $(_RULESDIR)\rules.vc
!include "$(_RULESDIR)\rules.vc"
!else
!error *** Could not locate rules.vc in $(_RULESDIR)
!endif

!endif # _RULES_EXT_VC

Changes to win/rules.vc.

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
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
...
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
#------------------------------------------------------------------------------
# rules.vc --
#
#	Microsoft Visual C++ makefile include for decoding the commandline
#	macros.  This file does not need editing to build Tcl.


#
#	This version is modified from the Tcl source version to support
#	building extensions using nmake.


#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# Copyright (c) 2001-2002 David Gravereaux.
# Copyright (c) 2003-2008 Patrick Thoyts

#------------------------------------------------------------------------------

!ifndef _RULES_VC
_RULES_VC = 1

cc32		= $(CC)   # built-in default.
link32		= link
lib32		= lib
rc32		= $(RC)   # built-in default.





















!ifndef INSTALLDIR
### Assume the normal default.
_INSTALLDIR	= C:\Program Files\Tcl



!else
### Fix the path separators.
_INSTALLDIR	= $(INSTALLDIR:/=\)

!endif



















































































#----------------------------------------------------------
# Set the proper copy method to avoid overwrite questions
# to the user when copying files and selecting the right
# "delete all" method.
#----------------------------------------------------------

!if "$(OS)" == "Windows_NT"
RMDIR	= rmdir /S /Q
ERRNULL  = 2>NUL
!if ![ver | find "4.0" > nul]
CPY	= echo y | xcopy /i >NUL

COPY	= copy >NUL




























































!else
CPY	= xcopy /i /y >NUL
COPY	= copy /y >NUL

!endif
!else # "$(OS)" != "Windows_NT"
CPY	= xcopy /i >_JUNK.OUT # On Win98 NUL does not work here.
COPY	= copy >_JUNK.OUT # On Win98 NUL does not work here.
RMDIR	= deltree /Y
NULL    = \NUL # Used in testing directory existence
ERRNULL = >NUL # Win9x shell cannot redirect stderr
!endif
MKDIR   = mkdir


























































































































































































































#------------------------------------------------------------------------------
# Determine the host and target architectures and compiler version.
#------------------------------------------------------------------------------




_HASH=^#
_VC_MANIFEST_EMBED_EXE=
_VC_MANIFEST_EMBED_DLL=
VCVER=0
!if ![echo VCVERSION=_MSC_VER > vercl.x] \
    && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \
    && ![echo ARCH=IX86 >> vercl.x] \
    && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
    && ![echo ARCH=AMD64 >> vercl.x] \
    && ![echo $(_HASH)endif >> vercl.x] \
    && ![cl -nologo -TC -P vercl.x $(ERRNULL)]
!include vercl.i

!if ![echo VCVER= ^\> vercl.vc] \
    && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
!include vercl.vc
!endif




!endif


!if ![del $(ERRNUL) /q/f vercl.x vercl.i vercl.vc]
!endif





















!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86]
NATIVE_ARCH=IX86
!else
NATIVE_ARCH=AMD64
!endif

# Since MSVC8 we must deal with manifest resources.
!if $(VCVERSION) >= 1400
_VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1
_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
!endif

!ifndef MACHINE
MACHINE=$(ARCH)
!endif

!ifndef CFG_ENCODING
CFG_ENCODING	= \"cp1252\"
!endif

!message ===============================================================================

#----------------------------------------------------------


# build the helper app we need to overcome nmake's limiting
# environment.
#----------------------------------------------------------


!if !exist(nmakehlp.exe)
!if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul]
!endif
!endif



#----------------------------------------------------------
# Test for compiler features
#----------------------------------------------------------






### test for optimizations
!if [nmakehlp -c -Ot]
!message *** Compiler has 'Optimizations'
OPTIMIZING	= 1
!else
!message *** Compiler does not have 'Optimizations'
OPTIMIZING	= 0
!endif


OPTIMIZATIONS   =






!if [nmakehlp -c -Ot]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -Ot
!endif

!if [nmakehlp -c -Oi]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -Oi




!endif







!if [nmakehlp -c -Op]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -Op
!endif

!if [nmakehlp -c -fp:strict]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -fp:strict



!endif




!if [nmakehlp -c -Gs]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -Gs
!endif



!if [nmakehlp -c -GS]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -GS
!endif


!if [nmakehlp -c -GL]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -GL
!endif


DEBUGFLAGS     =




!if [nmakehlp -c -RTC1]
DEBUGFLAGS     = $(DEBUGFLAGS) -RTC1
!elseif [nmakehlp -c -GZ]
DEBUGFLAGS     = $(DEBUGFLAGS) -GZ

!endif

COMPILERFLAGS  =-W3

# In v13 -GL and -YX are incompatible.

!if [nmakehlp -c -YX]
!if ![nmakehlp -c -GL]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -YX
!endif

!endif

!if "$(MACHINE)" == "IX86"
### test for pentium errata
!if [nmakehlp -c -QI0f]
!message *** Compiler has 'Pentium 0x0f fix'
COMPILERFLAGS  = $(COMPILERFLAGS) -QI0f
!else
!message *** Compiler does not have 'Pentium 0x0f fix'
!endif
!endif

!if "$(MACHINE)" == "IA64"
### test for Itanium errata










!if [nmakehlp -c -QIA64_Bx]
!message *** Compiler has 'B-stepping errata workarounds'
COMPILERFLAGS   = $(COMPILERFLAGS) -QIA64_Bx


!else

!message *** Compiler does not have 'B-stepping errata workarounds'

!endif




!endif

!if "$(MACHINE)" == "IX86"
### test for -align:4096, when align:512 will do.



!if [nmakehlp -l -opt:nowin98]
!message *** Linker has 'Win98 alignment problem'
ALIGN98_HACK	= 1


!else
!message *** Linker does not have 'Win98 alignment problem'
ALIGN98_HACK	= 0



!endif










!else
ALIGN98_HACK	= 0











!endif

LINKERFLAGS     =



!if [nmakehlp -l -ltcg]
LINKERFLAGS     =-ltcg
!endif


#----------------------------------------------------------
# Decode the options requested.
#----------------------------------------------------------



























!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"]

STATIC_BUILD	= 0
TCL_THREADS	= 1
DEBUG		= 0

PROFILE		= 0

MSVCRT		= 0
LOIMPACT	= 0
TCL_USE_STATIC_PACKAGES	= 0
USE_THREAD_ALLOC = 1
USE_THREAD_STORAGE = 1
UNCHECKED       = 0



!else









!if [nmakehlp -f $(OPTS) "static"]
!message *** Doing static
STATIC_BUILD	= 1
!else
STATIC_BUILD	= 0




!endif





!if [nmakehlp -f $(OPTS) "msvcrt"]
!message *** Doing msvcrt
MSVCRT		= 1
!else



MSVCRT		= 0
!endif



!if [nmakehlp -f $(OPTS) "staticpkg"]
!message *** Doing staticpkg
TCL_USE_STATIC_PACKAGES	= 1
!else
TCL_USE_STATIC_PACKAGES	= 0
!endif

!if [nmakehlp -f $(OPTS) "nothreads"]
!message *** Compile explicitly for non-threaded tcl
TCL_THREADS	= 0

!else
TCL_THREADS     = 1

!endif

!if [nmakehlp -f $(OPTS) "symbols"]
!message *** Doing symbols
DEBUG		= 1
!else
DEBUG		= 0
!endif








!if [nmakehlp -f $(OPTS) "profile"]
!message *** Doing profile
PROFILE		= 1
!else
PROFILE		= 0
!endif

!if [nmakehlp -f $(OPTS) "loimpact"]
!message *** Doing loimpact
LOIMPACT	= 1



!else
LOIMPACT	= 0
!endif






!if [nmakehlp -f $(OPTS) "thrdalloc"]
!message *** Doing thrdalloc
USE_THREAD_ALLOC = 1
!else
USE_THREAD_ALLOC = 0
!endif

!if [nmakehlp -f $(OPTS) "thrdstorage"]
!message *** Doing thrdstorage
USE_THREAD_STORAGE = 1
!else
USE_THREAD_STORAGE = 0
!endif

!if [nmakehlp -f $(OPTS) "unchecked"]
!message *** Doing unchecked
UNCHECKED = 1
!else
UNCHECKED = 0
!endif





!endif



!if !$(STATIC_BUILD)
# Make sure we don't build overly fat DLLs.
MSVCRT		= 1
# We shouldn't statically put the extensions inside the shell when dynamic.
TCL_USE_STATIC_PACKAGES = 0








!endif


























































































#----------------------------------------------------------

































































# Figure-out how to name our intermediate and output directories.
# We wouldn't want different builds to use the same .obj files
# by accident.
#----------------------------------------------------------



#----------------------------------------
# Naming convention:
#   t = full thread support.
#   s = static library (as opposed to an
#	import library)
#   g = linked to the debug enabled C
#	run-time.
#   x = special static build when it
#	links to the dynamic C run-time.
#----------------------------------------


















SUFX	    = sgx

!if $(DEBUG)
BUILDDIRTOP = Debug
!else
BUILDDIRTOP = Release
!endif

................................................................................

TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX

!if !$(STATIC_BUILD)
TMP_DIRFULL = $(TMP_DIRFULL:Static=)
SUFX	    = $(SUFX:s=)
EXT	    = dll
!if $(MSVCRT)
TMP_DIRFULL = $(TMP_DIRFULL:X=)
SUFX	    = $(SUFX:x=)
!endif
!else
TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=)
EXT	    = lib
!if !$(MSVCRT)
TMP_DIRFULL = $(TMP_DIRFULL:X=)
SUFX	    = $(SUFX:x=)
!endif
................................................................................
!endif
!else
!ifndef OUT_DIR
OUT_DIR	    = $(TMP_DIR)
!endif
!endif












#----------------------------------------------------------
# Decode the statistics requested.
#----------------------------------------------------------



!if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"]
TCL_MEM_DEBUG	    = 0
TCL_COMPILE_DEBUG   = 0























!else
!if [nmakehlp -f $(STATS) "memdbg"]
!message *** Doing memdbg
TCL_MEM_DEBUG	    = 1


!else
TCL_MEM_DEBUG	    = 0







!endif
!if [nmakehlp -f $(STATS) "compdbg"]
!message *** Doing compdbg
TCL_COMPILE_DEBUG   = 1











!else
TCL_COMPILE_DEBUG   = 0


!endif






!endif








#----------------------------------------------------------
# Decode the checks requested.
#----------------------------------------------------------


!if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"]
TCL_NO_DEPRECATED	    = 0
WARNINGS		    = -W3







!else
!if [nmakehlp -f $(CHECKS) "nodep"]
!message *** Doing nodep check
TCL_NO_DEPRECATED	    = 1

!else
TCL_NO_DEPRECATED	    = 0
!endif
!if [nmakehlp -f $(CHECKS) "fullwarn"]
!message *** Doing full warnings check
WARNINGS		    = -W4
!if [nmakehlp -l -warn:3]
LINKERFLAGS		    = $(LINKERFLAGS) -warn:3



























!endif































!else
WARNINGS		    = -W3

!endif
!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
!message *** Doing 64bit portability warnings
WARNINGS		    = $(WARNINGS) -Wp64
!endif




















!endif



#----------------------------------------------------------
# Set our defines now armed with our options.
#----------------------------------------------------------

































OPTDEFINES	= -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS

!if $(TCL_MEM_DEBUG)
OPTDEFINES	= $(OPTDEFINES) -DTCL_MEM_DEBUG
!endif
!if $(TCL_COMPILE_DEBUG)
................................................................................
OPTDEFINES	= $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
!endif
!if $(TCL_THREADS)
OPTDEFINES	= $(OPTDEFINES) -DTCL_THREADS=1
!if $(USE_THREAD_ALLOC)
OPTDEFINES	= $(OPTDEFINES) -DUSE_THREAD_ALLOC=1
!endif
!if $(USE_THREAD_STORAGE)
OPTDEFINES	= $(OPTDEFINES) -DUSE_THREAD_STORAGE=1
!endif
!endif
!if $(STATIC_BUILD)
OPTDEFINES	= $(OPTDEFINES) -DSTATIC_BUILD
!endif
!if $(TCL_NO_DEPRECATED)
OPTDEFINES	= $(OPTDEFINES) -DTCL_NO_DEPRECATED
!endif












!if $(DEBUG)
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_DEBUG
!elseif $(OPTIMIZING)
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_OPTIMIZED

!endif
!if $(PROFILE)
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_PROFILED
!endif
!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_DO64BIT
!endif







#----------------------------------------------------------
# Get common info used when building extensions.
#----------------------------------------------------------













!if "$(PROJECT)" != "tcl"




# If INSTALLDIR set to tcl root dir then reset to the lib dir.
!if exist("$(_INSTALLDIR)\include\tcl.h")
_INSTALLDIR=$(_INSTALLDIR)\lib








!endif

!if !defined(TCLDIR)
!if exist("$(_INSTALLDIR)\..\include\tcl.h")
TCLINSTALL	= 1
_TCLDIR		= $(_INSTALLDIR)\..
_TCL_H          = $(_INSTALLDIR)\..\include\tcl.h
TCLDIR          = $(_INSTALLDIR)\..




!else
MSG=^
Failed to find tcl.h.  Set the TCLDIR macro.
!error $(MSG)

!endif
!else
_TCLDIR	= $(TCLDIR:/=\)
!if exist("$(_TCLDIR)\include\tcl.h")
TCLINSTALL	= 1
_TCL_H          = $(_TCLDIR)\include\tcl.h
!elseif exist("$(_TCLDIR)\generic\tcl.h")
TCLINSTALL	= 0
_TCL_H          = $(_TCLDIR)\generic\tcl.h


!else
MSG =^
Failed to find tcl.h.  The TCLDIR macro does not appear correct.
!error $(MSG)

!endif
!endif

!if [echo REM = This file is generated from rules.vc > version.vc]











!endif
!if exist("$(_TCL_H)")
!if [echo TCL_DOTVERSION = \>> version.vc] \
   && [nmakehlp -V "$(_TCL_H)" TCL_VERSION >> version.vc]













!endif






!endif
!include version.vc
TCL_VERSION	= $(TCL_DOTVERSION:.=)

!if $(TCLINSTALL)
TCLSH		= "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe"
!if !exist($(TCLSH)) && $(TCL_THREADS)
TCLSH           = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe"


!endif
TCLSTUBLIB	= "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib"
TCLIMPLIB	= "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib"
TCL_LIBRARY	= $(_TCLDIR)\lib
TCL_INCLUDES    = -I"$(_TCLDIR)\include"









































!else
TCLSH		= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe"
!if !exist($(TCLSH)) && $(TCL_THREADS)
TCLSH		= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe"



!endif
TCLSTUBLIB	= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib"
TCLIMPLIB	= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib"
TCL_LIBRARY	= $(_TCLDIR)\library
TCL_INCLUDES	= -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
!endif




!endif

#----------------------------------------------------------
# Optionally check for Tk info for building extensions.
#----------------------------------------------------------



!ifdef PROJECT_REQUIRES_TK
!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk"




!if !defined(TKDIR)
!if exist("$(_INSTALLDIR)\..\include\tk.h")
TKINSTALL      = 1
_TKDIR         = $(_INSTALLDIR)\..
_TK_H          = $(_TKDIR)\include\tk.h
TKDIR          = $(_TKDIR)
!elseif exist("$(_TCLDIR)\include\tk.h")
TKINSTALL      = 1
_TKDIR         = $(_TCLDIR)
_TK_H          = $(_TKDIR)\include\tk.h
TKDIR          = $(_TKDIR)










!endif






!else
_TKDIR = $(TKDIR:/=\)
!if exist("$(_TKDIR)\include\tk.h")
TKINSTALL      = 1
_TK_H          = $(_TKDIR)\include\tk.h
!elseif exist("$(_TKDIR)\generic\tk.h")
TKINSTALL      = 0
_TK_H          = $(_TKDIR)\generic\tk.h
!else
MSG =^
Failed to find tk.h. The TKDIR macro does not appear correct.
!error $(MSG)
!endif





!endif

!if defined(TKDIR)
















TK_DOTVERSION = 8.4
!if exist("$(_TK_H)")
!if [echo TK_DOTVERSION = \>> version.vc] \
   && [nmakehlp -V "$(_TK_H)" TK_VERSION >> version.vc]







!endif
















































































!endif



























!include version.vc
TK_VERSION = $(TK_DOTVERSION:.=)

!if $(TKINSTALL)
WISH		= "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe"
!if !exist($(WISH)) && $(TCL_THREADS)
WISH		= "$(_TKDIR)\bin\wish$(TK_VERSION)t$(SUFX).exe"




































!endif
TKSTUBLIB	= "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib"
TKIMPLIB	= "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib"
TK_INCLUDES     = -I"$(_TKDIR)\include"
TK_LIBRARY	= $(_TKDIR)\lib





































!else
WISH		= "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe"
!if !exist($(WISH)) && $(TCL_THREADS)
WISH		= "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)t$(SUFX).exe"











!endif
TKSTUBLIB	= "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib"
TKIMPLIB	= "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib"
TK_INCLUDES     = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
TK_LIBRARY	= $(_TKDIR)\library



!endif








!endif


!endif


!endif




#----------------------------------------------------------
# Setup the fully qualified OUT_DIR path as OUT_DIR_PATH
#----------------------------------------------------------
!if [echo OUT_DIR_PATH = \>> version.vc] \
    && [nmakehlp -Q "$(OUT_DIR)" >> version.vc]
!endif
!include version.vc


#----------------------------------------------------------
# Display stats being used.
#----------------------------------------------------------







!message *** Intermediate directory will be '$(TMP_DIR)'
!message *** Output directory will be '$(OUT_DIR)'

!message *** Suffix for binaries will be '$(SUFX)'
!message *** Optional defines are '$(OPTDEFINES)'
!message *** Compiler version $(VCVER). Target machine is $(MACHINE)
!message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)'
!message *** Link options '$(LINKERFLAGS)'

!endif
|


|
|
>
>

<
<
>
>




|

>





<
|
|
|
>
>

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

<
<
>

>

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






<

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

<
<
>

<
<
<
<
<
<

<
>
>
>
>
>
>
>
>
>
>
>
>

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











|

>




>
>
>
>

>
>
|


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












<
<
<
<




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

>
>
|
<
<

<
<
>
>
>
>

>
>
>
>
>
>

<
<
|

<
<
>
>
>


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

>
>
|
<
<
<
>


<
<
<
>
|
<
<
<
>






|





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

>
|
>

>
>
>
>


<
<
>
>
>
|
<
<
>
>

<
<
>
>
>

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




>
>
|
|

>

<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
>



>

>
|
<


<
|
>
>
>

>
>
>
>
>
>
>
>
>



|
<
>
>
>
>

>
>
>
>
>




>
>
>


>
>
>
|





>



>

|
>

>






>
>
>
>
>
>
>
>






>
|
|
|
>
>
>

|

>
>
>
>
>
>



<
<

>
|
<
|
<
<

>






>
>
>
>
>


>

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

>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>

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

<
<
<
>
>
|
<
|

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







 







<


<







 







>
>
>
>
>
>
>
>
>
>

<
<
<
>
>

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

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

>
>
>
>
>
>

>
>
>
>
>

>

<
<
<
>

<
<
<
>
>
>
>
>
>
>

<
<
<
>
|
<

<
<
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

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

<
>

<
<
<

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

>
>

<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<








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

>




|


>
>
>

>
>

<
<
<
>
>
>
>
>
>
>
>
>
>
>
>

<
>
>
>

<
<
<
>
>
>
>
>
>
>
>


<
<
<
<
<
<
>
>
>
>

<
<
<
>


<
<
<
<
<
<
<
>
>

<
<
<
>



<
>
>
>
>
>
>
>
>
>
>
>

<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>

<
<

|
<
<
<
>
>

<
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
<
<
>
>
>

<
<
<
<


>
>
>


<
<
<
>
>

<
<
>
>
>

<
<
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
>
>
>
>

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

>
>
>
>
>


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

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

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

<
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

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

<
<
<
<
>
>
>

>

>
>
>
>
>
>

>
>

>
>

>

>

<
<
<
<
<
|
<






>
>
>
>
>
>


>

<
|
<
<

|
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
....
1013
1014
1015
1016
1017
1018
1019

1020
1021

1022
1023
1024
1025
1026
1027
1028
....
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
#------------------------------------------------------------- -*- makefile -*-
# rules.vc --
#
# Part of the nmake based build system for Tcl and its extensions.
# This file does all the hard work in terms of parsing build options,
# compiler switches, defining common targets and macros. The Tcl makefile
# directly includes this. Extensions include it via "rules-ext.vc".
#


# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
# detailed documentation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# Copyright (c) 2001-2003 David Gravereaux.
# Copyright (c) 2003-2008 Patrick Thoyts
# Copyright (c) 2017      Ashok P. Nadkarni
#------------------------------------------------------------------------------

!ifndef _RULES_VC
_RULES_VC = 1


# The following macros define the version of the rules.vc nmake build system
# For modifications that are not backward-compatible, you *must* change
# the major version.
RULES_VERSION_MAJOR = 1
RULES_VERSION_MINOR = 2

# The PROJECT macro must be defined by parent makefile.
!if "$(PROJECT)" == ""
!error *** Error: Macro PROJECT not defined! Please define it before including rules.vc
!endif

!if "$(PRJ_PACKAGE_TCLNAME)" == ""
PRJ_PACKAGE_TCLNAME = $(PROJECT)
!endif

# Also special case Tcl and Tk to save some typing later
DOING_TCL = 0
DOING_TK  = 0
!if "$(PROJECT)" == "tcl"
DOING_TCL = 1
!elseif "$(PROJECT)" == "tk"
DOING_TK = 1
!endif

!ifndef NEED_TK


# Backwards compatibility
!ifdef PROJECT_REQUIRES_TK
NEED_TK = $(PROJECT_REQUIRES_TK)
!else


NEED_TK = 0
!endif
!endif

!ifndef NEED_TCL_SOURCE
NEED_TCL_SOURCE = 0
!endif

!ifdef NEED_TK_SOURCE
!if $(NEED_TK_SOURCE)
NEED_TK = 1
!endif
!else
NEED_TK_SOURCE = 0
!endif

################################################################
# Nmake is a pretty weak environment in syntax and capabilities
# so this file is necessarily verbose. It's broken down into
# the following parts.
#
# 0. Sanity check that compiler environment is set up and initialize
#    any built-in settings from the parent makefile
# 1. First define the external tools used for compiling, copying etc.
#    as this is independent of everything else.
# 2. Figure out our build structure in terms of the directory, whether
#    we are building Tcl or an extension, etc.
# 3. Determine the compiler and linker versions
# 4. Build the nmakehlp helper application
# 5. Determine the supported compiler options and features
# 6. Parse the OPTS macro value for user-specified build configuration
# 7. Parse the STATS macro value for statistics instrumentation
# 8. Parse the CHECKS macro for additional compilation checks
# 9. Extract Tcl, and possibly Tk, version numbers from the headers
# 10. Based on this selected configuration, construct the output
#     directory and file paths
# 11. Construct the paths where the package is to be installed
# 12. Set up the actual options passed to compiler and linker based
#     on the information gathered above.
# 13. Define some standard build targets and implicit rules. These may
#     be optionally disabled by the parent makefile.
# 14. (For extensions only.) Compare the configuration of the target
#     Tcl and the extensions and warn against discrepancies.
#
# One final note about the macro names used. They are as they are
# for historical reasons. We would like legacy extensions to
# continue to work with this make include file so be wary of
# changing them for consistency or clarity.

# 0. Sanity check compiler environment

# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)

!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
MSG = ^
Visual C++ compiler environment not initialized.
!error $(MSG)
!endif

# We need to run from the directory the parent makefile is located in.
# nmake does not tell us what makefile was used to invoke it so parent
# makefile has to set the MAKEFILEVC macro or we just make a guess and
# warn if we think that is not the case.
!if "$(MAKEFILEVC)" == ""

!if exist("$(PROJECT).vc")
MAKEFILEVC = $(PROJECT).vc
!elseif exist("makefile.vc")
MAKEFILEVC = makefile.vc
!endif
!endif # "$(MAKEFILEVC)" == ""

!if !exist("$(MAKEFILEVC)")
MSG = ^
You must run nmake from the directory containing the project makefile.^
If you are doing that and getting this message, set the MAKEFILEVC^
macro to the name of the project makefile.
!message WARNING: $(MSG)
!endif


################################################################
# 1. Define external programs being used

#----------------------------------------------------------
# Set the proper copy method to avoid overwrite questions
# to the user when copying files and selecting the right
# "delete all" method.
#----------------------------------------------------------


RMDIR	= rmdir /S /Q


CPY	= xcopy /i /y >NUL
CPYDIR  = xcopy /e /i /y >NUL
COPY	= copy /y >NUL
MKDIR   = mkdir

######################################################################
# 2. Figure out our build environment in terms of what we're building.
#
# (a) Tcl itself
# (b) Tk
# (c) a Tcl extension using libraries/includes from an *installed* Tcl
# (d) a Tcl extension using libraries/includes from Tcl source directory
#
# This last is needed because some extensions still need
# some Tcl interfaces that are not publicly exposed.
#
# The fragment will set the following macros:
# ROOT - root of this module sources
# COMPATDIR - source directory that holds compatibility sources
# DOCDIR - source directory containing documentation files
# GENERICDIR - platform-independent source directory
# WINDIR - Windows-specific source directory
# TESTDIR - directory containing test files
# TOOLSDIR - directory containing build tools
# _TCLDIR - root of the Tcl installation OR the Tcl sources. Not set
#    when building Tcl itself.
# _INSTALLDIR - native form of the installation path. For Tcl
#    this will be the root of the Tcl installation. For extensions
#    this will be the lib directory under the root.
# TCLINSTALL  - set to 1 if _TCLDIR refers to
#    headers and libraries from an installed Tcl, and 0 if built against
#    Tcl sources. Not set when building Tcl itself. Yes, not very well
#    named.
# _TCL_H - native path to the tcl.h file
#
# If Tk is involved, also sets the following
# _TKDIR - native form Tk installation OR Tk source. Not set if building
#    Tk itself.
# TKINSTALL - set 1 if _TKDIR refers to installed Tk and 0 if Tk sources
# _TK_H - native path to the tk.h file

# Root directory for sources and assumed subdirectories
ROOT = $(MAKEDIR)\..
# The following paths CANNOT have spaces in them as they appear on the
# left side of implicit rules.
!ifndef COMPATDIR
COMPATDIR	= $(ROOT)\compat
!endif
!ifndef DOCDIR
DOCDIR		= $(ROOT)\doc
!endif
!ifndef GENERICDIR
GENERICDIR	= $(ROOT)\generic
!endif
!ifndef TOOLSDIR
TOOLSDIR	= $(ROOT)\tools
!endif
!ifndef TESTDIR
TESTDIR	= $(ROOT)\tests
!endif
!ifndef LIBDIR
!if exist("$(ROOT)\library")
LIBDIR          = $(ROOT)\library
!else


LIBDIR          = $(ROOT)\lib
!endif






!endif

!ifndef DEMODIR
!if exist("$(LIBDIR)\demos")
DEMODIR		= $(LIBDIR)\demos
!else
DEMODIR		= $(ROOT)\demos
!endif
!endif # ifndef DEMODIR
# Do NOT enclose WINDIR in a !ifndef because Windows always defines
# WINDIR env var to point to c:\windows!
# TBD - This is a potentially dangerous conflict, rename WINDIR to
# something else
WINDIR		= $(ROOT)\win

!ifndef RCDIR
!if exist("$(WINDIR)\rc")
RCDIR           = $(WINDIR)\rc
!else
RCDIR           = $(WINDIR)
!endif
!endif
RCDIR = $(RCDIR:/=\)

# The target directory where the built packages and binaries will be installed.
# INSTALLDIR is the (optional) path specified by the user.
# _INSTALLDIR is INSTALLDIR using the backslash separator syntax
!ifdef INSTALLDIR
### Fix the path separators.
_INSTALLDIR	= $(INSTALLDIR:/=\)
!else
### Assume the normal default.
_INSTALLDIR	= $(HOMEDRIVE)\Tcl
!endif

!if $(DOING_TCL)

# BEGIN Case 2(a) - Building Tcl itself

# Only need to define _TCL_H
_TCL_H = ..\generic\tcl.h

# END Case 2(a) - Building Tcl itself

!elseif $(DOING_TK)

# BEGIN Case 2(b) - Building Tk

TCLINSTALL = 0 # Tk always builds against Tcl source, not an installed Tcl
!if "$(TCLDIR)" == ""
!if [echo TCLDIR = \> nmakehlp.out] \
   || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
!error *** Could not locate Tcl source directory.
!endif
!include nmakehlp.out
!endif # TCLDIR == ""

_TCLDIR	= $(TCLDIR:/=\)
_TCL_H  = $(_TCLDIR)\generic\tcl.h
!if !exist("$(_TCL_H)")
!error Could not locate tcl.h. Please set the TCLDIR macro to point to the Tcl *source* directory.
!endif

_TK_H = ..\generic\tk.h

# END Case 2(b) - Building Tk

!else

# BEGIN Case 2(c) or (d) - Building an extension other than Tk

# If command line has specified Tcl location through TCLDIR, use it
# else default to the INSTALLDIR setting
!if "$(TCLDIR)" != ""

_TCLDIR	= $(TCLDIR:/=\)
!if exist("$(_TCLDIR)\include\tcl.h") # Case 2(c) with TCLDIR defined
TCLINSTALL	= 1
_TCL_H          = $(_TCLDIR)\include\tcl.h
!elseif exist("$(_TCLDIR)\generic\tcl.h") # Case 2(d) with TCLDIR defined
TCLINSTALL	= 0
_TCL_H          = $(_TCLDIR)\generic\tcl.h
!endif

!else  #  # Case 2(c) for extensions with TCLDIR undefined

# Need to locate Tcl depending on whether it needs Tcl source or not.
# If we don't, check the INSTALLDIR for an installed Tcl first

!if exist("$(_INSTALLDIR)\include\tcl.h") && !$(NEED_TCL_SOURCE)

TCLINSTALL	= 1
TCLDIR          = $(_INSTALLDIR)\..
# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
# later so the \.. accounts for the /lib
_TCLDIR		= $(_INSTALLDIR)\..
_TCL_H          = $(_TCLDIR)\include\tcl.h

!else # exist(...) && ! $(NEED_TCL_SOURCE)

!if [echo _TCLDIR = \> nmakehlp.out] \
   || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
!error *** Could not locate Tcl source directory.
!endif
!include nmakehlp.out
TCLINSTALL      = 0
TCLDIR         = $(_TCLDIR)
_TCL_H          = $(_TCLDIR)\generic\tcl.h

!endif # exist(...) && ! $(NEED_TCL_SOURCE)

!endif # TCLDIR

!ifndef _TCL_H
MSG =^
Failed to find tcl.h. The TCLDIR macro is set incorrectly or is not set and default path does not contain tcl.h.
!error $(MSG)
!endif

# Now do the same to locate Tk headers and libs if project requires Tk
!if $(NEED_TK)

!if "$(TKDIR)" != ""

_TKDIR = $(TKDIR:/=\)
!if exist("$(_TKDIR)\include\tk.h")
TKINSTALL      = 1
_TK_H          = $(_TKDIR)\include\tk.h
!elseif exist("$(_TKDIR)\generic\tk.h")
TKINSTALL      = 0
_TK_H          = $(_TKDIR)\generic\tk.h
!endif

!else # TKDIR not defined

# Need to locate Tcl depending on whether it needs Tcl source or not.
# If we don't, check the INSTALLDIR for an installed Tcl first

!if exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)

TKINSTALL      = 1
# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
# later so the \.. accounts for the /lib
_TKDIR         = $(_INSTALLDIR)\..
_TK_H          = $(_TKDIR)\include\tk.h
TKDIR          = $(_TKDIR)

!else # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)

!if [echo _TKDIR = \> nmakehlp.out] \
   || [nmakehlp -L generic\tk.h >> nmakehlp.out]
!error *** Could not locate Tk source directory.
!endif
!include nmakehlp.out
TKINSTALL      = 0
TKDIR          = $(_TKDIR)
_TK_H          = $(_TKDIR)\generic\tk.h

!endif # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)

!endif # TKDIR

!ifndef _TK_H
MSG =^
Failed to find tk.h. The TKDIR macro is set incorrectly or is not set and default path does not contain tk.h.
!error $(MSG)
!endif

!endif # NEED_TK

!if $(NEED_TCL_SOURCE) && $(TCLINSTALL)
MSG = ^
*** Warning: This extension requires the source distribution of Tcl.^
*** Please set the TCLDIR macro to point to the Tcl sources.
!error $(MSG)
!endif

!if $(NEED_TK_SOURCE)
!if $(TKINSTALL)
MSG = ^
*** Warning: This extension requires the source distribution of Tk.^
*** Please set the TKDIR macro to point to the Tk sources.
!error $(MSG)
!endif
!endif


# If INSTALLDIR set to Tcl installation root dir then reset to the
# lib dir for installing extensions
!if exist("$(_INSTALLDIR)\include\tcl.h")
_INSTALLDIR=$(_INSTALLDIR)\lib
!endif

# END Case 2(c) or (d) - Building an extension
!endif # if $(DOING_TCL)

################################################################
# 3. Determine compiler version and architecture
# In this section, we figure out the compiler version and the
# architecture for which we are building. This sets the
# following macros:
# VCVERSION - the internal compiler version as 1200, 1400, 1910 etc.
#     This is also printed by the compiler in dotted form 19.10 etc.
# VCVER - the "marketing version", for example Visual C++ 6 for internal
#     compiler version 1200. This is kept only for legacy reasons as it
#     does not make sense for recent Microsoft compilers. Only used for
#     output directory names.
# ARCH - set to IX86 or AMD64 depending on 32- or 64-bit target
# NATIVE_ARCH - set to IX86 or AMD64 for the host machine
# MACHINE - same as $(ARCH) - legacy
# _VC_MANIFEST_EMBED_{DLL,EXE} - commands for embedding a manifest if needed
# CFG_ENCODING - set to an character encoding.
#   TBD - this is passed to compiler as TCL_CFGVAL_ENCODING but can't
#   see where it is used

cc32		= $(CC)   # built-in default.
link32		= link
lib32		= lib
rc32		= $(RC)   # built-in default.

#----------------------------------------------------------------


# Figure out the compiler architecture and version by writing
# the C macros to a file, preprocessing them with the C
# preprocessor and reading back the created file

_HASH=^#
_VC_MANIFEST_EMBED_EXE=
_VC_MANIFEST_EMBED_DLL=
VCVER=0
!if ![echo VCVERSION=_MSC_VER > vercl.x] \
    && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \
    && ![echo ARCH=IX86 >> vercl.x] \
    && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
    && ![echo ARCH=AMD64 >> vercl.x] \
    && ![echo $(_HASH)endif >> vercl.x] \
    && ![$(cc32) -nologo -TC -P vercl.x 2>NUL]
!include vercl.i
!if $(VCVERSION) < 1900
!if ![echo VCVER= ^\> vercl.vc] \
    && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
!include vercl.vc
!endif
!else
# The simple calculation above does not apply to new Visual Studio releases
# Keep the compiler version in its native form.
VCVER = $(VCVERSION)
!endif
!endif

!if ![del 2>NUL /q/f vercl.x vercl.i vercl.vc]
!endif

#----------------------------------------------------------------
# The MACHINE macro is used by legacy makefiles so set it as well
!ifdef MACHINE
!if "$(MACHINE)" == "x86"
!undef MACHINE
MACHINE = IX86
!elseif "$(MACHINE)" == "x64"
!undef MACHINE
MACHINE = AMD64
!endif
!if "$(MACHINE)" != "$(ARCH)"
!error Specified MACHINE macro $(MACHINE) does not match detected target architecture $(ARCH).
!endif
!else
MACHINE=$(ARCH)
!endif

#------------------------------------------------------------
# Figure out the *host* architecture by reading the registry

!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86]
NATIVE_ARCH=IX86
!else
NATIVE_ARCH=AMD64
!endif

# Since MSVC8 we must deal with manifest resources.
!if $(VCVERSION) >= 1400
_VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1
_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
!endif





!ifndef CFG_ENCODING
CFG_ENCODING	= \"cp1252\"
!endif




################################################################
# 4. Build the nmakehlp program
# This is a helper app we need to overcome nmake's limiting
# environment. We will call out to it to get various bits of

# information about supported compiler options etc.
#




# Tcl itself will always use the nmakehlp.c program which is
# in its own source. This is the "master" copy and kept updated.
#



# Extensions built against an installed Tcl will use the installed
# copy of Tcl's nmakehlp.c if there is one and their own version
# otherwise. In the latter case, they would also be using their own
# rules.vc. Note that older versions of Tcl do not install nmakehlp.c
# or rules.vc.
#








# Extensions built against Tcl sources will use the one from the Tcl source.
#

# When building an extension using a sufficiently new version of Tcl,
# rules-ext.vc will define NMAKEHLPC appropriately to point to the
# copy of nmakehlp.c to be used.

!ifndef NMAKEHLPC
# Default to the one in the current directory (the extension's own nmakehlp.c)
NMAKEHLPC = nmakehlp.c





!if !$(DOING_TCL)
!if $(TCLINSTALL)
!if exist("$(_TCLDIR)\lib\nmake\nmakehlp.c")
NMAKEHLPC = $(_TCLDIR)\lib\nmake\nmakehlp.c
!endif
!else # ! $(TCLINSTALL)
!if exist("$(_TCLDIR)\win\nmakehlp.c")
NMAKEHLPC = $(_TCLDIR)\win\nmakehlp.c
!endif
!endif # $(TCLINSTALL)
!endif # !$(DOING_TCL)



!endif # NMAKEHLPC



# We always build nmakehlp even if it exists since we do not know
# what source it was built from.
!if [$(cc32) -nologo "$(NMAKEHLPC)" -link -subsystem:console > nul]
!endif

################################################################
# 5. Test for compiler features
# Visual C++ compiler options have changed over the years. Check
# which options are supported by the compiler in use.


#
# The following macros are set:
# OPTIMIZATIONS - the compiler flags to be used for optimized builds
# DEBUGFLAGS - the compiler flags to be used for debug builds
# LINKERFLAGS - Flags passed to the linker

#
# Note that these are the compiler settings *available*, not those
# that will be *used*. The latter depends on the OPTS macro settings
# which we have not yet parsed.

#
# Also note that some of the flags in OPTIMIZATIONS are not really
# related to optimization. They are placed there only for legacy reasons
# as some extensions expect them to be included in that macro.

# -Op improves float consistency. Note only needed for older compilers
# Newer compilers do not need or support this option.
!if [nmakehlp -c -Op]



FPOPTS  = -Op
!endif




# Strict floating point semantics - present in newer compilers in lieu of -Op
!if [nmakehlp -c -fp:strict]



FPOPTS  = $(FPOPTS) -fp:strict
!endif

!if "$(MACHINE)" == "IX86"
### test for pentium errata
!if [nmakehlp -c -QI0f]
!message *** Compiler has 'Pentium 0x0f fix'
FPOPTS  = $(FPOPTS) -QI0f
!else
!message *** Compiler does not have 'Pentium 0x0f fix'
!endif
!endif


### test for optimizations
# /O2 optimization includes /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy as per
# documentation. Note we do NOT want /Gs as that inserts a _chkstk
# stack probe at *every* function entry, not just those with more than
# a page of stack allocation resulting in a performance hit.  However,
# /O2 documentation is misleading as its stack probes are simply the
# default page size locals allocation probes and not what is implied
# by an explicit /Gs option.

OPTIMIZATIONS = $(FPOPTS)

!if [nmakehlp -c -O2]


OPTIMIZING = 1
OPTIMIZATIONS   = $(OPTIMIZATIONS) -O2
!else
# Legacy, really. All modern compilers support this
!message *** Compiler does not have 'Optimizations'
OPTIMIZING = 0
!endif

# Checks for buffer overflows in local arrays
!if [nmakehlp -c -GS]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -GS
!endif



# Link time optimization. Note that this option (potentially) makes
# generated libraries only usable by the specific VC++ version that
# created it. Requires /LTCG linker option
!if [nmakehlp -c -GL]


OPTIMIZATIONS  = $(OPTIMIZATIONS) -GL
CC_GL_OPT_ENABLED = 1
!else


# In newer compilers -GL and -YX are incompatible.
!if [nmakehlp -c -YX]
OPTIMIZATIONS  = $(OPTIMIZATIONS) -YX
!endif
!endif # [nmakehlp -c -GL]

DEBUGFLAGS     = $(FPOPTS)

# Run time error checks. Not available or valid in a release, non-debug build
# RTC is for modern compilers, -GZ is legacy
!if [nmakehlp -c -RTC1]
DEBUGFLAGS     = $(DEBUGFLAGS) -RTC1
!elseif [nmakehlp -c -GZ]
DEBUGFLAGS     = $(DEBUGFLAGS) -GZ
!endif


#----------------------------------------------------------------
# Linker flags

# LINKER_TESTFLAGS are for internal use when we call nmakehlp to test
# if the linker supports a specific option. Without these flags link will
# return "LNK1561: entry point must be defined" error compiling from VS-IDE:
# They are not passed through to the actual application / extension
# link rules.
!ifndef LINKER_TESTFLAGS
LINKER_TESTFLAGS = /DLL /NOENTRY /OUT:nmakehlp.out
!endif

LINKERFLAGS     =

# If compiler has enabled link time optimization, linker must too with -ltcg
!ifdef CC_GL_OPT_ENABLED
!if [nmakehlp -l -ltcg $(LINKER_TESTFLAGS)]
LINKERFLAGS     = $(LINKERFLAGS) -ltcg
!endif
!endif




########################################################################
# 6. Parse the OPTS macro to work out the requested build configuration.
# Based on this, we will construct the actual switches to be passed to the
# compiler and linker using the macros defined in the previous section.
# The following macros are defined by this section based on OPTS
# STATIC_BUILD - 0 -> Tcl is to be built as a shared library
#                1 -> build as a static library and shell
# TCL_THREADS - legacy but always 1 on Windows since winsock requires it.
# DEBUG - 1 -> debug build, 0 -> release builds
# SYMBOLS - 1 -> generate PDB's, 0 -> no PDB's
# PROFILE - 1 -> generate profiling info, 0 -> no profiling
# PGO     - 1 -> profile based optimization, 0 -> no
# MSVCRT  - 1 -> link to dynamic C runtime even when building static Tcl build
#           0 -> link to static C runtime for static Tcl build.
#           Does not impact shared Tcl builds (STATIC_BUILD == 0)
# TCL_USE_STATIC_PACKAGES - 1 -> statically link the registry and dde extensions
#           in the Tcl shell. 0 -> keep them as shared libraries
#           Does not impact shared Tcl builds.
# USE_THREAD_ALLOC - 1 -> Use a shared global free pool for allocation.
#           0 -> Use the non-thread allocator.
# UNCHECKED - 1 -> when doing a debug build with symbols, use the release
#           C runtime, 0 -> use the debug C runtime.
# USE_STUBS - 1 -> compile to use stubs interfaces, 0 -> direct linking
# CONFIG_CHECK - 1 -> check current build configuration against Tcl
#           configuration (ignored for Tcl itself)
# Further, LINKERFLAGS are modified based on above.


# Default values for all the above
STATIC_BUILD	= 0
TCL_THREADS	= 1
DEBUG		= 0
SYMBOLS		= 0
PROFILE		= 0
PGO		= 0
MSVCRT		= 1

TCL_USE_STATIC_PACKAGES	= 0
USE_THREAD_ALLOC = 1

UNCHECKED	= 0
CONFIG_CHECK    = 1
!if $(DOING_TCL)
USE_STUBS       = 0
!else
USE_STUBS       = 1
!endif

# If OPTS is not empty AND does not contain "none" which turns off all OPTS
# set the above macros based on OPTS content
!if "$(OPTS)" != "" && ![nmakehlp -f "$(OPTS)" "none"]

# OPTS are specified, parse them

!if [nmakehlp -f $(OPTS) "static"]
!message *** Doing static
STATIC_BUILD	= 1
!endif


!if [nmakehlp -f $(OPTS) "nostubs"]
!message *** Not using stubs
USE_STUBS	= 0
!endif

!if [nmakehlp -f $(OPTS) "nomsvcrt"]
!message *** Doing nomsvcrt
MSVCRT		= 0
!else
!if [nmakehlp -f $(OPTS) "msvcrt"]
!message *** Doing msvcrt
MSVCRT		= 1
!else
!if !$(STATIC_BUILD)
MSVCRT		= 1
!else
MSVCRT		= 0
!endif
!endif
!endif # [nmakehlp -f $(OPTS) "nomsvcrt"]

!if [nmakehlp -f $(OPTS) "staticpkg"] && $(STATIC_BUILD)
!message *** Doing staticpkg
TCL_USE_STATIC_PACKAGES	= 1
!else
TCL_USE_STATIC_PACKAGES	= 0
!endif

!if [nmakehlp -f $(OPTS) "nothreads"]
!message *** Compile explicitly for non-threaded tcl
TCL_THREADS	= 0
USE_THREAD_ALLOC= 0
!else
TCL_THREADS	= 1
USE_THREAD_ALLOC= 1
!endif

!if [nmakehlp -f $(OPTS) "symbols"]
!message *** Doing symbols
DEBUG		= 1
!else
DEBUG		= 0
!endif

!if [nmakehlp -f $(OPTS) "pdbs"]
!message *** Doing pdbs
SYMBOLS		= 1
!else
SYMBOLS		= 0
!endif

!if [nmakehlp -f $(OPTS) "profile"]
!message *** Doing profile
PROFILE		= 1
!else
PROFILE		= 0
!endif

!if [nmakehlp -f $(OPTS) "pgi"]
!message *** Doing profile guided optimization instrumentation
PGO		= 1
!elseif [nmakehlp -f $(OPTS) "pgo"]
!message *** Doing profile guided optimization
PGO		= 2
!else
PGO		= 0
!endif

!if [nmakehlp -f $(OPTS) "loimpact"]
!message *** Warning: ignoring option "loimpact" - deprecated on modern Windows.
!endif

# TBD - should get rid of this option
!if [nmakehlp -f $(OPTS) "thrdalloc"]
!message *** Doing thrdalloc
USE_THREAD_ALLOC = 1


!endif

!if [nmakehlp -f $(OPTS) "tclalloc"]

USE_THREAD_ALLOC = 0


!endif

!if [nmakehlp -f $(OPTS) "unchecked"]
!message *** Doing unchecked
UNCHECKED = 1
!else
UNCHECKED = 0
!endif

!if [nmakehlp -f $(OPTS) "noconfigcheck"]
CONFIG_CHECK = 1
!else
CONFIG_CHECK = 0
!endif

!endif # "$(OPTS)" != ""  && ... parsing of OPTS




# Set linker flags based on above


!if $(PGO) > 1
!if [nmakehlp -l -ltcg:pgoptimize $(LINKER_TESTFLAGS)]
LINKERFLAGS	= $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
!else
MSG=^
This compiler does not support profile guided optimization.
!error $(MSG)
!endif
!elseif $(PGO) > 0
!if [nmakehlp -l -ltcg:pginstrument $(LINKER_TESTFLAGS)]
LINKERFLAGS	= $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
!else
MSG=^
This compiler does not support profile guided optimization.
!error $(MSG)
!endif
!endif

################################################################
# 7. Parse the STATS macro to configure code instrumentation
# The following macros are set by this section:
# TCL_MEM_DEBUG - 1 -> enables memory allocation instrumentation
#                 0 -> disables
# TCL_COMPILE_DEBUG - 1 -> enables byte compiler logging
#                     0 -> disables

# Default both are off
TCL_MEM_DEBUG	    = 0
TCL_COMPILE_DEBUG   = 0

!if "$(STATS)" != "" && ![nmakehlp -f "$(STATS)" "none"]

!if [nmakehlp -f $(STATS) "memdbg"]
!message *** Doing memdbg
TCL_MEM_DEBUG	    = 1
!else
TCL_MEM_DEBUG	    = 0
!endif

!if [nmakehlp -f $(STATS) "compdbg"]
!message *** Doing compdbg
TCL_COMPILE_DEBUG   = 1
!else
TCL_COMPILE_DEBUG   = 0
!endif

!endif

####################################################################
# 8. Parse the CHECKS macro to configure additional compiler checks
# The following macros are set by this section:
# WARNINGS - compiler switches that control the warnings level
# TCL_NO_DEPRECATED - 1 -> disable support for deprecated functions
#                     0 -> enable deprecated functions

# Defaults - Permit deprecated functions and warning level 3
TCL_NO_DEPRECATED	    = 0
WARNINGS		    = -W3

!if "$(CHECKS)" != "" && ![nmakehlp -f "$(CHECKS)" "none"]

!if [nmakehlp -f $(CHECKS) "nodep"]
!message *** Doing nodep check
TCL_NO_DEPRECATED	    = 1
!endif

!if [nmakehlp -f $(CHECKS) "fullwarn"]
!message *** Doing full warnings check
WARNINGS		    = -W4
!if [nmakehlp -l -warn:3 $(LINKER_TESTFLAGS)]
LINKERFLAGS		    = $(LINKERFLAGS) -warn:3
!endif
!endif

!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
!message *** Doing 64bit portability warnings
WARNINGS		    = $(WARNINGS) -Wp64
!endif

!endif

################################################################
# 9. Extract various version numbers
# For Tcl and Tk, version numbers are extracted from tcl.h and tk.h
# respectively. For extensions, versions are extracted from the
# configure.in or configure.ac from the TEA configuration if it
# exists, and unset otherwise.
# Sets the following macros:
# TCL_MAJOR_VERSION
# TCL_MINOR_VERSION
# TCL_PATCH_LEVEL
# TCL_VERSION
# TK_MAJOR_VERSION
# TK_MINOR_VERSION
# TK_PATCH_LEVEL
# TK_VERSION
# DOTVERSION - set as (for example) 2.5
# VERSION - set as (for example 25)
#--------------------------------------------------------------

!if [echo REM = This file is generated from rules.vc > versions.vc]
!endif
!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
   && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc]
!endif
!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
   && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
!endif
!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
   && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
!endif

!if defined(_TK_H)
!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
   && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc]
!endif
!if [echo TK_MINOR_VERSION = \>> versions.vc] \
   && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
!endif
!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
   && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
!endif
!endif # _TK_H

!include versions.vc

TCL_VERSION	= $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
TCL_DOTVERSION	= $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
!if defined(_TK_H)
TK_VERSION	= $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
TK_DOTVERSION	= $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
!endif

# Set DOTVERSION and VERSION
!if $(DOING_TCL)

DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
VERSION = $(TCL_VERSION)

!elseif $(DOING_TK)

DOTVERSION = $(TK_DOTVERSION)
VERSION = $(TK_VERSION)

!else # Doing a non-Tk extension

# If parent makefile has not defined DOTVERSION, try to get it from TEA
# first from a configure.in file, and then from configure.ac
!ifndef DOTVERSION
!if [echo DOTVERSION = \> versions.vc] \
   || [nmakehlp -V $(ROOT)\configure.in ^[$(PROJECT)^] >> versions.vc]
!if [echo DOTVERSION = \> versions.vc] \
   || [nmakehlp -V $(ROOT)\configure.ac ^[$(PROJECT)^] >> versions.vc]
!error *** Could not figure out extension version. Please define DOTVERSION in parent makefile before including rules.vc.
!endif
!endif
!include versions.vc
!endif # DOTVERSION
VERSION         = $(DOTVERSION:.=)

!endif # $(DOING_TCL) ... etc.

################################################################
# 10. Construct output directory and file paths
# Figure-out how to name our intermediate and output directories.



# In order to avoid inadvertent mixing of object files built using
# different compilers, build configurations etc.,
#

# Naming convention (suffixes):
#   t = full thread support.
#   s = static library (as opposed to an import library)

#   g = linked to the debug enabled C run-time.

#   x = special static build when it links to the dynamic C run-time.


#
# The following macros are set in this section:
# SUFX - the suffix to use for binaries based on above naming convention
# BUILDDIRTOP - the toplevel default output directory
#      is of the form {Release,Debug}[_AMD64][_COMPILERVERSION]
# TMP_DIR - directory where object files are created
# OUT_DIR - directory where output executables are created
# Both TMP_DIR and OUT_DIR are defaulted only if not defined by the
# parent makefile (or command line). The default values are
# based on BUILDDIRTOP.
# STUBPREFIX - name of the stubs library for this project
# PRJIMPLIB - output path of the generated project import library
# PRJLIBNAME - name of generated project library
# PRJLIB     - output path of generated project library
# PRJSTUBLIBNAME - name of the generated project stubs library
# PRJSTUBLIB - output path of the generated project stubs library
# RESFILE - output resource file (only if not static build)

SUFX	    = tsgx

!if $(DEBUG)
BUILDDIRTOP = Debug
!else
BUILDDIRTOP = Release
!endif

................................................................................

TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX

!if !$(STATIC_BUILD)
TMP_DIRFULL = $(TMP_DIRFULL:Static=)
SUFX	    = $(SUFX:s=)
EXT	    = dll

TMP_DIRFULL = $(TMP_DIRFULL:X=)
SUFX	    = $(SUFX:x=)

!else
TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=)
EXT	    = lib
!if !$(MSVCRT)
TMP_DIRFULL = $(TMP_DIRFULL:X=)
SUFX	    = $(SUFX:x=)
!endif
................................................................................
!endif
!else
!ifndef OUT_DIR
OUT_DIR	    = $(TMP_DIR)
!endif
!endif

# Relative paths -> absolute
!if [echo OUT_DIR = \> nmakehlp.out] \
   || [nmakehlp -Q "$(OUT_DIR)" >> nmakehlp.out]
!error *** Could not fully qualify path OUT_DIR=$(OUT_DIR)
!endif
!if [echo TMP_DIR = \>> nmakehlp.out] \
   || [nmakehlp -Q "$(TMP_DIR)" >> nmakehlp.out]
!error *** Could not fully qualify path TMP_DIR=$(TMP_DIR)
!endif
!include nmakehlp.out




# The name of the stubs library for the project being built
STUBPREFIX      = $(PROJECT)stub




# Set up paths to various Tcl executables and libraries needed by extensions
!if $(DOING_TCL)

TCLSHNAME       = $(PROJECT)sh$(VERSION)$(SUFX).exe
TCLSH		= $(OUT_DIR)\$(TCLSHNAME)
TCLIMPLIB	= $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
TCLLIBNAME	= $(PROJECT)$(VERSION)$(SUFX).$(EXT)
TCLLIB		= $(OUT_DIR)\$(TCLLIBNAME)

TCLSTUBLIBNAME	= $(STUBPREFIX)$(VERSION).lib
TCLSTUBLIB	= $(OUT_DIR)\$(TCLSTUBLIBNAME)
TCL_INCLUDES    = -I"$(WINDIR)" -I"$(GENERICDIR)"

!else # ! $(DOING_TCL)

!if $(TCLINSTALL) # Building against an installed Tcl

# When building extensions, we need to locate tclsh. Depending on version
# of Tcl we are building against, this may or may not have a "t" suffix.
# Try various possibilities in turn.
TCLSH		= $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe
!if !exist("$(TCLSH)") && $(TCL_THREADS)
TCLSH           = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe
!endif



!if !exist("$(TCLSH)")
TCLSH           = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX:t=).exe
!endif


TCLSTUBLIB	= $(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib
TCLIMPLIB	= $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib
# When building extensions, may be linking against Tcl that does not add
# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
!if !exist("$(TCLIMPLIB)")
TCLIMPLIB	= $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX:t=).lib
!endif



TCL_LIBRARY	= $(_TCLDIR)\lib
TCLREGLIB	= $(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib
TCLDDELIB	= $(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib
TCLTOOLSDIR	= \must\have\tcl\sources\to\build\this\target
TCL_INCLUDES    = -I"$(_TCLDIR)\include"

!else # Building against Tcl sources

TCLSH		= $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe
!if !exist($(TCLSH)) && $(TCL_THREADS)
TCLSH		= $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe
!endif

!if !exist($(TCLSH))
TCLSH		= $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX:t=).exe
!endif
TCLSTUBLIB	= $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib
TCLIMPLIB	= $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib
# When building extensions, may be linking against Tcl that does not add
# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
!if !exist("$(TCLIMPLIB)")
TCLIMPLIB	= $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX:t=).lib
!endif
TCL_LIBRARY	= $(_TCLDIR)\library
TCLREGLIB	= $(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib
TCLDDELIB	= $(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib
TCLTOOLSDIR	= $(_TCLDIR)\tools
TCL_INCLUDES	= -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"

!endif # TCLINSTALL




tcllibs = "$(TCLSTUBLIB)" "$(TCLIMPLIB)"




!endif # $(DOING_TCL)

# We need a tclsh that will run on the host machine as part of the build.
# IX86 runs on all architectures.
!ifndef TCLSH_NATIVE
!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
TCLSH_NATIVE	= $(TCLSH)
!else



!error You must explicitly set TCLSH_NATIVE for cross-compilation
!endif

!endif






# Do the same for Tk and Tk extensions that require the Tk libraries
!if $(DOING_TK) || $(NEED_TK)
WISHNAMEPREFIX = wish
WISHNAME = $(WISHNAMEPREFIX)$(TK_VERSION)$(SUFX).exe
TKLIBNAME	= $(PROJECT)$(TK_VERSION)$(SUFX).$(EXT)
TKSTUBLIBNAME	= tkstub$(TK_VERSION).lib
TKIMPLIBNAME	= tk$(TK_VERSION)$(SUFX).lib

!if $(DOING_TK)
WISH 		= $(OUT_DIR)\$(WISHNAME)
TKSTUBLIB	= $(OUT_DIR)\$(TKSTUBLIBNAME)
TKIMPLIB	= $(OUT_DIR)\$(TKIMPLIBNAME)
TKLIB		= $(OUT_DIR)\$(TKLIBNAME)
TK_INCLUDES    = -I"$(WINDIR)" -I"$(GENERICDIR)"

!else # effectively NEED_TK

!if $(TKINSTALL) # Building against installed Tk
WISH		= $(_TKDIR)\bin\$(WISHNAME)
TKSTUBLIB	= $(_TKDIR)\lib\$(TKSTUBLIBNAME)
TKIMPLIB	= $(_TKDIR)\lib\$(TKIMPLIBNAME)
# When building extensions, may be linking against Tk that does not add
# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
!if !exist("$(TKIMPLIB)")
TKIMPLIBNAME	= tk$(TK_VERSION)$(SUFX:t=).lib
TKIMPLIB	= $(_TKDIR)\lib\$(TKIMPLIBNAME)
!endif
TK_INCLUDES     = -I"$(_TKDIR)\include"
!else # Building against Tk sources
WISH		= $(_TKDIR)\win\$(BUILDDIRTOP)\$(WISHNAME)
TKSTUBLIB	= $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKSTUBLIBNAME)
TKIMPLIB	= $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
# When building extensions, may be linking against Tk that does not add
# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
!if !exist("$(TKIMPLIB)")
TKIMPLIBNAME	= tk$(TK_VERSION)$(SUFX:t=).lib
TKIMPLIB	= $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
!endif
TK_INCLUDES     = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
!endif # TKINSTALL
tklibs = "$(TKSTUBLIB)" "$(TKIMPLIB)"

!endif # $(DOING_TK)
!endif # $(DOING_TK) || $(NEED_TK)

# Various output paths
PRJIMPLIB	= $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX:t=).lib
PRJLIBNAME	= $(PROJECT)$(VERSION)$(SUFX:t=).$(EXT)
PRJLIB		= $(OUT_DIR)\$(PRJLIBNAME)

PRJSTUBLIBNAME	= $(STUBPREFIX)$(VERSION).lib
PRJSTUBLIB	= $(OUT_DIR)\$(PRJSTUBLIBNAME)

# If extension parent makefile has not defined a resource definition file,
# we will generate one from standard template.
!if !$(DOING_TCL) && !$(DOING_TK) && !$(STATIC_BUILD)
!ifdef RCFILE
RESFILE = $(TMP_DIR)\$(RCFILE:.rc=.res)
!else

RESFILE = $(TMP_DIR)\$(PROJECT).res
!endif



!endif

###################################################################
# 11. Construct the paths for the installation directories
# The following macros get defined in this section:
# LIB_INSTALL_DIR - where libraries should be installed
# BIN_INSTALL_DIR - where the executables should be installed
# DOC_INSTALL_DIR - where documentation should be installed
# SCRIPT_INSTALL_DIR - where scripts should be installed
# INCLUDE_INSTALL_DIR - where C include files should be installed
# DEMO_INSTALL_DIR - where demos should be installed
# PRJ_INSTALL_DIR - where package will be installed (not set for Tcl and Tk)

!if $(DOING_TCL) || $(DOING_TK)
LIB_INSTALL_DIR		= $(_INSTALLDIR)\lib
BIN_INSTALL_DIR		= $(_INSTALLDIR)\bin
DOC_INSTALL_DIR		= $(_INSTALLDIR)\doc
!if $(DOING_TCL)
SCRIPT_INSTALL_DIR	= $(_INSTALLDIR)\lib\$(PROJECT)$(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
!else # DOING_TK
SCRIPT_INSTALL_DIR	= $(_INSTALLDIR)\lib\$(PROJECT)$(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
!endif
DEMO_INSTALL_DIR	= $(SCRIPT_INSTALL_DIR)\demos
INCLUDE_INSTALL_DIR	= $(_INSTALLDIR)\include




!else # extension other than Tk

PRJ_INSTALL_DIR         = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
LIB_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
BIN_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
DOC_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
SCRIPT_INSTALL_DIR	= $(PRJ_INSTALL_DIR)
DEMO_INSTALL_DIR	= $(PRJ_INSTALL_DIR)\demos
INCLUDE_INSTALL_DIR	= $(_INSTALLDIR)\..\include

!endif

###################################################################
# 12. Set up actual options to be passed to the compiler and linker
# Now we have all the information we need, set up the actual flags and
# options that we will pass to the compiler and linker. The main
# makefile should use these in combination with whatever other flags
# and switches are specific to it.
# The following macros are defined, names are for historical compatibility:
# OPTDEFINES - /Dxxx C macro flags based on user-specified OPTS
# COMPILERFLAGS - /Dxxx C macro flags independent of any configuration opttions
# crt - Compiler switch that selects the appropriate C runtime
# cdebug - Compiler switches related to debug AND optimizations
# cwarn - Compiler switches that set warning levels
# cflags - complete compiler switches (subsumes cdebug and cwarn)
# ldebug - Linker switches controlling debug information and optimization
# lflags - complete linker switches (subsumes ldebug) except subsystem type
# dlllflags - complete linker switches to build DLLs (subsumes lflags)
# conlflags - complete linker switches for console program (subsumes lflags)
# guilflags - complete linker switches for GUI program (subsumes lflags)
# baselibs - minimum Windows libraries required. Parent makefile can
#    define PRJ_LIBS before including rules.rc if additional libs are needed

OPTDEFINES	= -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS

!if $(TCL_MEM_DEBUG)
OPTDEFINES	= $(OPTDEFINES) -DTCL_MEM_DEBUG
!endif
!if $(TCL_COMPILE_DEBUG)
................................................................................
OPTDEFINES	= $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
!endif
!if $(TCL_THREADS)
OPTDEFINES	= $(OPTDEFINES) -DTCL_THREADS=1
!if $(USE_THREAD_ALLOC)
OPTDEFINES	= $(OPTDEFINES) -DUSE_THREAD_ALLOC=1
!endif



!endif
!if $(STATIC_BUILD)
OPTDEFINES	= $(OPTDEFINES) -DSTATIC_BUILD
!endif
!if $(TCL_NO_DEPRECATED)
OPTDEFINES	= $(OPTDEFINES) -DTCL_NO_DEPRECATED
!endif

!if $(USE_STUBS)
# Note we do not define USE_TCL_STUBS even when building tk since some
# test targets in tk do not use stubs
!if ! $(DOING_TCL)
USE_STUBS_DEFS  = -DUSE_TCL_STUBS -DUSE_TCLOO_STUBS
!if $(NEED_TK)
USE_STUBS_DEFS  = $(USE_STUBS_DEFS) -DUSE_TK_STUBS
!endif
!endif
!endif # USE_STUBS

!if !$(DEBUG)
OPTDEFINES	= $(OPTDEFINES) -DNDEBUG
!if $(OPTIMIZING)
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_OPTIMIZED
!endif
!endif
!if $(PROFILE)
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_PROFILED
!endif
!if "$(MACHINE)" == "AMD64"
OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_DO64BIT
!endif
!if $(VCVERSION) < 1300
OPTDEFINES	= $(OPTDEFINES) -DNO_STRTOI64
!endif

# _ATL_XP_TARGETING - Newer SDK's need this to build for XP
COMPILERFLAGS  = /D_ATL_XP_TARGETING




# Following is primarily for the benefit of extensions. Tcl 8.5 builds
# Tcl without /DUNICODE, while 8.6 builds with it defined. When building
# an extension, it is advisable (but not mandated) to use the same Windows
# API as the Tcl build. This is accordingly defaulted below. A particular
# extension can override this by pre-definining USE_WIDECHAR_API.
!ifndef USE_WIDECHAR_API
!if $(TCL_VERSION) > 85
USE_WIDECHAR_API = 1
!else
USE_WIDECHAR_API = 0
!endif
!endif


!if $(USE_WIDECHAR_API)
COMPILERFLAGS = $(COMPILERFLAGS) /DUNICODE /D_UNICODE
!endif




# Like the TEA system only set this non empty for non-Tk extensions
# Note: some extensions use PACKAGE_NAME and others use PACKAGE_TCLNAME
# so we pass both
!if !$(DOING_TCL) && !$(DOING_TK)
PKGNAMEFLAGS = -DPACKAGE_NAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
               -DPACKAGE_TCLNAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
               -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \
               -DMODULE_SCOPE=extern
!endif







# crt picks the C run time based on selected OPTS
!if $(MSVCRT)
!if $(DEBUG) && !$(UNCHECKED)
crt = -MDd
!else



crt = -MD
!endif
!else







!if $(DEBUG) && !$(UNCHECKED)
crt = -MTd
!else



crt = -MT
!endif
!endif


# cdebug includes compiler options for debugging as well as optimization.
!if $(DEBUG)

# In debugging mode, optimizations need to be disabled
cdebug = -Zi -Od $(DEBUGFLAGS)

!else

cdebug = $(OPTIMIZATIONS)
!if $(SYMBOLS)
cdebug = $(cdebug) -Zi
!endif




!endif # $(DEBUG)

# cwarn includes default warning levels.
cwarn = $(WARNINGS)

!if "$(MACHINE)" == "AMD64"
# Disable pointer<->int warnings related to cast between different sizes
# There are a gadzillion of these due to use of ClientData and
# clutter up compiler
# output increasing chance of a real warning getting lost. So disable them.
# Eventually some day, Tcl will be 64-bit clean.
cwarn = $(cwarn) -wd4311 -wd4312
!endif

### Common compiler options that are architecture specific
!if "$(MACHINE)" == "ARM"
carch = -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
!else
carch =
!endif



!if $(DEBUG)



# Turn warnings into errors
cwarn = $(cwarn) -WX
!endif





INCLUDES = $(TCL_INCLUDES) $(TK_INCLUDES) $(PRJ_INCLUDES)
!if !$(DOING_TCL) && !$(DOING_TK)
INCLUDES = $(INCLUDES) -I"$(GENERICDIR)" -I"$(WINDIR)" -I"$(COMPATDIR)"
!endif

# These flags are defined roughly in the order of the pre-reform
# rules.vc/makefile.vc to help visually compare that the pre- and
# post-reform build logs

# cflags contains generic flags used for building practically all object files
cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\ $(cdebug)

# appcflags contains $(cflags) and flags for building the application
# object files (e.g. tclsh, or wish) pkgcflags contains $(cflags) plus
# flags used for building shared object files The two differ in the
# BUILD_$(PROJECT) macro which should be defined only for the shared
# library *implementation* and not for its caller interface

appcflags = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES) $(USE_STUBS_DEFS)
appcflags_nostubs = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES)
pkgcflags = $(appcflags) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT)
pkgcflags_nostubs = $(appcflags_nostubs) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT)

# stubscflags contains $(cflags) plus flags used for building a stubs
# library for the package.  Note: -DSTATIC_BUILD is defined in
# $(OPTDEFINES) only if the OPTS configuration indicates a static
# library. However the stubs library is ALWAYS static hence included
# here irrespective of the OPTS setting.
#
# TBD - tclvfs has a comment that stubs libs should not be compiled with -GL
# without stating why. Tcl itself compiled stubs libs with this flag.
# so we do not remove it from cflags. -GL may prevent extensions
# compiled with one VC version to fail to link against stubs library
# compiled with another VC version. Check for this and fix accordingly.
stubscflags = $(cflags) $(PKGNAMEFLAGS) $(PRJ_DEFINES) $(OPTDEFINES) -Zl -DSTATIC_BUILD $(INCLUDES)

# Link flags

!if $(DEBUG)
ldebug	= -debug -debugtype:cv
!else



ldebug	= -release -opt:ref -opt:icf,3
!if $(SYMBOLS)
ldebug	= $(ldebug) -debug -debugtype:cv
!endif




!endif

# Note: Profiling is currently only possible with the Visual Studio Enterprise
!if $(PROFILE)
ldebug= $(ldebug) -profile
!endif




### Declarations common to all linker versions
lflags	= -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)



!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
lflags	= $(lflags) -nodefaultlib:libucrt.lib
!endif












dlllflags = $(lflags) -dll
conlflags = $(lflags) -subsystem:console
guilflags = $(lflags) -subsystem:windows

# Libraries that are required for every image.
# Extensions should define any additional libraries with $(PRJ_LIBS)
winlibs   = kernel32.lib advapi32.lib

!if $(NEED_TK)
winlibs = $(winlibs) gdi32.lib user32.lib uxtheme.lib
!endif

# Avoid 'unresolved external symbol __security_cookie' errors.
# c.f. http://support.microsoft.com/?id=894573
!if "$(MACHINE)" == "AMD64"
!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500
winlibs   = $(winlibs) bufferoverflowU.lib
!endif











!endif

baselibs = $(winlibs) $(PRJ_LIBS)

!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
baselibs   = $(baselibs) ucrt.lib
!endif


################################################################
# 13. Define standard commands, common make targets and implicit rules

CCPKGCMD = $(cc32) $(pkgcflags) -Fo$(TMP_DIR)^\
CCAPPCMD = $(cc32) $(appcflags) -Fo$(TMP_DIR)^\
CCSTUBSCMD = $(cc32) $(stubscflags) -Fo$(TMP_DIR)^\

LIBCMD = $(lib32) -nologo $(LINKERFLAGS) -out:$@
DLLCMD = $(link32) $(dlllflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)

CONEXECMD = $(link32) $(conlflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
GUIEXECMD = $(link32) $(guilflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
RESCMD  = $(rc32) -fo $@ -r -i "$(GENERICDIR)" -i "$(TMP_DIR)" \
	    $(TCL_INCLUDES) \
	    -DDEBUG=$(DEBUG) -d UNCHECKED=$(UNCHECKED) \
	    -DCOMMAVERSION=$(DOTVERSION:.=,),0 \
	    -DDOTVERSION=\"$(DOTVERSION)\" \



	    -DVERSION=\"$(VERSION)\" \
	    -DSUFX=\"$(SUFX:t=)\" \
	    -DPROJECT=\"$(PROJECT)\" \
	    -DPRJLIBNAME=\"$(PRJLIBNAME)\"

!ifndef DEFAULT_BUILD_TARGET
DEFAULT_BUILD_TARGET = $(PROJECT)
!endif

default-target: $(DEFAULT_BUILD_TARGET)

default-pkgindex:
	@echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
	    [list load [file join $$dir $(PRJLIBNAME)]] > $(OUT_DIR)\pkgIndex.tcl

default-pkgindex-tea:
	@if exist $(ROOT)\pkgIndex.tcl.in nmakehlp -s << $(ROOT)\pkgIndex.tcl.in > $(OUT_DIR)\pkgIndex.tcl
@PACKAGE_VERSION@    $(DOTVERSION)
@PACKAGE_NAME@       $(PRJ_PACKAGE_TCLNAME)
@PACKAGE_TCLNAME@    $(PRJ_PACKAGE_TCLNAME)
@PKG_LIB_FILE@       $(PRJLIBNAME)
<<


default-install: default-install-binaries default-install-libraries

default-install-binaries: $(PRJLIB)
	@echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
	@if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
	@$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL

default-install-libraries: $(OUT_DIR)\pkgIndex.tcl
	@echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
	@if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
	@echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
	@$(CPY) $(OUT_DIR)\pkgIndex.tcl $(SCRIPT_INSTALL_DIR)

default-install-stubs:
	@echo Installing stubs library to '$(SCRIPT_INSTALL_DIR)'
	@if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
	@$(CPY) $(PRJSTUBLIB) "$(SCRIPT_INSTALL_DIR)" >NUL

default-install-docs-html:
	@echo Installing documentation files to '$(DOC_INSTALL_DIR)'
	@if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
	@if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.html" "$(DOCDIR)\*.css" "$(DOCDIR)\*.png") do @$(COPY) %f "$(DOC_INSTALL_DIR)"

default-install-docs-n:
	@echo Installing documentation files to '$(DOC_INSTALL_DIR)'
	@if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
	@if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.n") do @$(COPY) %f "$(DOC_INSTALL_DIR)"

default-install-demos:
	@echo Installing demos to '$(DEMO_INSTALL_DIR)'
	@if not exist "$(DEMO_INSTALL_DIR)" mkdir "$(DEMO_INSTALL_DIR)"
	@if exist $(DEMODIR) $(CPYDIR) "$(DEMODIR)" "$(DEMO_INSTALL_DIR)"

default-clean:
	@echo Cleaning $(TMP_DIR)\* ...
	@if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
	@echo Cleaning $(WINDIR)\nmakehlp.obj, nmakehlp.exe ...
	@if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
	@if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
	@if exist $(WINDIR)\nmakehlp.out del $(WINDIR)\nmakehlp.out
	@echo Cleaning $(WINDIR)\nmhlp-out.txt ...
	@if exist $(WINDIR)\nmhlp-out.txt del $(WINDIR)\nmhlp-out.txt
	@echo Cleaning $(WINDIR)\_junk.pch ...
	@if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch
	@echo Cleaning $(WINDIR)\vercl.x, vercl.i ...
	@if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x
	@if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i
	@echo Cleaning $(WINDIR)\versions.vc, version.vc ...
	@if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc
	@if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc

default-hose: default-clean
	@echo Hosing $(OUT_DIR)\* ...
	@if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)

# Only for backward compatibility
default-distclean: default-hose

default-setup:
	@if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
	@if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)

!if "$(TESTPAT)" != ""
TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
!endif

default-test: default-setup $(PROJECT)
	@set TCLLIBPATH=$(OUT_DIR:\=/)
	@if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
	cd "$(TESTDIR)" && $(DEBUGGER) $(TCLSH) all.tcl $(TESTFLAGS)

default-shell: default-setup $(PROJECT)
	@set TCLLIBPATH=$(OUT_DIR:\=/)
	@if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
	$(DEBUGGER) $(TCLSH)

# Generation of Windows version resource
!ifdef RCFILE

# Note: don't use $** in below rule because there may be other dependencies
# and only the "master" rc must be passed to the resource compiler
$(TMP_DIR)\$(PROJECT).res: $(RCDIR)\$(PROJECT).rc
	$(RESCMD) $(RCDIR)\$(PROJECT).rc

!else

# If parent makefile has not defined a resource definition file,
# we will generate one from standard template.
$(TMP_DIR)\$(PROJECT).res: $(TMP_DIR)\$(PROJECT).rc

$(TMP_DIR)\$(PROJECT).rc:
	@$(COPY) << $(TMP_DIR)\$(PROJECT).rc
#include <winver.h>






VS_VERSION_INFO VERSIONINFO
 FILEVERSION	COMMAVERSION
 PRODUCTVERSION	COMMAVERSION
 FILEFLAGSMASK	0x3fL
#ifdef DEBUG
 FILEFLAGS	VS_FF_DEBUG
#else
 FILEFLAGS	0x0L
#endif
 FILEOS		VOS_NT_WINDOWS32
 FILETYPE	VFT_DLL
 FILESUBTYPE	0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "FileDescription",  "Tcl extension " PROJECT
            VALUE "OriginalFilename", PRJLIBNAME
            VALUE "FileVersion",      DOTVERSION
            VALUE "ProductName",      "Package " PROJECT " for Tcl"
            VALUE "ProductVersion",   DOTVERSION
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

<<

!endif # ifdef RCFILE

!ifndef DISABLE_IMPLICIT_RULES
DISABLE_IMPLICIT_RULES = 0
!endif





!if !$(DISABLE_IMPLICIT_RULES)
# Implicit rule definitions - only for building library objects. For stubs and
# main application, the master makefile should define explicit rules.

{$(ROOT)}.c{$(TMP_DIR)}.obj::
	$(CCPKGCMD) @<<
$<
<<

{$(WINDIR)}.c{$(TMP_DIR)}.obj::
	$(CCPKGCMD) @<<
$<
<<

{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
	$(CCPKGCMD) @<<
$<
<<

{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
	$(CCPKGCMD) @<<
$<
<<

{$(RCDIR)}.rc{$(TMP_DIR)}.res:
	$(RESCMD) $<

{$(WINDIR)}.rc{$(TMP_DIR)}.res:
	$(RESCMD) $<

{$(TMP_DIR)}.rc{$(TMP_DIR)}.res:
	$(RESCMD) $<

.SUFFIXES:
.SUFFIXES:.c .rc

!endif




################################################################
# 14. Sanity check selected options against Tcl build options
# When building an extension, certain configuration options should
# match the ones used when Tcl was built. Here we check and
# warn on a mismatch.
!if ! $(DOING_TCL)

!if $(TCLINSTALL) # Building against an installed Tcl
!if exist("$(_TCLDIR)\lib\nmake\tcl.nmake")
TCLNMAKECONFIG = "$(_TCLDIR)\lib\nmake\tcl.nmake"
!endif




!else # ! $(TCLINSTALL) - building against Tcl source
!if exist("$(OUT_DIR)\tcl.nmake")
TCLNMAKECONFIG = "$(OUT_DIR)\tcl.nmake"
!endif
!endif # TCLINSTALL

!if $(CONFIG_CHECK)
!ifdef TCLNMAKECONFIG
!include $(TCLNMAKECONFIG)

!if defined(CORE_MACHINE) && "$(CORE_MACHINE)" != "$(MACHINE)"
!error ERROR: Build target ($(MACHINE)) does not match the Tcl library architecture ($(CORE_MACHINE)).
!endif
!if defined(CORE_USE_THREAD_ALLOC) && $(CORE_USE_THREAD_ALLOC) != $(USE_THREAD_ALLOC)
!message WARNING: Value of USE_THREAD_ALLOC ($(USE_THREAD_ALLOC)) does not match its Tcl core value ($(CORE_USE_THREAD_ALLOC)).
!endif
!if defined(CORE_DEBUG) && $(CORE_DEBUG) != $(DEBUG)
!message WARNING: Value of DEBUG ($(DEBUG)) does not match its Tcl library configuration ($(DEBUG)).
!endif
!endif

!endif # TCLNMAKECONFIG






!endif # ! $(DOING_TCL)



#----------------------------------------------------------
# Display stats being used.
#----------------------------------------------------------

!if !$(DOING_TCL)
!message *** Building against Tcl at '$(_TCLDIR)'
!endif
!if !$(DOING_TK) && $(NEED_TK)
!message *** Building against Tk at '$(_TKDIR)'
!endif
!message *** Intermediate directory will be '$(TMP_DIR)'
!message *** Output directory will be '$(OUT_DIR)'
!message *** Installation, if selected, will be in '$(_INSTALLDIR)'
!message *** Suffix for binaries will be '$(SUFX)'

!message *** Compiler version $(VCVER). Target $(MACHINE), host $(NATIVE_ARCH).



!endif # ifdef _RULES_VC

Added win/targets.vc.





































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#------------------------------------------------------------- -*- makefile -*-
# targets.vc --
#
# Part of the nmake based build system for Tcl and its extensions.
# This file defines some standard targets for the convenience of extensions
# and can be optionally included by the extension makefile.
# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for docs.

$(PROJECT): setup pkgindex $(PRJLIB)

!ifdef PRJ_STUBOBJS
$(PROJECT): $(PRJSTUBLIB)
$(PRJSTUBLIB): $(PRJ_STUBOBJS)
	$(LIBCMD) $**

$(PRJ_STUBOBJS):
	$(CCSTUBSCMD) %s
!endif # PRJ_STUBOBJS

!ifdef PRJ_MANIFEST
$(PROJECT): $(PRJLIB).manifest
$(PRJLIB).manifest: $(PRJ_MANIFEST)
	@nmakehlp -s << $** >$@
@MACHINE@	  $(MACHINE:IX86=X86)
<<
!endif

!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk"
$(PRJLIB): $(PRJ_OBJS) $(RESFILE)
!if $(STATIC_BUILD)
       $(LIBCMD) $**
!else
       $(DLLCMD) $**
       $(_VC_MANIFEST_EMBED_DLL)
!endif
       -@del $*.exp
!endif

!if "$(PRJ_HEADERS)" != "" && "$(PRJ_OBJS)" != ""
$(PRJ_OBJS): $(PRJ_HEADERS)
!endif

# If parent makefile has defined stub objects, add their installation
# to the default install
!if "$(PRJ_STUBOBJS)" != ""
default-install: default-install-stubs
!endif

# Unlike the other default targets, these cannot be in rules.vc because
# the executed command depends on existence of macro PRJ_HEADERS_PUBLIC
# that the parent makefile will not define until after including rules-ext.vc
!if "$(PRJ_HEADERS_PUBLIC)" != ""
default-install: default-install-headers
default-install-headers:
	@echo Installing headers to '$(INCLUDE_INSTALL_DIR)'
	@for %f in ($(PRJ_HEADERS_PUBLIC)) do @$(COPY) %f "$(INCLUDE_INSTALL_DIR)"
!endif

!if "$(DISABLE_STANDARD_TARGETS)" == ""
DISABLE_STANDARD_TARGETS = 0
!endif

!if "$(DISABLE_TARGET_setup)" == ""
DISABLE_TARGET_setup = 0
!endif
!if "$(DISABLE_TARGET_install)" == ""
DISABLE_TARGET_install = 0
!endif
!if "$(DISABLE_TARGET_clean)" == ""
DISABLE_TARGET_clean = 0
!endif
!if "$(DISABLE_TARGET_test)" == ""
DISABLE_TARGET_test = 0
!endif
!if "$(DISABLE_TARGET_shell)" == ""
DISABLE_TARGET_shell = 0
!endif

!if !$(DISABLE_STANDARD_TARGETS)
!if !$(DISABLE_TARGET_setup)
setup: default-setup
!endif
!if !$(DISABLE_TARGET_install)
install: default-install
!endif
!if !$(DISABLE_TARGET_clean)
clean: default-clean
realclean: hose
hose: default-hose
distclean: realclean default-distclean
!endif
!if !$(DISABLE_TARGET_test)
test: default-test
!endif
!if !$(DISABLE_TARGET_shell)
shell: default-shell
!endif
!endif # DISABLE_STANDARD_TARGETS

Changes to win/thread.rc.

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
// Version resource script
//

#include <winver.h>

#define RESOURCE_INCLUDED
#include <tcl.h>

LANGUAGE 0x9, 0x1    /* LANG_ENGLISH, SUBLANG_DEFAULT */

















VS_VERSION_INFO VERSIONINFO
 FILEVERSION    PACKAGE_MAJOR,PACKAGE_MINOR,0,0
 PRODUCTVERSION PACKAGE_MAJOR,PACKAGE_MINOR,0,0
 FILEFLAGSMASK  0x3fL
#if DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS     0x4    /* VOS__WINDOWS32 */
 FILETYPE     0x2    /* VFT_DLL */
 FILESUBTYPE     0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
        BEGIN
            VALUE "FileDescription", "Threading extension library for Tcl\0"
#if DEBUG
            VALUE "OriginalFilename", "thread" STRINGIFY(JOIN(PACKAGE_MAJOR,PACKAGE_MINOR)) "d.dll\0"
#else
            VALUE "OriginalFilename", "thread" STRINGIFY(JOIN(PACKAGE_MAJOR,PACKAGE_MINOR)) ".dll\0"
#endif
            VALUE "CompanyName", "NONE! Open-sourced with no owner\0"
            VALUE "FileVersion", PACKAGE_VERSION
            VALUE "LegalCopyright", "Under BSD license\0"
            VALUE "ProductName", "Tcl for Windows\0"
            VALUE "ProductVersion", PACKAGE_VERSION
            VALUE "Authors", "Brent Welch,\r\n" "Andreas Kupries, \r\n" "David Gravereaux,\r\n" "Zoran Vasiljevic" "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END






<



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

|
|






|
|






|
<
|
<
<
<

|


|








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
// Version resource script
//

#include <winver.h>

#define RESOURCE_INCLUDED


LANGUAGE 0x9, 0x1    /* LANG_ENGLISH, SUBLANG_DEFAULT */

#ifndef COMMAVERSION
#define COMMAVERSION PACKAGE_MAJOR,PACKAGE_MINOR,0,0
#endif

#ifndef DOTVERSION
#define DOTVERSION PACKAGE_VERSION
#endif

#ifndef PRJLIBNAME
#ifdef DEBUG
#define PRJLIBNAME "thread" STRINGIFY(JOIN(PACKAGE_MAJOR,PACKAGE_MINOR)) "d.dll\0"
#else
#define PRJLIBNAME "thread" STRINGIFY(JOIN(PACKAGE_MAJOR,PACKAGE_MINOR)) ".dll\0"
#endif
#endif

VS_VERSION_INFO VERSIONINFO
 FILEVERSION    COMMAVERSION
 PRODUCTVERSION COMMAVERSION
 FILEFLAGSMASK  0x3fL
#if DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS     VOS_NT_WINDOWS32
 FILETYPE     VFT_DLL
 FILESUBTYPE     0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
        BEGIN
            VALUE "FileDescription", "Threading extension library for Tcl"

            VALUE "OriginalFilename", PRJLIBNAME



            VALUE "CompanyName", "NONE! Open-sourced with no owner\0"
            VALUE "FileVersion", DOTVERSION
            VALUE "LegalCopyright", "Under BSD license\0"
            VALUE "ProductName", "Tcl for Windows\0"
            VALUE "ProductVersion", DOTVERSION
            VALUE "Authors", "Brent Welch,\r\n" "Andreas Kupries, \r\n" "David Gravereaux,\r\n" "Zoran Vasiljevic" "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

Changes to win/threadWin.c.

11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
 * Copyright (c) 1998 by Sun Microsystems, Inc.
 * Copyright (c) 1999,2000 by Scriptics Corporation.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "../generic/tclThread.h"
#include <windows.h>
#include <process.h>


#if 0
/* only Windows 2000 (XP, too??) has this function */
HANDLE (WINAPI *winOpenThreadProc)(DWORD, BOOL, DWORD);

void
ThreadpInit (void)







<


>







11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
 * Copyright (c) 1998 by Sun Microsystems, Inc.
 * Copyright (c) 1999,2000 by Scriptics Corporation.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */


#include <windows.h>
#include <process.h>
#include "../generic/tclThread.h"

#if 0
/* only Windows 2000 (XP, too??) has this function */
HANDLE (WINAPI *winOpenThreadProc)(DWORD, BOOL, DWORD);

void
ThreadpInit (void)

Changes to win/thread_win.dsp.

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
..
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
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) External Target" 0x0106

CFG=thread - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "thread_win.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "thread_win.mak" CFG="thread - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "thread - Win32 Release" (based on "Win32 (x86) External Target")
!MESSAGE "thread - Win32 Debug" (based on "Win32 (x86) External Target")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""

!IF  "$(CFG)" == "thread - Win32 Release"
................................................................................
# PROP Intermediate_Dir "Debug"
# PROP Cmd_Line "nmake -nologo -f makefile.vc OPTS=symbols TCLDIR=E:\tcl MSVCDIR=IDE"
# PROP Rebuild_Opt "-a"
# PROP Target_File "Debug\thread27d.dll"
# PROP Bsc_Name ""
# PROP Target_Dir ""

!ENDIF 

# Begin Target

# Name "thread - Win32 Release"
# Name "thread - Win32 Debug"

!IF  "$(CFG)" == "thread - Win32 Release"

!ELSEIF  "$(CFG)" == "thread - Win32 Debug"

!ENDIF 

ROOT=..

# Begin Group "generic"

# PROP Default_Filter ""
# Begin Source File







|

|


|

|

|


|







 







|










|







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
..
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
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) External Target" 0x0106

CFG=thread - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "thread_win.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "thread_win.mak" CFG="thread - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "thread - Win32 Release" (based on "Win32 (x86) External Target")
!MESSAGE "thread - Win32 Debug" (based on "Win32 (x86) External Target")
!MESSAGE

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""

!IF  "$(CFG)" == "thread - Win32 Release"
................................................................................
# PROP Intermediate_Dir "Debug"
# PROP Cmd_Line "nmake -nologo -f makefile.vc OPTS=symbols TCLDIR=E:\tcl MSVCDIR=IDE"
# PROP Rebuild_Opt "-a"
# PROP Target_File "Debug\thread27d.dll"
# PROP Bsc_Name ""
# PROP Target_Dir ""

!ENDIF

# Begin Target

# Name "thread - Win32 Release"
# Name "thread - Win32 Debug"

!IF  "$(CFG)" == "thread - Win32 Release"

!ELSEIF  "$(CFG)" == "thread - Win32 Debug"

!ENDIF

ROOT=..

# Begin Group "generic"

# PROP Default_Filter ""
# Begin Source File