# Tcl package Thread source code

Check-in [03912025a6]

Many hyperlinks are disabled.

Overview
Comment: Pulling changes from trunk Tarball | ZIP archive | SQL archive family | ancestors | descendants | both | practcl files | file ages | folders 03912025a6495064d38cddcdbe9a2c50d9280d2f5f58ad87d2a21b74be890d5b 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

     > > > > > > >  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 "xCONFIG_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_suggestedLINENO;as_suggested=as_suggested" as_lineno_1a=\LINENO as_lineno_2=";as_suggested=as_suggestedLINENO;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 "xCONFIG_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 xas_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%Pas_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 #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 "xhost_alias" != x; then if test "xbuild_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 "xbuild_alias" != "xhost_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 conftestac_exeext && { test "cross_compiling" = yes || as_test_x conftestac_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 declares 2. For example, HP-UX 11i declares gettimeofday. */ #define 2 innocuous_2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char 2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #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 \ conftestac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CYGPATH="cygpath -m" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CC="{ac_tool_prefix}gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CC="{ac_tool_prefix}cc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then if test "as_dir/ac_wordac_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_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CC="ac_tool_prefixac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="ac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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 conftestac_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 conftestac_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 #include #include #include /* 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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CYGPATH="cygpath -m" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CC="{ac_tool_prefix}gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CC="{ac_tool_prefix}cc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then if test "as_dir/ac_wordac_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_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_CC="ac_tool_prefixac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="ac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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 #include #include #include /* 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 conftestac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_RANLIB="{ac_tool_prefix}ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_exec_ext" >&5 break 2 fi done done IFS=as_save_IFS ................................................................................ for as_dir in PATHPATH_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_progac_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 PATHPATH_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_progac_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 "xac_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 \ conftestac_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 "xac_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 \ conftestac_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 "xac_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 "xac_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 "xac_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 "xac_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 \ conftestac_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 "xac_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 "xac_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 "xac_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 \ conftestac_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 "xac_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 "xac_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 "xac_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 "xac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_RANLIB="{ac_tool_prefix}ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_AR="{ac_tool_prefix}ar" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_AR="ar" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_exec_ext" >&5 break 2 fi done done IFS=as_save_IFS ................................................................................ case "xecho \{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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_RC="{ac_tool_prefix}windres" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext" && as_test_x "as_dir/ac_wordac_exec_ext"; }; then ac_cv_prog_ac_ct_RC="windres" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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%Pas_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_modeac_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 "xCONFIG_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 "xCONFIG_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_suggestedLINENO;as_suggested=as_suggested" as_lineno_1a=\LINENO as_lineno_2=";as_suggested=as_suggestedLINENO;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 "xCONFIG_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 xas_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%Pas_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 #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 "xhost_alias" != x; then if test "xbuild_alias" = x; then cross_compiling=maybe elif test "xbuild_alias" != "xhost_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 conftestac_exeext && { test "cross_compiling" = yes || test -x conftestac_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 declares $2. For example, HP-UX 11i declares gettimeofday. */ #define$2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char$2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #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_wordac_exec_ext"; then ac_cv_prog_CYGPATH="cygpath -m" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_CC="{ac_tool_prefix}gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_CC="{ac_tool_prefix}cc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then if test "as_dir/ac_wordac_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_wordac_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_wordac_exec_ext"; then ac_cv_prog_CC="ac_tool_prefixac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_CC="ac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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 conftestac_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 conftestac_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 #include 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_wordac_exec_ext"; then ac_cv_prog_CYGPATH="cygpath -m" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_CC="{ac_tool_prefix}gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_CC="{ac_tool_prefix}cc" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then if test "as_dir/ac_wordac_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_wordac_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_wordac_exec_ext"; then ac_cv_prog_CC="ac_tool_prefixac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_CC="ac_prog" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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 #include 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 conftestac_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_wordac_exec_ext"; then ac_cv_prog_RANLIB="{ac_tool_prefix}ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_exec_ext" >&5 break 2 fi done done IFS=as_save_IFS ................................................................................ for as_dir in PATHPATH_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_progac_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 PATHPATH_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_progac_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 "xac_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 "xac_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=-lecho "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_wordac_exec_ext"; then ac_cv_prog_RANLIB="{ac_tool_prefix}ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_AR="{ac_tool_prefix}ar" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_AR="ar" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_exec_ext" >&5 break 2 fi done done IFS=as_save_IFS ................................................................................ case "xecho \{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=-lecho "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=-lecho "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_wordac_exec_ext"; then ac_cv_prog_RC="{ac_tool_prefix}windres" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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_wordac_exec_ext"; then ac_cv_prog_ac_ct_RC="windres" as_echo "as_me:{as_lineno-LINENO}: found as_dir/ac_wordac_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%Pas_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_modeac_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   • thread::cond destroy cond • thread::cond notify cond • thread::cond wait cond mutex ?ms? • Description The 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. COMMANDS This section describes commands for creating and destroying threads and sending scripts to threads for evaluation. thread::create ?-joinable? ?-preserved? ?script? This command creates a thread that contains a Tcl interpreter. The Tcl interpreter either evaluates the optional script, if specified, or it waits in the event loop for scripts that arrive via the thread::send command. The result, if any, of the optional script is never returned to the caller. The result of 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 thread::release command below). If the optional script argument contains the thread::wait command the thread will enter into the event loop. If such command is not found in the script the thread will run the 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. Using flag -joinable it is possible to create a joinable thread, i.e. one upon whose exit can be waited upon by using thread::join command. Note that failure to join a thread created with -joinable flag results in resource and memory leaks. Threads created by the thread::create cannot be destroyed forcefully. Consequently, there is no corresponding thread destroy command. A thread may only be released using the 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 script argument, following the thread::wait command. If this was the last command in the script, as usualy the case, the thread will exit. 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 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: # 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 thread::release call, the thread will jump out of the thread::wait and continue to execute commands following. It will enter vwait command and wait endlessly for events. There is no way one can terminate such thread, so you wouldn't want to do this! 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 thread::preserve and decremented by a call to 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 -preserved of the 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. thread::preserve ?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 id, the command preserves the given thread. Otherwise the current thread is preserved. 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. thread::release ?-wait? ?id? This command decrements the thread reference counter. Each call to this command decrements the reference counter by one (1). If called with the optional thread 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 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. Optional flag -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 -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 -joinable option of the thread::create command and the thread::join to wait for thread to exit. thread::id This command returns the ID of the current thread. thread::errorproc ?procname? This command sets a handler for errors that occur in scripts sent asynchronously, using the -async flag of the 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 procname is called in the interpreter that invoked the thread::errorproc command. The procname ................................................................................ stack. With the -unwind 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 result is present, it will be used as the error message string; otherwise, a default error message string will be used. thread::unwind Use of this command is deprecated in favour of more advanced thread reservation system implemented with thread::preserve and thread::release commands. Support for thread::unwind command will dissapear in some future major release of the extension. This command stops a prior thread::wait command. Execution of the script passed to newly created thread will continue from the thread::wait command. If 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. thread::exit ?status? Use of this command is deprecated in favour of more advanced thread reservation system implemented with thread::preserve and thread::release commands. Support for thread::exit command will dissapear in some future major release of the extension. This command forces a thread stuck in the thread::wait command to unconditionaly exit. The thread's exit status defaults to 666 and can be specified using the optional status argument. The execution of 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 ................................................................................ situations. thread::names This command returns a list of thread IDs. These are only for threads that have been created via thread::create command. If your application creates other threads at the C level, they are not reported by this command. thread::exists id Returns true (1) if thread given by the id parameter exists, false (0) otherwise. This applies only for threads that have been created via thread::create command. thread::send ?-async? ?-head? id script ?varname? This command passes a script to another thread and, optionally, waits for the result. If the -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 thread::wait or any other relevant Tcl/Tk command, like update, vwait, etc. Optional varname specifies name of the variable to store the result of the script. Without the -async flag, the command returns the evaluation code, similarily to the standard Tcl catch command. If, however, the -async flag is specified, the command returns immediately and caller can later vwait on ?varname? to get the result of the passed script 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. Many threads can simultaneously send scripts to the target thread for execution. All of them are entered into the event queue of the target thread and executed on the FIFO basis, intermingled with optional other events pending in the event queue of the target thread. Using the optional ?-head? switch, scripts posted to the thread's event queue can be placed on the head, instead on the tail of the queue, thus being executed in the LIFO fashion. thread::broadcast script This command passes a script to all threads created by the package for execution. It does not wait for response from any of the threads. thread::wait This enters the event loop so a thread can receive messages from the thread::send command. This command should only be used within the script passed to the 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 thread::wait which is usually not what you want and/or expect. ................................................................................ # Do some initialization work here # thread::wait ; # Enter the event loop }] thread::eval ?-lock mutex? arg ?arg ...? This command concatenates passed arguments and evaluates the resulting script under the mutex protection. If no mutex is specified by using the ?-lock mutex? optional argument, the internal static mutex is used. thread::join id This command waits for the thread with ID id 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. thread::configure id ?option? ?value? ?...? This command configures various low-level aspects of the thread with ID id in the similar way as the standard Tcl command fconfigure configures some Tcl channel options. Options currently supported are: -eventmark and -unwindonerror. The -eventmark option, when set, limits the number of asynchronously posted scripts to the thread event loop. The thread::send -async command will block until the number of pending scripts in the event loop does not drop below the value configured with -eventmark. Default value for the -eventmark is 0 (zero) which effectively disables the checking, i.e. allows for unlimited number of posted scripts. The -unwindonerror option, when set, causes the target thread to unwind if the result of the script processing resulted in error. Default value for the -unwindonerror is 0 (false), i.e. thread continues to process scripts after one of the posted scripts fails. thread::transfer id channel This moves the specified channel from the current thread and interpreter to the main interpreter of the thread with the given 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 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. 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 socket commands callback procedures: 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 } thread::detach channel This detaches the specified 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 thread::attach. Restrictions: same as for transferring shared channels with the thread::transfer command. thread::attach channel This attaches the previously detached 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 channel cannot be found in the list of detached channels and/or in the current interpreter. 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. 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. The thread::mutex command supports following subcommands and options: thread::mutex create ?-recursive? Creates the mutex and returns it's opaque handle. This handle should be used for any future reference to the newly created mutex. If no optional ?-recursive? argument was specified, the command creates the exclusive mutex. With the ?-recursive? argument, the command creates a recursive mutex. thread::mutex destroy mutex Destroys the mutex. Mutex should be in unlocked state before the destroy attempt. If the mutex is locked, the command will throw Tcl error. thread::mutex lock mutex Locks the 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. thread::mutex unlock mutex Unlocks the mutex so some other thread may lock it again. Attempt to unlock the already unlocked mutex will throw Tcl error. 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. 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. The thread::rwmutex command supports following subcommands and options: thread::rwmutex 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. thread::rwmutex destroy mutex Destroys the reader/writer mutex. If the mutex is already locked, attempt to destroy it will throw Tcl error. thread::rwmutex rlock mutex Locks the mutex for reading. More than one thread may read-lock the same mutex at the same time. ................................................................................ mutex twice from the same thread will throw Tcl error. thread::rwmutex unlock mutex Unlocks the mutex so some other thread may lock it again. Attempt to unlock already unlocked mutex will throw Tcl error. 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. The command supports following subcommands and options: thread::cond 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. thread::cond destroy cond Destroys condition variable cond. Extreme care has to be taken that nobody is using (i.e. waiting on) the condition variable, otherwise unexpected errors may happen. thread::cond notify cond Wakes up all threads waiting on the condition variable cond. thread::cond wait cond mutex ?ms? This command is used to suspend program execution until the condition variable cond has been signalled or the optional timer has expired. The exclusive 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 cond, the command releases mutex. Before returning to the calling thread, the command re-acquires the mutex again. Unlocking the mutex and waiting on the condition variable cond is done atomically. The 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. 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: ................................................................................ thread::mutex lock mutex 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). 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, vwait on variables, and so forth. For the transfer of bulk information it is possible to move channels between the threads. For advanced multithreading scripts, script-level access to two basic synchronization primitives, mutex and condition variables, is also supported. See Also   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |  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   • thread::cond destroy cond • thread::cond notify cond • thread::cond wait cond mutex ?ms? • Description The 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. COMMANDS This section describes commands for creating and destroying threads and sending scripts to threads for evaluation. thread::create ?-joinable? ?-preserved? ?script? This command creates a thread that contains a Tcl interpreter. The Tcl interpreter either evaluates the optional script, if specified, or it waits in the event loop for scripts that arrive via the thread::send command. The result, if any, of the optional script is never returned to the caller. The result of 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 thread::release command below). If the optional script argument contains the thread::wait command the thread will enter into the event loop. If such command is not found in the script the thread will run the 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. Using flag -joinable it is possible to create a joinable thread, i.e. one upon whose exit can be waited upon by using thread::join command. Note that failure to join a thread created with -joinable flag results in resource and memory leaks. Threads created by the thread::create cannot be destroyed forcefully. Consequently, there is no corresponding thread destroy command. A thread may only be released using the 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 script argument, following the thread::wait command. If this was the last command in the script, as usualy the case, the thread will exit. 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 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: # 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 thread::release call, the thread will jump out of the thread::wait and continue to execute commands following. It will enter vwait command and wait endlessly for events. There is no way one can terminate such thread, so you wouldn't want to do this! 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 thread::preserve and decremented by a call to 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 -preserved of the 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. thread::preserve ?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 id, the command preserves the given thread. Otherwise the current thread is preserved. 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. thread::release ?-wait? ?id? This command decrements the thread reference counter. Each call to this command decrements the reference counter by one (1). If called with the optional thread 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 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. Optional flag -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 -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 -joinable option of the thread::create command and the thread::join to wait for thread to exit. thread::id This command returns the ID of the current thread. thread::errorproc ?procname? This command sets a handler for errors that occur in scripts sent asynchronously, using the -async flag of the 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 procname is called in the interpreter that invoked the thread::errorproc command. The procname ................................................................................ stack. With the -unwind 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 result is present, it will be used as the error message string; otherwise, a default error message string will be used. thread::unwind Use of this command is deprecated in favour of more advanced thread reservation system implemented with thread::preserve and thread::release commands. Support for thread::unwind command will dissapear in some future major release of the extension. This command stops a prior thread::wait command. Execution of the script passed to newly created thread will continue from the thread::wait command. If 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. thread::exit ?status? Use of this command is deprecated in favour of more advanced thread reservation system implemented with thread::preserve and thread::release commands. Support for thread::exit command will dissapear in some future major release of the extension. This command forces a thread stuck in the thread::wait command to unconditionaly exit. The thread's exit status defaults to 666 and can be specified using the optional status argument. The execution of 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 ................................................................................ situations. thread::names This command returns a list of thread IDs. These are only for threads that have been created via thread::create command. If your application creates other threads at the C level, they are not reported by this command. thread::exists id Returns true (1) if thread given by the id parameter exists, false (0) otherwise. This applies only for threads that have been created via thread::create command. thread::send ?-async? ?-head? id script ?varname? This command passes a script to another thread and, optionally, waits for the result. If the -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 thread::wait or any other relevant Tcl/Tk command, like update, vwait, etc. Optional varname specifies name of the variable to store the result of the script. Without the -async flag, the command returns the evaluation code, similarily to the standard Tcl catch command. If, however, the -async flag is specified, the command returns immediately and caller can later vwait on ?varname? to get the result of the passed script 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. Many threads can simultaneously send scripts to the target thread for execution. All of them are entered into the event queue of the target thread and executed on the FIFO basis, intermingled with optional other events pending in the event queue of the target thread. Using the optional ?-head? switch, scripts posted to the thread's event queue can be placed on the head, instead on the tail of the queue, thus being executed in the LIFO fashion. thread::broadcast script This command passes a script to all threads created by the package for execution. It does not wait for response from any of the threads. thread::wait This enters the event loop so a thread can receive messages from the thread::send command. This command should only be used within the script passed to the 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 thread::wait which is usually not what you want and/or expect. ................................................................................ # Do some initialization work here # thread::wait ; # Enter the event loop }] thread::eval ?-lock mutex? arg ?arg ...? This command concatenates passed arguments and evaluates the resulting script under the mutex protection. If no mutex is specified by using the ?-lock mutex? optional argument, the internal static mutex is used. thread::join id This command waits for the thread with ID id 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. thread::configure id ?option? ?value? ?...? This command configures various low-level aspects of the thread with ID id in the similar way as the standard Tcl command fconfigure configures some Tcl channel options. Options currently supported are: -eventmark and -unwindonerror. The -eventmark option, when set, limits the number of asynchronously posted scripts to the thread event loop. The thread::send -async command will block until the number of pending scripts in the event loop does not drop below the value configured with -eventmark. Default value for the -eventmark is 0 (zero) which effectively disables the checking, i.e. allows for unlimited number of posted scripts. The -unwindonerror option, when set, causes the target thread to unwind if the result of the script processing resulted in error. Default value for the -unwindonerror is 0 (false), i.e. thread continues to process scripts after one of the posted scripts fails. thread::transfer id channel This moves the specified channel from the current thread and interpreter to the main interpreter of the thread with the given 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 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. 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 socket commands callback procedures: 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 } thread::detach channel This detaches the specified 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 thread::attach. Restrictions: same as for transferring shared channels with the thread::transfer command. thread::attach channel This attaches the previously detached 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 channel cannot be found in the list of detached channels and/or in the current interpreter. 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. 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. The thread::mutex command supports following subcommands and options: thread::mutex create ?-recursive? Creates the mutex and returns it's opaque handle. This handle should be used for any future reference to the newly created mutex. If no optional ?-recursive? argument was specified, the command creates the exclusive mutex. With the ?-recursive? argument, the command creates a recursive mutex. thread::mutex destroy mutex Destroys the mutex. Mutex should be in unlocked state before the destroy attempt. If the mutex is locked, the command will throw Tcl error. thread::mutex lock mutex Locks the 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. thread::mutex unlock mutex Unlocks the mutex so some other thread may lock it again. Attempt to unlock the already unlocked mutex will throw Tcl error. 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. 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. The thread::rwmutex command supports following subcommands and options: thread::rwmutex 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. thread::rwmutex destroy mutex Destroys the reader/writer mutex. If the mutex is already locked, attempt to destroy it will throw Tcl error. thread::rwmutex rlock mutex Locks the mutex for reading. More than one thread may read-lock the same mutex at the same time. ................................................................................ mutex twice from the same thread will throw Tcl error. thread::rwmutex unlock mutex Unlocks the mutex so some other thread may lock it again. Attempt to unlock already unlocked mutex will throw Tcl error. 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. The command supports following subcommands and options: thread::cond 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. thread::cond destroy cond Destroys condition variable cond. Extreme care has to be taken that nobody is using (i.e. waiting on) the condition variable, otherwise unexpected errors may happen. thread::cond notify cond Wakes up all threads waiting on the condition variable cond. thread::cond wait cond mutex ?ms? This command is used to suspend program execution until the condition variable cond has been signalled or the optional timer has expired. The exclusive 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 cond, the command releases mutex. Before returning to the calling thread, the command re-acquires the mutex again. Unlocking the mutex and waiting on the condition variable cond is done atomically. The 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. 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: ................................................................................ thread::mutex lock mutex 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). 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, vwait on variables, and so forth. For the transfer of bulk information it is possible to move channels between the threads. For advanced multithreading scripts, script-level access to two basic synchronization primitives, mutex and condition variables, is also supported. 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   • tpool::suspend tpool • tpool::resume tpool • 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. COMMANDS tpool::create ?options? ................................................................................ -maxworkers number Maximum number of worker threads allowed for this threadpool instance. If a new job is pending and there are no idle worker threads available, the implementation will try to create new worker thread. If the number of available worker threads is lower than the given number, new worker thread will start. The caller will automatically enter the event loop and wait until the worker thread has initialized. If. however, the number of available worker threads is equal to the given number, the caller will enter the event loop and wait for the first worker thread to get idle, thus ready to run the job. Default value of this parameter is 4 (four), which means that the threadpool instance will allow maximum of 4 worker threads running jobs or being idle waiting for new jobs to get posted to the job queue. -idletime seconds Time in seconds an idle worker thread waits for the job to get posted to the job queue. If no job arrives during this interval and the time expires, the worker thread will check the number of currently available worker threads and if the number is higher than the number set by the minthreads option, it will exit. If an 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. The idle worker thread is not servicing the event loop. If you, however, put the worker thread into the event loop, by evaluating the 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. -initcmd script Sets a Tcl script used to initialize new worker thread. This is usually used to load packages and commands in the worker, set default variables, create namespaces, and such. If the passed script runs into a Tcl error, the worker will not be created and the initiating command (either the tpool::create or 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. -exitcmd script Sets a Tcl script run when the idle worker thread exits. This is normaly used to cleanup the state of the worker thread, release reserved resources, cleanup memory and such. Default value for this option is unspecified, thus no Tcl script will run on the worker thread exit. tpool::names This command returns a list of IDs of threadpools created with the tpool::create command. If no threadpools were found, the command will return empty list. tpool::post ?-detached? ?-nowait? tpool script This command sends a script to the target 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 ?-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. The command returns the ID of the posted job. This ID is used for subsequent tpool::wait, tpool::get and tpool::cancel commands to wait for and retrieve result of the posted script, or cancel the posted job respectively. If the optional ?-detached? argument is specified, the command will post a detached job. A detached job can not be cancelled or waited upon and is not identified by the job ID. If the threadpool 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. tpool::wait tpool joblist ?varname? This command waits for one or many jobs, whose job IDs are given in the 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. The command returns the list of completed job IDs. If the optional variable ?varname? is given, it will be set to the list of jobs in the joblist which are still pending. If the threadpool tpool is not found in the list of active thread pools, the command will throw error. tpool::cancel tpool joblist ?varname? This command cancels the previously posted jobs given by the joblist to the pool 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 ?varname? is given, it will be set to the list of jobs in the joblist which were not cancelled. If the threadpool tpool is not found in the list of active thread pools, the command will throw error. tpool::get tpool job This command retrieves the result of the previously posted job. Only results of jobs waited upon with the tpool::wait command can be retrieved. If the execution of the script resulted in error, the command will throw the error and update the errorInfo and errorCode variables correspondingly. If the pool tpool is not found in the list of threadpools, the command will throw error. If the job job is not ready for retrieval, because it is currently being executed by the worker thread, the command will throw error. tpool::preserve tpool Each call to this command increments the reference counter of the threadpool 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. tpool::release tpool Each call to this command decrements the reference counter of the threadpool tpool by one (1).Command returns the value of the reference counter after the decrement. When the reference counter reaches zero (0), the threadpool tpool is marked for termination. You should not reference the threadpool after the tpool::release command returns zero. The 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. tpool::suspend 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 minworkers option of the tpool::create command. tpool::resume 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. 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. 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 thread, tsv, ttrace Keywords thread, threadpool   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |  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   • tpool::suspend tpool • tpool::resume tpool • 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. COMMANDS tpool::create ?options? ................................................................................ -maxworkers number Maximum number of worker threads allowed for this threadpool instance. If a new job is pending and there are no idle worker threads available, the implementation will try to create new worker thread. If the number of available worker threads is lower than the given number, new worker thread will start. The caller will automatically enter the event loop and wait until the worker thread has initialized. If. however, the number of available worker threads is equal to the given number, the caller will enter the event loop and wait for the first worker thread to get idle, thus ready to run the job. Default value of this parameter is 4 (four), which means that the threadpool instance will allow maximum of 4 worker threads running jobs or being idle waiting for new jobs to get posted to the job queue. -idletime seconds Time in seconds an idle worker thread waits for the job to get posted to the job queue. If no job arrives during this interval and the time expires, the worker thread will check the number of currently available worker threads and if the number is higher than the number set by the minthreads option, it will exit. If an 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. The idle worker thread is not servicing the event loop. If you, however, put the worker thread into the event loop, by evaluating the 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. -initcmd script Sets a Tcl script used to initialize new worker thread. This is usually used to load packages and commands in the worker, set default variables, create namespaces, and such. If the passed script runs into a Tcl error, the worker will not be created and the initiating command (either the tpool::create or 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. -exitcmd script Sets a Tcl script run when the idle worker thread exits. This is normaly used to cleanup the state of the worker thread, release reserved resources, cleanup memory and such. Default value for this option is unspecified, thus no Tcl script will run on the worker thread exit. tpool::names This command returns a list of IDs of threadpools created with the tpool::create command. If no threadpools were found, the command will return empty list. tpool::post ?-detached? ?-nowait? tpool script This command sends a script to the target 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 ?-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. The command returns the ID of the posted job. This ID is used for subsequent tpool::wait, tpool::get and tpool::cancel commands to wait for and retrieve result of the posted script, or cancel the posted job respectively. If the optional ?-detached? argument is specified, the command will post a detached job. A detached job can not be cancelled or waited upon and is not identified by the job ID. If the threadpool 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. tpool::wait tpool joblist ?varname? This command waits for one or many jobs, whose job IDs are given in the 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. The command returns the list of completed job IDs. If the optional variable ?varname? is given, it will be set to the list of jobs in the joblist which are still pending. If the threadpool tpool is not found in the list of active thread pools, the command will throw error. tpool::cancel tpool joblist ?varname? This command cancels the previously posted jobs given by the joblist to the pool 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 ?varname? is given, it will be set to the list of jobs in the joblist which were not cancelled. If the threadpool tpool is not found in the list of active thread pools, the command will throw error. tpool::get tpool job This command retrieves the result of the previously posted job. Only results of jobs waited upon with the tpool::wait command can be retrieved. If the execution of the script resulted in error, the command will throw the error and update the errorInfo and errorCode variables correspondingly. If the pool tpool is not found in the list of threadpools, the command will throw error. If the job job is not ready for retrieval, because it is currently being executed by the worker thread, the command will throw error. tpool::preserve tpool Each call to this command increments the reference counter of the threadpool 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. tpool::release tpool Each call to this command decrements the reference counter of the threadpool tpool by one (1).Command returns the value of the reference counter after the decrement. When the reference counter reaches zero (0), the threadpool tpool is marked for termination. You should not reference the threadpool after the tpool::release command returns zero. The 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. tpool::suspend 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 minworkers option of the tpool::create command. tpool::resume 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. 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. 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 thread, tsv, ttrace Keywords thread, threadpool  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   • tsv::keylkeys varname keylist ?key? • tsv::keylset varname keylist key value ?key value..? • 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. 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. ELEMENT COMMANDS tsv::names ?pattern? Returns names of shared variables matching optional ?pattern? or all known variables if pattern is ommited. tsv::object varname element Creates object accessor command for the element in the shared variable 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. % tsv::set foo bar "A shared string" % set string [tsv::object foo bar] % string append " appended" => A shared string appended tsv::set varname element ?value? Sets the value of the element in the shared variable varname to value and returns the value to caller. The 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. tsv::get varname element ?namedvar? Retrieves the value of the element from the shared variable varname. If the optional argument namedvar is given, the value is stored in the named variable. Return value of the command depends of the existence of the optional argument 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. tsv::unset varname ?element? Unsets the element from the shared variable varname. If the optional element is not given, it deletes the variable. tsv::exists varname element Checks wether the element exists in the shared variable varname and returns true (1) if it does or false (0) if it doesn't. ................................................................................ and unsets the element, all in one atomic operation. tsv::move varname oldname newname Renames the element oldname to the newname in the shared variable varname. This effectively performs an get/unset/set sequence of operations but all in one atomic step. tsv::incr varname element ?count? Similar to standard Tcl incr command but increments the value of the element in shared variaboe varname instead of the Tcl variable. tsv::append varname element value ?value ...? Similar to standard Tcl append command but appends one or more values to the element in shared variable varname instead of the Tcl variable. tsv::lock varname arg ?arg ...? This command concatenates passed arguments and evaluates the resulting script under the internal mutex protection. During the script evaluation, the entire shared variable is locked. For shared variable commands within the script, internal locking is disabled so no deadlock can occur. It is also allowed to unset the shared ................................................................................ LIST COMMANDS Those command are similar to the equivalently named Tcl command. The difference is that they operate on elements of shared arrays. tsv::lappend varname element value ?value ...? Similar to standard Tcl lappend command but appends one or more values to the element in shared variable varname instead of the Tcl variable. tsv::linsert varname element index value ?value ...? Similar to standard Tcl linsert command but inserts one or more values at the index list position in the element in the shared variable varname instead of the Tcl variable. tsv::lreplace varname element first last ?value ...? Similar to standard Tcl lreplace command but replaces one or more values between the first and last position in the element of the shared variable varname instead of the Tcl variable. tsv::llength varname element Similar to standard Tcl llength command but returns length of the element in the shared variable varname instead of the Tcl variable. tsv::lindex varname element ?index? Similar to standard Tcl lindex command but returns the value at the index list position of the element from the shared variable varname instead of the Tcl variable. tsv::lrange varname element from to ................................................................................ in the shared variable varname instead of the Tcl variable. tsv::lset varname element index ?index ...? value Similar to standard Tcl lset command but sets the element in the shared variable varname instead of the Tcl variable. tsv::lpop varname element ?index? Similar to the standard Tcl lindex command but in addition to returning, it also splices the value out of the element from the shared variable varname in one atomic operation. In contrast to the Tcl lindex command, this command returns no value to the caller. tsv::lpush varname element ?index? This command performes the opposite of the tsv::lpop command. As its counterpart, it returns no value to the caller. ARRAY COMMANDS ................................................................................ Binds the varname to the persistent storage handle. The format of the handle is <handler>:<address>, where <handler> is "gdbm" for GNU Gdbm and "lmdb" for LMDB and <address> is the path to the database file. tsv::array unbind varname Unbinds the shared array from its bound persistent storage. tsv::array isbound varname Returns true (1) if the shared varname is bound to some persistent storage or zero (0) if not. 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. 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: {{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: {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. tsv::keyldel varname keylist key Delete the field specified by key from the keyed list keylist in the shared variable varname. This removes both the key and the value from the keyed list. tsv::keylget varname keylist key ?retvar? Return the value associated with key from the keyed list keylist in the shared variable varname. If the optional 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. If retvar is specified and key is in the list, then the value is returned in the variable retvar and the command returns 1 if the key was present within the list. If key isn't in the list, the command will return 0, and retvar will be left unchanged. If {} is specified for retvar, the value is not returned, allowing the Tcl programmer to determine if a key is present in a keyed list without setting a variable as a side-effect. tsv::keylkeys varname keylist ?key? Return the a list of the keys in the keyed list keylist in the shared variable varname. If key is specified, then it is the name of a key field who's subfield keys are to be retrieved. tsv::keylset varname keylist key value ?key value..? Set the value associated with key, in the keyed list keylist to value. If the keylist does not exists, it is created. If key is not currently in the list, it will be added. If it already exists, value replaces the existing value. Multiple keywords and values may be specified, if desired. 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. 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. CREDITS Thread shared variables are inspired by the nsv interface found in AOLserver, a highly scalable Web server from America Online. See Also thread, tpool, ttrace Keywords locking, synchronization, thread shared data, threads   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |  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   • tsv::keylkeys varname keylist ?key? • tsv::keylset varname keylist key value ?key value..? • 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. 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. ELEMENT COMMANDS tsv::names ?pattern? Returns names of shared variables matching optional ?pattern? or all known variables if pattern is ommited. tsv::object varname element Creates object accessor command for the element in the shared variable 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. % tsv::set foo bar "A shared string" % set string [tsv::object foo bar] % string append " appended" => A shared string appended tsv::set varname element ?value? Sets the value of the element in the shared variable varname to value and returns the value to caller. The 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. tsv::get varname element ?namedvar? Retrieves the value of the element from the shared variable varname. If the optional argument namedvar is given, the value is stored in the named variable. Return value of the command depends of the existence of the optional argument 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. tsv::unset varname ?element? Unsets the element from the shared variable varname. If the optional element is not given, it deletes the variable. tsv::exists varname element Checks wether the element exists in the shared variable varname and returns true (1) if it does or false (0) if it doesn't. ................................................................................ and unsets the element, all in one atomic operation. tsv::move varname oldname newname Renames the element oldname to the newname in the shared variable varname. This effectively performs an get/unset/set sequence of operations but all in one atomic step. tsv::incr varname element ?count? Similar to standard Tcl incr command but increments the value of the element in shared variaboe varname instead of the Tcl variable. tsv::append varname element value ?value ...? Similar to standard Tcl append command but appends one or more values to the element in shared variable varname instead of the Tcl variable. tsv::lock varname arg ?arg ...? This command concatenates passed arguments and evaluates the resulting script under the internal mutex protection. During the script evaluation, the entire shared variable is locked. For shared variable commands within the script, internal locking is disabled so no deadlock can occur. It is also allowed to unset the shared ................................................................................ LIST COMMANDS Those command are similar to the equivalently named Tcl command. The difference is that they operate on elements of shared arrays. tsv::lappend varname element value ?value ...? Similar to standard Tcl lappend command but appends one or more values to the element in shared variable varname instead of the Tcl variable. tsv::linsert varname element index value ?value ...? Similar to standard Tcl linsert command but inserts one or more values at the index list position in the element in the shared variable varname instead of the Tcl variable. tsv::lreplace varname element first last ?value ...? Similar to standard Tcl lreplace command but replaces one or more values between the first and last position in the element of the shared variable varname instead of the Tcl variable. tsv::llength varname element Similar to standard Tcl llength command but returns length of the element in the shared variable varname instead of the Tcl variable. tsv::lindex varname element ?index? Similar to standard Tcl lindex command but returns the value at the index list position of the element from the shared variable varname instead of the Tcl variable. tsv::lrange varname element from to ................................................................................ in the shared variable varname instead of the Tcl variable. tsv::lset varname element index ?index ...? value Similar to standard Tcl lset command but sets the element in the shared variable varname instead of the Tcl variable. tsv::lpop varname element ?index? Similar to the standard Tcl lindex command but in addition to returning, it also splices the value out of the element from the shared variable varname in one atomic operation. In contrast to the Tcl lindex command, this command returns no value to the caller. tsv::lpush varname element ?index? This command performes the opposite of the tsv::lpop command. As its counterpart, it returns no value to the caller. ARRAY COMMANDS ................................................................................ Binds the varname to the persistent storage handle. The format of the handle is <handler>:<address>, where <handler> is "gdbm" for GNU Gdbm and "lmdb" for LMDB and <address> is the path to the database file. tsv::array unbind varname Unbinds the shared array from its bound persistent storage. tsv::array isbound varname Returns true (1) if the shared varname is bound to some persistent storage or zero (0) if not. 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. 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: {{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: {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. tsv::keyldel varname keylist key Delete the field specified by key from the keyed list keylist in the shared variable varname. This removes both the key and the value from the keyed list. tsv::keylget varname keylist key ?retvar? Return the value associated with key from the keyed list keylist in the shared variable varname. If the optional 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. If retvar is specified and key is in the list, then the value is returned in the variable retvar and the command returns 1 if the key was present within the list. If key isn't in the list, the command will return 0, and retvar will be left unchanged. If {} is specified for retvar, the value is not returned, allowing the Tcl programmer to determine if a key is present in a keyed list without setting a variable as a side-effect. tsv::keylkeys varname keylist ?key? Return the a list of the keys in the keyed list keylist in the shared variable varname. If key is specified, then it is the name of a key field who's subfield keys are to be retrieved. tsv::keylset varname keylist key value ?key value..? Set the value associated with key, in the keyed list keylist to value. If the keylist does not exists, it is created. If key is not currently in the list, it will be added. If it already exists, value replaces the existing value. Multiple keywords and values may be specified, if desired. 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. 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. CREDITS Thread shared variables are inspired by the nsv interface found in AOLserver, a highly scalable Web server from America Online. See Also thread, tpool, ttrace Keywords locking, synchronization, thread shared data, threads  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   • ttrace::preload cmd • 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 unknown command and mechanism. The package requires Tcl threading extension but can be alternatively used stand-alone within the AOLserver, a scalable webserver from America Online. In a nutshell, a short sample illustrating the usage of the ttrace with the Tcl threading extension: % 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 As seen from above, the ttrace::eval and 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. USER COMMANDS This section describes user-level commands. Those commands can be used by script writers to control the execution of the tracing framework. ttrace::eval arg ?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. 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. 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. ttrace::disable Deactivates all registered callbacks in the framework and closes the current trace epoch. ttrace::cleanup Used to clean-up all on-demand loaded resources in the interpreter. It effectively brings Tcl interpreter to its pristine state. ttrace::update ?epoch? Used to refresh the state of the interpreter to match the optional trace ?epoch?. If the optional ?epoch? is not given, it takes the most recent trace epoch. ttrace::getscript Returns a synthetized Tcl script which may be sourced in any interpreter. This script sets the stage for the Tcl 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 ttrace::eval and ttrace::update. CALLBACK COMMANDS A word upfront: the package already includes callbacks for tracing following Tcl commands: proc, namespace, variable, load, and 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. 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. ................................................................................ Registers Tcl callback to be activated at ttrace::disable. Registered callbacks are activated on FIFO basis. The callback definition includes the name of the callback, cmd, a list of callback arguments, arglist and the body of the callback. Effectively, this actually resembles the call interface of the standard Tcl proc command. ttrace::addtrace cmd arglist body Registers Tcl callback to be activated for tracing the Tcl cmd command. The callback definition includes the name of the Tcl command to trace, cmd, a list of callback arguments, arglist and the body of the callback. Effectively, this actually resembles the call interface of the standard Tcl proc command. ttrace::addscript name body 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 unknown command. Registered callbacks are activated on FIFO basis. The callback definition includes the name of the callback, name and the body of the callback. ttrace::addresolver cmd arglist body Registers Tcl callback to be activated by the overloaded Tcl 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. ttrace::addcleanup body Registers Tcl callback to be activated by the trace::cleanup. Registered callbacks are activated on FIFO basis. ttrace::addentry cmd var val Adds one entry to the named in-memory database. ttrace::getentry cmd var ................................................................................ Returns the value of the entry from the named in-memory database. ttrace::getentries cmd ?pattern? Returns names of all entries from the named in-memory database. ttrace::delentry cmd Deletes an entry from the named in-memory database. ttrace::preload 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 unknown command. 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 proc, namespace, 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 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. 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 thread, tpool, tsv Keywords command tracing, introspection   | | | | | | | | | | | | | | | | | | | | | | | | | | | |  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   • ttrace::preload cmd • 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 unknown command and mechanism. The package requires Tcl threading extension but can be alternatively used stand-alone within the AOLserver, a scalable webserver from America Online. In a nutshell, a short sample illustrating the usage of the ttrace with the Tcl threading extension: % 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 As seen from above, the ttrace::eval and 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. USER COMMANDS This section describes user-level commands. Those commands can be used by script writers to control the execution of the tracing framework. ttrace::eval arg ?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. 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. 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. ttrace::disable Deactivates all registered callbacks in the framework and closes the current trace epoch. ttrace::cleanup Used to clean-up all on-demand loaded resources in the interpreter. It effectively brings Tcl interpreter to its pristine state. ttrace::update ?epoch? Used to refresh the state of the interpreter to match the optional trace ?epoch?. If the optional ?epoch? is not given, it takes the most recent trace epoch. ttrace::getscript Returns a synthetized Tcl script which may be sourced in any interpreter. This script sets the stage for the Tcl 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 ttrace::eval and ttrace::update. CALLBACK COMMANDS A word upfront: the package already includes callbacks for tracing following Tcl commands: proc, namespace, variable, load, and 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. 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. ................................................................................ Registers Tcl callback to be activated at ttrace::disable. Registered callbacks are activated on FIFO basis. The callback definition includes the name of the callback, cmd, a list of callback arguments, arglist and the body of the callback. Effectively, this actually resembles the call interface of the standard Tcl proc command. ttrace::addtrace cmd arglist body Registers Tcl callback to be activated for tracing the Tcl cmd command. The callback definition includes the name of the Tcl command to trace, cmd, a list of callback arguments, arglist and the body of the callback. Effectively, this actually resembles the call interface of the standard Tcl proc command. ttrace::addscript name body 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 unknown command. Registered callbacks are activated on FIFO basis. The callback definition includes the name of the callback, name and the body of the callback. ttrace::addresolver cmd arglist body Registers Tcl callback to be activated by the overloaded Tcl 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. ttrace::addcleanup body Registers Tcl callback to be activated by the trace::cleanup. Registered callbacks are activated on FIFO basis. ttrace::addentry cmd var val Adds one entry to the named in-memory database. ttrace::getentry cmd var ................................................................................ Returns the value of the entry from the named in-memory database. ttrace::getentries cmd ?pattern? Returns names of all entries from the named in-memory database. ttrace::delentry cmd Deletes an entry from the named in-memory database. ttrace::preload 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 unknown command. 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 proc, namespace, 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 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. 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 thread, tpool, tsv Keywords command tracing, introspection  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 {} { 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 {} { 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 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 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 #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]::Httpdnewsock 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]::Httpdnewsock 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 Microoft. To build the extension invoke the following command: nmake -f makefile.vc TCLDIR= You would need to give the 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 Microoft. To build the extension invoke the following command: nmake -f makefile.vc INSTALLDIR= 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= 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. # -- 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= # 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= # OUT_DIR= # 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)\ by default. # # TESTPAT= # 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 /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 #define NO_SHLWAPI_GDI #define NO_SHLWAPI_STREAM #define NO_SHLWAPI_REG #include #pragma comment (lib, "user32.lib") #pragma comment (lib, "kernel32.lib") #pragma comment (lib, "shlwapi.lib") #include #include /* * 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 \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 \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 #pragma comment (lib, "user32.lib") #pragma comment (lib, "kernel32.lib") #include #include /* * 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 ? ...?\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 \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= * 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 joindir(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 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 

     > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  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 #define RESOURCE_INCLUDED #include 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 #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 #include #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 #include #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 `