├── tests ├── tpool.test ├── tsv.test ├── ttrace.test └── all.tcl ├── tcl ├── phttpd │ ├── index.htm │ └── uhttpd.tcl ├── README ├── cmdsrv │ └── cmdsrv.tcl └── tpool │ └── tpool.tcl ├── .cvsignore ├── win ├── vc │ ├── .cvsignore │ ├── pkg.vc │ ├── thread_win.dsw │ ├── README.txt │ └── thread_win.dsp ├── CONFIG ├── thread.rc ├── threadWin.c └── README.txt ├── generic ├── psGdbm.h ├── threadSvListCmd.h ├── threadSvKeylistCmd.h ├── tclXkeylist.h ├── aolstub.cpp ├── tclThread.h ├── threadSpCmd.h ├── threadSvCmd.h ├── psGdbm.c └── threadSvKeylistCmd.c ├── unix ├── threadUnix.c ├── CONFIG └── README ├── doc ├── format.tcl ├── man.macros ├── ttrace.man ├── tpool.man ├── html │ ├── tpool.html │ └── ttrace.html ├── tsv.man └── man │ └── ttrace.n ├── pkgIndex.tcl.in ├── aolserver.m4 ├── README ├── license.terms ├── aclocal.m4 └── configure.in /tests/tpool.test: -------------------------------------------------------------------------------- 1 | return 2 | -------------------------------------------------------------------------------- /tests/tsv.test: -------------------------------------------------------------------------------- 1 | return 2 | -------------------------------------------------------------------------------- /tests/ttrace.test: -------------------------------------------------------------------------------- 1 | return 2 | -------------------------------------------------------------------------------- /tcl/phttpd/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hallo World

4 | 5 | 6 | -------------------------------------------------------------------------------- /.cvsignore: -------------------------------------------------------------------------------- 1 | config.status 2 | Makefile 3 | pkgIndex.tcl 4 | *.dll 5 | *.so 6 | autom4te.cache 7 | *.dylib 8 | -------------------------------------------------------------------------------- /win/vc/.cvsignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | *.opt 4 | *.ncb 5 | *.plg 6 | *.00? 7 | .#* 8 | nmakehlp.exe 9 | nmakehlp.obj 10 | -------------------------------------------------------------------------------- /win/vc/pkg.vc: -------------------------------------------------------------------------------- 1 | # remember to change configure.in as well when these change 2 | # (then re-autoconf) 3 | 4 | PACKAGE_MAJOR = 2 5 | PACKAGE_MINOR = 6 6 | PACKAGE_VERSION = "2.6.7" 7 | -------------------------------------------------------------------------------- /generic/psGdbm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * psGdbm.h -- 3 | * 4 | * See the file "license.txt" for information on usage and redistribution 5 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 6 | * 7 | * Rcsid: @(#)$Id$ 8 | * --------------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef _PSGDBM_H_ 12 | #define _PSGDBM_H_ 13 | 14 | void Sv_RegisterGdbmStore(); 15 | 16 | #endif /* _PSGDBM_H_ */ 17 | 18 | /* EOF $RCSfile */ 19 | 20 | /* Emacs Setup Variables */ 21 | /* Local Variables: */ 22 | /* mode: C */ 23 | /* indent-tabs-mode: nil */ 24 | /* c-basic-offset: 4 */ 25 | /* End: */ 26 | 27 | -------------------------------------------------------------------------------- /win/vc/thread_win.dsw: -------------------------------------------------------------------------------- 1 | Microsoft Developer Studio Workspace File, Format Version 6.00 2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! 3 | 4 | ############################################################################### 5 | 6 | Project: "thread"=.\thread.dsp - Package Owner=<4> 7 | 8 | Package=<5> 9 | {{{ 10 | }}} 11 | 12 | Package=<4> 13 | {{{ 14 | }}} 15 | 16 | ############################################################################### 17 | 18 | Global: 19 | 20 | Package=<5> 21 | {{{ 22 | }}} 23 | 24 | Package=<3> 25 | {{{ 26 | }}} 27 | 28 | ############################################################################### 29 | 30 | -------------------------------------------------------------------------------- /generic/threadSvListCmd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002 by Zoran Vasiljevic. 3 | * 4 | * See the file "license.txt" for information on usage and redistribution 5 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 6 | * 7 | * Rcsid: @(#)$Id$ 8 | * --------------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef _SV_LIST_H_ 12 | #define _SV_LIST_H_ 13 | 14 | void Sv_RegisterListCommands(); 15 | 16 | #endif /* _SV_LIST_H_ */ 17 | 18 | /* EOF $RCSfile$ */ 19 | 20 | /* Emacs Setup Variables */ 21 | /* Local Variables: */ 22 | /* mode: C */ 23 | /* indent-tabs-mode: nil */ 24 | /* c-basic-offset: 4 */ 25 | /* End: */ 26 | 27 | -------------------------------------------------------------------------------- /win/vc/README.txt: -------------------------------------------------------------------------------- 1 | 2 | Files in this directory may be useful if you have not set up 3 | your TEA (i.e., MinGW) environment and you're using the MSVC++ 4 | from Micro$oft. 5 | 6 | To build the extension invoke the following command: 7 | 8 | nmake -f makefile.vc TCLDIR= 9 | 10 | You would need to give the of the Tcl distribution where 11 | tcl.h and other needed Tcl files are located. 12 | Please look into the makefile.vc file for more information. 13 | 14 | Alternatively, you can open the extension workspace and project files 15 | (thread_win.dsw and thread_win.dsp) from within the MSVC++ and press 16 | the F7 key to build the extension under the control of the MSVC IDE. 17 | 18 | -EOF- 19 | -------------------------------------------------------------------------------- /generic/threadSvKeylistCmd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * threadSvKeylistCmd.h -- 3 | * 4 | * See the file "license.txt" for information on usage and redistribution 5 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 6 | * 7 | * Rcsid: @(#)$Id$ 8 | * --------------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef _KEYLISTCMDS_H_ 12 | #define _KEYLISTCMDS_H_ 13 | 14 | void Sv_RegisterKeylistCommands(void); 15 | void TclX_KeyedListInit(Tcl_Interp *interp); 16 | 17 | #endif /* _KEYLISTCMDS_H_ */ 18 | 19 | /* EOF $RCSfile$ */ 20 | 21 | /* Emacs Setup Variables */ 22 | /* Local Variables: */ 23 | /* mode: C */ 24 | /* indent-tabs-mode: nil */ 25 | /* c-basic-offset: 4 */ 26 | /* End: */ 27 | 28 | -------------------------------------------------------------------------------- /unix/threadUnix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * threadUnix.c -- 3 | * 4 | * Unix specific aspects for the thread extension. 5 | * 6 | * see http://dev.activestate.com/doc/howto/thread_model.html 7 | * 8 | * Some of this code is based on work done by Richard Hipp on behalf of 9 | * Conservation Through Innovation, Limited, with their permission. 10 | * 11 | * Copyright (c) 1998 by Sun Microsystems, Inc. 12 | * Copyright (c) 1999,2000 by Scriptics Corporation. 13 | * 14 | * See the file "license.terms" for information on usage and redistribution 15 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 16 | * 17 | * RCS: @(#) $Id$ 18 | */ 19 | 20 | #include "../generic/tclThread.h" 21 | 22 | /* EOF $RCSfile$ */ 23 | 24 | /* Emacs Setup Variables */ 25 | /* Local Variables: */ 26 | /* mode: C */ 27 | /* indent-tabs-mode: nil */ 28 | /* c-basic-offset: 4 */ 29 | /* End: */ 30 | -------------------------------------------------------------------------------- /win/CONFIG: -------------------------------------------------------------------------------- 1 | # 2 | # This is how I run configure. You'll want to change the 3 | # pathnames to match your system, of course. 4 | # 5 | # Remember that if you use the --enable-sybols, you need to 6 | # use the thread25d.dll in a tclsh that has also been compiled 7 | # with symbols (e.g., tclsh84g.exe or tclsh84d.exe). 8 | # If you want to build both debug and non-debug versions, then 9 | # create "debug" and "release" directories and run configure 10 | # from in those directories with the appropriate flags. 11 | # 12 | # Note the CC=gcc must be set *before* the "configure" is ran. 13 | # This is really needed, otherwise configure will not be able 14 | # to compile the small test file which checks the presence 15 | # of the MinGW build environment. It is *not* enough to use 16 | # "--enable-gcc" configure option; you *need* to define CC. 17 | # 18 | 19 | export CC=gcc 20 | sh ../configure --enable-threads --with-tcl=e:/tcl/win 21 | 22 | -------------------------------------------------------------------------------- /doc/format.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/tclsh 2 | set mydir [file dirname [info script]] 3 | lappend auto_path /usr/local/lib 4 | package req doctools 5 | doctools::new dt 6 | set wd [pwd] 7 | cd $mydir 8 | file rename html htm 9 | set code [catch { 10 | set f [open man.macros] 11 | set m [read $f] 12 | close $f 13 | foreach file [glob -nocomplain *.man] { 14 | set xx [file root $file] 15 | set f [open $xx.man] 16 | set t [read $f] 17 | close $f 18 | foreach {fmt ext dir} {nroff n man html html htm} { 19 | dt configure -format $fmt 20 | set o [dt format $t] 21 | set f [open $dir/$xx.$ext w] 22 | if {$fmt == "nroff"} { 23 | set o [string map [list {.so man.macros} $m] $o] 24 | } 25 | puts $f $o 26 | close $f 27 | } 28 | } 29 | } err] 30 | file rename htm html 31 | cd $wd 32 | if {$code} { 33 | error $err 34 | } 35 | exit 0 36 | -------------------------------------------------------------------------------- /tcl/README: -------------------------------------------------------------------------------- 1 | 2 | Software here is provided as example of making some interesting 3 | things and applications using the Tcl threading extension. 4 | 5 | Currently, following packages are supplied: 6 | 7 | tpool/ Example Tcl-only implementation of thread pools. 8 | The threading extension includes an efficient 9 | threadpool implementation in C. This file is 10 | provided as a fully functional example on how this 11 | functionality could be implemented in Tcl alone. 12 | 13 | phttpd/ MT-enabled httpd server. It uses threadpool to 14 | distribute incoming requests among several worker 15 | threads in the threadpool. This way blocking 16 | requests may be handled much better, w/o halting 17 | the event loop of the main responder thread. 18 | In this directory you will also find the uhttpd. 19 | This is the same web-server but operating in the 20 | event-loop mode alone, no threadpool support. 21 | This is good for comparison purposes. 22 | 23 | cmdsrv/ Socket command-line server. Each new connection 24 | gets new thread, thus allowing multiple outstanding 25 | blocking calls without halting the event loop. 26 | 27 | To play around with above packages, change to the corresponding 28 | directory and source files in the Tcl8.4 (or later) Tcl shell. 29 | Be sure to have the latest Tcl threading extension installed in 30 | your package path. 31 | 32 | - EOF 33 | -------------------------------------------------------------------------------- /win/thread.rc: -------------------------------------------------------------------------------- 1 | // RCS: @(#) $Id$ 2 | // 3 | // Version resource script 4 | // 5 | 6 | #include 7 | 8 | #define RESOURCE_INCLUDED 9 | #include 10 | 11 | LANGUAGE 0x9, 0x1 /* LANG_ENGLISH, SUBLANG_DEFAULT */ 12 | 13 | VS_VERSION_INFO VERSIONINFO 14 | FILEVERSION PACKAGE_MAJOR,PACKAGE_MINOR,0,0 15 | PRODUCTVERSION PACKAGE_MAJOR,PACKAGE_MINOR,0,0 16 | FILEFLAGSMASK 0x3fL 17 | #if DEBUG 18 | FILEFLAGS 0x1L 19 | #else 20 | FILEFLAGS 0x0L 21 | #endif 22 | FILEOS 0x4 /* VOS__WINDOWS32 */ 23 | FILETYPE 0x2 /* VFT_DLL */ 24 | FILESUBTYPE 0x0L 25 | BEGIN 26 | BLOCK "StringFileInfo" 27 | BEGIN 28 | BLOCK "040904b0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */ 29 | BEGIN 30 | VALUE "FileDescription", "Threading extension library for Tcl\0" 31 | #if DEBUG 32 | VALUE "OriginalFilename", "thread" STRINGIFY(JOIN(PACKAGE_MAJOR,PACKAGE_MINOR)) "d.dll\0" 33 | #else 34 | VALUE "OriginalFilename", "thread" STRINGIFY(JOIN(PACKAGE_MAJOR,PACKAGE_MINOR)) ".dll\0" 35 | #endif 36 | VALUE "CompanyName", "NONE! Open-sourced with no owner\0" 37 | VALUE "FileVersion", PACKAGE_VERSION 38 | VALUE "LegalCopyright", "Under BSD license\0" 39 | VALUE "ProductName", "Tcl for Windows\0" 40 | VALUE "ProductVersion", PACKAGE_VERSION 41 | VALUE "Authors", "Brent Welch,\r\n" "Andreas Kupries, \r\n" "David Gravereaux,\r\n" "Zoran Vasiljevic" "\0" 42 | END 43 | END 44 | BLOCK "VarFileInfo" 45 | BEGIN 46 | VALUE "Translation", 0x409, 1200 47 | END 48 | END 49 | -------------------------------------------------------------------------------- /win/threadWin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * threadWin.c -- 3 | * 4 | * Windows specific aspects for the thread extension. 5 | * 6 | * see http://dev.activestate.com/doc/howto/thread_model.html 7 | * 8 | * Some of this code is based on work done by Richard Hipp on behalf of 9 | * Conservation Through Innovation, Limited, with their permission. 10 | * 11 | * Copyright (c) 1998 by Sun Microsystems, Inc. 12 | * Copyright (c) 1999,2000 by Scriptics Corporation. 13 | * 14 | * See the file "license.terms" for information on usage and redistribution 15 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 16 | * 17 | * RCS: @(#) $Id$ 18 | */ 19 | 20 | #include "../generic/tclThread.h" 21 | #include 22 | #include 23 | 24 | #if 0 25 | /* only Windows 2000 (XP, too??) has this function */ 26 | HANDLE (WINAPI *winOpenThreadProc)(DWORD, BOOL, DWORD); 27 | 28 | void 29 | ThreadpInit (void) 30 | { 31 | HMODULE hKernel = GetModuleHandle("kernel32.dll"); 32 | winOpenThreadProc = (HANDLE (WINAPI *)(DWORD, BOOL, DWORD)) 33 | GetProcAddress(hKernel, "OpenThread"); 34 | } 35 | 36 | int 37 | ThreadpKill (Tcl_Interp *interp, long id) 38 | { 39 | HANDLE hThread; 40 | int result = TCL_OK; 41 | 42 | if (winOpenThreadProc) { 43 | hThread = winOpenThreadProc(THREAD_TERMINATE, FALSE, id); 44 | /* 45 | * not to be misunderstood as "devilishly clever", 46 | * but evil in it's pure form. 47 | */ 48 | TerminateThread(hThread, 666); 49 | } else { 50 | Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 51 | "Can't (yet) kill threads on this OS, sorry.", NULL); 52 | result = TCL_ERROR; 53 | } 54 | return result; 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /pkgIndex.tcl.in: -------------------------------------------------------------------------------- 1 | # -*- tcl -*- 2 | # Tcl package index file, version 1.1 3 | # 4 | if {[package vsatisfies [package provide Tcl] 8.4]} { 5 | 6 | package ifneeded Thread @PACKAGE_VERSION@ [list load [file join $dir @PKG_LIB_FILE@]] 7 | 8 | if {[llength [info commands apply]]} { 9 | # We can use a lambda (anon function). 10 | 11 | package ifneeded Ttrace @PACKAGE_VERSION@ [list ::apply {{dir} { 12 | if {[info exists ::env(TCL_THREAD_LIBRARY)] && 13 | [file readable $::env(TCL_THREAD_LIBRARY)/ttrace.tcl]} { 14 | source $::env(TCL_THREAD_LIBRARY)/ttrace.tcl 15 | } elseif {[file readable [file join $dir .. lib ttrace.tcl]]} { 16 | source [file join $dir .. lib ttrace.tcl] 17 | } elseif {[file readable [file join $dir ttrace.tcl]]} { 18 | source [file join $dir ttrace.tcl] 19 | } 20 | if {[llength [info commands ttrace::update]]} { 21 | ttrace::update 22 | } 23 | }} $dir] 24 | } else { 25 | # No anon functions available, go with the necessary evil of a 26 | # named procedure, but use package specific prefix and no 27 | # hardwired data changing between package versions. 28 | 29 | package ifneeded Ttrace @PACKAGE_VERSION@ [list @PACKAGE_NAME@_source $dir] 30 | 31 | proc @PACKAGE_NAME@_source {dir} { 32 | if {[info exists ::env(TCL_THREAD_LIBRARY)] && 33 | [file readable $::env(TCL_THREAD_LIBRARY)/ttrace.tcl]} { 34 | source $::env(TCL_THREAD_LIBRARY)/ttrace.tcl 35 | } elseif {[file readable [file join $dir .. lib ttrace.tcl]]} { 36 | source [file join $dir .. lib ttrace.tcl] 37 | } elseif {[file readable [file join $dir ttrace.tcl]]} { 38 | source [file join $dir ttrace.tcl] 39 | } 40 | if {[llength [info commands ttrace::update]]} { 41 | ttrace::update 42 | } 43 | rename @PACKAGE_NAME@_source {} 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /aolserver.m4: -------------------------------------------------------------------------------- 1 | 2 | #------------------------------------------------------------------------ 3 | # NS_PATH_AOLSERVER 4 | # 5 | # Allows the building with support for AOLserver 6 | # 7 | # Arguments: 8 | # none 9 | # 10 | # Results: 11 | # 12 | # Adds the following arguments to configure: 13 | # --with-aolserver=... 14 | # 15 | # Defines the following vars: 16 | # AOL_DIR Full path to the directory containing AOLserver distro 17 | # AOL_INCLUDES 18 | # AOL_LIBS 19 | # 20 | # Sets the following vars: 21 | # NS_AOLSERVER 22 | # 23 | # Updates following vars: 24 | #------------------------------------------------------------------------ 25 | 26 | AC_DEFUN(NS_PATH_AOLSERVER, [ 27 | AC_MSG_CHECKING([for AOLserver configuration]) 28 | AC_ARG_WITH(aol, 29 | [ --with-aolserver directory with AOLserver distribution],\ 30 | with_aolserver=${withval}) 31 | 32 | AC_CACHE_VAL(ac_cv_c_aolserver,[ 33 | if test x"${with_aolserver}" != x ; then 34 | if test -f "${with_aolserver}/include/ns.h" ; then 35 | ac_cv_c_aolserver=`(cd ${with_aolserver}; pwd)` 36 | else 37 | AC_MSG_ERROR([${with_aolserver} directory doesn't contain ns.h]) 38 | fi 39 | fi 40 | ]) 41 | if test x"${ac_cv_c_aolserver}" = x ; then 42 | AC_MSG_RESULT([none found]) 43 | else 44 | AOL_DIR=${ac_cv_c_aolserver} 45 | AC_MSG_RESULT([found AOLserver in $AOL_DIR]) 46 | AOL_INCLUDES="-I\"${AOL_DIR}/include\"" 47 | if test "`uname -s`" = Darwin ; then 48 | aollibs=`ls ${AOL_DIR}/lib/libns* 2>/dev/null` 49 | if test x"$aollibs" != x ; then 50 | AOL_LIBS="-L\"${AOL_DIR}/lib\" -lnsd -lnsthread" 51 | fi 52 | fi 53 | AC_DEFINE(NS_AOLSERVER) 54 | fi 55 | ]) 56 | 57 | # EOF 58 | -------------------------------------------------------------------------------- /generic/tclXkeylist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * tclXkeylist.h -- 3 | * 4 | * Extended Tcl keyed list commands and interfaces. 5 | *----------------------------------------------------------------------------- 6 | * Copyright 1991-1999 Karl Lehenbauer and Mark Diekhans. 7 | * 8 | * Permission to use, copy, modify, and distribute this software and its 9 | * documentation for any purpose and without fee is hereby granted, provided 10 | * that the above copyright notice appear in all copies. Karl Lehenbauer and 11 | * Mark Diekhans make no representations about the suitability of this 12 | * software for any purpose. It is provided "as is" without express or 13 | * implied warranty. 14 | *----------------------------------------------------------------------------- 15 | * 16 | * Rcsid: @(#)$Id$ 17 | *----------------------------------------------------------------------------- 18 | */ 19 | 20 | #ifndef _KEYLIST_H_ 21 | #define _KEYLIST_H_ 22 | 23 | /* 24 | * Keyed list object interface commands 25 | */ 26 | 27 | Tcl_Obj* TclX_NewKeyedListObj(); 28 | 29 | void TclX_KeyedListInit(Tcl_Interp*); 30 | int TclX_KeyedListGet(Tcl_Interp*, Tcl_Obj*, const char*, Tcl_Obj**); 31 | int TclX_KeyedListSet(Tcl_Interp*, Tcl_Obj*, const char*, Tcl_Obj*); 32 | int TclX_KeyedListDelete(Tcl_Interp*, Tcl_Obj*, const char*); 33 | int TclX_KeyedListGetKeys(Tcl_Interp*, Tcl_Obj*, const char*, Tcl_Obj**); 34 | 35 | /* 36 | * Exported for usage in Sv_DuplicateObj. This is slightly 37 | * modified version of the DupKeyedListInternalRep() function. 38 | * It does a proper deep-copy of the keyed list object. 39 | */ 40 | 41 | void DupKeyedListInternalRepShared(Tcl_Obj*, Tcl_Obj*); 42 | 43 | #endif /* _KEYLIST_H_ */ 44 | 45 | /* EOF $RCSfile$ */ 46 | 47 | /* Emacs Setup Variables */ 48 | /* Local Variables: */ 49 | /* mode: C */ 50 | /* indent-tabs-mode: nil */ 51 | /* c-basic-offset: 4 */ 52 | /* End: */ 53 | 54 | -------------------------------------------------------------------------------- /unix/CONFIG: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This file contains collection of configure directives 4 | # for building the Threading extension. 5 | # 6 | # Comment-out next line if building with GCC compiler. 7 | # 8 | # CC=gcc; export CC 9 | # 10 | # 11 | # Tcl on Unix (uses public Tcl library) 12 | # ---------------------------------------------------- 13 | # ../configure --enable-threads 14 | # 15 | # As of 2.6, the threading extension supports persistent 16 | # shared variables. As an working example of this, there 17 | # is a simple wrapper for the popular Gdbm library. 18 | # Uncomment the following line if you like to compile the 19 | # Gdbm wrapper for persistent shared variables. 20 | # 21 | # ../configure --enable-threads --with-gdbm 22 | # 23 | # If your Gdbm library is not installed in one of the 24 | # default system locations (/usr/lib, /usr/local/lib ...) 25 | # please use following directive. Note that both library 26 | # file *and* includes should be located in "/my/gdbm". 27 | # Of course, you have to replace the "/my/gdbm" below 28 | # with the exact location, as found in your system: 29 | # 30 | # ../configure --enable-threads --with-gdbm=/my/gdbm 31 | # 32 | # 33 | # AOLserver 4.X; Uses public Tcl library. 34 | # ---------------------------------------------------- 35 | # aoldir="/usr/local/aolserver" 36 | # ../configure --enable-threads \ 37 | # --with-aolserver=$aoldir \ 38 | # --prefix=$aoldir --exec-prefix=$aoldir 39 | # 40 | # AOLserver uses its own package loading mechanism. 41 | # To load, just do "ns_eval package require Thread" 42 | # at the AOLserver startup or later from any thread. 43 | # 44 | # 45 | # Mac OS X; Uses public Tcl library. 46 | # ---------------------------------------------------- 47 | # ../configure --enable-threads \ 48 | # --mandir=/usr/local/share/man \ 49 | # --libdir=/Library/Tcl \ 50 | # --with-tcl=/Library/Frameworks/Tcl.framework \ 51 | # --with-tclinclude=/Library/Frameworks/Tcl.framework/Headers 52 | # 53 | # EOF 54 | -------------------------------------------------------------------------------- /win/README.txt: -------------------------------------------------------------------------------- 1 | 2 | I. Building the Tcl thread extension for Windows 3 | ================================================ 4 | 5 | Thread extension supports two build options: 6 | 7 | 8 | o. MinGW builds: 9 | ---------------- 10 | 11 | The extension can be compiled under Windows using the 12 | MinGW (http://www.mingw.org) environment. You can also 13 | download the ready-to-go copy of the MinGW from the 14 | same place you've downloaded this extension. 15 | 16 | You should compile the Tcl core with MinGW first. After 17 | that, you can compile the extension by running the 18 | configure/make from this directory. You can also use the 19 | CONFIG script to do this. You might want to edit the 20 | script to match your environment and then just do: 21 | 22 | sh CONFIG 23 | 24 | This should go smoothly, once you got Tcl core compiled ok. 25 | 26 | 27 | o. Microsoft MSVC++ build: 28 | -------------------------- 29 | 30 | You should use the makefile.vc file for the MSVC++ located 31 | in the vc/ directory. Please consult the README.vc.txt and 32 | makefile.vc files for more details. 33 | Alternatively, you can use the MSVC++ IDE and open the 34 | thread_win.dsw workspace file. 35 | 36 | 37 | II. Building optional support libraries 38 | ======================================= 39 | 40 | As of 2.6 release, this extension supports persistent shared 41 | variables. To use this functionality, you might need to download 42 | and compile some other supporting libraries. Currently, there is 43 | a simple implementation of shared variable persistency built atop 44 | of popular GNU Gdbm package. You can obtain the latest version of 45 | the Gdbm from: http://www.gnu.org/software/gdbm/gdbm.html. 46 | 47 | For the impatient, there are Windows ports of GNU Gdbm found on 48 | various places on the Internet. The easiest way to start is to go 49 | to the GnuWin32 project: http://sourceforge.net/projects/gnuwin32 50 | and fetch yourself a compiled GNU Gdbm DLL. 51 | 52 | -EOF- 53 | -------------------------------------------------------------------------------- /tests/all.tcl: -------------------------------------------------------------------------------- 1 | # all.tcl -- 2 | # 3 | # This file contains a top-level script to run all of the Tcl 4 | # tests. Execute it by invoking "source all.test" when running tcltest 5 | # in this directory. 6 | # 7 | # Copyright (c) 1998-1999 by Scriptics Corporation. 8 | # All rights reserved. 9 | # 10 | # RCS: @(#) $Id$ 11 | 12 | package require tcltest 13 | namespace import -force ::tcltest::* 14 | 15 | set ::tcltest::testSingleFile false 16 | set ::tcltest::testsDirectory [file dir [info script]] 17 | 18 | # We need to ensure that the testsDirectory is absolute 19 | ::tcltest::normalizePath ::tcltest::testsDirectory 20 | 21 | puts stdout "Tcl $tcl_patchLevel tests running in interp: [info nameofexecutable]" 22 | puts stdout "Tests running in working dir: $::tcltest::testsDirectory" 23 | if {[llength $::tcltest::skip] > 0} { 24 | puts stdout "Skipping tests that match: $::tcltest::skip" 25 | } 26 | if {[llength $::tcltest::match] > 0} { 27 | puts stdout "Only running tests that match: $::tcltest::match" 28 | } 29 | 30 | if {[llength $::tcltest::skipFiles] > 0} { 31 | puts stdout "Skipping test files that match: $::tcltest::skipFiles" 32 | } 33 | if {[llength $::tcltest::matchFiles] > 0} { 34 | puts stdout "Only sourcing test files that match: $::tcltest::matchFiles" 35 | } 36 | 37 | set timeCmd {clock format [clock seconds]} 38 | puts stdout "Tests began at [eval $timeCmd]" 39 | 40 | 41 | # These tests need to know which is the main thread 42 | 43 | # Require the accurate version for the tests so we don't pick up 44 | # older Thread packages by accident 45 | package require Tcl 8.4 46 | package require Thread 2.6 47 | set ::tcltest::mainThread [thread::id] 48 | 49 | puts stdout "Thread [package provide Thread]" 50 | puts stdout "Mainthread id is $::tcltest::mainThread" 51 | 52 | # Source each of the specified tests 53 | foreach file [lsort [::tcltest::getMatchingFiles]] { 54 | set tail [file tail $file] 55 | puts stdout $tail 56 | if {[catch {source $file} msg]} { 57 | puts stdout $msg 58 | } 59 | } 60 | 61 | # Cleanup 62 | puts stdout "\nTests ended at [eval $timeCmd]" 63 | ::tcltest::cleanupTests 1 64 | 65 | return 66 | 67 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | WHAT IS THIS ? 3 | ============== 4 | 5 | This is the source distribution of the Tcl Thread extension. 6 | You can use this extension to gain script-level access to Tcl 7 | threading capabilities. 8 | The extension can be used with Tcl cores starting from Tcl8.4 and later. 9 | Also, this extension supports, i.e. can be used as a loadable module of, 10 | AOLserver 4.x series of the highly-scalable web server from America Online. 11 | 12 | You need to have your Tcl core compiled with "--enable-threads" in order 13 | to turn on internal directives supporting thread-specific details of the 14 | Tcl API. The extension will not load in an Tcl shell built w/o thread 15 | support. 16 | 17 | This extension is a freely available open source package. You can do 18 | virtually anything you like with it, such as modifying it, redistributing 19 | it, and selling it either in whole or in part. See the "license.terms" 20 | file in the top-level distribution directory for complete information. 21 | 22 | 23 | HOW TO COMPILE ? 24 | ================ 25 | 26 | Only Unix-like and Windows platforms are supported at the moment. Depending 27 | on your platform (Unix-like or Windows) go to the appropriate directory 28 | (unix or win) and start with the README file. Macintosh platform is supported 29 | with the Mac OS X only. The Mac OS 9 (and previous) are not supported. 30 | 31 | 32 | WHERE IS THE DOCUMENTATION ? 33 | ============================ 34 | 35 | Documentation in Unix man and standard HTML format is available in the 36 | doc/man and doc/html directories respectively. 37 | Currently, documentation is in reference-style only. The tutorial-style 38 | documentation will be provided with future releases of the extension. 39 | That is, if I ever get time to do that. Everybody is more than welcome 40 | to jump in and help with the docs. 41 | 42 | 43 | HOW TO GET SUPPORT ? 44 | ==================== 45 | 46 | The extension is maintained, enhanced, and distributed freely by the Tcl 47 | community. The home for sources and bug/patch database is on SourceForge: 48 | 49 | http://tcl.sourceforge.net/ 50 | 51 | Alternatively, you are always welcome to post your questions, problems 52 | and/or suggestions relating the extension (or any other Tcl issue) 53 | to news:comp.lang.tcl newsgroup. 54 | 55 | -EOF- 56 | -------------------------------------------------------------------------------- /license.terms: -------------------------------------------------------------------------------- 1 | This software is copyrighted by the Regents of the University of 2 | California, Sun Microsystems, Inc., Scriptics Corporation, 3 | and other parties. The following terms apply to all files associated 4 | with the software unless explicitly disclaimed in individual files. 5 | 6 | The authors hereby grant permission to use, copy, modify, distribute, 7 | and license this software and its documentation for any purpose, provided 8 | that existing copyright notices are retained in all copies and that this 9 | notice is included verbatim in any distributions. No written agreement, 10 | license, or royalty fee is required for any of the authorized uses. 11 | Modifications to this software may be copyrighted by their authors 12 | and need not follow the licensing terms described here, provided that 13 | the new terms are clearly indicated on the first page of each file where 14 | they apply. 15 | 16 | IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 17 | FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 18 | ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 19 | DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 20 | POSSIBILITY OF SUCH DAMAGE. 21 | 22 | THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 23 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 25 | IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 26 | NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 27 | MODIFICATIONS. 28 | 29 | GOVERNMENT USE: If you are acquiring this software on behalf of the 30 | U.S. government, the Government shall have only "Restricted Rights" 31 | in the software and related documentation as defined in the Federal 32 | Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you 33 | are acquiring the software on behalf of the Department of Defense, the 34 | software shall be classified as "Commercial Computer Software" and the 35 | Government shall have only "Restricted Rights" as defined in Clause 36 | 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the 37 | authors grant the U.S. Government and others acting in its behalf 38 | permission to use and distribute the software in accordance with the 39 | terms specified in this license. 40 | -------------------------------------------------------------------------------- /aclocal.m4: -------------------------------------------------------------------------------- 1 | # 2 | # Pull in the standard Tcl autoconf macros. 3 | # If you don't have the "tclconfig" subdirectory, it is a dependent CVS 4 | # module. Either "cvs -d checkout tclconfig" right here, or 5 | # re-checkout the thread module 6 | # 7 | builtin(include,tclconfig/tcl.m4) 8 | builtin(include,aolserver.m4) 9 | 10 | # 11 | # Handle the "--with-gdbm" option for linking-in 12 | # the gdbm-based peristent store for shared arrays. 13 | # It tries to locate gdbm files in couple of standard 14 | # system directories and/or common install locations 15 | # in addition to the directory passed by the user. 16 | # In the latter case, expect all gdbm lib files and 17 | # include files located in the same directory. 18 | # 19 | 20 | AC_DEFUN(TCLTHREAD_WITH_GDBM, [ 21 | AC_ARG_WITH(gdbm, 22 | [ --with-gdbm link with optional GDBM support],\ 23 | with_gdbm=${withval}) 24 | 25 | if test x"${with_gdbm}" != x; then 26 | 27 | AC_MSG_CHECKING([for GNU gdbm library]) 28 | 29 | AC_CACHE_VAL(ac_cv_c_gdbm,[ 30 | if test x"${with_gdbm}" != x -a "${with_gdbm}" != "yes"; then 31 | if test -f "${with_gdbm}/gdbm.h" -a x"`ls ${with_gdbm}/libgdbm* 2>/dev/null`" != x; then 32 | ac_cv_c_gdbm=`(cd ${with_gdbm}; pwd)` 33 | gincdir=$ac_cv_c_gdbm 34 | glibdir=$ac_cv_c_gdbm 35 | AC_MSG_RESULT([found in $glibdir]) 36 | else 37 | AC_MSG_ERROR([${with_gdbm} directory doesn't contain gdbm library]) 38 | fi 39 | fi 40 | ]) 41 | if test x"${gincdir}" = x -o x"${glibdir}" = x; then 42 | for i in \ 43 | `ls -d ${exec_prefix}/lib 2>/dev/null`\ 44 | `ls -d ${prefix}/lib 2>/dev/null`\ 45 | `ls -d /usr/local/lib 2>/dev/null`\ 46 | `ls -d /usr/lib 2>/dev/null` ; do 47 | if test x"`ls $i/libgdbm* 2>/dev/null`" != x ; then 48 | glibdir=`(cd $i; pwd)` 49 | break 50 | fi 51 | done 52 | for i in \ 53 | `ls -d ${prefix}/include 2>/dev/null`\ 54 | `ls -d /usr/local/include 2>/dev/null`\ 55 | `ls -d /usr/include 2>/dev/null` ; do 56 | if test -f "$i/gdbm.h" ; then 57 | gincdir=`(cd $i; pwd)` 58 | break 59 | fi 60 | done 61 | if test x"$glibdir" = x -o x"$gincdir" = x ; then 62 | AC_MSG_ERROR([none found]) 63 | else 64 | AC_MSG_RESULT([found in $glibdir, includes in $gincdir]) 65 | AC_DEFINE(HAVE_GDBM) 66 | GDBM_CFLAGS="-I\"$gincdir\"" 67 | GDBM_LIBS="-L\"$glibdir\" -lgdbm" 68 | fi 69 | fi 70 | fi 71 | ]) 72 | 73 | # EOF 74 | -------------------------------------------------------------------------------- /generic/aolstub.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * aolstub.cpp -- 3 | * 4 | * Adds interface for loading the extension into the AOLserver. 5 | * 6 | * Copyright (c) 2002 by Zoran Vasiljevic. 7 | * 8 | * See the file "license.terms" for information on usage and redistribution 9 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 10 | * 11 | * Rcsid: @(#)$Id$ 12 | * --------------------------------------------------------------------------- 13 | */ 14 | 15 | #ifdef NS_AOLSERVER 16 | #include 17 | 18 | int Ns_ModuleVersion = 1; 19 | 20 | /* 21 | * Structure to pass to NsThread_Init. This holds the module 22 | * and virtual server name for proper interp initializations. 23 | */ 24 | 25 | struct mydata { 26 | char *modname; 27 | char *server; 28 | }; 29 | 30 | /* 31 | *---------------------------------------------------------------------------- 32 | * 33 | * NsThread_Init -- 34 | * 35 | * Loads the package for the first time, i.e. in the startup thread. 36 | * 37 | * Results: 38 | * Standard Tcl result 39 | * 40 | * Side effects: 41 | * Package initialized. Tcl commands created. 42 | * 43 | *---------------------------------------------------------------------------- 44 | */ 45 | 46 | static int 47 | NsThread_Init (Tcl_Interp *interp, void *cd) 48 | { 49 | struct mydata *md = (struct mydata*)cd; 50 | int ret = Thread_Init(interp); 51 | 52 | if (ret != TCL_OK) { 53 | Ns_Log(Warning, "can't load module %s: %s", md->modname, 54 | Tcl_GetStringResult(interp)); 55 | return TCL_ERROR; 56 | } 57 | Tcl_SetAssocData(interp, "thread:nsd", NULL, (ClientData)md); 58 | 59 | return TCL_OK; 60 | } 61 | 62 | /* 63 | *---------------------------------------------------------------------------- 64 | * 65 | * Ns_ModuleInit -- 66 | * 67 | * Called by the AOLserver when loading shared object file. 68 | * 69 | * Results: 70 | * Standard AOLserver result 71 | * 72 | * Side effects: 73 | * Many. Depends on the package. 74 | * 75 | *---------------------------------------------------------------------------- 76 | */ 77 | 78 | int 79 | Ns_ModuleInit(char *srv, char *mod) 80 | { 81 | struct mydata *md = NULL; 82 | 83 | md = (struct mydata*)ns_malloc(sizeof(struct mydata)); 84 | md->modname = strcpy(ns_malloc(strlen(mod)+1), mod); 85 | md->server = strcpy(ns_malloc(strlen(srv)+1), srv); 86 | 87 | return (Ns_TclInitInterps(srv, NsThread_Init, (void*)md) == TCL_OK) 88 | ? NS_OK : NS_ERROR; 89 | } 90 | 91 | #endif /* NS_AOLSERVER */ 92 | 93 | /* EOF $RCSfile$ */ 94 | 95 | /* Emacs Setup Variables */ 96 | /* Local Variables: */ 97 | /* mode: C */ 98 | /* indent-tabs-mode: nil */ 99 | /* c-basic-offset: 4 */ 100 | /* End: */ 101 | -------------------------------------------------------------------------------- /unix/README: -------------------------------------------------------------------------------- 1 | 2 | I. Building the Tcl thread extension for Unix 3 | ============================================= 4 | 5 | Extension can be compiled on several Unix derivates including various 6 | distributions of Linux. Build process is pretty straightforward. I've 7 | checked some versions of Solaris, Linux and Darwin, but the extension 8 | should compile without problems on any Unix-like operating system 9 | with a proper pthreads library implementation. 10 | 11 | To build on Unix-like operating systems, start with the CONFIG script 12 | and see if there is already a combination of the "configure" options 13 | which may satisfy your needs. If not, you can run the configure script 14 | located in the root of the distribution directory with a choice of 15 | supported options yourself. If yes, you can uncomment corresponding 16 | lines from the CONFIG script and do: 17 | 18 | % sh CONFIG 19 | 20 | Either way, this will create a Makefile which you use to run "make" and 21 | "make install". 22 | You can use "make clean" to clean the directory from temporary compilation 23 | files and/or "make distclean" to additionaly remove local config files. 24 | You might want to do "make test" before doing the "make install" in order 25 | to run the regression tests on the package. 26 | 27 | To explore other building options, look into the CONFIG file for more 28 | information. 29 | 30 | 31 | Note for AOLserver users 32 | ------------------------ 33 | 34 | The extension can be compiled as a loadable module for the AOLserver 35 | version 4.0 or higher. In order to do this, use "--with-aolserver" 36 | configure option to specify the directory containing the AOLserver 37 | distribution. The CONFIG script has an example how to invoke configure 38 | in order to build the extension as AOLserver module. 39 | Note, however, that "make install" and "make test" targets are still 40 | not supported for AOLserver builds. This will be corrected in one of 41 | the future releases. 42 | 43 | To fine-tune, you might also want to make the tsv::* commands replace 44 | the AOLserver built-in nsv_* family of commands, since they are API 45 | compatible and provide richer command set plus advanced shared-object 46 | storage of shared data. Go to the generic/threadSvCmd.h file and look 47 | at the beginning of the file for the: 48 | 49 | /* #define NSV_COMPAT 1 */ 50 | 51 | So, uncomment the line, recompile and there you go. 52 | 53 | 54 | II. Building optional support libraries 55 | ======================================= 56 | 57 | As of 2.6 release, this extension supports persistent shared variables. 58 | To use this functionality, you might need to download and compile some 59 | other supporting libraries. Currently, there is a simple implementation 60 | of shared variable persistency built atop of popular GNU Gdbm package. 61 | You can obtain the latest version of the Gdbm package from the GNU 62 | website at: http://www.gnu.org/software/gdbm/gdbm.html 63 | To compile with GNU Gdbm support you must configure with --with-gdbm 64 | switch. This option, if used, will try to locate the Gdbm library on 65 | your system at couple of standard locations. You might override this 66 | behaviour by giving --with-gdbm=/some/dir. Note that both library file 67 | and the include file must then reside in this directory. 68 | 69 | -EOF- 70 | -------------------------------------------------------------------------------- /generic/tclThread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * -------------------------------------------------------------------------- 3 | * tclthread.h -- 4 | * 5 | * Global header file for the thread extension. 6 | * 7 | * Copyright (c) 2002 ActiveState Corporation. 8 | * Copyright (c) 2002 by Zoran Vasiljevic. 9 | * 10 | * See the file "license.terms" for information on usage and redistribution 11 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 | * 13 | * RCS: @(#) $Id$ 14 | * --------------------------------------------------------------------------- 15 | */ 16 | 17 | /* 18 | * Thread extension version numbers are not stored here 19 | * because this isn't a public export file. 20 | */ 21 | 22 | #ifndef _TCL_THREAD_H_ 23 | #define _TCL_THREAD_H_ 24 | 25 | #include 26 | #include /* For strtoul */ 27 | #include /* For memset and friends */ 28 | 29 | #undef TCL_STORAGE_CLASS 30 | #define TCL_STORAGE_CLASS DLLEXPORT 31 | 32 | /* 33 | * For linking against AOLserver require V4 at least 34 | */ 35 | 36 | #ifdef NS_AOLSERVER 37 | # include 38 | # if !defined(NS_MAJOR_VERSION) || NS_MAJOR_VERSION < 4 39 | # error "unsupported AOLserver version" 40 | # endif 41 | #endif 42 | 43 | /* 44 | * Allow for some command names customization. 45 | * Only thread:: and tpool:: are handled here. 46 | * Shared variable commands are more complicated. 47 | * Look into the threadSvCmd.h for more info. 48 | */ 49 | 50 | #define THREAD_CMD_PREFIX "thread::" 51 | #define TPOOL_CMD_PREFIX "tpool::" 52 | 53 | /* 54 | * Exported from threadCmd.c file. 55 | */ 56 | 57 | EXTERN int Thread_Init _ANSI_ARGS_((Tcl_Interp *interp)); 58 | EXTERN int Thread_SafeInit _ANSI_ARGS_((Tcl_Interp *interp)); 59 | EXTERN int Thread_Unload _ANSI_ARGS_((Tcl_Interp *interp)); 60 | EXTERN int Thread_SafeUnload _ANSI_ARGS_((Tcl_Interp *interp)); 61 | 62 | /* 63 | * Exported from threadSvCmd.c file. 64 | */ 65 | 66 | EXTERN int Sv_Init _ANSI_ARGS_((Tcl_Interp *interp)); 67 | 68 | /* 69 | * Exported from threadSpCmd.c file. 70 | */ 71 | 72 | EXTERN int Sp_Init _ANSI_ARGS_((Tcl_Interp *interp)); 73 | 74 | /* 75 | * Exported from threadPoolCmd.c file. 76 | */ 77 | 78 | EXTERN int Tpool_Init _ANSI_ARGS_((Tcl_Interp *interp)); 79 | 80 | /* 81 | * Macros for splicing in/out of linked lists 82 | */ 83 | 84 | #define SpliceIn(a,b) \ 85 | (a)->nextPtr = (b); \ 86 | if ((b) != NULL) \ 87 | (b)->prevPtr = (a); \ 88 | (a)->prevPtr = NULL, (b) = (a) 89 | 90 | #define SpliceOut(a,b) \ 91 | if ((a)->prevPtr != NULL) \ 92 | (a)->prevPtr->nextPtr = (a)->nextPtr; \ 93 | else \ 94 | (b) = (a)->nextPtr; \ 95 | if ((a)->nextPtr != NULL) \ 96 | (a)->nextPtr->prevPtr = (a)->prevPtr 97 | 98 | /* 99 | * Utility macros 100 | */ 101 | 102 | #define TCL_CMD(a,b,c) \ 103 | if (Tcl_CreateObjCommand((a),(b),(c),(ClientData)NULL, NULL) == NULL) \ 104 | return TCL_ERROR 105 | 106 | #define OPT_CMP(a,b) \ 107 | ((a) && (b) && (*(a)==*(b)) && (*(a+1)==*(b+1)) && (!strcmp((a),(b)))) 108 | 109 | #ifndef TCL_TSD_INIT 110 | #define TCL_TSD_INIT(keyPtr) \ 111 | (ThreadSpecificData*)Tcl_GetThreadData((keyPtr),sizeof(ThreadSpecificData)) 112 | #endif 113 | 114 | #undef TCL_STORAGE_CLASS 115 | #define TCL_STORAGE_CLASS DLLIMPORT 116 | 117 | #endif /* _TCL_THREAD_H_ */ 118 | -------------------------------------------------------------------------------- /generic/threadSpCmd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the header file for the module that implements some missing 3 | * synchronization priomitives from the Tcl API. 4 | * 5 | * Copyright (c) 2002 by Zoran Vasiljevic. 6 | * 7 | * See the file "license.txt" for information on usage and redistribution 8 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 9 | * 10 | * Rcsid: @(#)$Id$ 11 | * --------------------------------------------------------------------------- 12 | */ 13 | 14 | #ifndef _SP_H_ 15 | #define _SP_H_ 16 | 17 | #include 18 | 19 | /* 20 | * The following structure defines a locking bucket. A locking 21 | * bucket is associated with a mutex and protects access to 22 | * objects stored in bucket hash table. 23 | */ 24 | 25 | typedef struct SpBucket { 26 | Tcl_Mutex lock; /* For locking the bucket */ 27 | Tcl_Condition cond; /* For waiting on threads to release items */ 28 | Tcl_ThreadId lockt; /* Thread holding the lock */ 29 | Tcl_HashTable handles; /* Hash table of given-out handles in bucket */ 30 | struct Container *freeCt; /* List of free Tcl-object containers */ 31 | } SpBucket; 32 | 33 | #define NUMSPBUCKETS 32 34 | 35 | /* 36 | * All types of mutexes share this common part. 37 | */ 38 | 39 | typedef struct Sp_AnyMutex_ { 40 | int lockcount; /* If !=0 mutex is locked */ 41 | int numlocks; /* Number of times the mutex got locked */ 42 | Tcl_Mutex lock; /* Regular mutex */ 43 | Tcl_ThreadId owner; /* Current lock owner thread (-1 = any) */ 44 | } Sp_AnyMutex; 45 | 46 | /* 47 | * Implementation of the exclusive mutex. 48 | */ 49 | 50 | typedef struct Sp_ExclusiveMutex_ { 51 | int lockcount; /* Flag: 1-locked, 0-not locked */ 52 | int numlocks; /* Number of times the mutex got locked */ 53 | Tcl_Mutex lock; /* Regular mutex */ 54 | Tcl_ThreadId owner; /* Current lock owner thread */ 55 | /* --- */ 56 | Tcl_Mutex mutex; /* Mutex being locked */ 57 | } Sp_ExclusiveMutex_; 58 | 59 | typedef Sp_ExclusiveMutex_* Sp_ExclusiveMutex; 60 | 61 | /* 62 | * Implementation of the recursive mutex. 63 | */ 64 | 65 | typedef struct Sp_RecursiveMutex_ { 66 | int lockcount; /* # of times this mutex is locked */ 67 | int numlocks; /* Number of time the mutex got locked */ 68 | Tcl_Mutex lock; /* Regular mutex */ 69 | Tcl_ThreadId owner; /* Current lock owner thread */ 70 | /* --- */ 71 | Tcl_Condition cond; /* Wait to be allowed to lock the mutex */ 72 | } Sp_RecursiveMutex_; 73 | 74 | typedef Sp_RecursiveMutex_* Sp_RecursiveMutex; 75 | 76 | /* 77 | * Implementation of the read/writer mutex. 78 | */ 79 | 80 | typedef struct Sp_ReadWriteMutex_ { 81 | int lockcount; /* >0: # of readers, -1: sole writer */ 82 | int numlocks; /* Number of time the mutex got locked */ 83 | Tcl_Mutex lock; /* Regular mutex */ 84 | Tcl_ThreadId owner; /* Current lock owner thread */ 85 | /* --- */ 86 | unsigned int numrd; /* # of readers waiting for lock */ 87 | unsigned int numwr; /* # of writers waiting for lock */ 88 | Tcl_Condition rcond; /* Reader lockers wait here */ 89 | Tcl_Condition wcond; /* Writer lockers wait here */ 90 | } Sp_ReadWriteMutex_; 91 | 92 | typedef Sp_ReadWriteMutex_* Sp_ReadWriteMutex; 93 | 94 | 95 | /* 96 | * API for exclusive mutexes. 97 | */ 98 | 99 | int Sp_ExclusiveMutexLock(Sp_ExclusiveMutex *mutexPtr); 100 | int Sp_ExclusiveMutexIsLocked(Sp_ExclusiveMutex *mutexPtr); 101 | int Sp_ExclusiveMutexUnlock(Sp_ExclusiveMutex *mutexPtr); 102 | void Sp_ExclusiveMutexFinalize(Sp_ExclusiveMutex *mutexPtr); 103 | 104 | /* 105 | * API for recursive mutexes. 106 | */ 107 | 108 | int Sp_RecursiveMutexLock(Sp_RecursiveMutex *mutexPtr); 109 | int Sp_RecursiveMutexIsLocked(Sp_RecursiveMutex *mutexPtr); 110 | int Sp_RecursiveMutexUnlock(Sp_RecursiveMutex *mutexPtr); 111 | void Sp_RecursiveMutexFinalize(Sp_RecursiveMutex *mutexPtr); 112 | 113 | /* 114 | * API for reader/writer mutexes. 115 | */ 116 | 117 | int Sp_ReadWriteMutexRLock(Sp_ReadWriteMutex *mutexPtr); 118 | int Sp_ReadWriteMutexWLock(Sp_ReadWriteMutex *mutexPtr); 119 | int Sp_ReadWriteMutexIsLocked(Sp_ReadWriteMutex *mutexPtr); 120 | int Sp_ReadWriteMutexUnlock(Sp_ReadWriteMutex *mutexPtr); 121 | void Sp_ReadWriteMutexFinalize(Sp_ReadWriteMutex *mutexPtr); 122 | 123 | #endif /* _SP_H_ */ 124 | 125 | /* EOF $RCSfile$ */ 126 | 127 | /* Emacs Setup Variables */ 128 | /* Local Variables: */ 129 | /* mode: C */ 130 | /* indent-tabs-mode: nil */ 131 | /* c-basic-offset: 4 */ 132 | /* End: */ 133 | -------------------------------------------------------------------------------- /doc/man.macros: -------------------------------------------------------------------------------- 1 | '\" The definitions below are for supplemental macros used in Tcl/Tk 2 | '\" manual entries. 3 | '\" 4 | '\" .AP type name in/out ?indent? 5 | '\" Start paragraph describing an argument to a library procedure. 6 | '\" type is type of argument (int, etc.), in/out is either "in", "out", 7 | '\" or "in/out" to describe whether procedure reads or modifies arg, 8 | '\" and indent is equivalent to second arg of .IP (shouldn't ever be 9 | '\" needed; use .AS below instead) 10 | '\" 11 | '\" .AS ?type? ?name? 12 | '\" Give maximum sizes of arguments for setting tab stops. Type and 13 | '\" name are examples of largest possible arguments that will be passed 14 | '\" to .AP later. If args are omitted, default tab stops are used. 15 | '\" 16 | '\" .BS 17 | '\" Start box enclosure. From here until next .BE, everything will be 18 | '\" enclosed in one large box. 19 | '\" 20 | '\" .BE 21 | '\" End of box enclosure. 22 | '\" 23 | '\" .CS 24 | '\" Begin code excerpt. 25 | '\" 26 | '\" .CE 27 | '\" End code excerpt. 28 | '\" 29 | '\" .VS ?version? ?br? 30 | '\" Begin vertical sidebar, for use in marking newly-changed parts 31 | '\" of man pages. The first argument is ignored and used for recording 32 | '\" the version when the .VS was added, so that the sidebars can be 33 | '\" found and removed when they reach a certain age. If another argument 34 | '\" is present, then a line break is forced before starting the sidebar. 35 | '\" 36 | '\" .VE 37 | '\" End of vertical sidebar. 38 | '\" 39 | '\" .DS 40 | '\" Begin an indented unfilled display. 41 | '\" 42 | '\" .DE 43 | '\" End of indented unfilled display. 44 | '\" 45 | '\" .SO 46 | '\" Start of list of standard options for a Tk widget. The 47 | '\" options follow on successive lines, in four columns separated 48 | '\" by tabs. 49 | '\" 50 | '\" .SE 51 | '\" End of list of standard options for a Tk widget. 52 | '\" 53 | '\" .OP cmdName dbName dbClass 54 | '\" Start of description of a specific option. cmdName gives the 55 | '\" option's name as specified in the class command, dbName gives 56 | '\" the option's name in the option database, and dbClass gives 57 | '\" the option's class in the option database. 58 | '\" 59 | '\" .UL arg1 arg2 60 | '\" Print arg1 underlined, then print arg2 normally. 61 | '\" 62 | '\" RCS: @(#) $Id$ 63 | '\" 64 | '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. 65 | .if t .wh -1.3i ^B 66 | .nr ^l \n(.l 67 | .ad b 68 | '\" # Start an argument description 69 | .de AP 70 | .ie !"\\$4"" .TP \\$4 71 | .el \{\ 72 | . ie !"\\$2"" .TP \\n()Cu 73 | . el .TP 15 74 | .\} 75 | .ta \\n()Au \\n()Bu 76 | .ie !"\\$3"" \{\ 77 | \&\\$1 \\fI\\$2\\fP (\\$3) 78 | .\".b 79 | .\} 80 | .el \{\ 81 | .br 82 | .ie !"\\$2"" \{\ 83 | \&\\$1 \\fI\\$2\\fP 84 | .\} 85 | .el \{\ 86 | \&\\fI\\$1\\fP 87 | .\} 88 | .\} 89 | .. 90 | '\" # define tabbing values for .AP 91 | .de AS 92 | .nr )A 10n 93 | .if !"\\$1"" .nr )A \\w'\\$1'u+3n 94 | .nr )B \\n()Au+15n 95 | .\" 96 | .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n 97 | .nr )C \\n()Bu+\\w'(in/out)'u+2n 98 | .. 99 | .AS Tcl_Interp Tcl_CreateInterp in/out 100 | '\" # BS - start boxed text 101 | '\" # ^y = starting y location 102 | '\" # ^b = 1 103 | .de BS 104 | .br 105 | .mk ^y 106 | .nr ^b 1u 107 | .if n .nf 108 | .if n .ti 0 109 | .if n \l'\\n(.lu\(ul' 110 | .if n .fi 111 | .. 112 | '\" # BE - end boxed text (draw box now) 113 | .de BE 114 | .nf 115 | .ti 0 116 | .mk ^t 117 | .ie n \l'\\n(^lu\(ul' 118 | .el \{\ 119 | .\" Draw four-sided box normally, but don't draw top of 120 | .\" box if the box started on an earlier page. 121 | .ie !\\n(^b-1 \{\ 122 | \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' 123 | .\} 124 | .el \}\ 125 | \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' 126 | .\} 127 | .\} 128 | .fi 129 | .br 130 | .nr ^b 0 131 | .. 132 | '\" # VS - start vertical sidebar 133 | '\" # ^Y = starting y location 134 | '\" # ^v = 1 (for troff; for nroff this doesn't matter) 135 | .de VS 136 | .if !"\\$2"" .br 137 | .mk ^Y 138 | .ie n 'mc \s12\(br\s0 139 | .el .nr ^v 1u 140 | .. 141 | '\" # VE - end of vertical sidebar 142 | .de VE 143 | .ie n 'mc 144 | .el \{\ 145 | .ev 2 146 | .nf 147 | .ti 0 148 | .mk ^t 149 | \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' 150 | .sp -1 151 | .fi 152 | .ev 153 | .\} 154 | .nr ^v 0 155 | .. 156 | '\" # Special macro to handle page bottom: finish off current 157 | '\" # box/sidebar if in box/sidebar mode, then invoked standard 158 | '\" # page bottom macro. 159 | .de ^B 160 | .ev 2 161 | 'ti 0 162 | 'nf 163 | .mk ^t 164 | .if \\n(^b \{\ 165 | .\" Draw three-sided box if this is the box's first page, 166 | .\" draw two sides but no top otherwise. 167 | .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c 168 | .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c 169 | .\} 170 | .if \\n(^v \{\ 171 | .nr ^x \\n(^tu+1v-\\n(^Yu 172 | \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c 173 | .\} 174 | .bp 175 | 'fi 176 | .ev 177 | .if \\n(^b \{\ 178 | .mk ^y 179 | .nr ^b 2 180 | .\} 181 | .if \\n(^v \{\ 182 | .mk ^Y 183 | .\} 184 | .. 185 | '\" # DS - begin display 186 | .de DS 187 | .RS 188 | .nf 189 | .sp 190 | .. 191 | '\" # DE - end display 192 | .de DE 193 | .fi 194 | .RE 195 | .sp 196 | .. 197 | '\" # SO - start of list of standard options 198 | .de SO 199 | .SH "STANDARD OPTIONS" 200 | .LP 201 | .nf 202 | .ta 5.5c 11c 203 | .ft B 204 | .. 205 | '\" # SE - end of list of standard options 206 | .de SE 207 | .fi 208 | .ft R 209 | .LP 210 | See the \\fBoptions\\fR manual entry for details on the standard options. 211 | .. 212 | '\" # OP - start of full description for a single option 213 | .de OP 214 | .LP 215 | .nf 216 | .ta 4c 217 | Command-Line Name: \\fB\\$1\\fR 218 | Database Name: \\fB\\$2\\fR 219 | Database Class: \\fB\\$3\\fR 220 | .fi 221 | .IP 222 | .. 223 | '\" # CS - begin code excerpt 224 | .de CS 225 | .RS 226 | .nf 227 | .ta .25i .5i .75i 1i 228 | .if t .ft C 229 | .. 230 | '\" # CE - end code excerpt 231 | .de CE 232 | .fi 233 | .if t .ft R 234 | .RE 235 | .. 236 | .de UL 237 | \\$1\l'|0\(ul'\\$2 238 | .. 239 | -------------------------------------------------------------------------------- /win/vc/thread_win.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="thread" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) External Target" 0x0106 6 | 7 | CFG=thread - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "thread_win.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "thread_win.mak" CFG="thread - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "thread - Win32 Release" (based on "Win32 (x86) External Target") 21 | !MESSAGE "thread - Win32 Debug" (based on "Win32 (x86) External Target") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | 29 | !IF "$(CFG)" == "thread - Win32 Release" 30 | 31 | # PROP BASE Use_MFC 0 32 | # PROP BASE Use_Debug_Libraries 0 33 | # PROP BASE Output_Dir "Release" 34 | # PROP BASE Intermediate_Dir "Release" 35 | # PROP BASE Cmd_Line "NMAKE /f thread.mak" 36 | # PROP BASE Rebuild_Opt "/a" 37 | # PROP BASE Target_File "thread.exe" 38 | # PROP BASE Bsc_Name "thread.bsc" 39 | # PROP BASE Target_Dir "" 40 | # PROP Use_MFC 0 41 | # PROP Use_Debug_Libraries 0 42 | # PROP Output_Dir "Release" 43 | # PROP Intermediate_Dir "Release" 44 | # PROP Cmd_Line "nmake -nologo -f makefile.vc TCLDIR=E:\tcl MSVCDIR=IDE" 45 | # PROP Rebuild_Opt "-a" 46 | # PROP Target_File "Release\thread26.dll" 47 | # PROP Bsc_Name "" 48 | # PROP Target_Dir "" 49 | 50 | !ELSEIF "$(CFG)" == "thread - Win32 Debug" 51 | 52 | # PROP BASE Use_MFC 0 53 | # PROP BASE Use_Debug_Libraries 1 54 | # PROP BASE Output_Dir "Debug" 55 | # PROP BASE Intermediate_Dir "Debug" 56 | # PROP BASE Cmd_Line "NMAKE /f thread.mak" 57 | # PROP BASE Rebuild_Opt "/a" 58 | # PROP BASE Target_File "thread.exe" 59 | # PROP BASE Bsc_Name "thread.bsc" 60 | # PROP BASE Target_Dir "" 61 | # PROP Use_MFC 0 62 | # PROP Use_Debug_Libraries 1 63 | # PROP Output_Dir "Debug" 64 | # PROP Intermediate_Dir "Debug" 65 | # PROP Cmd_Line "nmake -nologo -f makefile.vc OPTS=symbols TCLDIR=E:\tcl MSVCDIR=IDE" 66 | # PROP Rebuild_Opt "-a" 67 | # PROP Target_File "Debug\thread26d.dll" 68 | # PROP Bsc_Name "" 69 | # PROP Target_Dir "" 70 | 71 | !ENDIF 72 | 73 | # Begin Target 74 | 75 | # Name "thread - Win32 Release" 76 | # Name "thread - Win32 Debug" 77 | 78 | !IF "$(CFG)" == "thread - Win32 Release" 79 | 80 | !ELSEIF "$(CFG)" == "thread - Win32 Debug" 81 | 82 | !ENDIF 83 | 84 | ROOT=.. 85 | 86 | # Begin Group "generic" 87 | 88 | # PROP Default_Filter "" 89 | # Begin Source File 90 | 91 | SOURCE=$(ROOT)\generic\aolstub.cpp 92 | # End Source File 93 | # Begin Source File 94 | 95 | SOURCE=$(ROOT)\generic\psGdbm.c 96 | # End Source File 97 | # Begin Source File 98 | 99 | SOURCE=$(ROOT)\generic\psGdbm.h 100 | # End Source File 101 | # Begin Source File 102 | 103 | SOURCE=$(ROOT)\generic\tclThread.h 104 | # End Source File 105 | # Begin Source File 106 | 107 | SOURCE=$(ROOT)\generic\tclXkeylist.c 108 | # End Source File 109 | # Begin Source File 110 | 111 | SOURCE=$(ROOT)\generic\tclXkeylist.h 112 | # End Source File 113 | # Begin Source File 114 | 115 | SOURCE=$(ROOT)\generic\threadCmd.c 116 | # End Source File 117 | # Begin Source File 118 | 119 | SOURCE=$(ROOT)\generic\threadPoolCmd.c 120 | # End Source File 121 | # Begin Source File 122 | 123 | SOURCE=$(ROOT)\generic\threadSpCmd.c 124 | # End Source File 125 | # Begin Source File 126 | 127 | SOURCE=$(ROOT)\generic\threadSvCmd.c 128 | # End Source File 129 | # Begin Source File 130 | 131 | SOURCE=$(ROOT)\generic\threadSvCmd.h 132 | # End Source File 133 | # Begin Source File 134 | 135 | SOURCE=$(ROOT)\generic\threadSvKeylistCmd.c 136 | # End Source File 137 | # Begin Source File 138 | 139 | SOURCE=$(ROOT)\generic\threadSvKeylistCmd.h 140 | # End Source File 141 | # Begin Source File 142 | 143 | SOURCE=$(ROOT)\generic\threadSvListCmd.c 144 | # End Source File 145 | # Begin Source File 146 | 147 | SOURCE=$(ROOT)\generic\threadSvListCmd.h 148 | # End Source File 149 | # End Group 150 | # Begin Group "doc" 151 | 152 | # PROP Default_Filter "" 153 | # Begin Group "html" 154 | 155 | # PROP Default_Filter "" 156 | # Begin Source File 157 | 158 | SOURCE=$(ROOT)\doc\html\thread.html 159 | # End Source File 160 | # Begin Source File 161 | 162 | SOURCE=$(ROOT)\doc\html\tpool.html 163 | # End Source File 164 | # Begin Source File 165 | 166 | SOURCE=$(ROOT)\doc\html\tsv.html 167 | # End Source File 168 | # Begin Source File 169 | 170 | SOURCE=$(ROOT)\doc\html\ttrace.html 171 | # End Source File 172 | # End Group 173 | # Begin Group "man" 174 | 175 | # PROP Default_Filter "" 176 | # Begin Source File 177 | 178 | SOURCE=$(ROOT)\doc\man\thread.n 179 | # End Source File 180 | # Begin Source File 181 | 182 | SOURCE=$(ROOT)\doc\man\tpool.n 183 | # End Source File 184 | # Begin Source File 185 | 186 | SOURCE=$(ROOT)\doc\man\tsv.n 187 | # End Source File 188 | # Begin Source File 189 | 190 | SOURCE=$(ROOT)\doc\man\ttrace.n 191 | # End Source File 192 | # End Group 193 | # Begin Source File 194 | 195 | SOURCE=$(ROOT)\doc\format.tcl 196 | # End Source File 197 | # Begin Source File 198 | 199 | SOURCE=$(ROOT)\doc\man.macros 200 | # End Source File 201 | # Begin Source File 202 | 203 | SOURCE=$(ROOT)\doc\thread.man 204 | # End Source File 205 | # Begin Source File 206 | 207 | SOURCE=$(ROOT)\doc\tpool.man 208 | # End Source File 209 | # Begin Source File 210 | 211 | SOURCE=$(ROOT)\doc\tsv.man 212 | # End Source File 213 | # Begin Source File 214 | 215 | SOURCE=$(ROOT)\doc\ttrace.man 216 | # End Source File 217 | # End Group 218 | # Begin Group "win" 219 | 220 | # PROP Default_Filter "" 221 | # Begin Group "vc" 222 | 223 | # PROP Default_Filter "" 224 | # Begin Source File 225 | 226 | SOURCE=.\makefile.vc 227 | # End Source File 228 | # Begin Source File 229 | 230 | SOURCE=.\nmakehlp.c 231 | # End Source File 232 | # Begin Source File 233 | 234 | SOURCE=.\pkg.vc 235 | # End Source File 236 | # Begin Source File 237 | 238 | SOURCE=.\README.vc.txt 239 | # End Source File 240 | # Begin Source File 241 | 242 | SOURCE=.\rules.vc 243 | # End Source File 244 | # End Group 245 | # Begin Source File 246 | 247 | SOURCE=$(ROOT)\win\README.txt 248 | # End Source File 249 | # Begin Source File 250 | 251 | SOURCE=$(ROOT)\win\thread.rc 252 | # End Source File 253 | # Begin Source File 254 | 255 | SOURCE=$(ROOT)\win\threadWin.c 256 | # End Source File 257 | # End Group 258 | # Begin Source File 259 | 260 | SOURCE=$(ROOT)\ChangeLog 261 | # End Source File 262 | # Begin Source File 263 | 264 | SOURCE=$(ROOT)\license.terms 265 | # End Source File 266 | # Begin Source File 267 | 268 | SOURCE=$(ROOT)\README 269 | # End Source File 270 | # End Target 271 | # End Project 272 | -------------------------------------------------------------------------------- /tcl/cmdsrv/cmdsrv.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # cmdsrv.tcl -- 3 | # 4 | # Simple socket command server. Supports many simultaneous sessions. 5 | # Works in thread mode with each new connection receiving a new thread. 6 | # 7 | # Usage: 8 | # cmdsrv::create port ?-idletime value? ?-initcmd cmd? 9 | # 10 | # port Tcp port where the server listens 11 | # -idletime # of sec to idle before tearing down socket (def: 300 sec) 12 | # -initcmd script to initialize new worker thread (def: empty) 13 | # 14 | # Example: 15 | # 16 | # # tclsh8.4 17 | # % source cmdsrv.tcl 18 | # % cmdsrv::create 5000 -idletime 60 19 | # % vwait forever 20 | # 21 | # Starts the server on the port 5000, sets idle timer to 1 minute. 22 | # You can now use "telnet" utility to connect. 23 | # 24 | # Copyright (c) 2002 by Zoran Vasiljevic. 25 | # 26 | # See the file "license.terms" for information on usage and 27 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. 28 | # 29 | # ----------------------------------------------------------------------------- 30 | # RCS: @(#) $Id$ 31 | # 32 | 33 | package require Tcl 8.4 34 | package require Thread 2.5 35 | 36 | namespace eval cmdsrv { 37 | variable data; # Stores global configuration options 38 | } 39 | 40 | # 41 | # cmdsrv::create -- 42 | # 43 | # Start the server on the given Tcp port. 44 | # 45 | # Arguments: 46 | # port Port where the server is listening 47 | # args Variable number of arguments 48 | # 49 | # Side Effects: 50 | # None. 51 | # 52 | # Results: 53 | # None. 54 | # 55 | 56 | proc cmdsrv::create {port args} { 57 | 58 | variable data 59 | 60 | if {[llength $args] % 2} { 61 | error "wrong \# arguments, should be: key1 val1 key2 val2..." 62 | } 63 | 64 | # 65 | # Setup default pool data. 66 | # 67 | 68 | array set data { 69 | -idletime 300000 70 | -initcmd {source cmdsrv.tcl} 71 | } 72 | 73 | # 74 | # Override with user-supplied data 75 | # 76 | 77 | foreach {arg val} $args { 78 | switch -- $arg { 79 | -idletime {set data($arg) [expr {$val*1000}]} 80 | -initcmd {append data($arg) \n $val} 81 | default { 82 | error "unsupported pool option \"$arg\"" 83 | } 84 | } 85 | } 86 | 87 | # 88 | # Start the server on the given port. Note that we wrap 89 | # the actual accept with a helper after/idle callback. 90 | # This is a workaround for a well-known Tcl bug. 91 | # 92 | 93 | socket -server [namespace current]::_Accept -myaddr 127.0.0.1 $port 94 | } 95 | 96 | # 97 | # cmdsrv::_Accept -- 98 | # 99 | # Helper procedure to solve Tcl shared channel bug when responding 100 | # to incoming socket connection and transfering the channel to other 101 | # thread(s). 102 | # 103 | # Arguments: 104 | # s incoming socket 105 | # ipaddr IP address of the remote peer 106 | # port Tcp port used for this connection 107 | # 108 | # Side Effects: 109 | # None. 110 | # 111 | # Results: 112 | # None. 113 | # 114 | 115 | proc cmdsrv::_Accept {s ipaddr port} { 116 | after idle [list [namespace current]::Accept $s $ipaddr $port] 117 | } 118 | 119 | # 120 | # cmdsrv::Accept -- 121 | # 122 | # Accepts the incoming socket connection, creates the worker thread. 123 | # 124 | # Arguments: 125 | # s incoming socket 126 | # ipaddr IP address of the remote peer 127 | # port Tcp port used for this connection 128 | # 129 | # Side Effects: 130 | # Creates new worker thread. 131 | # 132 | # Results: 133 | # None. 134 | # 135 | 136 | proc cmdsrv::Accept {s ipaddr port} { 137 | 138 | variable data 139 | 140 | # 141 | # Configure socket for sane operation 142 | # 143 | 144 | fconfigure $s -blocking 0 -buffering none -translation {auto crlf} 145 | 146 | # 147 | # Emit the prompt 148 | # 149 | 150 | puts -nonewline $s "% " 151 | 152 | # 153 | # Create worker thread and transfer socket ownership 154 | # 155 | 156 | set tid [thread::create [append data(-initcmd) \n thread::wait]] 157 | thread::transfer $tid $s ; # This flushes the socket as well 158 | 159 | # 160 | # Start event-loop processing in the remote thread 161 | # 162 | 163 | thread::send -async $tid [subst { 164 | array set [namespace current]::data {[array get data]} 165 | fileevent $s readable {[namespace current]::Read $s} 166 | proc exit args {[namespace current]::SockDone $s} 167 | [namespace current]::StartIdleTimer $s 168 | }] 169 | } 170 | 171 | # 172 | # cmdsrv::Read -- 173 | # 174 | # Event loop procedure to read data from socket and collect the 175 | # command to execute. If the command read from socket is complete 176 | # it executes the command are prints the result back. 177 | # 178 | # Arguments: 179 | # s incoming socket 180 | # 181 | # Side Effects: 182 | # None. 183 | # 184 | # Results: 185 | # None. 186 | # 187 | 188 | proc cmdsrv::Read {s} { 189 | 190 | variable data 191 | 192 | StopIdleTimer $s 193 | 194 | # 195 | # Cover client closing connection 196 | # 197 | 198 | if {[eof $s] || [catch {read $s} line]} { 199 | return [SockDone $s] 200 | } 201 | if {$line == "\n" || $line == ""} { 202 | if {[catch {puts -nonewline $s "% "}]} { 203 | return [SockDone $s] 204 | } 205 | return [StartIdleTimer $s] 206 | } 207 | 208 | # 209 | # Construct command line to eval 210 | # 211 | 212 | append data(cmd) $line 213 | if {[info complete $data(cmd)] == 0} { 214 | if {[catch {puts -nonewline $s "> "}]} { 215 | return [SockDone $s] 216 | } 217 | return [StartIdleTimer $s] 218 | } 219 | 220 | # 221 | # Run the command 222 | # 223 | 224 | catch {uplevel \#0 $data(cmd)} ret 225 | if {[catch {puts $s $ret}]} { 226 | return [SockDone $s] 227 | } 228 | set data(cmd) "" 229 | if {[catch {puts -nonewline $s "% "}]} { 230 | return [SockDone $s] 231 | } 232 | StartIdleTimer $s 233 | } 234 | 235 | # 236 | # cmdsrv::SockDone -- 237 | # 238 | # Tears down the thread and closes the socket if the remote peer has 239 | # closed his side of the comm channel. 240 | # 241 | # Arguments: 242 | # s incoming socket 243 | # 244 | # Side Effects: 245 | # Worker thread gets released. 246 | # 247 | # Results: 248 | # None. 249 | # 250 | 251 | proc cmdsrv::SockDone {s} { 252 | 253 | catch {close $s} 254 | thread::release 255 | } 256 | 257 | # 258 | # cmdsrv::StopIdleTimer -- 259 | # 260 | # Cancel the connection idle timer. 261 | # 262 | # Arguments: 263 | # s incoming socket 264 | # 265 | # Side Effects: 266 | # After event gets cancelled. 267 | # 268 | # Results: 269 | # None. 270 | # 271 | 272 | proc cmdsrv::StopIdleTimer {s} { 273 | 274 | variable data 275 | 276 | if {[info exists data(idleevent)]} { 277 | after cancel $data(idleevent) 278 | unset data(idleevent) 279 | } 280 | } 281 | 282 | # 283 | # cmdsrv::StartIdleTimer -- 284 | # 285 | # Initiates the connection idle timer. 286 | # 287 | # Arguments: 288 | # s incoming socket 289 | # 290 | # Side Effects: 291 | # After event gets posted. 292 | # 293 | # Results: 294 | # None. 295 | # 296 | 297 | proc cmdsrv::StartIdleTimer {s} { 298 | 299 | variable data 300 | 301 | set data(idleevent) \ 302 | [after $data(-idletime) [list [namespace current]::SockDone $s]] 303 | } 304 | 305 | # EOF $RCSfile$ 306 | 307 | # Emacs Setup Variables 308 | # Local Variables: 309 | # mode: Tcl 310 | # indent-tabs-mode: nil 311 | # tcl-basic-offset: 4 312 | # End: 313 | 314 | -------------------------------------------------------------------------------- /generic/threadSvCmd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the header file for the module that implements shared variables. 3 | * for protected multithreaded access. 4 | * 5 | * Copyright (c) 2002 by Zoran Vasiljevic. 6 | * 7 | * See the file "license.txt" for information on usage and redistribution 8 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 9 | * 10 | * Rcsid: @(#)$Id$ 11 | * --------------------------------------------------------------------------- 12 | */ 13 | 14 | #ifndef _SV_H_ 15 | #define _SV_H_ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "threadSpCmd.h" /* For recursive locks */ 22 | 23 | /* 24 | * Uncomment following line to get command-line 25 | * compatibility with AOLserver nsv_* commands 26 | */ 27 | 28 | /* #define NSV_COMPAT 1 */ 29 | 30 | /* 31 | * Uncomment following line to force command-line 32 | * compatibility with older thread::sv_ commands 33 | * If you leave it commented-out, the older style 34 | * command is going to be included in addition to 35 | * the new tsv::* style. 36 | */ 37 | 38 | /* #define OLD_COMPAT 1 */ 39 | 40 | #ifdef NS_AOLSERVER 41 | # ifdef NSV_COMPAT 42 | # define TSV_CMD_PREFIX "nsv_" /* Compatiblity prefix for AOLserver */ 43 | # else 44 | # define TSV_CMD_PREFIX "sv_" /* Regular command prefix for AOLserver */ 45 | # endif 46 | #else 47 | # ifdef OLD_COMPAT 48 | # define TSV_CMD_PREFIX "thread::sv_" /* Old command prefix for Tcl */ 49 | # else 50 | # define TSV_CMD_PREFIX "tsv::" /* Regular command prefix for Tcl */ 51 | # endif 52 | #endif 53 | 54 | /* 55 | * Used when creating arrays/variables 56 | */ 57 | 58 | #define FLAGS_CREATEARRAY 1 /* Create the array in bucket if none found */ 59 | #define FLAGS_NOERRMSG 2 /* Do not format error message */ 60 | #define FLAGS_CREATEVAR 4 /* Create the array variable if none found */ 61 | 62 | /* 63 | * Macros for handling locking and unlocking 64 | */ 65 | #define LOCK_BUCKET(a) Sp_RecursiveMutexLock(&(a)->lock) 66 | #define UNLOCK_BUCKET(a) Sp_RecursiveMutexUnlock(&(a)->lock) 67 | 68 | #define LOCK_CONTAINER(a) Sp_RecursiveMutexLock(&(a)->bucketPtr->lock) 69 | #define UNLOCK_CONTAINER(a) Sp_RecursiveMutexUnlock(&(a)->bucketPtr->lock) 70 | 71 | /* 72 | * This is named synetrically to LockArray as function 73 | * rather than as a macro just to improve readability. 74 | */ 75 | 76 | #define UnlockArray(a) UNLOCK_CONTAINER(a) 77 | 78 | /* 79 | * Mode for Sv_PutContainer, so it knows what 80 | * happened with the embedded shared object. 81 | */ 82 | 83 | #define SV_UNCHANGED 0 /* Object has not been modified */ 84 | #define SV_CHANGED 1 /* Object has been modified */ 85 | #define SV_ERROR -1 /* Object may be in incosistent state */ 86 | 87 | /* 88 | * Definitions of functions implementing simple key/value 89 | * persistent storage for shared variable arrays. 90 | */ 91 | 92 | typedef ClientData (ps_open_proc)(const char*); 93 | 94 | typedef int (ps_get_proc) (ClientData, const char*, char**, int*); 95 | typedef int (ps_put_proc) (ClientData, const char*, char*, int); 96 | typedef int (ps_first_proc) (ClientData, char**, char**, int*); 97 | typedef int (ps_next_proc) (ClientData, char**, char**, int*); 98 | typedef int (ps_delete_proc)(ClientData, const char*); 99 | typedef int (ps_close_proc) (ClientData); 100 | typedef void(ps_free_proc) (char*); 101 | 102 | typedef char* (ps_geterr_proc)(ClientData); 103 | 104 | /* 105 | * This structure maintains a bunch of pointers to functions implementing 106 | * the simple persistence layer for the shared variable arrays. 107 | */ 108 | 109 | typedef struct PsStore { 110 | char *type; /* Type identifier of the persistent storage */ 111 | ClientData psHandle; /* Handle to the opened storage */ 112 | ps_open_proc *psOpen; /* Function to open the persistent key store */ 113 | ps_get_proc *psGet; /* Function to retrieve value bound to key */ 114 | ps_put_proc *psPut; /* Function to store user key and value */ 115 | ps_first_proc *psFirst; /* Function to retrieve the first key/value */ 116 | ps_next_proc *psNext; /* Function to retrieve the next key/value */ 117 | ps_delete_proc *psDelete; /* Function to delete user key and value */ 118 | ps_close_proc *psClose; /* Function to close the persistent store */ 119 | ps_free_proc *psFree; /* Fuction to free allocated memory */ 120 | ps_geterr_proc *psError; /* Function to return last store error */ 121 | struct PsStore *nextPtr; /* For linking into linked lists */ 122 | } PsStore; 123 | 124 | /* 125 | * The following structure defines a collection of arrays. 126 | * Only the arrays within a given bucket share a lock, 127 | * allowing for more concurency. 128 | */ 129 | 130 | typedef struct Bucket { 131 | Sp_RecursiveMutex lock; /* */ 132 | Tcl_ThreadId lockt; /* Thread holding the lock */ 133 | Tcl_HashTable arrays; /* Hash table of all arrays in bucket */ 134 | Tcl_HashTable handles; /* Hash table of given-out handles in bucket */ 135 | struct Container *freeCt; /* List of free Tcl-object containers */ 136 | } Bucket; 137 | 138 | /* 139 | * The following structure maintains the context for each variable array. 140 | */ 141 | 142 | typedef struct Array { 143 | char *bindAddr; /* Array is bound to this address */ 144 | PsStore *psPtr; /* Persistent storage functions */ 145 | Bucket *bucketPtr; /* Array bucket. */ 146 | Tcl_HashEntry *entryPtr; /* Entry in bucket array table. */ 147 | Tcl_HashEntry *handlePtr; /* Entry in handles table */ 148 | Tcl_HashTable vars; /* Table of variables. */ 149 | } Array; 150 | 151 | /* 152 | * The object container for Tcl-objects stored within shared arrays. 153 | */ 154 | 155 | typedef struct Container { 156 | Bucket *bucketPtr; /* Bucket holding the array below */ 157 | Array *arrayPtr; /* Array with the object container*/ 158 | Tcl_HashEntry *entryPtr; /* Entry in array table. */ 159 | Tcl_HashEntry *handlePtr; /* Entry in handles table */ 160 | Tcl_Obj *tclObj; /* Tcl object to hold shared values */ 161 | int epoch; /* Track object changes */ 162 | char *chunkAddr; /* Address of one chunk of object containers */ 163 | struct Container *nextPtr; /* Next object container in the free list */ 164 | } Container; 165 | 166 | /* 167 | * Structure for generating command names in Tcl 168 | */ 169 | 170 | typedef struct SvCmdInfo { 171 | char *name; /* The short name of the command */ 172 | char *cmdName; /* Real (rewritten) name of the command */ 173 | Tcl_ObjCmdProc *objProcPtr; /* The object-based command procedure */ 174 | Tcl_CmdDeleteProc *delProcPtr; /* Pointer to command delete function */ 175 | ClientData *clientData; /* Pointer passed to above command */ 176 | struct SvCmdInfo *nextPtr; /* Next in chain of registered commands */ 177 | } SvCmdInfo; 178 | 179 | /* 180 | * Structure for registering special object duplicator functions. 181 | * Reason for this is that even some regular Tcl duplicators 182 | * produce shallow instead of proper deep copies of the object. 183 | * While this is considered to be ok in single-threaded apps, 184 | * a multithreaded app could have problems when accessing objects 185 | * which live in (i.e. are accessed from) different interpreters. 186 | * So, for each object type which should be stored in shared object 187 | * pools, we must assure that the object is copied properly. 188 | */ 189 | 190 | typedef struct RegType { 191 | const Tcl_ObjType *typePtr; /* Type of the registered object */ 192 | Tcl_DupInternalRepProc *dupIntRepProc; /* Special deep-copy duper */ 193 | struct RegType *nextPtr; /* Next in chain of registered types */ 194 | } RegType; 195 | 196 | /* 197 | * Limited API functions 198 | */ 199 | 200 | void 201 | Sv_RegisterCommand(const char*,Tcl_ObjCmdProc*,Tcl_CmdDeleteProc*,ClientData); 202 | 203 | void 204 | Sv_RegisterObjType(const Tcl_ObjType*, Tcl_DupInternalRepProc*); 205 | 206 | void 207 | Sv_RegisterPsStore(PsStore*); 208 | 209 | int 210 | Sv_GetContainer(Tcl_Interp*,int,Tcl_Obj*const objv[],Container**,int*,int); 211 | 212 | int 213 | Sv_PutContainer(Tcl_Interp*, Container*, int); 214 | 215 | /* 216 | * Private version of Tcl_DuplicateObj which takes care about 217 | * copying objects when loaded to and retrieved from shared array. 218 | */ 219 | 220 | Tcl_Obj* Sv_DuplicateObj(Tcl_Obj*); 221 | 222 | #endif /* _SV_H_ */ 223 | 224 | /* EOF $RCSfile$ */ 225 | 226 | /* Emacs Setup Variables */ 227 | /* Local Variables: */ 228 | /* mode: C */ 229 | /* indent-tabs-mode: nil */ 230 | /* c-basic-offset: 4 */ 231 | /* End: */ 232 | 233 | -------------------------------------------------------------------------------- /configure.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash -norc 2 | dnl This file is an input file used by the GNU "autoconf" program to 3 | dnl generate the file "configure", which is run during Tcl installation 4 | dnl to configure the system for the local environment. 5 | # 6 | # RCS: @(#) $Id$ 7 | 8 | #----------------------------------------------------------------------- 9 | # Sample configure.in for Tcl Extensions. The only places you should 10 | # need to modify this file are marked by the string __CHANGE__ 11 | #----------------------------------------------------------------------- 12 | 13 | #----------------------------------------------------------------------- 14 | # __CHANGE__ 15 | # Set your package name and version numbers here. 16 | # 17 | # This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION 18 | # set as provided. These will also be added as -D defs in your Makefile 19 | # so you can encode the package version directly into the source files. 20 | #----------------------------------------------------------------------- 21 | 22 | AC_INIT([thread], [2.6.7]) 23 | 24 | #-------------------------------------------------------------------- 25 | # Call TEA_INIT as the first TEA_ macro to set up initial vars. 26 | # This will define a ${TEA_PLATFORM} variable == "unix" or "windows" 27 | # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. 28 | #-------------------------------------------------------------------- 29 | 30 | TEA_INIT([3.9]) 31 | 32 | AC_CONFIG_AUX_DIR(tclconfig) 33 | 34 | #-------------------------------------------------------------------- 35 | # Load the tclConfig.sh file 36 | #-------------------------------------------------------------------- 37 | 38 | TEA_PATH_TCLCONFIG 39 | TEA_LOAD_TCLCONFIG 40 | 41 | #-------------------------------------------------------------------- 42 | # Load the tkConfig.sh file if necessary (Tk extension) 43 | #-------------------------------------------------------------------- 44 | 45 | #TEA_PATH_TKCONFIG 46 | #TEA_LOAD_TKCONFIG 47 | 48 | #----------------------------------------------------------------------- 49 | # Handle the --prefix=... option by defaulting to what Tcl gave. 50 | # Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. 51 | #----------------------------------------------------------------------- 52 | 53 | TEA_PREFIX 54 | 55 | #----------------------------------------------------------------------- 56 | # Standard compiler checks. 57 | # This sets up CC by using the CC env var, or looks for gcc otherwise. 58 | # This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create 59 | # the basic setup necessary to compile executables. 60 | #----------------------------------------------------------------------- 61 | 62 | TEA_SETUP_COMPILER 63 | 64 | #-------------------------------------------------------------------- 65 | # Check if building with optional Gdbm package. This will declare 66 | # GDBM_CFLAGS and GDBM_LIBS variables. 67 | #-------------------------------------------------------------------- 68 | 69 | TCLTHREAD_WITH_GDBM 70 | 71 | #-------------------------------------------------------------------- 72 | # Locate the AOLserver dir for compilation as AOLserver module. 73 | # This will declare AOL_INCLUDES, AOL_LIBS and define NS_AOLSERVER. 74 | #-------------------------------------------------------------------- 75 | 76 | NS_PATH_AOLSERVER 77 | 78 | #----------------------------------------------------------------------- 79 | # __CHANGE__ 80 | # Specify the C source files to compile in TEA_ADD_SOURCES, 81 | # public headers that need to be installed in TEA_ADD_HEADERS, 82 | # stub library C source files to compile in TEA_ADD_STUB_SOURCES, 83 | # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. 84 | # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS 85 | # and PKG_TCL_SOURCES. 86 | #----------------------------------------------------------------------- 87 | 88 | TEA_ADD_SOURCES([generic/threadCmd.c \ 89 | generic/threadSvCmd.c \ 90 | generic/threadSpCmd.c \ 91 | generic/threadPoolCmd.c \ 92 | generic/psGdbm.c \ 93 | generic/threadSvListCmd.c \ 94 | generic/threadSvKeylistCmd.c \ 95 | generic/tclXkeylist.c \ 96 | ]) 97 | 98 | TEA_ADD_HEADERS([]) 99 | TEA_ADD_INCLUDES([${AOL_INCLUDES}]) 100 | TEA_ADD_LIBS([${GDBM_LIBS} ${AOL_LIBS}]) 101 | TEA_ADD_CFLAGS([${GDBM_CFLAGS}]) 102 | TEA_ADD_STUB_SOURCES([]) 103 | TEA_ADD_TCL_SOURCES([lib/ttrace.tcl]) 104 | 105 | #-------------------------------------------------------------------- 106 | # __CHANGE__ 107 | # A few miscellaneous platform-specific items: 108 | # 109 | # Define a special symbol for Windows (BUILD_sample in this case) so 110 | # that we create the export library with the dll. 111 | # 112 | # Windows creates a few extra files that need to be cleaned up. 113 | # You can add more files to clean if your extension creates any extra 114 | # files. 115 | # 116 | # TEA_ADD_* any platform specific compiler/build info here. 117 | #-------------------------------------------------------------------- 118 | 119 | if test "${TEA_PLATFORM}" = "windows" ; then 120 | TEA_ADD_SOURCES([win/threadWin.c]) 121 | TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) 122 | else 123 | TEA_ADD_SOURCES([unix/threadUnix.c]) 124 | fi 125 | 126 | #-------------------------------------------------------------------- 127 | # __CHANGE__ 128 | # Choose which headers you need. Extension authors should try very 129 | # hard to only rely on the Tcl public header files. Internal headers 130 | # contain private data structures and are subject to change without 131 | # notice. 132 | # This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG 133 | #-------------------------------------------------------------------- 134 | 135 | TEA_PUBLIC_TCL_HEADERS 136 | #TEA_PRIVATE_TCL_HEADERS 137 | 138 | #TEA_PUBLIC_TK_HEADERS 139 | #TEA_PRIVATE_TK_HEADERS 140 | #TEA_PATH_X 141 | 142 | #-------------------------------------------------------------------- 143 | # Check whether --enable-threads or --disable-threads was given. 144 | # This auto-enables if Tcl was compiled threaded. 145 | #-------------------------------------------------------------------- 146 | 147 | TEA_ENABLE_THREADS 148 | 149 | #-------------------------------------------------------------------- 150 | # The statement below defines a collection of symbols related to 151 | # building as a shared library instead of a static library. 152 | #-------------------------------------------------------------------- 153 | 154 | TEA_ENABLE_SHARED 155 | 156 | #-------------------------------------------------------------------- 157 | # This macro figures out what flags to use with the compiler/linker 158 | # when building shared/static debug/optimized objects. This information 159 | # can be taken from the tclConfig.sh file, but this figures it all out. 160 | #-------------------------------------------------------------------- 161 | 162 | TEA_CONFIG_CFLAGS 163 | 164 | #-------------------------------------------------------------------- 165 | # Set the default compiler switches based on the --enable-symbols option. 166 | #-------------------------------------------------------------------- 167 | 168 | TEA_ENABLE_SYMBOLS 169 | 170 | #-------------------------------------------------------------------- 171 | # Everyone should be linking against the Tcl stub library. If you 172 | # can't for some reason, remove this definition. If you aren't using 173 | # stubs, you also need to modify the SHLIB_LD_LIBS setting below to 174 | # link against the non-stubbed Tcl library. Add Tk too if necessary. 175 | #-------------------------------------------------------------------- 176 | 177 | AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs]) 178 | 179 | #-------------------------------------------------------------------- 180 | # This macro generates a line to use when building a library. It 181 | # depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS, 182 | # and TEA_LOAD_TCLCONFIG macros above. 183 | #-------------------------------------------------------------------- 184 | 185 | TEA_MAKE_LIB 186 | 187 | #-------------------------------------------------------------------- 188 | # Determine the name of the tclsh and/or wish executables in the 189 | # Tcl and Tk build directories or the location they were installed 190 | # into. These paths are used to support running test cases only, 191 | # the Makefile should not be making use of these paths to generate 192 | # a pkgIndex.tcl file or anything else at extension build time. 193 | #-------------------------------------------------------------------- 194 | 195 | TEA_PROG_TCLSH 196 | #TEA_PROG_WISH 197 | 198 | #-------------------------------------------------------------------- 199 | # Finally, substitute all of the various values into the Makefile. 200 | # You may alternatively have a special pkgIndex.tcl.in or other files 201 | # which require substituting th AC variables in. Include these here. 202 | #-------------------------------------------------------------------- 203 | 204 | AC_OUTPUT([Makefile pkgIndex.tcl]) 205 | -------------------------------------------------------------------------------- /doc/ttrace.man: -------------------------------------------------------------------------------- 1 | [comment {-*- tcl -*- doctools manpage}] 2 | [manpage_begin ttrace n 2.6] 3 | [moddesc {Tcl Threading}] 4 | [titledesc {Trace-based interpreter initialization}] 5 | [require Tcl 8.4] 6 | [require Thread [opt 2.6]] 7 | 8 | [description] 9 | This package creates a framework for on-demand replication of the 10 | interpreter state accross threads in an multithreading application. 11 | It relies on the mechanics of Tcl command tracing and the Tcl 12 | [cmd unknown] command and mechanism. 13 | [para] 14 | The package requires Tcl threading extension but can be alternatively 15 | used stand-alone within the AOLserver, a scalable webserver from 16 | America Online. 17 | [para] 18 | In a nutshell, a short sample illustrating the usage of the ttrace 19 | with the Tcl threading extension: 20 | 21 | [example { 22 | 23 | % package require Ttrace 24 | 2.6.5 25 | 26 | % set t1 [thread::create {package require Ttrace; thread::wait}] 27 | tid0x1802800 28 | 29 | % ttrace::eval {proc test args {return test-[thread::id]}} 30 | % thread::send $t1 test 31 | test-tid0x1802800 32 | 33 | % set t2 [thread::create {package require Ttrace; thread::wait}] 34 | tid0x1804000 35 | 36 | % thread::send $t2 test 37 | test-tid0x1804000 38 | 39 | }] 40 | [para] 41 | As seen from above, the [cmd ttrace::eval] and [cmd ttrace::update] 42 | commands are used to create a thread-wide definition of a simple 43 | Tcl procedure and replicate that definition to all, already existing 44 | or later created, threads. 45 | 46 | [section {USER COMMANDS}] 47 | This section describes user-level commands. Those commands can be 48 | used by script writers to control the execution of the tracing 49 | framework. 50 | 51 | [list_begin definitions] 52 | 53 | [call [cmd ttrace::eval] [arg arg] [opt {arg ...}]] 54 | 55 | This command concatenates given arguments and evaluates the resulting 56 | Tcl command with trace framework enabled. If the command execution 57 | was ok, it takes necessary steps to automatically propagate the 58 | trace epoch change to all threads in the application. 59 | For AOLserver, only newly created threads actually receive the 60 | epoch change. For the Tcl threading extension, all threads created by 61 | the extension are automatically updated. If the command execution 62 | resulted in Tcl error, no state propagation takes place. 63 | [para] 64 | This is the most important user-level command of the package as 65 | it wraps most of the commands described below. This greatly 66 | simplifies things, because user need to learn just this (one) 67 | command in order to effectively use the package. Other commands, 68 | as desribed below, are included mostly for the sake of completeness. 69 | 70 | [call [cmd ttrace::enable]] 71 | 72 | Activates all registered callbacks in the framework 73 | and starts a new trace epoch. The trace epoch encapsulates all 74 | changes done to the interpreter during the time traces are activated. 75 | 76 | [call [cmd ttrace::disable]] 77 | 78 | Deactivates all registered callbacks in the framework 79 | and closes the current trace epoch. 80 | 81 | [call [cmd ttrace::cleanup]] 82 | 83 | Used to clean-up all on-demand loaded resources in the interpreter. 84 | It effectively brings Tcl interpreter to its pristine state. 85 | 86 | [call [cmd ttrace::update] [opt epoch]] 87 | 88 | Used to refresh the state of the interpreter to match the optional 89 | trace [opt epoch]. If the optional [opt epoch] is not given, it takes 90 | the most recent trace epoch. 91 | 92 | [call [cmd ttrace::getscript]] 93 | 94 | Returns a synthetized Tcl script which may be sourced in any interpreter. 95 | This script sets the stage for the Tcl [cmd unknown] command so it can 96 | load traced resources from the in-memory database. Normally, this command 97 | is automatically invoked by other higher-level commands like 98 | [cmd ttrace::eval] and [cmd ttrace::update]. 99 | 100 | [list_end] 101 | 102 | [section {CALLBACK COMMANDS}] 103 | A word upfront: the package already includes callbacks for tracing 104 | following Tcl commands: [cmd proc], [cmd namespace], [cmd variable], 105 | [cmd load], and [cmd rename]. Additionaly, a set of callbacks for 106 | tracing resources (object, clasess) for the XOTcl v1.3.8+, an 107 | OO-extension to Tcl, is also provided. 108 | This gives a solid base for solving most of the real-life needs and 109 | serves as an example for people wanting to customize the package 110 | to cover their specific needs. 111 | [para] 112 | Below, you can find commands for registering callbacks in the 113 | framework and for writing callback scripts. These callbacks are 114 | invoked by the framework in order to gather interpreter state 115 | changes, build in-memory database, perform custom-cleanups and 116 | various other tasks. 117 | 118 | 119 | [list_begin definitions] 120 | 121 | [call [cmd ttrace::atenable] [arg cmd] [arg arglist] [arg body]] 122 | 123 | Registers Tcl callback to be activated at [cmd ttrace::enable]. 124 | Registered callbacks are activated on FIFO basis. The callback 125 | definition includes the name of the callback, [arg cmd], a list 126 | of callback arguments, [arg arglist] and the [arg body] of the 127 | callback. Effectively, this actually resembles the call interface 128 | of the standard Tcl [cmd proc] command. 129 | 130 | 131 | [call [cmd ttrace::atdisable] [arg cmd] [arg arglist] [arg body]] 132 | 133 | Registers Tcl callback to be activated at [cmd ttrace::disable]. 134 | Registered callbacks are activated on FIFO basis. The callback 135 | definition includes the name of the callback, [arg cmd], a list 136 | of callback arguments, [arg arglist] and the [arg body] of the 137 | callback. Effectively, this actually resembles the call interface 138 | of the standard Tcl [cmd proc] command. 139 | 140 | 141 | [call [cmd ttrace::addtrace] [arg cmd] [arg arglist] [arg body]] 142 | 143 | Registers Tcl callback to be activated for tracing the Tcl 144 | [cmd cmd] command. The callback definition includes the name of 145 | the Tcl command to trace, [arg cmd], a list of callback arguments, 146 | [arg arglist] and the [arg body] of the callback. Effectively, 147 | this actually resembles the call interface of the standard Tcl 148 | [cmd proc] command. 149 | 150 | 151 | [call [cmd ttrace::addscript] [arg name] [arg body]] 152 | 153 | Registers Tcl callback to be activated for building a Tcl 154 | script to be passed to other interpreters. This script is 155 | used to set the stage for the Tcl [cmd unknown] command. 156 | Registered callbacks are activated on FIFO basis. 157 | The callback definition includes the name of the callback, 158 | [arg name] and the [arg body] of the callback. 159 | 160 | [call [cmd ttrace::addresolver] [arg cmd] [arg arglist] [arg body]] 161 | 162 | Registers Tcl callback to be activated by the overloaded Tcl 163 | [cmd unknown] command. 164 | Registered callbacks are activated on FIFO basis. 165 | This callback is used to resolve the resource and load the 166 | resource in the current interpreter. 167 | 168 | [call [cmd ttrace::addcleanup] [arg body]] 169 | 170 | Registers Tcl callback to be activated by the [cmd trace::cleanup]. 171 | Registered callbacks are activated on FIFO basis. 172 | 173 | [call [cmd ttrace::addentry] [arg cmd] [arg var] [arg val]] 174 | 175 | Adds one entry to the named in-memory database. 176 | 177 | [call [cmd ttrace::getentry] [arg cmd] [arg var]] 178 | 179 | Returns the value of the entry from the named in-memory database. 180 | 181 | [call [cmd ttrace::getentries] [arg cmd] [opt pattern]] 182 | 183 | Returns names of all entries from the named in-memory database. 184 | 185 | [call [cmd ttrace::delentry] [arg cmd]] 186 | 187 | Deletes an entry from the named in-memory database. 188 | 189 | [call [cmd ttrace::preload] [arg cmd]] 190 | 191 | Registers the Tcl command to be loaded in the interpreter. 192 | Commands registered this way will always be the part of 193 | the interpreter and not be on-demand loaded by the Tcl 194 | [cmd unknown] command. 195 | 196 | [list_end] 197 | 198 | [section DISCUSSION] 199 | Common introspective state-replication approaches use a custom Tcl 200 | script to introspect the running interpreter and synthesize another 201 | Tcl script to replicate this state in some other interpreter. 202 | This package, on the contrary, uses Tcl command traces. Command 203 | traces are registered on selected Tcl commands, like [cmd proc], 204 | [cmd namespace], [cmd load] and other standard (and/or user-defined) 205 | Tcl commands. When activated, those traces build an in-memory 206 | database of created resources. This database is used as a resource 207 | repository for the (overloaded) Tcl [cmd unknown] command which 208 | creates the requested resource in the interpreter on demand. 209 | This way, users can update just one interpreter (master) in one 210 | thread and replicate that interpreter state (or part of it) to other 211 | threads/interpreters in the process. 212 | [para] 213 | Immediate benefit of such approach is the much smaller memory footprint 214 | of the application and much faster thread creation. By not actually 215 | loading all necessary procedures (and other resources) in every thread 216 | at the thread initialization time, but by deffering this to the time the 217 | resource is actually referenced, significant improvements in both 218 | memory consumption and thread initialization time can be achieved. Some 219 | tests have shown that memory footprint of an multithreading Tcl application 220 | went down more than three times and thread startup time was reduced for 221 | about 50 times. Note that your mileage may vary. 222 | 223 | Other benefits include much finer control about what (and when) gets 224 | replicated from the master to other Tcl thread/interpreters. 225 | 226 | [see_also tsv tpool thread] 227 | 228 | [keywords {command tracing} introspection] 229 | 230 | [manpage_end] 231 | -------------------------------------------------------------------------------- /generic/psGdbm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file implements wrappers for persistent gdbm storage for the 3 | * shared variable arrays. 4 | * 5 | * See the file "license.terms" for information on usage and redistribution 6 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 7 | * 8 | * RCS: @(#) $Id$ 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | 12 | #ifdef HAVE_GDBM 13 | 14 | #include "threadSvCmd.h" 15 | #include 16 | #include /* For free() */ 17 | 18 | /* 19 | * Functions implementing the persistent store interface 20 | */ 21 | 22 | static ps_open_proc ps_gdbm_open; 23 | static ps_close_proc ps_gdbm_close; 24 | static ps_get_proc ps_gdbm_get; 25 | static ps_put_proc ps_gdbm_put; 26 | static ps_first_proc ps_gdbm_first; 27 | static ps_next_proc ps_gdbm_next; 28 | static ps_delete_proc ps_gdbm_delete; 29 | static ps_free_proc ps_gdbm_free; 30 | static ps_geterr_proc ps_gdbm_geterr; 31 | 32 | /* 33 | * This structure collects all the various pointers 34 | * to the functions implementing the gdbm store. 35 | */ 36 | 37 | PsStore GdbmStore = { 38 | "gdbm", 39 | NULL, 40 | ps_gdbm_open, 41 | ps_gdbm_get, 42 | ps_gdbm_put, 43 | ps_gdbm_first, 44 | ps_gdbm_next, 45 | ps_gdbm_delete, 46 | ps_gdbm_close, 47 | ps_gdbm_free, 48 | ps_gdbm_geterr, 49 | NULL 50 | }; 51 | 52 | /* 53 | *----------------------------------------------------------------------------- 54 | * 55 | * Sv_RegisterGdbmStore -- 56 | * 57 | * Register the gdbm store with shared variable implementation. 58 | * 59 | * Results: 60 | * None. 61 | * 62 | * Side effects: 63 | * None. 64 | * 65 | *----------------------------------------------------------------------------- 66 | */ 67 | void 68 | Sv_RegisterGdbmStore(void) 69 | { 70 | Sv_RegisterPsStore(&GdbmStore); 71 | } 72 | 73 | /* 74 | *----------------------------------------------------------------------------- 75 | * 76 | * ps_gdbm_open -- 77 | * 78 | * Opens the dbm-based persistent storage. 79 | * 80 | * Results: 81 | * Opaque handle of the opened dbm storage. 82 | * 83 | * Side effects: 84 | * The gdbm file might be created if not found. 85 | * 86 | *----------------------------------------------------------------------------- 87 | */ 88 | static ClientData 89 | ps_gdbm_open(path) 90 | const char *path; 91 | { 92 | GDBM_FILE dbf; 93 | char *ext; 94 | Tcl_DString toext; 95 | 96 | Tcl_DStringInit(&toext); 97 | ext = Tcl_UtfToExternalDString(NULL, (char*)path, strlen(path), &toext); 98 | dbf = gdbm_open(ext, 512, GDBM_WRCREAT|GDBM_SYNC|GDBM_NOLOCK, 0666, NULL); 99 | Tcl_DStringFree(&toext); 100 | 101 | return (ClientData)dbf; 102 | } 103 | 104 | /* 105 | *----------------------------------------------------------------------------- 106 | * 107 | * ps_gdbm_close -- 108 | * 109 | * Closes the gdbm-based persistent storage. 110 | * 111 | * Results: 112 | * 0 - ok 113 | * 114 | * Side effects: 115 | * None. 116 | * 117 | *----------------------------------------------------------------------------- 118 | */ 119 | static int 120 | ps_gdbm_close(handle) 121 | ClientData handle; 122 | { 123 | gdbm_close((GDBM_FILE)handle); 124 | 125 | return 0; 126 | } 127 | 128 | /* 129 | *----------------------------------------------------------------------------- 130 | * 131 | * ps_gdbm_get -- 132 | * 133 | * Retrieves data for the key from the dbm storage. 134 | * 135 | * Results: 136 | * 1 - no such key 137 | * 0 - ok 138 | * 139 | * Side effects: 140 | * Data returned must be freed by the caller. 141 | * 142 | *----------------------------------------------------------------------------- 143 | */ 144 | static int 145 | ps_gdbm_get(handle, key, dataptrptr, lenptr) 146 | ClientData handle; 147 | const char *key; 148 | char **dataptrptr; 149 | int *lenptr; 150 | { 151 | GDBM_FILE dbf = (GDBM_FILE)handle; 152 | datum drec, dkey; 153 | 154 | dkey.dptr = (char*)key; 155 | dkey.dsize = strlen(key) + 1; 156 | 157 | drec = gdbm_fetch(dbf, dkey); 158 | if (drec.dptr == NULL) { 159 | return 1; 160 | } 161 | 162 | *dataptrptr = drec.dptr; 163 | *lenptr = drec.dsize; 164 | 165 | return 0; 166 | } 167 | 168 | /* 169 | *----------------------------------------------------------------------------- 170 | * 171 | * ps_gdbm_first -- 172 | * 173 | * Starts the iterator over the dbm file and returns the first record. 174 | * 175 | * Results: 176 | * 1 - no more records in the iterator 177 | * 0 - ok 178 | * 179 | * Side effects: 180 | * Data returned must be freed by the caller. 181 | * 182 | *----------------------------------------------------------------------------- 183 | */ 184 | static int 185 | ps_gdbm_first(handle, keyptrptr, dataptrptr, lenptr) 186 | ClientData handle; 187 | char **keyptrptr; 188 | char **dataptrptr; 189 | int *lenptr; 190 | { 191 | GDBM_FILE dbf = (GDBM_FILE)handle; 192 | datum drec, dkey; 193 | 194 | dkey = gdbm_firstkey(dbf); 195 | if (dkey.dptr == NULL) { 196 | return 1; 197 | } 198 | drec = gdbm_fetch(dbf, dkey); 199 | if (drec.dptr == NULL) { 200 | return 1; 201 | } 202 | 203 | *dataptrptr = drec.dptr; 204 | *lenptr = drec.dsize; 205 | *keyptrptr = dkey.dptr; 206 | 207 | return 0; 208 | } 209 | 210 | /* 211 | *----------------------------------------------------------------------------- 212 | * 213 | * ps_gdbm_next -- 214 | * 215 | * Uses the iterator over the dbm file and returns the next record. 216 | * 217 | * Results: 218 | * 1 - no more records in the iterator 219 | * 0 - ok 220 | * 221 | * Side effects: 222 | * Data returned must be freed by the caller. 223 | * 224 | *----------------------------------------------------------------------------- 225 | */ 226 | static int ps_gdbm_next(handle, keyptrptr, dataptrptr, lenptr) 227 | ClientData handle; 228 | char **keyptrptr; 229 | char **dataptrptr; 230 | int *lenptr; 231 | { 232 | GDBM_FILE dbf = (GDBM_FILE)handle; 233 | datum drec, dkey, dnext; 234 | 235 | dkey.dptr = *keyptrptr; 236 | dkey.dsize = strlen(*keyptrptr) + 1; 237 | 238 | dnext = gdbm_nextkey(dbf, dkey); 239 | free(*keyptrptr), *keyptrptr = NULL; 240 | 241 | if (dnext.dptr == NULL) { 242 | return 1; 243 | } 244 | drec = gdbm_fetch(dbf, dnext); 245 | if (drec.dptr == NULL) { 246 | return 1; 247 | } 248 | 249 | *dataptrptr = drec.dptr; 250 | *lenptr = drec.dsize; 251 | *keyptrptr = dnext.dptr; 252 | 253 | return 0; 254 | } 255 | 256 | /* 257 | *----------------------------------------------------------------------------- 258 | * 259 | * ps_gdbm_put -- 260 | * 261 | * Stores used data bound to a key in dbm storage. 262 | * 263 | * Results: 264 | * 0 - ok 265 | * -1 - error; use ps_dbm_geterr to retrieve the error message 266 | * 267 | * Side effects: 268 | * If the key is already associated with some user data, this will 269 | * be replaced by the new data chunk. 270 | * 271 | *----------------------------------------------------------------------------- 272 | */ 273 | static int 274 | ps_gdbm_put(handle, key, dataptr, len) 275 | ClientData handle; 276 | const char *key; 277 | char *dataptr; 278 | int len; 279 | { 280 | GDBM_FILE dbf = (GDBM_FILE)handle; 281 | datum drec, dkey; 282 | int ret; 283 | 284 | dkey.dptr = (char*)key; 285 | dkey.dsize = strlen(key) + 1; 286 | 287 | drec.dptr = dataptr; 288 | drec.dsize = len; 289 | 290 | ret = gdbm_store(dbf, dkey, drec, GDBM_REPLACE); 291 | if (ret == -1) { 292 | return -1; 293 | } 294 | 295 | return 0; 296 | } 297 | 298 | /* 299 | *----------------------------------------------------------------------------- 300 | * 301 | * ps_gdbm_delete -- 302 | * 303 | * Deletes the key and associated data from the dbm storage. 304 | * 305 | * Results: 306 | * 0 - ok 307 | * -1 - error; use ps_dbm_geterr to retrieve the error message 308 | * 309 | * Side effects: 310 | * If the key is already associated with some user data, this will 311 | * be replaced by the new data chunk. 312 | * 313 | *----------------------------------------------------------------------------- 314 | */ 315 | static int 316 | ps_gdbm_delete(handle, key) 317 | ClientData handle; 318 | const char *key; 319 | { 320 | GDBM_FILE dbf = (GDBM_FILE)handle; 321 | datum dkey; 322 | int ret; 323 | 324 | dkey.dptr = (char*)key; 325 | dkey.dsize = strlen(key) + 1; 326 | 327 | ret = gdbm_delete(dbf, dkey); 328 | if (ret == -1) { 329 | return -1; 330 | } 331 | 332 | return 0; 333 | } 334 | 335 | /* 336 | *----------------------------------------------------------------------------- 337 | * 338 | * ps_gdbm_free -- 339 | * 340 | * Frees memory allocated by the gdbm implementation. 341 | * 342 | * Results: 343 | * None. 344 | * 345 | * Side effects: 346 | * Memory gets reclaimed. 347 | * 348 | *----------------------------------------------------------------------------- 349 | */ 350 | static void 351 | ps_gdbm_free(data) 352 | char *data; 353 | { 354 | free(data); 355 | } 356 | 357 | /* 358 | *----------------------------------------------------------------------------- 359 | * 360 | * ps_gdbm_geterr -- 361 | * 362 | * Retrieves the textual representation of the error caused 363 | * by the last dbm command. 364 | * 365 | * Results: 366 | * Pointer to the strimg message. 367 | * 368 | * Side effects: 369 | * None. 370 | * 371 | *----------------------------------------------------------------------------- 372 | */ 373 | static char* 374 | ps_gdbm_geterr(handle) 375 | ClientData handle; 376 | { 377 | /* 378 | * The problem with gdbm interface is that it uses the global 379 | * gdbm_errno variable which is not per-thread nor mutex 380 | * protected. This variable is used to reference array of gdbm 381 | * error text strings. It is very dangeours to use this in the 382 | * MT-program without proper locking. For this kind of app 383 | * we should not be concerned with that, since all ps_gdbm_xxx 384 | * operations are performed under shared variable lock anyway. 385 | */ 386 | 387 | return gdbm_strerror(gdbm_errno); 388 | } 389 | 390 | #endif /* HAVE_GDBM */ 391 | 392 | /* EOF $RCSfile*/ 393 | 394 | /* Emacs Setup Variables */ 395 | /* Local Variables: */ 396 | /* mode: C */ 397 | /* indent-tabs-mode: nil */ 398 | /* c-basic-offset: 4 */ 399 | /* End: */ 400 | -------------------------------------------------------------------------------- /doc/tpool.man: -------------------------------------------------------------------------------- 1 | [comment {-*- tcl -*- doctools manpage}] 2 | [manpage_begin tpool n 2.6] 3 | [moddesc {Tcl Threading}] 4 | [titledesc {Part of the Tcl threading extension implementing pools of worker threads.}] 5 | [require Tcl 8.4] 6 | [require Thread [opt 2.6]] 7 | 8 | [description] 9 | This package creates and manages pools of worker threads. It allows you 10 | to post jobs to worker threads and wait for their completion. The 11 | threadpool implementation is Tcl event-loop aware. That means that any 12 | time a caller is forced to wait for an event (job being completed or 13 | a worker thread becoming idle or initialized), the implementation will 14 | enter the event loop and allow for servicing of other pending file or 15 | timer (or any other supported) events. 16 | 17 | [section COMMANDS] 18 | 19 | [list_begin definitions] 20 | 21 | [call [cmd tpool::create] [opt options]] 22 | 23 | This command creates new threadpool. It accepts several options as 24 | key-value pairs. Options are used to tune some threadpool parameters. 25 | The command returns the ID of the newly created threadpool. 26 | [para] 27 | Following options are supported: 28 | 29 | [list_begin options] 30 | 31 | [opt_def -minworkers [arg number]] 32 | Minimum number of worker threads needed for this threadpool instance. 33 | During threadpool creation, the implementation will create somany 34 | worker threads upfront and will keep at least number of them alive 35 | during the lifetime of the threadpool instance. 36 | Default value of this parameter is 0 (zero). which means that a newly 37 | threadpool will have no worker threads initialy. All worker threads 38 | will be started on demand by callers running [cmd tpool::post] command 39 | and posting jobs to the job queue. 40 | 41 | [opt_def -maxworkers [arg number]] 42 | Maximum number of worker threads allowed for this threadpool instance. 43 | If a new job is pending and there are no idle worker threads available, 44 | the implementation will try to create new worker thread. If the number 45 | of available worker threads is lower than the given number, 46 | new worker thread will start. The caller will automatically enter the 47 | event loop and wait until the worker thread has initialized. If. however, 48 | the number of available worker threads is equal to the given number, 49 | the caller will enter the event loop and wait for the first worker thread 50 | to get idle, thus ready to run the job. 51 | Default value of this parameter is 4 (four), which means that the 52 | threadpool instance will allow maximum of 4 worker threads running jobs 53 | or being idle waiting for new jobs to get posted to the job queue. 54 | 55 | [opt_def -idletime [arg seconds]] 56 | Time in seconds an idle worker thread waits for the job to get posted 57 | to the job queue. If no job arrives during this interval and the time 58 | expires, the worker thread will check the number of currently available 59 | worker threads and if the number is higher than the number set by the 60 | [option minthreads] option, it will exit. 61 | If an [option exitscript] has been defined, the exiting worker thread 62 | will first run the script and then exit. Errors from the exit script, 63 | if any, are ignored. 64 | [para] 65 | The idle worker thread is not servicing the event loop. If you, however, 66 | put the worker thread into the event loop, by evaluating the 67 | [cmd vwait] or other related Tcl commands, the worker thread 68 | will not be in the idle state, hence the idle timer will not be 69 | taken into account. 70 | Default value for this option is unspecified, hence, the Tcl interpreter 71 | of the worker thread will contain just the initial set of Tcl commands. 72 | 73 | [opt_def -initcmd [arg script]] 74 | Sets a Tcl script used to initialize new worker thread. This is usually 75 | used to load packages and commands in the worker, set default variables, 76 | create namespaces, and such. If the passed script runs into a Tcl error, 77 | the worker will not be created and the initiating command (either the 78 | [cmd tpool::create] or [cmd tpool::post]) will throw error. 79 | Default value for this option is unspecified, hence, the Tcl interpreter of 80 | the worker thread will contain just the initial set of Tcl commands. 81 | 82 | [opt_def -exitcmd [arg script]] 83 | Sets a Tcl script run when the idle worker thread exits. This is normaly 84 | used to cleanup the state of the worker thread, release reserved resources, 85 | cleanup memory and such. 86 | Default value for this option is unspecified, thus no Tcl script will run 87 | on the worker thread exit. 88 | 89 | [list_end] 90 | 91 | [para] 92 | 93 | [call [cmd tpool::names]] 94 | 95 | This command returns a list of IDs of threadpools created with the 96 | [cmd tpool::create] command. If no threadpools were found, the 97 | command will return empty list. 98 | 99 | [call [cmd tpool::post] [opt -detached] [opt -nowait] [arg tpool] [arg script]] 100 | 101 | This command sends a [arg script] to the target [arg tpool] threadpool 102 | for execution. The script will be executed in the first available idle 103 | worker thread. If there are no idle worker threads available, the command 104 | will create new one, enter the event loop and service events until the 105 | newly created thread is initialized. If the current number of worker 106 | threads is equal to the maximum number of worker threads, as defined 107 | during the threadpool creation, the command will enter the event loop and 108 | service events while waiting for one of the worker threads to become idle. 109 | If the optional [opt -nowait] argument is given, the command will not wait 110 | for one idle worker. It will just place the job in the pool's job queue 111 | and return immediately. 112 | [para] 113 | The command returns the ID of the posted job. This ID is used for subsequent 114 | [cmd tpool::wait], [cmd tpool::get] and [cmd tpool::cancel] commands to wait 115 | for and retrieve result of the posted script, or cancel the posted job 116 | respectively. If the optional [opt -detached] argument is specified, the 117 | command will post a detached job. A detached job can not be cancelled or 118 | waited upon and is not identified by the job ID. 119 | [para] 120 | If the threadpool [arg tpool] is not found in the list of active 121 | thread pools, the command will throw error. The error will also be triggered 122 | if the newly created worker thread fails to initialize. 123 | 124 | [call [cmd tpool::wait] [arg tpool] [arg joblist] [opt varname]] 125 | 126 | This command waits for one or many jobs, whose job IDs are given in the 127 | [arg joblist] to get processed by the worker thread(s). If none of the 128 | specified jobs are ready, the command will enter the event loop, service 129 | events and wait for the first job to get ready. 130 | [para] 131 | The command returns the list of completed job IDs. If the optional variable 132 | [opt varname] is given, it will be set to the list of jobs in the 133 | [arg joblist] which are still pending. If the threadpool [arg tpool] 134 | is not found in the list of active thread pools, the command will throw error. 135 | 136 | [call [cmd tpool::cancel] [arg tpool] [arg joblist] [opt varname]] 137 | 138 | This command cancels the previously posted jobs given by the [arg joblist] 139 | to the pool [arg tpool]. Job cancellation succeeds only for job still 140 | waiting to be processed. If the job is already being executed by one of 141 | the worker threads, the job will not be cancelled. 142 | The command returns the list of cancelled job IDs. If the optional variable 143 | [opt varname] is given, it will be set to the list of jobs in the 144 | [arg joblist] which were not cancelled. If the threadpool [arg tpool] 145 | is not found in the list of active thread pools, the command will throw error. 146 | 147 | [call [cmd tpool::get] [arg tpool] [arg job]] 148 | 149 | This command retrieves the result of the previously posted [arg job]. 150 | Only results of jobs waited upon with the [cmd tpool::wait] command 151 | can be retrieved. If the execution of the script resulted in error, 152 | the command will throw the error and update the [var errorInfo] and 153 | [var errorCode] variables correspondingly. If the pool [arg tpool] 154 | is not found in the list of threadpools, the command will throw error. 155 | If the job [arg job] is not ready for retrieval, because it is currently 156 | being executed by the worker thread, the command will throw error. 157 | 158 | [call [cmd tpool::preserve] [arg tpool]] 159 | 160 | Each call to this command increments the reference counter of the 161 | threadpool [arg tpool] by one (1). Command returns the value of the 162 | reference counter after the increment. 163 | By incrementing the reference counter, the caller signalizes that 164 | he/she wishes to use the resource for a longer period of time. 165 | 166 | [call [cmd tpool::release] [arg tpool]] 167 | 168 | Each call to this command decrements the reference counter of the 169 | threadpool [arg tpool] by one (1).Command returns the value of the 170 | reference counter after the decrement. 171 | When the reference counter reaches zero (0), the threadpool [arg tpool] 172 | is marked for termination. You should not reference the threadpool 173 | after the [cmd tpool::release] command returns zero. The [arg tpool] 174 | handle goes out of scope and should not be used any more. Any following 175 | reference to the same threadpool handle will result in Tcl error. 176 | 177 | [call [cmd tpool::suspend] [arg tpool]] 178 | 179 | Suspends processing work on this queue. All pool workers are paused 180 | but additional work can be added to the pool. Note that adding the 181 | additional work will not increase the number of workers dynamically 182 | as the pool processing is suspended. Number of workers is maintained 183 | to the count that was found prior suspending worker activity. 184 | If you need to assure certain number of worker threads, use the 185 | [option minworkers] option of the [cmd tpool::create] command. 186 | 187 | [call [cmd tpool::resume] [arg tpool]] 188 | 189 | Resume processing work on this queue. All paused (suspended) 190 | workers are free to get work from the pool. Note that resuming pool 191 | operation will just let already created workers to proceed. 192 | It will not create additional worker threads to handle the work 193 | posted to the pool's work queue. 194 | 195 | [list_end] 196 | 197 | 198 | [section DISCUSSION] 199 | 200 | Threadpool is one of the most common threading paradigm when it comes 201 | to server applications handling a large number of relatively small tasks. 202 | A very simplistic model for building a server application would be to 203 | create a new thread each time a request arrives and service the request 204 | in the new thread. One of the disadvantages of this approach is that 205 | the overhead of creating a new thread for each request is significant; 206 | a server that created a new thread for each request would spend more time 207 | and consume more system resources in creating and destroying threads than 208 | in processing actual user requests. In addition to the overhead of 209 | creating and destroying threads, active threads consume system resources. 210 | Creating too many threads can cause the system to run out of memory or 211 | trash due to excessive memory consumption. 212 | [para] 213 | A thread pool offers a solution to both the problem of thread life-cycle 214 | overhead and the problem of resource trashing. By reusing threads for 215 | multiple tasks, the thread-creation overhead is spread over many tasks. 216 | As a bonus, because the thread already exists when a request arrives, 217 | the delay introduced by thread creation is eliminated. Thus, the request 218 | can be serviced immediately. Furthermore, by properly tuning the number 219 | of threads in the thread pool, resource thrashing may also be eliminated 220 | by forcing any request to wait until a thread is available to process it. 221 | 222 | [see_also tsv ttrace thread] 223 | 224 | [keywords thread threadpool] 225 | 226 | [manpage_end] 227 | -------------------------------------------------------------------------------- /generic/threadSvKeylistCmd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * threadSvKeylist.c -- 3 | * 4 | * This file implements keyed-list commands as part of the thread 5 | * shared variable implementation. 6 | * 7 | * Keyed list implementation is borrowed from Mark Diekhans and 8 | * Karl Lehenbauer "TclX" (extended Tcl) extension. Please look 9 | * into the keylist.c file for more information. 10 | * 11 | * See the file "license.txt" for information on usage and redistribution 12 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 | * 14 | * Rcsid: @(#)$Id$ 15 | * --------------------------------------------------------------------------- 16 | */ 17 | 18 | #include "threadSvCmd.h" 19 | #include "tclXkeylist.h" 20 | 21 | /* 22 | * This is defined in keylist.c. We need it here 23 | * to be able to plug-in our custom keyed-list 24 | * object duplicator which produces proper deep 25 | * copies of the keyed-list objects. The standard 26 | * one produces shallow copies which are not good 27 | * for usage in the thread shared variables code. 28 | */ 29 | 30 | extern Tcl_ObjType keyedListType; 31 | 32 | /* 33 | * Wrapped keyed-list commands 34 | */ 35 | 36 | static Tcl_ObjCmdProc SvKeylsetObjCmd; 37 | static Tcl_ObjCmdProc SvKeylgetObjCmd; 38 | static Tcl_ObjCmdProc SvKeyldelObjCmd; 39 | static Tcl_ObjCmdProc SvKeylkeysObjCmd; 40 | 41 | /* 42 | * This mutex protects a static variable which tracks 43 | * registration of commands and object types. 44 | */ 45 | 46 | static Tcl_Mutex initMutex; 47 | 48 | 49 | /* 50 | *----------------------------------------------------------------------------- 51 | * 52 | * Sv_RegisterKeylistCommands -- 53 | * 54 | * Register shared variable commands for TclX keyed lists. 55 | * 56 | * Results: 57 | * A standard Tcl result. 58 | * 59 | * Side effects: 60 | * Memory gets allocated 61 | * 62 | *----------------------------------------------------------------------------- 63 | */ 64 | void 65 | Sv_RegisterKeylistCommands(void) 66 | { 67 | static int initialized; 68 | 69 | if (initialized == 0) { 70 | Tcl_MutexLock(&initMutex); 71 | if (initialized == 0) { 72 | Sv_RegisterCommand("keylset", SvKeylsetObjCmd, NULL, NULL); 73 | Sv_RegisterCommand("keylget", SvKeylgetObjCmd, NULL, NULL); 74 | Sv_RegisterCommand("keyldel", SvKeyldelObjCmd, NULL, NULL); 75 | Sv_RegisterCommand("keylkeys", SvKeylkeysObjCmd, NULL, NULL); 76 | Sv_RegisterObjType(&keyedListType, DupKeyedListInternalRepShared); 77 | initialized = 1; 78 | } 79 | Tcl_MutexUnlock(&initMutex); 80 | } 81 | } 82 | 83 | /* 84 | *----------------------------------------------------------------------------- 85 | * 86 | * SvKeylsetObjCmd -- 87 | * 88 | * This procedure is invoked to process the "tsv::keylset" command. 89 | * See the user documentation for details on what it does. 90 | * 91 | * Results: 92 | * A standard Tcl result. 93 | * 94 | * Side effects: 95 | * See the user documentation. 96 | * 97 | *----------------------------------------------------------------------------- 98 | */ 99 | 100 | static int 101 | SvKeylsetObjCmd(arg, interp, objc, objv) 102 | ClientData arg; /* Not used. */ 103 | Tcl_Interp *interp; /* Current interpreter. */ 104 | int objc; /* Number of arguments. */ 105 | Tcl_Obj *const objv[]; /* Argument objects. */ 106 | { 107 | int i, off, ret, flg; 108 | char *key; 109 | Tcl_Obj *val; 110 | Container *svObj = (Container*)arg; 111 | 112 | /* 113 | * Syntax: 114 | * sv::keylset array lkey key value ?key value ...? 115 | * $keylist keylset key value ?key value ...? 116 | */ 117 | 118 | flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR; 119 | ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg); 120 | if (ret != TCL_OK) { 121 | return TCL_ERROR; 122 | } 123 | if ((objc - off) < 2 || ((objc - off) % 2)) { 124 | Tcl_WrongNumArgs(interp, off, objv, "key value ?key value ...?"); 125 | goto cmd_err; 126 | } 127 | for (i = off; i < objc; i += 2) { 128 | key = Tcl_GetString(objv[i]); 129 | val = Sv_DuplicateObj(objv[i+1]); 130 | ret = TclX_KeyedListSet(interp, svObj->tclObj, key, val); 131 | if (ret != TCL_OK) { 132 | goto cmd_err; 133 | } 134 | } 135 | 136 | return Sv_PutContainer(interp, svObj, SV_CHANGED); 137 | 138 | cmd_err: 139 | return Sv_PutContainer(interp, svObj, SV_ERROR); 140 | } 141 | 142 | /* 143 | *----------------------------------------------------------------------------- 144 | * 145 | * SvKeylgetObjCmd -- 146 | * 147 | * This procedure is invoked to process the "tsv::keylget" command. 148 | * See the user documentation for details on what it does. 149 | * 150 | * Results: 151 | * A standard Tcl result. 152 | * 153 | * Side effects: 154 | * See the user documentation. 155 | * 156 | *----------------------------------------------------------------------------- 157 | */ 158 | 159 | static int 160 | SvKeylgetObjCmd(arg, interp, objc, objv) 161 | ClientData arg; /* Not used. */ 162 | Tcl_Interp *interp; /* Current interpreter. */ 163 | int objc; /* Number of arguments. */ 164 | Tcl_Obj *const objv[]; /* Argument objects. */ 165 | { 166 | int ret, flg, off; 167 | char *key; 168 | Tcl_Obj *varObjPtr = NULL, *valObjPtr = NULL; 169 | Container *svObj = (Container*)arg; 170 | 171 | /* 172 | * Syntax: 173 | * sv::keylget array lkey ?key? ?var? 174 | * $keylist keylget ?key? ?var? 175 | */ 176 | 177 | flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR; 178 | ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg); 179 | if (ret != TCL_OK) { 180 | return TCL_ERROR; 181 | } 182 | if ((objc - off) > 2) { 183 | Tcl_WrongNumArgs(interp, off, objv, "?key? ?var?"); 184 | goto cmd_err; 185 | } 186 | if ((objc - off) == 0) { 187 | if (Sv_PutContainer(interp, svObj, SV_UNCHANGED) != TCL_OK) { 188 | return TCL_ERROR; 189 | } 190 | return SvKeylkeysObjCmd(arg, interp, objc, objv); 191 | } 192 | if ((objc - off) == 2) { 193 | varObjPtr = objv[off+1]; 194 | } else { 195 | varObjPtr = NULL; 196 | } 197 | 198 | key = Tcl_GetString(objv[off]); 199 | ret = TclX_KeyedListGet(interp, svObj->tclObj, key, &valObjPtr); 200 | if (ret == TCL_ERROR) { 201 | goto cmd_err; 202 | } 203 | 204 | if (ret == TCL_BREAK) { 205 | if (varObjPtr) { 206 | Tcl_ResetResult(interp); 207 | Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0); 208 | } else { 209 | Tcl_AppendResult (interp, "key \"", key, "\" not found", NULL); 210 | goto cmd_err; 211 | } 212 | } else { 213 | Tcl_Obj *resObjPtr = Sv_DuplicateObj(valObjPtr); 214 | if (varObjPtr) { 215 | int len; 216 | Tcl_ResetResult(interp); 217 | Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1); 218 | Tcl_GetStringFromObj(varObjPtr, &len); 219 | if (len) { 220 | Tcl_ObjSetVar2(interp, varObjPtr, NULL, resObjPtr, 0); 221 | } 222 | } else { 223 | Tcl_SetObjResult(interp, resObjPtr); 224 | } 225 | } 226 | 227 | return Sv_PutContainer(interp, svObj, SV_UNCHANGED); 228 | 229 | cmd_err: 230 | return Sv_PutContainer(interp, svObj, SV_ERROR); 231 | } 232 | 233 | /* 234 | *----------------------------------------------------------------------------- 235 | * 236 | * SvKeyldelObjCmd -- 237 | * 238 | * This procedure is invoked to process the "tsv::keyldel" command. 239 | * See the user documentation for details on what it does. 240 | * 241 | * Results: 242 | * A standard Tcl result. 243 | * 244 | * Side effects: 245 | * See the user documentation. 246 | * 247 | *----------------------------------------------------------------------------- 248 | */ 249 | 250 | static int 251 | SvKeyldelObjCmd(arg, interp, objc, objv) 252 | ClientData arg; /* Not used. */ 253 | Tcl_Interp *interp; /* Current interpreter. */ 254 | int objc; /* Number of arguments. */ 255 | Tcl_Obj *const objv[]; /* Argument objects. */ 256 | { 257 | int i, off, ret; 258 | char *key; 259 | Container *svObj = (Container*)arg; 260 | 261 | /* 262 | * Syntax: 263 | * sv::keyldel array lkey key ?key ...? 264 | * $keylist keyldel ?key ...? 265 | */ 266 | 267 | ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 268 | if (ret != TCL_OK) { 269 | return TCL_ERROR; 270 | } 271 | if ((objc - off) < 1) { 272 | Tcl_WrongNumArgs(interp, off, objv, "key ?key ...?"); 273 | goto cmd_err; 274 | } 275 | for (i = off; i < objc; i++) { 276 | key = Tcl_GetString(objv[i]); 277 | ret = TclX_KeyedListDelete(interp, svObj->tclObj, key); 278 | if (ret == TCL_BREAK) { 279 | Tcl_AppendResult(interp, "key \"", key, "\" not found", NULL); 280 | } 281 | if (ret == TCL_BREAK || ret == TCL_ERROR) { 282 | goto cmd_err; 283 | } 284 | } 285 | 286 | return Sv_PutContainer(interp, svObj, SV_CHANGED); 287 | 288 | cmd_err: 289 | return Sv_PutContainer(interp, svObj, SV_ERROR); 290 | } 291 | 292 | /* 293 | *----------------------------------------------------------------------------- 294 | * 295 | * SvKeylkeysObjCmd -- 296 | * 297 | * This procedure is invoked to process the "tsv::keylkeys" command. 298 | * See the user documentation for details on what it does. 299 | * 300 | * Results: 301 | * A standard Tcl result. 302 | * 303 | * Side effects: 304 | * See the user documentation. 305 | * 306 | *----------------------------------------------------------------------------- 307 | */ 308 | 309 | static int 310 | SvKeylkeysObjCmd(arg, interp, objc, objv) 311 | ClientData arg; /* Not used. */ 312 | Tcl_Interp *interp; /* Current interpreter. */ 313 | int objc; /* Number of arguments. */ 314 | Tcl_Obj *const objv[]; /* Argument objects. */ 315 | { 316 | int ret, off; 317 | char *key = NULL; 318 | Tcl_Obj *listObj = NULL; 319 | Container *svObj = (Container*)arg; 320 | 321 | /* 322 | * Syntax: 323 | * sv::keylkeys array lkey ?key? 324 | * $keylist keylkeys ?key? 325 | */ 326 | 327 | ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 328 | if (ret != TCL_OK) { 329 | return TCL_ERROR; 330 | } 331 | if ((objc - off) > 1) { 332 | Tcl_WrongNumArgs(interp, 1, objv, "?lkey?"); 333 | goto cmd_err; 334 | } 335 | if ((objc - off) == 1) { 336 | key = Tcl_GetString(objv[off]); 337 | } 338 | 339 | ret = TclX_KeyedListGetKeys(interp, svObj->tclObj, key, &listObj); 340 | 341 | if (key && ret == TCL_BREAK) { 342 | Tcl_AppendResult(interp, "key \"", key, "\" not found", NULL); 343 | } 344 | if (ret == TCL_BREAK || ret == TCL_ERROR) { 345 | goto cmd_err; 346 | } 347 | 348 | Tcl_SetObjResult (interp, listObj); /* listObj allocated by API !*/ 349 | 350 | return Sv_PutContainer(interp, svObj, SV_UNCHANGED); 351 | 352 | cmd_err: 353 | return Sv_PutContainer(interp, svObj, SV_ERROR); 354 | } 355 | 356 | /* EOF $RCSfile$ */ 357 | 358 | /* Emacs Setup Variables */ 359 | /* Local Variables: */ 360 | /* mode: C */ 361 | /* indent-tabs-mode: nil */ 362 | /* c-basic-offset: 4 */ 363 | /* End: */ 364 | 365 | -------------------------------------------------------------------------------- /tcl/phttpd/uhttpd.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # uhttpd.tcl -- 3 | # 4 | # Simple Sample httpd/1.0 server in 250 lines of Tcl. 5 | # Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems. 6 | # 7 | # Modified to use namespaces and direct url-to-procedure access (zv). 8 | # Eh, due to this, and nicer indenting, it's now 150 lines longer :-) 9 | # 10 | # Usage: 11 | # phttpd::create port 12 | # 13 | # port Tcp port where the server listens 14 | # 15 | # Example: 16 | # 17 | # # tclsh8.4 18 | # % source uhttpd.tcl 19 | # % uhttpd::create 5000 20 | # % vwait forever 21 | # 22 | # Starts the server on the port 5000. Also, look at the Httpd array 23 | # definition in the "uhttpd" namespace declaration to find out 24 | # about other options you may put on the command line. 25 | # 26 | # You can use: http://localhost:5000/monitor URL to test the 27 | # server functionality. 28 | # 29 | # Copyright (c) Stephen Uhler / Brent Welch (c) 1996 Sun Microsystems. 30 | # Copyright (c) 2002 by Zoran Vasiljevic. 31 | # 32 | # See the file "license.terms" for information on usage and 33 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. 34 | # 35 | # ----------------------------------------------------------------------------- 36 | # Rcsid: @(#)$Id$ 37 | # 38 | 39 | namespace eval uhttpd { 40 | 41 | variable Httpd; # Internal server state and config params 42 | variable MimeTypes; # Cache of file-extension/mime-type 43 | variable HttpCodes; # Portion of well-known http return codes 44 | variable ErrorPage; # Format of error response page in html 45 | 46 | array set Httpd { 47 | -name uhttpd 48 | -vers 1.0 49 | -root "" 50 | -index index.htm 51 | } 52 | array set HttpCodes { 53 | 400 "Bad Request" 54 | 401 "Not Authorized" 55 | 404 "Not Found" 56 | 500 "Server error" 57 | } 58 | array set MimeTypes { 59 | {} "text/plain" 60 | .txt "text/plain" 61 | .htm "text/html" 62 | .htm "text/html" 63 | .gif "image/gif" 64 | .jpg "image/jpeg" 65 | .png "image/png" 66 | } 67 | set ErrorPage { 68 | Error: %1$s %2$s 69 |

%3$s

70 |

Problem in accessing "%4$s" on this server.

71 |
72 | %5$s/%6$s Server at %7$s Port %8$s 73 | } 74 | } 75 | 76 | proc uhttpd::create {port args} { 77 | 78 | # @c Start the server by listening for connections on the desired port. 79 | 80 | variable Httpd 81 | set arglen [llength $args] 82 | 83 | if {$arglen} { 84 | if {$arglen % 2} { 85 | error "wrong \# arguments, should be: key1 val1 key2 val2..." 86 | } 87 | set opts [array names Httpd] 88 | foreach {arg val} $args { 89 | if {[lsearch $opts $arg] == -1} { 90 | error "unknown option \"$arg\"" 91 | } 92 | set Httpd($arg) $val 93 | } 94 | } 95 | 96 | set Httpd(port) $port 97 | set Httpd(host) [info hostname] 98 | 99 | socket -server [namespace current]::Accept $port 100 | } 101 | 102 | proc uhttpd::respond {s status contype data {length 0}} { 103 | 104 | puts $s "HTTP/1.0 $status" 105 | puts $s "Date: [Date]" 106 | puts $s "Content-Type: $contype" 107 | 108 | if {$length} { 109 | puts $s "Content-Length: $length" 110 | } else { 111 | puts $s "Content-Length: [string length $data]" 112 | } 113 | 114 | puts $s "" 115 | puts $s $data 116 | } 117 | 118 | proc uhttpd::Accept {newsock ipaddr port} { 119 | 120 | # @c Accept a new connection from the client. 121 | 122 | variable Httpd 123 | upvar \#0 [namespace current]::Httpd$newsock data 124 | 125 | fconfigure $newsock -blocking 0 -translation {auto crlf} 126 | 127 | set data(ipaddr) $ipaddr 128 | fileevent $newsock readable [list [namespace current]::Read $newsock] 129 | } 130 | 131 | proc uhttpd::Read {s} { 132 | 133 | # @c Read data from client 134 | 135 | variable Httpd 136 | upvar \#0 [namespace current]::Httpd$s data 137 | 138 | if {[catch {gets $s line} readCount] || [eof $s]} { 139 | return [Done $s] 140 | } 141 | if {$readCount == -1} { 142 | return ;# Insufficient data on non-blocking socket ! 143 | } 144 | if {![info exists data(state)]} { 145 | set pat {(POST|GET) ([^?]+)\??([^ ]*) HTTP/1\.[0-9]} 146 | if {[regexp $pat $line x data(proto) data(url) data(query)]} { 147 | return [set data(state) mime] 148 | } else { 149 | Log error "bad request line: %s" $line 150 | Error $s 400 151 | return [Done $s] 152 | } 153 | } 154 | 155 | # string compare $readCount 0 maps -1 to -1, 0 to 0, and > 0 to 1 156 | 157 | set state [string compare $readCount 0],$data(state),$data(proto) 158 | switch -- $state { 159 | "0,mime,GET" - "0,query,POST" { 160 | Respond $s 161 | } 162 | "0,mime,POST" { 163 | set data(state) query 164 | set data(query) "" 165 | } 166 | "1,mime,POST" - "1,mime,GET" { 167 | if [regexp {([^:]+):[ ]*(.*)} $line dummy key value] { 168 | set data(mime,[string tolower $key]) $value 169 | } 170 | } 171 | "1,query,POST" { 172 | append data(query) $line 173 | set clen $data(mime,content-length) 174 | if {($clen - [string length $data(query)]) <= 0} { 175 | Respond $s 176 | } 177 | } 178 | default { 179 | if [eof $s] { 180 | Log error "unexpected eof; client closed connection" 181 | return [Done $s] 182 | } else { 183 | Log error "bad http protocol state: %s" $state 184 | Error $s 400 185 | return [Done $s] 186 | } 187 | } 188 | } 189 | } 190 | 191 | proc uhttpd::Done {s} { 192 | 193 | # @c Close the connection socket and discard token 194 | 195 | close $s 196 | unset [namespace current]::Httpd$s 197 | } 198 | 199 | proc uhttpd::Respond {s} { 200 | 201 | # @c Respond to the query. 202 | 203 | variable Httpd 204 | upvar \#0 [namespace current]::Httpd$s data 205 | 206 | if {[uplevel \#0 info proc $data(url)] == $data(url)} { 207 | 208 | # 209 | # Service URL-procedure first 210 | # 211 | 212 | if {[catch { 213 | puts $s "HTTP/1.0 200 OK" 214 | puts $s "Date: [Date]" 215 | puts $s "Last-Modified: [Date]" 216 | } err]} { 217 | Log error "client closed connection prematurely: %s" $err 218 | return [Done $s] 219 | } 220 | set data(sock) $s 221 | if {[catch {$data(url) data} err]} { 222 | Log error "%s: %s" $data(url) $err 223 | } 224 | 225 | } else { 226 | 227 | # 228 | # Service regular file path next. 229 | # 230 | 231 | set mypath [Url2File $data(url)] 232 | if {![catch {open $mypath} i]} { 233 | if {[catch { 234 | puts $s "HTTP/1.0 200 OK" 235 | puts $s "Date: [Date]" 236 | puts $s "Last-Modified: [Date [file mtime $mypath]]" 237 | puts $s "Content-Type: [ContentType $mypath]" 238 | puts $s "Content-Length: [file size $mypath]" 239 | puts $s "" 240 | fconfigure $s -translation binary -blocking 0 241 | fconfigure $i -translation binary 242 | fcopy $i $s 243 | close $i 244 | } err]} { 245 | Log error "client closed connection prematurely: %s" $err 246 | } 247 | } else { 248 | Log error "%s: %s" $data(url) $i 249 | Error $s 404 250 | } 251 | } 252 | 253 | Done $s 254 | } 255 | 256 | proc uhttpd::ContentType {path} { 257 | 258 | # @c Convert the file suffix into a mime type. 259 | 260 | variable MimeTypes 261 | 262 | set type "text/plain" 263 | catch {set type $MimeTypes([file extension $path])} 264 | 265 | return $type 266 | } 267 | 268 | proc uhttpd::Error {s code} { 269 | 270 | # @c Emit error page. 271 | 272 | variable Httpd 273 | variable HttpCodes 274 | variable ErrorPage 275 | 276 | upvar \#0 [namespace current]::Httpd$s data 277 | 278 | append data(url) "" 279 | set msg \ 280 | [format $ErrorPage \ 281 | $code \ 282 | $HttpCodes($code) \ 283 | $HttpCodes($code) \ 284 | $data(url) \ 285 | $Httpd(-name) \ 286 | $Httpd(-vers) \ 287 | $Httpd(host) \ 288 | $Httpd(port) \ 289 | ] 290 | if {[catch { 291 | puts $s "HTTP/1.0 $code $HttpCodes($code)" 292 | puts $s "Date: [Date]" 293 | puts $s "Content-Length: [string length $msg]" 294 | puts $s "" 295 | puts $s $msg 296 | } err]} { 297 | Log error "client closed connection prematurely: %s" $err 298 | } 299 | } 300 | 301 | proc uhttpd::Date {{seconds 0}} { 302 | 303 | # @c Generate a date string in HTTP format. 304 | 305 | if {$seconds == 0} { 306 | set seconds [clock seconds] 307 | } 308 | clock format $seconds -format {%a, %d %b %Y %T %Z} -gmt 1 309 | } 310 | 311 | proc uhttpd::Log {reason format args} { 312 | 313 | # @c Log an httpd transaction. 314 | 315 | set messg [eval format [list $format] $args] 316 | set stamp [clock format [clock seconds] -format "%d/%b/%Y:%H:%M:%S"] 317 | 318 | puts stderr "\[$stamp\] $reason: $messg" 319 | } 320 | 321 | proc uhttpd::Url2File {url} { 322 | 323 | # @c Convert a url into a pathname (this is probably not right) 324 | 325 | variable Httpd 326 | 327 | lappend pathlist $Httpd(-root) 328 | set level 0 329 | 330 | foreach part [split $url /] { 331 | set part [CgiMap $part] 332 | if [regexp {[:/]} $part] { 333 | return "" 334 | } 335 | switch -- $part { 336 | "." { } 337 | ".." {incr level -1} 338 | default {incr level} 339 | } 340 | if {$level <= 0} { 341 | return "" 342 | } 343 | lappend pathlist $part 344 | } 345 | 346 | set file [eval file join $pathlist] 347 | 348 | if {[file isdirectory $file]} { 349 | return [file join $file $Httpd(-index)] 350 | } else { 351 | return $file 352 | } 353 | } 354 | 355 | proc uhttpd::CgiMap {data} { 356 | 357 | # @c Decode url-encoded strings 358 | 359 | regsub -all {\+} $data { } data 360 | regsub -all {([][$\\])} $data {\\\1} data 361 | regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $data {[format %c 0x\1]} data 362 | 363 | return [subst $data] 364 | } 365 | 366 | proc uhttpd::QueryMap {query} { 367 | 368 | # @c Decode url-encoded query into key/value pairs 369 | 370 | set res [list] 371 | 372 | regsub -all {[&=]} $query { } query 373 | regsub -all { } $query { {} } query; # Othewise we lose empty values 374 | 375 | foreach {key val} $query { 376 | lappend res [CgiMap $key] [CgiMap $val] 377 | } 378 | return $res 379 | } 380 | 381 | proc /monitor {array} { 382 | 383 | upvar $array data ; # Holds the socket to remote client 384 | 385 | # 386 | # Emit headers 387 | # 388 | 389 | puts $data(sock) "HTTP/1.0 200 OK" 390 | puts $data(sock) "Date: [uhttpd::Date]" 391 | puts $data(sock) "Content-Type: text/html" 392 | puts $data(sock) "" 393 | 394 | # 395 | # Emit body 396 | # 397 | 398 | puts $data(sock) [subst { 399 | 400 | 401 |

[clock format [clock seconds]]

402 | }] 403 | 404 | after 1 ; # Simulate blocking call 405 | 406 | puts $data(sock) [subst { 407 | 408 | 409 | }] 410 | } 411 | 412 | # EOF $RCSfile$ 413 | # Emacs Setup Variables 414 | # Local Variables: 415 | # mode: Tcl 416 | # indent-tabs-mode: nil 417 | # tcl-basic-offset: 4 418 | # End: 419 | 420 | -------------------------------------------------------------------------------- /doc/html/tpool.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | tpool - Tcl Threading 5 | 6 | 8 | 10 | 11 | 12 |

tpool(n) 2.6 "Tcl Threading"

13 |

NAME

14 |

15 |

tpool - 16 | Part of the Tcl threading extension implementing pools of worker threads. 17 | 18 | 19 | 20 | 21 | 22 | 23 |

TABLE OF CONTENTS

24 |

    TABLE OF CONTENTS
25 |     SYNOPSIS
26 |     DESCRIPTION
27 |     COMMANDS
28 |     DISCUSSION
29 |     SEE ALSO
30 |     KEYWORDS
31 |

SYNOPSIS

32 |

33 | package require Tcl 8.4
34 | package require Thread ?2.6?
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
tpool::create ?options?
tpool::names
tpool::post ?-detached? ?-nowait? tpool script
tpool::wait tpool joblist ?varname?
tpool::cancel tpool joblist ?varname?
tpool::get tpool job
tpool::preserve tpool
tpool::release tpool
44 |

DESCRIPTION

45 |

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

COMMANDS

55 |

56 | 57 |

58 | 59 |
tpool::create ?options?
60 | 61 | 62 | This command creates new threadpool. It accepts several options as 63 | key-value pairs. Options are used to tune some threadpool parameters. 64 | The command returns the ID of the newly created threadpool. 65 |

66 | Following options are supported: 67 | 68 |

69 |
70 | 71 |
-minworkers number
72 | Minimum number of worker threads needed for this threadpool instance. 73 | During threadpool creation, the implementation will create somany 74 | worker threads upfront and will keep at least number of them alive 75 | during the lifetime of the threadpool instance. 76 | Default value of this parameter is 0 (zero). which means that a newly 77 | threadpool will have no worker threads initialy. All worker threads 78 | will be started on demand by callers running tpool::post command 79 | and posting jobs to the job queue. 80 | 81 |

82 |
-maxworkers number
83 | Maximum number of worker threads allowed for this threadpool instance. 84 | If a new job is pending and there are no idle worker threads available, 85 | the implementation will try to create new worker thread. If the number 86 | of available worker threads is lower than the given number, 87 | new worker thread will start. The caller will automatically enter the 88 | event loop and wait until the worker thread has initialized. If. however, 89 | the number of available worker threads is equal to the given number, 90 | the caller will enter the event loop and wait for the first worker thread 91 | to get idle, thus ready to run the job. 92 | Default value of this parameter is 4 (four), which means that the 93 | threadpool instance will allow maximum of 4 worker threads running jobs 94 | or being idle waiting for new jobs to get posted to the job queue. 95 | 96 | 97 |

98 |
-idletime seconds
99 | Time in seconds an idle worker thread waits for the job to get posted 100 | to the job queue. If no job arrives during this interval and the time 101 | expires, the worker thread will check the number of currently available 102 | worker threads and if the number is higher than the number set by the 103 | minthreads option, it will exit. 104 | If an exitscript has been defined, the exiting worker thread 105 | will first run the script and then exit. Errors from the exit script, 106 | if any, are ignored. 107 |

108 | The idle worker thread is not servicing the event loop. If you, however, 109 | put the worker thread into the event loop, by evaluating the 110 | vwait or other related Tcl commands, the worker thread 111 | will not be in the idle state, hence the idle timer will not be 112 | taken into account. 113 | Default value for this option is unspecified, hence, the Tcl interpreter 114 | of the worker thread will contain just the initial set of Tcl commands. 115 | 116 |

117 |
-initcmd script
118 | 119 | Sets a Tcl script used to initialize new worker thread. This is usually 120 | used to load packages and commands in the worker, set default variables, 121 | create namespaces, and such. If the passed script runs into a Tcl error, 122 | the worker will not be created and the initiating command (either the 123 | tpool::create or tpool::post) will throw error. 124 | Default value for this option is unspecified, hence, the Tcl interpreter of 125 | the worker thread will contain just the initial set of Tcl commands. 126 | 127 |

128 |
-exitcmd script
129 | 130 | Sets a Tcl script run when the idle worker thread exits. This is normaly 131 | used to cleanup the state of the worker thread, release reserved resources, 132 | cleanup memory and such. 133 | Default value for this option is unspecified, thus no Tcl script will run 134 | on the worker thread exit. 135 | 136 |
137 | 138 |

139 | 140 |
tpool::names
141 | 142 | 143 | This command returns a list of IDs of threadpools created with the 144 | tpool::create command. If no threadpools were found, the 145 | command will return empty list. 146 | 147 |

148 |
tpool::post ?-detached? ?-nowait? tpool script
149 | 150 | 151 | This command sends a script to the target tpool threadpool 152 | for execution. The script will be executed in the first available idle 153 | worker thread. If there are no idle worker threads available, the command 154 | will create new one, enter the event loop and service events until the 155 | newly created thread is initialized. If the current number of worker 156 | threads is equal to the maximum number of worker threads, as defined 157 | during the threadpool creation, the command will enter the event loop and 158 | service events while waiting for one of the worker threads to become idle. 159 | If the optional ?-nowait? argument is given, the command will not wait 160 | for one idle worker. It will just place the job in the pool's job queue 161 | and return immediately. 162 |

163 | The command returns the ID of the posted job. This ID is used for subsequent 164 | tpool::wait, tpool::get and tpool::cancel commands to wait 165 | for and retrieve result of the posted script, or cancel the posted job 166 | respectively. If the optional ?-detached? argument is specified, the 167 | command will post a detached job. A detached job can not be cancelled or 168 | waited upon and is not identified by the job ID. 169 |

170 | If the threadpool tpool is not found in the list of active 171 | thread pools, the command will throw error. The error will also be triggered 172 | if the newly created worker thread fails to initialize. 173 | 174 |

175 |
tpool::wait tpool joblist ?varname?
176 | 177 | 178 | This command waits for one or many jobs, whose job IDs are given in the 179 | joblist to get processed by the worker thread(s). If none of the 180 | specified jobs are ready, the command will enter the event loop, service 181 | events and wait for the first job to get ready. 182 |

183 | The command returns the list of completed job IDs. If the optional variable 184 | ?varname? is given, it will be set to the list of jobs in the 185 | joblist which are still pending. If the threadpool tpool 186 | is not found in the list of active thread pools, the command will throw error. 187 | 188 |

189 |
tpool::cancel tpool joblist ?varname?
190 | 191 | 192 | This command cancels the previously posted jobs given by the joblist 193 | to the pool tpool. Job cancellation succeeds only for job still 194 | waiting to be processed. If the job is already being executed by one of 195 | the worker threads, the job will not be cancelled. 196 | The command returns the list of cancelled job IDs. If the optional variable 197 | ?varname? is given, it will be set to the list of jobs in the 198 | joblist which were not cancelled. If the threadpool tpool 199 | is not found in the list of active thread pools, the command will throw error. 200 | 201 |

202 |
tpool::get tpool job
203 | 204 | 205 | This command retrieves the result of the previously posted job. 206 | Only results of jobs waited upon with the tpool::wait command 207 | can be retrieved. If the execution of the script resulted in error, 208 | the command will throw the error and update the errorInfo and 209 | errorCode variables correspondingly. If the pool tpool 210 | is not found in the list of threadpools, the command will throw error. 211 | If the job job is not ready for retrieval, because it is currently 212 | being executed by the worker thread, the command will throw error. 213 | 214 |

215 |
tpool::preserve tpool
216 | 217 | 218 | Each call to this command increments the reference counter of the 219 | threadpool tpool by one (1). Command returns the value of the 220 | reference counter after the increment. 221 | By incrementing the reference counter, the caller signalizes that 222 | he/she wishes to use the resource for a longer period of time. 223 | 224 |

225 |
tpool::release tpool
226 | 227 | 228 | Each call to this command decrements the reference counter of the 229 | threadpool tpool by one (1).Command returns the value of the 230 | reference counter after the decrement. 231 | When the reference counter reaches zero (0), the threadpool tpool 232 | is marked for termination. You should not reference the threadpool 233 | after the tpool::release command returns zero. The tpool 234 | handle goes out of scope and should not be used any more. Any following 235 | reference to the same threadpool handle will result in Tcl error. 236 | 237 |
238 | 239 | 240 |

DISCUSSION

241 |

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

256 | A thread pool offers a solution to both the problem of thread life-cycle 257 | overhead and the problem of resource trashing. By reusing threads for 258 | multiple tasks, the thread-creation overhead is spread over many tasks. 259 | As a bonus, because the thread already exists when a request arrives, 260 | the delay introduced by thread creation is eliminated. Thus, the request 261 | can be serviced immediately. Furthermore, by properly tuning the number 262 | of threads in the thread pool, resource thrashing may also be eliminated 263 | by forcing any request to wait until a thread is available to process it. 264 | 265 | 266 | 267 | 268 | 269 |

SEE ALSO

270 |

271 | thread, tsv, ttrace 272 |

KEYWORDS

273 |

274 | thread, threadpool 275 | 276 | 277 | -------------------------------------------------------------------------------- /doc/tsv.man: -------------------------------------------------------------------------------- 1 | [comment {-*- tcl -*- doctools manpage}] 2 | [manpage_begin tsv n 2.6] 3 | [moddesc {Tcl Threading}] 4 | [titledesc {Part of the Tcl threading extension allowing script level manipulation of data shared between threads.}] 5 | [require Tcl 8.4] 6 | [require Thread [opt 2.6]] 7 | 8 | [description] 9 | This section describes commands implementing thread shared variables. 10 | A thread shared variable is very similar to a Tcl array but in 11 | contrast to a Tcl array it is created in shared memory and can 12 | be accessed from many threads at the same time. Important feature of 13 | thread shared variable is that each access to the variable is internaly 14 | protected by a mutex so script programmer does not have to take care 15 | about locking the variable himself. 16 | [para] 17 | Thread shared variables are not bound to any thread explicitly. That 18 | means that when a thread which created any of thread shared variables 19 | exits, the variable and associated memory is not unset/reclaimed. 20 | User has to explicitly unset the variable to reclaim the memory 21 | consumed by the variable. 22 | 23 | [section {ELEMENT COMMANDS}] 24 | 25 | [list_begin definitions] 26 | 27 | [call [cmd tsv::names] [opt pattern]] 28 | 29 | Returns names of shared variables matching optional [opt pattern] 30 | or all known variables if pattern is ommited. 31 | 32 | [call [cmd tsv::object] [arg varname] [arg element]] 33 | 34 | Creates object accessor command for the [arg element] in the 35 | shared variable [arg varname]. Using this command, one can apply most 36 | of the other shared variable commands as method functions of 37 | the element object command. The object command is automatically 38 | deleted when the element which this command is pointing to is unset. 39 | 40 | [example { 41 | % tsv::set foo bar "A shared string" 42 | % set string [tsv::object foo bar] 43 | % $string append " appended" 44 | => A shared string appended 45 | }] 46 | 47 | [call [cmd tsv::set] [arg varname] [arg element] [opt value]] 48 | 49 | Sets the value of the [arg element] in the shared variable [arg varname] 50 | to [arg value] and returns the value to caller. The [arg value] 51 | may be ommited, in which case the command will return the current 52 | value of the element. If the element cannot be found, error is triggered. 53 | 54 | [call [cmd tsv::get] [arg varname] [arg element] [opt namedvar]] 55 | 56 | Retrieves the value of the [arg element] from the shared variable [arg varname]. 57 | If the optional argument [arg namedvar] is given, the value is 58 | stored in the named variable. Return value of the command depends 59 | of the existence of the optional argument [arg namedvar]. 60 | If the argument is ommited and the requested element cannot be found 61 | in the shared array, the command triggers error. If, however, the 62 | optional argument is given on the command line, the command returns 63 | true (1) if the element is found or false (0) if the element is not found. 64 | 65 | [call [cmd tsv::unset] [arg varname] [opt element]] 66 | 67 | Unsets the [arg element] from the shared variable [arg varname]. 68 | If the optional element is not given, it deletes the variable. 69 | 70 | [call [cmd tsv::exists] [arg varname] [arg element]] 71 | 72 | Checks wether the [arg element] exists in the shared variable [arg varname] 73 | and returns true (1) if it does or false (0) if it doesn't. 74 | 75 | [call [cmd tsv::pop] [arg varname] [arg element]] 76 | 77 | Returns value of the [arg element] in the shared variable [arg varname] 78 | and unsets the element, all in one atomic operation. 79 | 80 | [call [cmd tsv::move] [arg varname] [arg oldname] [arg newname]] 81 | 82 | Renames the element [arg oldname] to the [arg newname] in the 83 | shared variable [arg varname]. This effectively performs an get/unset/set 84 | sequence of operations but all in one atomic step. 85 | 86 | [call [cmd tsv::incr] [arg varname] [arg element] [opt count]] 87 | 88 | Similar to standard Tcl [cmd incr] command but increments the value 89 | of the [arg element] in shared variaboe [arg varname] instead of 90 | the Tcl variable. 91 | 92 | [call [cmd tsv::append] [arg varname] [arg element] [arg value] [opt {value ...}]] 93 | 94 | Similar to standard Tcl [cmd append] command but appends one or more 95 | values to the [arg element] in shared variable [arg varname] instead of the 96 | Tcl variable. 97 | 98 | [call [cmd tsv::lock] [arg varname] [arg arg] [opt {arg ...}]] 99 | 100 | This command concatenates passed arguments and evaluates the 101 | resulting script under the internal mutex protection. During the 102 | script evaluation, the entire shared variable is locked. For shared 103 | variable commands within the script, internal locking is disabled 104 | so no deadlock can occur. It is also allowed to unset the shared 105 | variable from within the script. The shared variable is automatically 106 | created if it did not exists at the time of the first lock operation. 107 | 108 | [example { 109 | % tsv::lock foo { 110 | tsv::lappend foo bar 1 111 | tsv::lappend foo bar 2 112 | puts stderr [tsv::set foo bar] 113 | tsv::unset foo 114 | } 115 | }] 116 | 117 | [list_end] 118 | 119 | [section {LIST COMMANDS}] 120 | 121 | Those command are similar to the equivalently named Tcl command. The difference 122 | is that they operate on elements of shared arrays. 123 | 124 | [list_begin definitions] 125 | 126 | [call [cmd tsv::lappend] [arg varname] [arg element] [arg value] [opt {value ...}]] 127 | 128 | Similar to standard Tcl [cmd lappend] command but appends one 129 | or more values to the [arg element] in shared variable [arg varname] 130 | instead of the Tcl variable. 131 | 132 | [call [cmd tsv::linsert] [arg varname] [arg element] [arg index] [arg value] [opt {value ...}]] 133 | 134 | Similar to standard Tcl [cmd linsert] command but inserts one 135 | or more values at the [arg index] list position in the 136 | [arg element] in the shared variable [arg varname] instead of the Tcl variable. 137 | 138 | [call [cmd tsv::lreplace] [arg varname] [arg element] [arg first] [arg last] [opt {value ...}]] 139 | 140 | Similar to standard Tcl [cmd lreplace] command but replaces one 141 | or more values between the [arg first] and [arg last] position 142 | in the [arg element] of the shared variable [arg varname] instead of 143 | the Tcl variable. 144 | 145 | [call [cmd tsv::llength] [arg varname] [arg element]] 146 | 147 | Similar to standard Tcl [cmd llength] command but returns length 148 | of the [arg element] in the shared variable [arg varname] instead of the Tcl 149 | variable. 150 | 151 | [call [cmd tsv::lindex] [arg varname] [arg element] [opt index]] 152 | 153 | Similar to standard Tcl [cmd lindex] command but returns the value 154 | at the [arg index] list position of the [arg element] from 155 | the shared variable [arg varname] instead of the Tcl variable. 156 | 157 | [call [cmd tsv::lrange] [arg varname] [arg element] [arg from] [arg to]] 158 | 159 | Similar to standard Tcl [cmd lrange] command but returns values 160 | between [arg from] and [arg to] list positions from the 161 | [arg element] in the shared variable [arg varname] instead of the Tcl variable. 162 | 163 | [call [cmd tsv::lsearch] [arg varname] [arg element] [opt options] [arg pattern]] 164 | 165 | Similar to standard Tcl [cmd lsearch] command but searches the [arg element] 166 | in the shared variable [arg varname] instead of the Tcl variable. 167 | 168 | [call [cmd tsv::lset] [arg varname] [arg element] [arg index] [opt {index ...}] [arg value]] 169 | 170 | Similar to standard Tcl [cmd lset] command but sets the [arg element] 171 | in the shared variable [arg varname] instead of the Tcl variable. 172 | 173 | [call [cmd tsv::lpop] [arg varname] [arg element] [opt index]] 174 | 175 | Similar to the standard Tcl [cmd lindex] command but in addition to 176 | returning, it also splices the value out of the [arg element] 177 | from the shared variable [arg varname] in one atomic operation. 178 | In contrast to the Tcl [cmd lindex] command, this command returns 179 | no value to the caller. 180 | 181 | [call [cmd tsv::lpush] [arg varname] [arg element] [opt index]] 182 | 183 | This command performes the opposite of the [cmd tsv::lpop] command. 184 | As its counterpart, it returns no value to the caller. 185 | 186 | [list_end] 187 | 188 | [section {ARRAY COMMANDS}] 189 | 190 | This command supports most of the options of the standard Tcl 191 | [cmd array] command. In addition to those, it allows binding 192 | a shared variable to some persisten storage databases. Currently 193 | the only persistent option supported is the famous GNU Gdbm 194 | database. This option has to be selected during the package 195 | compilation time. The implementation provides hooks for 196 | defining other persistency layers, if needed. 197 | 198 | [list_begin definitions] 199 | 200 | [call [cmd {tsv::array set}] [arg varname] [arg list]] 201 | 202 | Does the same as standard Tcl [cmd {array set}]. 203 | 204 | [call [cmd {tsv::array get}] [arg varname] [opt pattern]] 205 | 206 | Does the same as standard Tcl [cmd {array get}]. 207 | 208 | [call [cmd {tsv::array names}] [arg varname] [opt pattern]] 209 | 210 | Does the same as standard Tcl [cmd {array names}]. 211 | 212 | [call [cmd {tsv::array size}] [arg varname]] 213 | 214 | Does the same as standard Tcl [cmd {array size}]. 215 | 216 | [call [cmd {tsv::array reset}] [arg varname] [arg list]] 217 | 218 | Does the same as standard Tcl [cmd {array set}] but it clears 219 | the [arg varname] and sets new values from the list atomically. 220 | 221 | [call [cmd {tsv::array bind}] [arg varname] [arg handle]] 222 | Binds the [arg varname] to the persistent storage [arg handle]. 223 | The format of the [arg handle] is :

. For the built-in 224 | GNU Gdbm persistence layer, the format of the handle is "gdbm:" 225 | where is the path to the Gdbm database file. 226 | 227 | [call [cmd {tsv::array unbind}] [arg varname]] 228 | Unbinds the shared [arg array] from its bound persistent storage. 229 | 230 | [call [cmd {tsv::array isbound}] [arg varname]] 231 | Returns true (1) if the shared [arg varname] is bound to some 232 | persistent storage or zero (0) if not. 233 | 234 | 235 | [list_end] 236 | 237 | [section {KEYED LIST COMMANDS}] 238 | 239 | Keyed list commands are borrowed from the TclX package. Keyed lists provide 240 | a structured data type built upon standard Tcl lists. This is a functionality 241 | similar to structs in the C programming language. 242 | [para] 243 | A keyed list is a list in which each element contains a key and value 244 | pair. These element pairs are stored as lists themselves, where the key 245 | is the first element of the list, and the value is the second. The 246 | key-value pairs are referred to as fields. This is an example of a 247 | keyed list: 248 | 249 | [example { 250 | {{NAME {Frank Zappa}} {JOB {musician and composer}}} 251 | }] 252 | 253 | Fields may contain subfields; `.' is the separator character. Subfields 254 | are actually fields where the value is another keyed list. Thus the 255 | following list has the top level fields ID and NAME, and subfields 256 | NAME.FIRST and NAME.LAST: 257 | 258 | [example { 259 | {ID 106} {NAME {{FIRST Frank} {LAST Zappa}}} 260 | }] 261 | 262 | There is no limit to the recursive depth of subfields, 263 | allowing one to build complex data structures. Keyed lists are constructed 264 | and accessed via a number of commands. All keyed list management 265 | commands take the name of the variable containing the keyed list as an 266 | argument (i.e. passed by reference), rather than passing the list directly. 267 | 268 | [list_begin definitions] 269 | 270 | [call [cmd tsv::keyldel] [arg varname] [arg keylist] [arg key]] 271 | 272 | Delete the field specified by [arg key] from the keyed list [arg keylist] 273 | in the shared variable [arg varname]. 274 | This removes both the key and the value from the keyed list. 275 | 276 | [call [cmd tsv::keylget] [arg varname] [arg keylist] [arg key] [opt retvar]] 277 | 278 | Return the value associated with [arg key] from the keyed list [arg keylist] 279 | in the shared variable [arg varname]. 280 | If the optional [arg retvar] is not specified, then the value will be 281 | returned as the result of the command. In this case, if key is not found 282 | in the list, an error will result. 283 | [para] 284 | If [arg retvar] is specified and [arg key] is in the list, then the value 285 | is returned in the variable [arg retvar] and the command returns 1 if the 286 | key was present within the list. If [arg key] isn't in the list, the 287 | command will return 0, and [arg retvar] will be left unchanged. If {} is 288 | specified for [arg retvar], the value is not returned, allowing the Tcl 289 | programmer to determine if a [arg key] is present in a keyed list without 290 | setting a variable as a side-effect. 291 | 292 | [call [cmd tsv::keylkeys] [arg varname] [arg keylist] [opt key]] 293 | Return the a list of the keys in the keyed list [arg keylist] in the 294 | shared variable [arg varname]. If [arg key] is specified, then it is 295 | the name of a key field who's subfield keys are to be retrieved. 296 | 297 | 298 | [call [cmd tsv::keylset] [arg varname] [arg keylist] [arg key] [arg value] [opt {key value..}]] 299 | Set the value associated with [arg key], in the keyed list [arg keylist] 300 | to [arg value]. If the [arg keylist] does not exists, it is created. 301 | If [arg key] is not currently in the list, it will be added. If it already 302 | exists, [arg value] replaces the existing value. Multiple keywords and 303 | values may be specified, if desired. 304 | 305 | [list_end] 306 | 307 | 308 | [section DISCUSSION] 309 | The current implementation of thread shared variables allows for easy and 310 | convenient access to data shared between different threads. 311 | Internally, the data is stored in Tcl objects and all package commands 312 | operate on internal data representation, thus minimizing shimmering and 313 | improving performance. Special care has been taken to assure that all 314 | object data is properly locked and deep-copied when moving objects between 315 | threads. 316 | [para] 317 | Due to the internal design of the Tcl core, there is no provision of full 318 | integration of shared variables within the Tcl syntax, unfortunately. All 319 | access to shared data must be performed with the supplied package commands. 320 | Also, variable traces are not supported. But even so, benefits of easy, 321 | simple and safe shared data manipulation outweights imposed limitations. 322 | 323 | [section CREDITS] 324 | Thread shared variables are inspired by the nsv interface found in 325 | AOLserver, a highly scalable Web server from America Online. 326 | 327 | [see_also tpool ttrace thread] 328 | 329 | [keywords threads synchronization locking {thread shared data}] 330 | 331 | [manpage_end] 332 | -------------------------------------------------------------------------------- /doc/html/ttrace.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | ttrace - Tcl Threading 5 | 6 | 8 | 10 | 11 | 12 |

ttrace(n) 2.6 "Tcl Threading"

13 |

NAME

14 |

15 |

ttrace - Trace-based interpreter initialization 16 | 17 | 18 | 19 | 20 | 21 |

TABLE OF CONTENTS

22 |

    TABLE OF CONTENTS
23 |     SYNOPSIS
24 |     DESCRIPTION
25 |     USER COMMANDS
26 |     CALLBACK COMMANDS
27 |     DISCUSSION
28 |     SEE ALSO
29 |     KEYWORDS
30 |

SYNOPSIS

31 |

32 | package require Tcl 8.4
33 | package require Thread ?2.6?
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
ttrace::eval arg ?arg ...?
ttrace::enable
ttrace::disable
ttrace::cleanup
ttrace::update ?epoch?
ttrace::getscript
ttrace::atenable cmd arglist body
ttrace::atdisable cmd arglist body
ttrace::addtrace cmd arglist body
ttrace::addscript name body
ttrace::addresolver cmd arglist body
ttrace::addcleanup body
ttrace::addentry cmd var val
ttrace::getentry cmd var
ttrace::getentries cmd ?pattern?
ttrace::delentry cmd
ttrace::preload cmd
52 |

DESCRIPTION

53 |

54 | This package creates a framework for on-demand replication of the 55 | interpreter state accross threads in an multithreading application. 56 | It relies on the mechanics of Tcl command tracing and the Tcl 57 | unknown command and mechanism. 58 |

59 | The package requires Tcl threading extension but can be alternatively 60 | used stand-alone within the AOLserver, a scalable webserver from 61 | America Online. 62 |

63 | In a nutshell, a short sample illustrating the usage of the ttrace 64 | with the Tcl threading extension: 65 | 66 |

 
 67 | 
 68 |     % package require Ttrace
 69 |     2.6.5
 70 | 
 71 |     % set t1 [thread::create {package require Ttrace; thread::wait}]
 72 |     tid0x1802800
 73 | 
 74 |     % ttrace::eval {proc test args {return test-[thread::id]}}
 75 |     % thread::send $t1 test
 76 |     test-tid0x1802800
 77 | 
 78 |     % set t2 [thread::create {package require Ttrace; thread::wait}]
 79 |     tid0x1804000
 80 | 
 81 |     % thread::send $t2 test
 82 |     test-tid0x1804000
 83 | 
 84 | 

85 |

86 | As seen from above, the ttrace::eval and ttrace::update 87 | commands are used to create a thread-wide definition of a simple 88 | Tcl procedure and replicate that definition to all, already existing 89 | or later created, threads. 90 | 91 |

USER COMMANDS

92 |

93 | This section describes user-level commands. Those commands can be 94 | used by script writers to control the execution of the tracing 95 | framework. 96 | 97 |

98 | 99 |
ttrace::eval arg ?arg ...?
100 | 101 | 102 | This command concatenates given arguments and evaluates the resulting 103 | Tcl command with trace framework enabled. If the command execution 104 | was ok, it takes necessary steps to automatically propagate the 105 | trace epoch change to all threads in the application. 106 | For AOLserver, only newly created threads actually receive the 107 | epoch change. For the Tcl threading extension, all threads created by 108 | the extension are automatically updated. If the command execution 109 | resulted in Tcl error, no state propagation takes place. 110 |

111 | This is the most important user-level command of the package as 112 | it wraps most of the commands described below. This greatly 113 | simplifies things, because user need to learn just this (one) 114 | command in order to effectively use the package. Other commands, 115 | as desribed below, are included mostly for the sake of completeness. 116 | 117 |

118 |
ttrace::enable
119 | 120 | 121 | Activates all registered callbacks in the framework 122 | and starts a new trace epoch. The trace epoch encapsulates all 123 | changes done to the interpreter during the time traces are activated. 124 | 125 |

126 |
ttrace::disable
127 | 128 | 129 | Deactivates all registered callbacks in the framework 130 | and closes the current trace epoch. 131 | 132 |

133 |
ttrace::cleanup
134 | 135 | 136 | Used to clean-up all on-demand loaded resources in the interpreter. 137 | It effectively brings Tcl interpreter to its pristine state. 138 | 139 |

140 |
ttrace::update ?epoch?
141 | 142 | 143 | Used to refresh the state of the interpreter to match the optional 144 | trace ?epoch?. If the optional ?epoch? is not given, it takes 145 | the most recent trace epoch. 146 | 147 |

148 |
ttrace::getscript
149 | 150 | 151 | Returns a synthetized Tcl script which may be sourced in any interpreter. 152 | This script sets the stage for the Tcl unknown command so it can 153 | load traced resources from the in-memory database. Normally, this command 154 | is automatically invoked by other higher-level commands like 155 | ttrace::eval and ttrace::update. 156 | 157 |
158 | 159 |

CALLBACK COMMANDS

160 |

161 | A word upfront: the package already includes callbacks for tracing 162 | following Tcl commands: proc, namespace, variable, 163 | load, and rename. Additionaly, a set of callbacks for 164 | tracing resources (object, clasess) for the XOTcl v1.3.8+, an 165 | OO-extension to Tcl, is also provided. 166 | This gives a solid base for solving most of the real-life needs and 167 | serves as an example for people wanting to customize the package 168 | to cover their specific needs. 169 |

170 | Below, you can find commands for registering callbacks in the 171 | framework and for writing callback scripts. These callbacks are 172 | invoked by the framework in order to gather interpreter state 173 | changes, build in-memory database, perform custom-cleanups and 174 | various other tasks. 175 | 176 | 177 |

178 | 179 |
ttrace::atenable cmd arglist body
180 | 181 | 182 | Registers Tcl callback to be activated at ttrace::enable. 183 | Registered callbacks are activated on FIFO basis. The callback 184 | definition includes the name of the callback, cmd, a list 185 | of callback arguments, arglist and the body of the 186 | callback. Effectively, this actually resembles the call interface 187 | of the standard Tcl proc command. 188 | 189 | 190 |

191 |
ttrace::atdisable cmd arglist body
192 | 193 | 194 | Registers Tcl callback to be activated at ttrace::disable. 195 | Registered callbacks are activated on FIFO basis. The callback 196 | definition includes the name of the callback, cmd, a list 197 | of callback arguments, arglist and the body of the 198 | callback. Effectively, this actually resembles the call interface 199 | of the standard Tcl proc command. 200 | 201 | 202 |

203 |
ttrace::addtrace cmd arglist body
204 | 205 | 206 | Registers Tcl callback to be activated for tracing the Tcl 207 | cmd command. The callback definition includes the name of 208 | the Tcl command to trace, cmd, a list of callback arguments, 209 | arglist and the body of the callback. Effectively, 210 | this actually resembles the call interface of the standard Tcl 211 | proc command. 212 | 213 | 214 |

215 |
ttrace::addscript name body
216 | 217 | 218 | Registers Tcl callback to be activated for building a Tcl 219 | script to be passed to other interpreters. This script is 220 | used to set the stage for the Tcl unknown command. 221 | Registered callbacks are activated on FIFO basis. 222 | The callback definition includes the name of the callback, 223 | name and the body of the callback. 224 | 225 |

226 |
ttrace::addresolver cmd arglist body
227 | 228 | 229 | Registers Tcl callback to be activated by the overloaded Tcl 230 | unknown command. 231 | Registered callbacks are activated on FIFO basis. 232 | This callback is used to resolve the resource and load the 233 | resource in the current interpreter. 234 | 235 |

236 |
ttrace::addcleanup body
237 | 238 | 239 | Registers Tcl callback to be activated by the trace::cleanup. 240 | Registered callbacks are activated on FIFO basis. 241 | 242 |

243 |
ttrace::addentry cmd var val
244 | 245 | 246 | Adds one entry to the named in-memory database. 247 | 248 |

249 |
ttrace::getentry cmd var
250 | 251 | 252 | Returns the value of the entry from the named in-memory database. 253 | 254 |

255 |
ttrace::getentries cmd ?pattern?
256 | 257 | 258 | Returns names of all entries from the named in-memory database. 259 | 260 |

261 |
ttrace::delentry cmd
262 | 263 | 264 | Deletes an entry from the named in-memory database. 265 | 266 |

267 |
ttrace::preload cmd
268 | 269 | 270 | Registers the Tcl command to be loaded in the interpreter. 271 | Commands registered this way will always be the part of 272 | the interpreter and not be on-demand loaded by the Tcl 273 | unknown command. 274 | 275 |
276 | 277 |

DISCUSSION

278 |

279 | Common introspective state-replication approaches use a custom Tcl 280 | script to introspect the running interpreter and synthesize another 281 | Tcl script to replicate this state in some other interpreter. 282 | This package, on the contrary, uses Tcl command traces. Command 283 | traces are registered on selected Tcl commands, like proc, 284 | namespace, load and other standard (and/or user-defined) 285 | Tcl commands. When activated, those traces build an in-memory 286 | database of created resources. This database is used as a resource 287 | repository for the (overloaded) Tcl unknown command which 288 | creates the requested resource in the interpreter on demand. 289 | This way, users can update just one interpreter (master) in one 290 | thread and replicate that interpreter state (or part of it) to other 291 | threads/interpreters in the process. 292 |

293 | Immediate benefit of such approach is the much smaller memory footprint 294 | of the application and much faster thread creation. By not actually 295 | loading all necessary procedures (and other resources) in every thread 296 | at the thread initialization time, but by deffering this to the time the 297 | resource is actually referenced, significant improvements in both 298 | memory consumption and thread initialization time can be achieved. Some 299 | tests have shown that memory footprint of an multithreading Tcl application 300 | went down more than three times and thread startup time was reduced for 301 | about 50 times. Note that your mileage may vary. 302 | 303 | Other benefits include much finer control about what (and when) gets 304 | replicated from the master to other Tcl thread/interpreters. 305 | 306 | 307 | 308 | 309 | 310 |

SEE ALSO

311 |

312 | thread, tpool, tsv 313 |

KEYWORDS

314 |

315 | command tracing, introspection 316 | 317 | 318 | -------------------------------------------------------------------------------- /doc/man/ttrace.n: -------------------------------------------------------------------------------- 1 | '\" 2 | '\" Generated from file '' by tcllib/doctools with format 'nroff' 3 | '\" 4 | '\" -*- tcl -*- doctools manpage 5 | '\" The definitions below are for supplemental macros used in Tcl/Tk 6 | '\" manual entries. 7 | '\" 8 | '\" .AP type name in/out ?indent? 9 | '\" Start paragraph describing an argument to a library procedure. 10 | '\" type is type of argument (int, etc.), in/out is either "in", "out", 11 | '\" or "in/out" to describe whether procedure reads or modifies arg, 12 | '\" and indent is equivalent to second arg of .IP (shouldn't ever be 13 | '\" needed; use .AS below instead) 14 | '\" 15 | '\" .AS ?type? ?name? 16 | '\" Give maximum sizes of arguments for setting tab stops. Type and 17 | '\" name are examples of largest possible arguments that will be passed 18 | '\" to .AP later. If args are omitted, default tab stops are used. 19 | '\" 20 | '\" .BS 21 | '\" Start box enclosure. From here until next .BE, everything will be 22 | '\" enclosed in one large box. 23 | '\" 24 | '\" .BE 25 | '\" End of box enclosure. 26 | '\" 27 | '\" .CS 28 | '\" Begin code excerpt. 29 | '\" 30 | '\" .CE 31 | '\" End code excerpt. 32 | '\" 33 | '\" .VS ?version? ?br? 34 | '\" Begin vertical sidebar, for use in marking newly-changed parts 35 | '\" of man pages. The first argument is ignored and used for recording 36 | '\" the version when the .VS was added, so that the sidebars can be 37 | '\" found and removed when they reach a certain age. If another argument 38 | '\" is present, then a line break is forced before starting the sidebar. 39 | '\" 40 | '\" .VE 41 | '\" End of vertical sidebar. 42 | '\" 43 | '\" .DS 44 | '\" Begin an indented unfilled display. 45 | '\" 46 | '\" .DE 47 | '\" End of indented unfilled display. 48 | '\" 49 | '\" .SO 50 | '\" Start of list of standard options for a Tk widget. The 51 | '\" options follow on successive lines, in four columns separated 52 | '\" by tabs. 53 | '\" 54 | '\" .SE 55 | '\" End of list of standard options for a Tk widget. 56 | '\" 57 | '\" .OP cmdName dbName dbClass 58 | '\" Start of description of a specific option. cmdName gives the 59 | '\" option's name as specified in the class command, dbName gives 60 | '\" the option's name in the option database, and dbClass gives 61 | '\" the option's class in the option database. 62 | '\" 63 | '\" .UL arg1 arg2 64 | '\" Print arg1 underlined, then print arg2 normally. 65 | '\" 66 | '\" RCS: @(#) $Id$ 67 | '\" 68 | '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. 69 | .if t .wh -1.3i ^B 70 | .nr ^l \n(.l 71 | .ad b 72 | '\" # Start an argument description 73 | .de AP 74 | .ie !"\\$4"" .TP \\$4 75 | .el \{\ 76 | . ie !"\\$2"" .TP \\n()Cu 77 | . el .TP 15 78 | .\} 79 | .ta \\n()Au \\n()Bu 80 | .ie !"\\$3"" \{\ 81 | \&\\$1 \\fI\\$2\\fP (\\$3) 82 | .\".b 83 | .\} 84 | .el \{\ 85 | .br 86 | .ie !"\\$2"" \{\ 87 | \&\\$1 \\fI\\$2\\fP 88 | .\} 89 | .el \{\ 90 | \&\\fI\\$1\\fP 91 | .\} 92 | .\} 93 | .. 94 | '\" # define tabbing values for .AP 95 | .de AS 96 | .nr )A 10n 97 | .if !"\\$1"" .nr )A \\w'\\$1'u+3n 98 | .nr )B \\n()Au+15n 99 | .\" 100 | .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n 101 | .nr )C \\n()Bu+\\w'(in/out)'u+2n 102 | .. 103 | .AS Tcl_Interp Tcl_CreateInterp in/out 104 | '\" # BS - start boxed text 105 | '\" # ^y = starting y location 106 | '\" # ^b = 1 107 | .de BS 108 | .br 109 | .mk ^y 110 | .nr ^b 1u 111 | .if n .nf 112 | .if n .ti 0 113 | .if n \l'\\n(.lu\(ul' 114 | .if n .fi 115 | .. 116 | '\" # BE - end boxed text (draw box now) 117 | .de BE 118 | .nf 119 | .ti 0 120 | .mk ^t 121 | .ie n \l'\\n(^lu\(ul' 122 | .el \{\ 123 | .\" Draw four-sided box normally, but don't draw top of 124 | .\" box if the box started on an earlier page. 125 | .ie !\\n(^b-1 \{\ 126 | \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' 127 | .\} 128 | .el \}\ 129 | \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' 130 | .\} 131 | .\} 132 | .fi 133 | .br 134 | .nr ^b 0 135 | .. 136 | '\" # VS - start vertical sidebar 137 | '\" # ^Y = starting y location 138 | '\" # ^v = 1 (for troff; for nroff this doesn't matter) 139 | .de VS 140 | .if !"\\$2"" .br 141 | .mk ^Y 142 | .ie n 'mc \s12\(br\s0 143 | .el .nr ^v 1u 144 | .. 145 | '\" # VE - end of vertical sidebar 146 | .de VE 147 | .ie n 'mc 148 | .el \{\ 149 | .ev 2 150 | .nf 151 | .ti 0 152 | .mk ^t 153 | \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' 154 | .sp -1 155 | .fi 156 | .ev 157 | .\} 158 | .nr ^v 0 159 | .. 160 | '\" # Special macro to handle page bottom: finish off current 161 | '\" # box/sidebar if in box/sidebar mode, then invoked standard 162 | '\" # page bottom macro. 163 | .de ^B 164 | .ev 2 165 | 'ti 0 166 | 'nf 167 | .mk ^t 168 | .if \\n(^b \{\ 169 | .\" Draw three-sided box if this is the box's first page, 170 | .\" draw two sides but no top otherwise. 171 | .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c 172 | .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c 173 | .\} 174 | .if \\n(^v \{\ 175 | .nr ^x \\n(^tu+1v-\\n(^Yu 176 | \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c 177 | .\} 178 | .bp 179 | 'fi 180 | .ev 181 | .if \\n(^b \{\ 182 | .mk ^y 183 | .nr ^b 2 184 | .\} 185 | .if \\n(^v \{\ 186 | .mk ^Y 187 | .\} 188 | .. 189 | '\" # DS - begin display 190 | .de DS 191 | .RS 192 | .nf 193 | .sp 194 | .. 195 | '\" # DE - end display 196 | .de DE 197 | .fi 198 | .RE 199 | .sp 200 | .. 201 | '\" # SO - start of list of standard options 202 | .de SO 203 | .SH "STANDARD OPTIONS" 204 | .LP 205 | .nf 206 | .ta 5.5c 11c 207 | .ft B 208 | .. 209 | '\" # SE - end of list of standard options 210 | .de SE 211 | .fi 212 | .ft R 213 | .LP 214 | See the \\fBoptions\\fR manual entry for details on the standard options. 215 | .. 216 | '\" # OP - start of full description for a single option 217 | .de OP 218 | .LP 219 | .nf 220 | .ta 4c 221 | Command-Line Name: \\fB\\$1\\fR 222 | Database Name: \\fB\\$2\\fR 223 | Database Class: \\fB\\$3\\fR 224 | .fi 225 | .IP 226 | .. 227 | '\" # CS - begin code excerpt 228 | .de CS 229 | .RS 230 | .nf 231 | .ta .25i .5i .75i 1i 232 | .if t .ft C 233 | .. 234 | '\" # CE - end code excerpt 235 | .de CE 236 | .fi 237 | .if t .ft R 238 | .RE 239 | .. 240 | .de UL 241 | \\$1\l'|0\(ul'\\$2 242 | .. 243 | 244 | .TH "ttrace" n 2.6 "Tcl Threading" 245 | .BS 246 | .SH "NAME" 247 | ttrace \- Trace-based interpreter initialization 248 | .SH "SYNOPSIS" 249 | package require \fBTcl 8.4\fR 250 | .sp 251 | package require \fBThread ?2.6?\fR 252 | .sp 253 | \fBttrace::eval\fR \fIarg\fR ?arg ...? 254 | .sp 255 | \fBttrace::enable\fR 256 | .sp 257 | \fBttrace::disable\fR 258 | .sp 259 | \fBttrace::cleanup\fR 260 | .sp 261 | \fBttrace::update\fR ?epoch? 262 | .sp 263 | \fBttrace::getscript\fR 264 | .sp 265 | \fBttrace::atenable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 266 | .sp 267 | \fBttrace::atdisable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 268 | .sp 269 | \fBttrace::addtrace\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 270 | .sp 271 | \fBttrace::addscript\fR \fIname\fR \fIbody\fR 272 | .sp 273 | \fBttrace::addresolver\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 274 | .sp 275 | \fBttrace::addcleanup\fR \fIbody\fR 276 | .sp 277 | \fBttrace::addentry\fR \fIcmd\fR \fIvar\fR \fIval\fR 278 | .sp 279 | \fBttrace::getentry\fR \fIcmd\fR \fIvar\fR 280 | .sp 281 | \fBttrace::getentries\fR \fIcmd\fR ?pattern? 282 | .sp 283 | \fBttrace::delentry\fR \fIcmd\fR 284 | .sp 285 | \fBttrace::preload\fR \fIcmd\fR 286 | .sp 287 | .BE 288 | .SH "DESCRIPTION" 289 | This package creates a framework for on-demand replication of the 290 | interpreter state accross threads in an multithreading application. 291 | It relies on the mechanics of Tcl command tracing and the Tcl 292 | \fBunknown\fR command and mechanism. 293 | .PP 294 | The package requires Tcl threading extension but can be alternatively 295 | used stand-alone within the AOLserver, a scalable webserver from 296 | America Online. 297 | .PP 298 | In a nutshell, a short sample illustrating the usage of the ttrace 299 | with the Tcl threading extension: 300 | .nf 301 | 302 | % package require Ttrace 303 | 2.6.5 304 | 305 | % set t1 [thread::create {package require Ttrace; thread::wait}] 306 | tid0x1802800 307 | 308 | % ttrace::eval {proc test args {return test-[thread::id]}} 309 | % thread::send $t1 test 310 | test-tid0x1802800 311 | 312 | % set t2 [thread::create {package require Ttrace; thread::wait}] 313 | tid0x1804000 314 | 315 | % thread::send $t2 test 316 | test-tid0x1804000 317 | 318 | .fi 319 | .PP 320 | As seen from above, the \fBttrace::eval\fR and \fBttrace::update\fR 321 | commands are used to create a thread-wide definition of a simple 322 | Tcl procedure and replicate that definition to all, already existing 323 | or later created, threads. 324 | .SH "USER COMMANDS" 325 | This section describes user-level commands. Those commands can be 326 | used by script writers to control the execution of the tracing 327 | framework. 328 | .TP 329 | \fBttrace::eval\fR \fIarg\fR ?arg ...? 330 | This command concatenates given arguments and evaluates the resulting 331 | Tcl command with trace framework enabled. If the command execution 332 | was ok, it takes necessary steps to automatically propagate the 333 | trace epoch change to all threads in the application. 334 | For AOLserver, only newly created threads actually receive the 335 | epoch change. For the Tcl threading extension, all threads created by 336 | the extension are automatically updated. If the command execution 337 | resulted in Tcl error, no state propagation takes place. 338 | .sp 339 | This is the most important user-level command of the package as 340 | it wraps most of the commands described below. This greatly 341 | simplifies things, because user need to learn just this (one) 342 | command in order to effectively use the package. Other commands, 343 | as desribed below, are included mostly for the sake of completeness. 344 | .TP 345 | \fBttrace::enable\fR 346 | Activates all registered callbacks in the framework 347 | and starts a new trace epoch. The trace epoch encapsulates all 348 | changes done to the interpreter during the time traces are activated. 349 | .TP 350 | \fBttrace::disable\fR 351 | Deactivates all registered callbacks in the framework 352 | and closes the current trace epoch. 353 | .TP 354 | \fBttrace::cleanup\fR 355 | Used to clean-up all on-demand loaded resources in the interpreter. 356 | It effectively brings Tcl interpreter to its pristine state. 357 | .TP 358 | \fBttrace::update\fR ?epoch? 359 | Used to refresh the state of the interpreter to match the optional 360 | trace ?epoch?. If the optional ?epoch? is not given, it takes 361 | the most recent trace epoch. 362 | .TP 363 | \fBttrace::getscript\fR 364 | Returns a synthetized Tcl script which may be sourced in any interpreter. 365 | This script sets the stage for the Tcl \fBunknown\fR command so it can 366 | load traced resources from the in-memory database. Normally, this command 367 | is automatically invoked by other higher-level commands like 368 | \fBttrace::eval\fR and \fBttrace::update\fR. 369 | .SH "CALLBACK COMMANDS" 370 | A word upfront: the package already includes callbacks for tracing 371 | following Tcl commands: \fBproc\fR, \fBnamespace\fR, \fBvariable\fR, 372 | \fBload\fR, and \fBrename\fR. Additionaly, a set of callbacks for 373 | tracing resources (object, clasess) for the XOTcl v1.3.8+, an 374 | OO-extension to Tcl, is also provided. 375 | This gives a solid base for solving most of the real-life needs and 376 | serves as an example for people wanting to customize the package 377 | to cover their specific needs. 378 | .PP 379 | Below, you can find commands for registering callbacks in the 380 | framework and for writing callback scripts. These callbacks are 381 | invoked by the framework in order to gather interpreter state 382 | changes, build in-memory database, perform custom-cleanups and 383 | various other tasks. 384 | .TP 385 | \fBttrace::atenable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 386 | Registers Tcl callback to be activated at \fBttrace::enable\fR. 387 | Registered callbacks are activated on FIFO basis. The callback 388 | definition includes the name of the callback, \fIcmd\fR, a list 389 | of callback arguments, \fIarglist\fR and the \fIbody\fR of the 390 | callback. Effectively, this actually resembles the call interface 391 | of the standard Tcl \fBproc\fR command. 392 | .TP 393 | \fBttrace::atdisable\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 394 | Registers Tcl callback to be activated at \fBttrace::disable\fR. 395 | Registered callbacks are activated on FIFO basis. The callback 396 | definition includes the name of the callback, \fIcmd\fR, a list 397 | of callback arguments, \fIarglist\fR and the \fIbody\fR of the 398 | callback. Effectively, this actually resembles the call interface 399 | of the standard Tcl \fBproc\fR command. 400 | .TP 401 | \fBttrace::addtrace\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 402 | Registers Tcl callback to be activated for tracing the Tcl 403 | \fBcmd\fR command. The callback definition includes the name of 404 | the Tcl command to trace, \fIcmd\fR, a list of callback arguments, 405 | \fIarglist\fR and the \fIbody\fR of the callback. Effectively, 406 | this actually resembles the call interface of the standard Tcl 407 | \fBproc\fR command. 408 | .TP 409 | \fBttrace::addscript\fR \fIname\fR \fIbody\fR 410 | Registers Tcl callback to be activated for building a Tcl 411 | script to be passed to other interpreters. This script is 412 | used to set the stage for the Tcl \fBunknown\fR command. 413 | Registered callbacks are activated on FIFO basis. 414 | The callback definition includes the name of the callback, 415 | \fIname\fR and the \fIbody\fR of the callback. 416 | .TP 417 | \fBttrace::addresolver\fR \fIcmd\fR \fIarglist\fR \fIbody\fR 418 | Registers Tcl callback to be activated by the overloaded Tcl 419 | \fBunknown\fR command. 420 | Registered callbacks are activated on FIFO basis. 421 | This callback is used to resolve the resource and load the 422 | resource in the current interpreter. 423 | .TP 424 | \fBttrace::addcleanup\fR \fIbody\fR 425 | Registers Tcl callback to be activated by the \fBtrace::cleanup\fR. 426 | Registered callbacks are activated on FIFO basis. 427 | .TP 428 | \fBttrace::addentry\fR \fIcmd\fR \fIvar\fR \fIval\fR 429 | Adds one entry to the named in-memory database. 430 | .TP 431 | \fBttrace::getentry\fR \fIcmd\fR \fIvar\fR 432 | Returns the value of the entry from the named in-memory database. 433 | .TP 434 | \fBttrace::getentries\fR \fIcmd\fR ?pattern? 435 | Returns names of all entries from the named in-memory database. 436 | .TP 437 | \fBttrace::delentry\fR \fIcmd\fR 438 | Deletes an entry from the named in-memory database. 439 | .TP 440 | \fBttrace::preload\fR \fIcmd\fR 441 | Registers the Tcl command to be loaded in the interpreter. 442 | Commands registered this way will always be the part of 443 | the interpreter and not be on-demand loaded by the Tcl 444 | \fBunknown\fR command. 445 | .SH "DISCUSSION" 446 | Common introspective state-replication approaches use a custom Tcl 447 | script to introspect the running interpreter and synthesize another 448 | Tcl script to replicate this state in some other interpreter. 449 | This package, on the contrary, uses Tcl command traces. Command 450 | traces are registered on selected Tcl commands, like \fBproc\fR, 451 | \fBnamespace\fR, \fBload\fR and other standard (and/or user-defined) 452 | Tcl commands. When activated, those traces build an in-memory 453 | database of created resources. This database is used as a resource 454 | repository for the (overloaded) Tcl \fBunknown\fR command which 455 | creates the requested resource in the interpreter on demand. 456 | This way, users can update just one interpreter (master) in one 457 | thread and replicate that interpreter state (or part of it) to other 458 | threads/interpreters in the process. 459 | .PP 460 | Immediate benefit of such approach is the much smaller memory footprint 461 | of the application and much faster thread creation. By not actually 462 | loading all necessary procedures (and other resources) in every thread 463 | at the thread initialization time, but by deffering this to the time the 464 | resource is actually referenced, significant improvements in both 465 | memory consumption and thread initialization time can be achieved. Some 466 | tests have shown that memory footprint of an multithreading Tcl application 467 | went down more than three times and thread startup time was reduced for 468 | about 50 times. Note that your mileage may vary. 469 | Other benefits include much finer control about what (and when) gets 470 | replicated from the master to other Tcl thread/interpreters. 471 | .SH "SEE ALSO" 472 | thread, tpool, tsv 473 | .SH "KEYWORDS" 474 | command tracing, introspection 475 | -------------------------------------------------------------------------------- /tcl/tpool/tpool.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tpool.tcl -- 3 | # 4 | # Tcl implementation of a threadpool paradigm in pure Tcl using 5 | # the Tcl threading extension 2.5 (or higher). 6 | # 7 | # This file is for example purposes only. The efficient C-level 8 | # threadpool implementation is already a part of the threading 9 | # extension starting with 2.5 version. Both implementations have 10 | # the same Tcl API so both can be used interchangeably. Goal of 11 | # this implementation is to serve as an example of using the Tcl 12 | # extension to implement some very common threading paradigms. 13 | # 14 | # Beware: with time, as improvements are made to the C-level 15 | # implementation, this Tcl one might lag behind. 16 | # Please consider this code as a working example only. 17 | # 18 | # 19 | # 20 | # Copyright (c) 2002 by Zoran Vasiljevic. 21 | # 22 | # See the file "license.terms" for information on usage and 23 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. 24 | # 25 | # ----------------------------------------------------------------------------- 26 | # RCS: @(#) $Id$ 27 | # 28 | 29 | package require Thread 2.5 30 | set thisScript [info script] 31 | 32 | namespace eval tpool { 33 | 34 | variable afterevent "" ; # Idle timer event for worker threads 35 | variable result ; # Stores result from the worker thread 36 | variable waiter ; # Waits for an idle worker thread 37 | variable jobsdone ; # Accumulates results from worker threads 38 | 39 | # 40 | # Create shared array with a single element. 41 | # It is used for automatic pool handles creation. 42 | # 43 | 44 | set ns [namespace current] 45 | tsv::lock $ns { 46 | if {[tsv::exists $ns count] == 0} { 47 | tsv::set $ns count 0 48 | } 49 | tsv::set $ns count -1 50 | } 51 | variable thisScript [info script] 52 | } 53 | 54 | # 55 | # tpool::create -- 56 | # 57 | # Creates instance of a thread pool. 58 | # 59 | # Arguments: 60 | # args Variable number of key/value arguments, as follows: 61 | # 62 | # -minworkers minimum # of worker threads (def:0) 63 | # -maxworkers maximum # of worker threads (def:4) 64 | # -idletime # of sec worker is idle before exiting (def:0 = never) 65 | # -initcmd script used to initialize new worker thread 66 | # -exitcmd script run at worker thread exit 67 | # 68 | # Side Effects: 69 | # Might create many new threads if "-minworkers" option is > 0. 70 | # 71 | # Results: 72 | # The id of the newly created thread pool. This id must be used 73 | # in all other tpool::* commands. 74 | # 75 | 76 | proc tpool::create {args} { 77 | 78 | variable thisScript 79 | 80 | # 81 | # Get next threadpool handle and create the pool array. 82 | # 83 | 84 | set usage "wrong \# args: should be \"[lindex [info level 1] 0]\ 85 | ?-minworkers count? ?-maxworkers count?\ 86 | ?-initcmd script? ?-exitcmd script?\ 87 | ?-idletime seconds?\"" 88 | 89 | set ns [namespace current] 90 | set tpid [namespace tail $ns][tsv::incr $ns count] 91 | 92 | tsv::lock $tpid { 93 | tsv::set $tpid name $tpid 94 | } 95 | 96 | # 97 | # Setup default pool data. 98 | # 99 | 100 | tsv::array set $tpid { 101 | thrworkers "" 102 | thrwaiters "" 103 | jobcounter 0 104 | refcounter 0 105 | numworkers 0 106 | -minworkers 0 107 | -maxworkers 4 108 | -idletime 0 109 | -initcmd "" 110 | -exitcmd "" 111 | } 112 | 113 | tsv::set $tpid -initcmd "source $thisScript" 114 | 115 | # 116 | # Override with user-supplied data 117 | # 118 | 119 | if {[llength $args] % 2} { 120 | error $usage 121 | } 122 | 123 | foreach {arg val} $args { 124 | switch -- $arg { 125 | -minworkers - 126 | -maxworkers {tsv::set $tpid $arg $val} 127 | -idletime {tsv::set $tpid $arg [expr {$val*1000}]} 128 | -initcmd {tsv::append $tpid $arg \n $val} 129 | -exitcmd {tsv::append $tpid $arg \n $val} 130 | default { 131 | error $usage 132 | } 133 | } 134 | } 135 | 136 | # 137 | # Start initial (minimum) number of worker threads. 138 | # 139 | 140 | for {set ii 0} {$ii < [tsv::set $tpid -minworkers]} {incr ii} { 141 | Worker $tpid 142 | } 143 | 144 | return $tpid 145 | } 146 | 147 | # 148 | # tpool::names -- 149 | # 150 | # Returns list of currently created threadpools 151 | # 152 | # Arguments: 153 | # None. 154 | # 155 | # Side Effects: 156 | # None. 157 | # 158 | # Results 159 | # List of active threadpoool identifiers or empty if none found 160 | # 161 | # 162 | 163 | proc tpool::names {} { 164 | tsv::names [namespace tail [namespace current]]* 165 | } 166 | 167 | # 168 | # tpool::post -- 169 | # 170 | # Submits the new job to the thread pool. The caller might pass 171 | # the job in two modes: synchronous and asynchronous. 172 | # For the synchronous mode, the pool implementation will retain 173 | # the result of the passed script until the caller collects it 174 | # using the "thread::get" command. 175 | # For the asynchronous mode, the result of the script is ignored. 176 | # 177 | # Arguments: 178 | # args Variable # of arguments with the following syntax: 179 | # tpool::post ?-detached? tpid script 180 | # 181 | # -detached flag to turn the async operation (ignore result) 182 | # tpid the id of the thread pool 183 | # script script to pass to the worker thread for execution 184 | # 185 | # Side Effects: 186 | # Depends on the passed script. 187 | # 188 | # Results: 189 | # The id of the posted job. This id is used later on to collect 190 | # result of the job and set local variables accordingly. 191 | # For asynchronously posted jobs, the return result is ignored 192 | # and this function returns empty result. 193 | # 194 | 195 | proc tpool::post {args} { 196 | 197 | # 198 | # Parse command arguments. 199 | # 200 | 201 | set ns [namespace current] 202 | set usage "wrong \# args: should be \"[lindex [info level 1] 0]\ 203 | ?-detached? tpoolId script\"" 204 | 205 | if {[llength $args] == 2} { 206 | set detached 0 207 | set tpid [lindex $args 0] 208 | set cmd [lindex $args 1] 209 | } elseif {[llength $args] == 3} { 210 | if {[lindex $args 0] != "-detached"} { 211 | error $usage 212 | } 213 | set detached 1 214 | set tpid [lindex $args 1] 215 | set cmd [lindex $args 2] 216 | } else { 217 | error $usage 218 | } 219 | 220 | # 221 | # Find idle (or create new) worker thread. This is relatively 222 | # a complex issue, since we must honour the limits about number 223 | # of allowed worker threads imposed to us by the caller. 224 | # 225 | 226 | set tid "" 227 | 228 | while {$tid == ""} { 229 | tsv::lock $tpid { 230 | set tid [tsv::lpop $tpid thrworkers] 231 | if {$tid == "" || [catch {thread::preserve $tid}]} { 232 | set tid "" 233 | tsv::lpush $tpid thrwaiters [thread::id] end 234 | if {[tsv::set $tpid numworkers]<[tsv::set $tpid -maxworkers]} { 235 | Worker $tpid 236 | } 237 | } 238 | } 239 | if {$tid == ""} { 240 | vwait ${ns}::waiter 241 | } 242 | } 243 | 244 | # 245 | # Post the command to the worker thread 246 | # 247 | 248 | if {$detached} { 249 | set j "" 250 | thread::send -async $tid [list ${ns}::Run $tpid 0 $cmd] 251 | } else { 252 | set j [tsv::incr $tpid jobcounter] 253 | thread::send -async $tid [list ${ns}::Run $tpid $j $cmd] ${ns}::result 254 | } 255 | 256 | variable jobsdone 257 | set jobsdone($j) "" 258 | 259 | return $j 260 | } 261 | 262 | # 263 | # tpool::wait -- 264 | # 265 | # Waits for jobs sent with "thread::post" to finish. 266 | # 267 | # Arguments: 268 | # tpid Name of the pool shared array. 269 | # jobList List of job id's done. 270 | # jobLeft List of jobs still pending. 271 | # 272 | # Side Effects: 273 | # Might eventually enter the event loop while waiting 274 | # for the job result to arrive from the worker thread. 275 | # It ignores bogus job ids. 276 | # 277 | # Results: 278 | # Result of the job. If the job resulted in error, it sets 279 | # the global errorInfo and errorCode variables accordingly. 280 | # 281 | 282 | proc tpool::wait {tpid jobList {jobLeft ""}} { 283 | 284 | variable result 285 | variable jobsdone 286 | 287 | if {$jobLeft != ""} { 288 | upvar $jobLeft jobleft 289 | } 290 | 291 | set retlist "" 292 | set jobleft "" 293 | 294 | foreach j $jobList { 295 | if {[info exists jobsdone($j)] == 0} { 296 | continue ; # Ignore (skip) bogus job ids 297 | } 298 | if {$jobsdone($j) != ""} { 299 | lappend retlist $j 300 | } else { 301 | lappend jobleft $j 302 | } 303 | } 304 | if {[llength $retlist] == 0 && [llength $jobList]} { 305 | # 306 | # No jobs found; wait for the first one to get ready. 307 | # 308 | set jobleft $jobList 309 | while {1} { 310 | vwait [namespace current]::result 311 | set doneid [lindex $result 0] 312 | set jobsdone($doneid) $result 313 | if {[lsearch $jobList $doneid] >= 0} { 314 | lappend retlist $doneid 315 | set x [lsearch $jobleft $doneid] 316 | set jobleft [lreplace $jobleft $x $x] 317 | break 318 | } 319 | } 320 | } 321 | 322 | return $retlist 323 | } 324 | 325 | # 326 | # tpool::get -- 327 | # 328 | # Waits for a job sent with "thread::post" to finish. 329 | # 330 | # Arguments: 331 | # tpid Name of the pool shared array. 332 | # jobid Id of the previously posted job. 333 | # 334 | # Side Effects: 335 | # None. 336 | # 337 | # Results: 338 | # Result of the job. If the job resulted in error, it sets 339 | # the global errorInfo and errorCode variables accordingly. 340 | # 341 | 342 | proc tpool::get {tpid jobid} { 343 | 344 | variable jobsdone 345 | 346 | if {[lindex $jobsdone($jobid) 1] != 0} { 347 | eval error [lrange $jobsdone($jobid) 2 end] 348 | } 349 | 350 | return [lindex $jobsdone($jobid) 2] 351 | } 352 | 353 | # 354 | # tpool::preserve -- 355 | # 356 | # Increments the reference counter of the threadpool, reserving it 357 | # for the private usage.. 358 | # 359 | # Arguments: 360 | # tpid Name of the pool shared array. 361 | # 362 | # Side Effects: 363 | # None. 364 | # 365 | # Results: 366 | # Current number of threadpool reservations. 367 | # 368 | 369 | proc tpool::preserve {tpid} { 370 | tsv::incr $tpid refcounter 371 | } 372 | 373 | # 374 | # tpool::release -- 375 | # 376 | # Decrements the reference counter of the threadpool, eventually 377 | # tearing the pool down if this was the last reservation. 378 | # 379 | # Arguments: 380 | # tpid Name of the pool shared array. 381 | # 382 | # Side Effects: 383 | # If the number of reservations drops to zero or below 384 | # the threadpool is teared down. 385 | # 386 | # Results: 387 | # Current number of threadpool reservations. 388 | # 389 | 390 | proc tpool::release {tpid} { 391 | 392 | tsv::lock $tpid { 393 | if {[tsv::incr $tpid refcounter -1] <= 0} { 394 | # Release all workers threads 395 | foreach t [tsv::set $tpid thrworkers] { 396 | thread::release -wait $t 397 | } 398 | tsv::unset $tpid ; # This is not an error; it works! 399 | } 400 | } 401 | } 402 | 403 | # 404 | # Private procedures, not a part of the threadpool API. 405 | # 406 | 407 | # 408 | # tpool::Worker -- 409 | # 410 | # Creates new worker thread. This procedure must be executed 411 | # under the tsv lock. 412 | # 413 | # Arguments: 414 | # tpid Name of the pool shared array. 415 | # 416 | # Side Effects: 417 | # Depends on the thread initialization script. 418 | # 419 | # Results: 420 | # None. 421 | # 422 | 423 | proc tpool::Worker {tpid} { 424 | 425 | # 426 | # Create new worker thread 427 | # 428 | 429 | set tid [thread::create] 430 | 431 | thread::send $tid [tsv::set $tpid -initcmd] 432 | thread::preserve $tid 433 | 434 | tsv::incr $tpid numworkers 435 | tsv::lpush $tpid thrworkers $tid 436 | 437 | # 438 | # Signalize waiter threads if any 439 | # 440 | 441 | set waiter [tsv::lpop $tpid thrwaiters] 442 | if {$waiter != ""} { 443 | thread::send -async $waiter [subst { 444 | set [namespace current]::waiter 1 445 | }] 446 | } 447 | } 448 | 449 | # 450 | # tpool::Timer -- 451 | # 452 | # This procedure should be executed within the worker thread only. 453 | # It registers the callback for terminating the idle thread. 454 | # 455 | # Arguments: 456 | # tpid Name of the pool shared array. 457 | # 458 | # Side Effects: 459 | # Thread may eventually exit. 460 | # 461 | # Results: 462 | # None. 463 | # 464 | 465 | proc tpool::Timer {tpid} { 466 | 467 | tsv::lock $tpid { 468 | if {[tsv::set $tpid numworkers] > [tsv::set $tpid -minworkers]} { 469 | 470 | # 471 | # We have more workers than needed, so kill this one. 472 | # We first splice ourselves from the list of active 473 | # workers, adjust the number of workers and release 474 | # this thread, which may exit eventually. 475 | # 476 | 477 | set x [tsv::lsearch $tpid thrworkers [thread::id]] 478 | if {$x >= 0} { 479 | tsv::lreplace $tpid thrworkers $x $x 480 | tsv::incr $tpid numworkers -1 481 | set exitcmd [tsv::set $tpid -exitcmd] 482 | if {$exitcmd != ""} { 483 | catch {eval $exitcmd} 484 | } 485 | thread::release 486 | } 487 | } 488 | } 489 | } 490 | 491 | # 492 | # tpool::Run -- 493 | # 494 | # This procedure should be executed within the worker thread only. 495 | # It performs the actual command execution in the worker thread. 496 | # 497 | # Arguments: 498 | # tpid Name of the pool shared array. 499 | # jid The job id 500 | # cmd The command to execute 501 | # 502 | # Side Effects: 503 | # Many, depending of the passed command 504 | # 505 | # Results: 506 | # List for passing the evaluation result and status back. 507 | # 508 | 509 | proc tpool::Run {tpid jid cmd} { 510 | 511 | # 512 | # Cancel the idle timer callback, if any. 513 | # 514 | 515 | variable afterevent 516 | if {$afterevent != ""} { 517 | after cancel $afterevent 518 | } 519 | 520 | # 521 | # Evaluate passed command and build the result list. 522 | # 523 | 524 | set code [catch {uplevel \#0 $cmd} ret] 525 | if {$code == 0} { 526 | set res [list $jid 0 $ret] 527 | } else { 528 | set res [list $jid $code $ret $::errorInfo $::errorCode] 529 | } 530 | 531 | # 532 | # Check to see if any caller is waiting to be serviced. 533 | # If yes, kick it out of the waiting state. 534 | # 535 | 536 | set ns [namespace current] 537 | 538 | tsv::lock $tpid { 539 | tsv::lpush $tpid thrworkers [thread::id] 540 | set waiter [tsv::lpop $tpid thrwaiters] 541 | if {$waiter != ""} { 542 | thread::send -async $waiter [subst { 543 | set ${ns}::waiter 1 544 | }] 545 | } 546 | } 547 | 548 | # 549 | # Release the thread. If this turns out to be 550 | # the last refcount held, don't bother to do 551 | # any more work, since thread will soon exit. 552 | # 553 | 554 | if {[thread::release] <= 0} { 555 | return $res 556 | } 557 | 558 | # 559 | # Register the idle timer again. 560 | # 561 | 562 | if {[set idle [tsv::set $tpid -idletime]]} { 563 | set afterevent [after $idle [subst { 564 | ${ns}::Timer $tpid 565 | }]] 566 | } 567 | 568 | return $res 569 | } 570 | 571 | # EOF $RCSfile$ 572 | 573 | # Emacs Setup Variables 574 | # Local Variables: 575 | # mode: Tcl 576 | # indent-tabs-mode: nil 577 | # tcl-basic-offset: 4 578 | # End: 579 | 580 | --------------------------------------------------------------------------------