diff options
author | sebres <sebres@users.sourceforge.net> | 2017-05-11 18:53:21 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2017-05-11 18:53:21 (GMT) |
commit | c7ee05b881b42a4e4e39bbe519714eea4af7beba (patch) | |
tree | 7871ca975e33745ffa98d6cc3746e915a61fce21 /win | |
parent | db083499bfff8aee794512e3da2f0ad2f201f6b0 (diff) | |
parent | 53c066e2314b0166a9fe30fd1f0026bb48f0d1f3 (diff) | |
download | tcl-c7ee05b881b42a4e4e39bbe519714eea4af7beba.zip tcl-c7ee05b881b42a4e4e39bbe519714eea4af7beba.tar.gz tcl-c7ee05b881b42a4e4e39bbe519714eea4af7beba.tar.bz2 |
merge trunk (to sebres-trunk-timerate)
Diffstat (limited to 'win')
-rw-r--r-- | win/Makefile.in | 4 | ||||
-rwxr-xr-x | win/configure | 2 | ||||
-rw-r--r-- | win/configure.ac | 1 | ||||
-rw-r--r-- | win/makefile.bc | 594 | ||||
-rw-r--r-- | win/makefile.vc | 1 | ||||
-rw-r--r-- | win/tcl.dsp | 4 | ||||
-rw-r--r-- | win/tclConfig.sh.in | 3 | ||||
-rw-r--r-- | win/tclWinChan.c | 145 | ||||
-rw-r--r-- | win/tclWinConsole.c | 225 | ||||
-rw-r--r--[-rwxr-xr-x] | win/tclWinFile.c | 0 | ||||
-rw-r--r-- | win/tclWinInit.c | 20 | ||||
-rw-r--r-- | win/tclWinInt.h | 75 | ||||
-rw-r--r-- | win/tclWinPipe.c | 622 | ||||
-rw-r--r-- | win/tclWinReg.c | 10 | ||||
-rw-r--r-- | win/tclWinSerial.c | 93 | ||||
-rw-r--r-- | win/tclWinSock.c | 717 | ||||
-rw-r--r-- | win/tclWinTime.c | 8 |
17 files changed, 1190 insertions, 1334 deletions
diff --git a/win/Makefile.in b/win/Makefile.in index e967ef3..c46fac7 100644 --- a/win/Makefile.in +++ b/win/Makefile.in @@ -652,8 +652,8 @@ install-libraries: libraries install-tzdata install-msgs do \ $(COPY) "$$j" "$(SCRIPT_INSTALL_DIR)/http1.0"; \ done; - @echo "Installing package http 2.8.10 as a Tcl Module"; - @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.8.10.tm; + @echo "Installing package http 2.8.11 as a Tcl Module"; + @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.8.11.tm; @echo "Installing library opt0.4 directory"; @for j in $(ROOT_DIR)/library/opt/*.tcl; \ do \ diff --git a/win/configure b/win/configure index 147252c..d0431cb 100755 --- a/win/configure +++ b/win/configure @@ -639,6 +639,7 @@ TCL_EXP_FILE TCL_BUILD_EXP_FILE TCL_NEEDS_EXP_FILE TCL_LD_SEARCH_FLAGS +TCL_CC_SEARCH_FLAGS TCL_BUILD_LIB_SPEC MAKE_EXE MAKE_DLL @@ -5284,6 +5285,7 @@ TCL_WIN_VERSION="$TCL_VERSION.$TCL_RELEASE_LEVEL.`echo $TCL_PATCH_LEVEL | tr -d + # win only diff --git a/win/configure.ac b/win/configure.ac index 7405bf4..2821bc3 100644 --- a/win/configure.ac +++ b/win/configure.ac @@ -433,6 +433,7 @@ AC_SUBST(MAKE_EXE) # empty on win, but needs sub'ing AC_SUBST(TCL_BUILD_LIB_SPEC) +AC_SUBST(TCL_CC_SEARCH_FLAGS) AC_SUBST(TCL_LD_SEARCH_FLAGS) AC_SUBST(TCL_NEEDS_EXP_FILE) AC_SUBST(TCL_BUILD_EXP_FILE) diff --git a/win/makefile.bc b/win/makefile.bc deleted file mode 100644 index bee61d7..0000000 --- a/win/makefile.bc +++ /dev/null @@ -1,594 +0,0 @@ -#
-# Makefile for Borland C++ 5.5 (or C++ Builder 5), adapted from the makefile
-# for Visual C++ that came with tcl 8.3.3
-#
-# See the file "license.terms" for information on usage and redistribution
-# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-#
-# Copyright (c) 1995-1996 Sun Microsystems, Inc.
-# Copyright (c) 1998-1999 by Scriptics Corporation.
-
-# TIP #59 information.
-#
-# This makefile does not set the following configuration cpp
-# defines. Behind the defines are the makefile variables listed to set
-# to -D... when that feature is enabled.
-#
-# - TCL_CFG_PROFILED PROFDEFINES
-# - TCL_CFG_OPTIMIZED OPTDEFINES
-# - TCL_CFG_DO64BIT SIXFOURDEFINES
-
-# Have a look at the complete description on how to build and test Tcl with
-# the current Borland compilers at www.ratiosoft.com/tcl/borland.
-#
-# Usage:
-# - Adapt the paths below to match your compiler's location
-# - Make sure the compiler's bin directory is on your path
-# - Open a console
-# - To make a debug version enter
-# make -fmakefile.bc -DNODEBUG=0 xxx
-# where 'xxx' is the target you want (e.g. 'all', 'test', ...)
-# Please note: I omitted the 'd' suffix for debug versions because Tcl
-# will always call tclpip83.dll and not tclpip83d.dll, causing an error.
-# ^
-# Besides, the debug version goes into a separate directory, so there
-# should be no problem having DLLs and EXEs with the same name.
-# If you prefer your debug version having the 'd' suffix just uncomment
-# the line
-# #DBGX = d
-#
-# - To make a 'normal' version enter
-# make -fmakefile.bc xxx
-# where 'xxx' is the target you want (e.g. 'all', 'test', ...)
-#
-# DISCLAIMER:
-# This makefile has an experimental status - that is those targets which
-# have been modified do in fact compile and link with Borland's C++
-# Builder 5 and with the free Borland compiler (Borland C++ 5.5).
-# However the author assumes no responsiblity for any effect which the use of
-# this makefile or of the resulting programs might have on your system.
-#
-# Not yet modified:
-# - The 'plug-in-DLL' and the associated shell.
-#
-# Suggestions and / or improvements are always welcome.
-#
-# May 2001, H. Giese (hgiese@ratiosoft.com)
-#
-
-# Does not depend on the presence of any environment variables in
-# order to compile tcl; all needed information is derived from
-# location of the compiler directories.
-
-#
-# Project directories
-#
-# ROOT = top of source tree
-#
-# TOOLS32 = location of Borland development tools.
-#
-# INSTALLDIR = where the install-targets should copy the binaries and
-# support files
-#
-
-ROOT = ..
-INSTALLDIR = c:\program files\tcl
-
-# If you have C++ Builder 5 or the free Borland C++ 5.5 compiler
-# adapt the following paths as appropriate for your system
-TOOLS32 = c:\dev\bcc55
-TOOLS32_rc = c:\dev\bcc55
-#TOOLS32 = c:\bc55
-#TOOLS32_rc = c:\bc55
-
-cc32 = "$(TOOLS32)\bin\bcc32.exe"
-link32 = "$(TOOLS32)\bin\ilink32.exe"
-lib32 = "$(TOOLS32)\bin\tlib.exe"
-rc32 = "$(TOOLS32_rc)\bin\brcc32.exe"
-include32 = -I"$(TOOLS32)\include"
-libpath32 = -L"$(TOOLS32)\lib"
-
-# Uncomment the following line to compile with thread support
-#THREADDEFINES = -DTCL_THREADS=1
-
-# Allow definition of NDEBUG via command line
-# Set NODEBUG to 0 to compile with symbols
-!if !defined(NODEBUG)
-NODEBUG = 1
-!endif
-
-# CFG_ENCODING=encoding
-# name of encoding for configuration information. Defaults
-# to cp1252
-!if !defined(CFG_ENCODING)
-CFG_ENCODING = \"cp1252\"
-!endif
-
-# The following defines can be used to control the amount of debugging
-# code that is added to the compilation.
-#
-# -DTCL_MEM_DEBUG Enables the debugging memory allocator.
-# -DTCL_COMPILE_DEBUG Enables byte compilation logging.
-# -DTCL_COMPILE_STATS Enables byte compilation statistics gathering.
-# -DUSE_TCLALLOC=0 Disables the Tcl memory allocator in favor
-# of the native malloc implementation. This is
-# needed when using Purify.
-#
-#DEBUGDEFINES = -DTCL_MEM_DEBUG -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
-#DEBUGDEFINES = -DUSE_TCLALLOC=0
-
-######################################################################
-# Do not modify below this line
-######################################################################
-
-NAMEPREFIX = tcl
-STUBPREFIX = $(NAMEPREFIX)stub
-DOTVERSION = 8.7
-VERSION = 87
-
-DDEVERSION = 14
-DDEDOTVERSION = 1.4
-
-REGVERSION = 13
-REGDOTVERSION = 1.3
-
-BINROOT = ..
-!IF "$(NODEBUG)" == "1"
-TMPDIRNAME = Release
-DBGX =
-SYMDEFINES = -DNDEBUG
-!ELSE
-TMPDIRNAME = Debug
-#DBGX = d
-DBGX =
-SYMDEFINES = -DTCL_CFG_DEBUG
-!ENDIF
-TMPDIR = $(BINROOT)\$(TMPDIRNAME)
-OUTDIRNAME = $(TMPDIRNAME)
-OUTDIR = $(TMPDIR)
-
-TCLLIB = $(OUTDIR)\$(NAMEPREFIX)$(VERSION)$(DBGX).lib
-TCLDLLNAME = $(NAMEPREFIX)$(VERSION)$(DBGX).dll
-TCLDLL = $(OUTDIR)\$(TCLDLLNAME)
-
-TCLSTUBLIBNAME = $(STUBPREFIX)$(VERSION)$(DBGX).lib
-TCLSTUBLIB = $(OUTDIR)\$(TCLSTUBLIBNAME)
-
-TCLPLUGINLIB = $(OUTDIR)\$(NAMEPREFIX)$(VERSION)p$(DBGX).lib
-TCLPLUGINDLLNAME = $(NAMEPREFIX)$(VERSION)p$(DBGX).dll
-TCLPLUGINDLL = $(OUTDIR)\$(TCLPLUGINDLLNAME)
-TCLSH = $(OUTDIR)\$(NAMEPREFIX)sh$(VERSION)$(DBGX).exe
-TCLSHP = $(OUTDIR)\$(NAMEPREFIX)shp$(VERSION)$(DBGX).exe
-TCLREGDLLNAME = $(NAMEPREFIX)reg$(REGVERSION)$(DBGX).dll
-TCLREGDLL = $(OUTDIR)\$(TCLREGDLLNAME)
-TCLDDEDLLNAME = $(NAMEPREFIX)dde$(DDEVERSION)$(DBGX).dll
-TCLDDEDLL = $(OUTDIR)\$(TCLDDEDLLNAME)
-TCLTEST = $(OUTDIR)\$(NAMEPREFIX)test.exe
-CAT32 = $(TMPDIR)\cat32.exe
-RMDIR = .\rmd.bat
-MKDIR = .\mkd.bat
-RM = del
-
-LIB_INSTALL_DIR = $(INSTALLDIR)\lib
-BIN_INSTALL_DIR = $(INSTALLDIR)\bin
-SCRIPT_INSTALL_DIR = $(INSTALLDIR)\lib\tcl$(DOTVERSION)
-INCLUDE_INSTALL_DIR = $(INSTALLDIR)\include
-
-TCLSHOBJS = \
- $(TMPDIR)\tclAppInit.obj
-
-TCLTESTOBJS = \
- $(TMPDIR)\tclTest.obj \
- $(TMPDIR)\tclTestObj.obj \
- $(TMPDIR)\tclTestProcBodyObj.obj \
- $(TMPDIR)\tclThreadTest.obj \
- $(TMPDIR)\tclWinTest.obj \
- $(TMPDIR)\testMain.obj
-
-TCLOBJS = \
- $(TMPDIR)\regcomp.obj \
- $(TMPDIR)\regexec.obj \
- $(TMPDIR)\regfree.obj \
- $(TMPDIR)\regerror.obj \
- $(TMPDIR)\tclAlloc.obj \
- $(TMPDIR)\tclAsync.obj \
- $(TMPDIR)\tclBasic.obj \
- $(TMPDIR)\tclBinary.obj \
- $(TMPDIR)\tclCkalloc.obj \
- $(TMPDIR)\tclClock.obj \
- $(TMPDIR)\tclCmdAH.obj \
- $(TMPDIR)\tclCmdIL.obj \
- $(TMPDIR)\tclCmdMZ.obj \
- $(TMPDIR)\tclCompCmds.obj \
- $(TMPDIR)\tclCompCmdsGR.obj \
- $(TMPDIR)\tclCompCmdsSZ.obj \
- $(TMPDIR)\tclCompExpr.obj \
- $(TMPDIR)\tclCompile.obj \
- $(TMPDIR)\tclConfig.obj \
- $(TMPDIR)\tclDate.obj \
- $(TMPDIR)\tclDictObj.obj \
- $(TMPDIR)\tclDisassemble.obj \
- $(TMPDIR)\tclEncoding.obj \
- $(TMPDIR)\tclEnsemble.obj \
- $(TMPDIR)\tclEnv.obj \
- $(TMPDIR)\tclEvent.obj \
- $(TMPDIR)\tclExecute.obj \
- $(TMPDIR)\tclFCmd.obj \
- $(TMPDIR)\tclFileName.obj \
- $(TMPDIR)\tclGet.obj \
- $(TMPDIR)\tclHash.obj \
- $(TMPDIR)\tclHistory.obj \
- $(TMPDIR)\tclIndexObj.obj \
- $(TMPDIR)\tclInterp.obj \
- $(TMPDIR)\tclIO.obj \
- $(TMPDIR)\tclIOCmd.obj \
- $(TMPDIR)\tclIOGT.obj \
- $(TMPDIR)\tclIOSock.obj \
- $(TMPDIR)\tclIOUtil.obj \
- $(TMPDIR)\tclLink.obj \
- $(TMPDIR)\tclLiteral.obj \
- $(TMPDIR)\tclListObj.obj \
- $(TMPDIR)\tclLoad.obj \
- $(TMPDIR)\tclMain.obj \
- $(TMPDIR)\tclNamesp.obj \
- $(TMPDIR)\tclNotify.obj \
- $(TMPDIR)\tclOO.obj \
- $(TMPDIR)\tclOOBasic.obj \
- $(TMPDIR)\tclOOCall.obj \
- $(TMPDIR)\tclOODefineCmds.obj \
- $(TMPDIR)\tclOOInfo.obj \
- $(TMPDIR)\tclOOMethod.obj \
- $(TMPDIR)\tclOOStubInit.obj \
- $(TMPDIR)\tclObj.obj \
- $(TMPDIR)\tclOptimize.obj \
- $(TMPDIR)\tclPanic.obj \
- $(TMPDIR)\tclParse.obj \
- $(TMPDIR)\tclPipe.obj \
- $(TMPDIR)\tclPkg.obj \
- $(TMPDIR)\tclPkgConfig.obj \
- $(TMPDIR)\tclPosixStr.obj \
- $(TMPDIR)\tclPreserve.obj \
- $(TMPDIR)\tclProc.obj \
- $(TMPDIR)\tclRegexp.obj \
- $(TMPDIR)\tclResolve.obj \
- $(TMPDIR)\tclResult.obj \
- $(TMPDIR)\tclScan.obj \
- $(TMPDIR)\tclStringObj.obj \
- $(TMPDIR)\tclStubInit.obj \
- $(TMPDIR)\tclThread.obj \
- $(TMPDIR)\tclThreadJoin.obj \
- $(TMPDIR)\tclTimer.obj \
- $(TMPDIR)\tclTrace.obj \
- $(TMPDIR)\tclUtf.obj \
- $(TMPDIR)\tclUtil.obj \
- $(TMPDIR)\tclVar.obj \
- $(TMPDIR)\tclWin32Dll.obj \
- $(TMPDIR)\tclWinChan.obj \
- $(TMPDIR)\tclWinConsole.obj \
- $(TMPDIR)\tclWinSerial.obj \
- $(TMPDIR)\tclWinError.obj \
- $(TMPDIR)\tclWinFCmd.obj \
- $(TMPDIR)\tclWinFile.obj \
- $(TMPDIR)\tclWinInit.obj \
- $(TMPDIR)\tclWinLoad.obj \
- $(TMPDIR)\tclWinNotify.obj \
- $(TMPDIR)\tclWinPipe.obj \
- $(TMPDIR)\tclWinSock.obj \
- $(TMPDIR)\tclWinThrd.obj \
- $(TMPDIR)\tclWinTime.obj \
- $(TMPDIR)\tclZlib.obj
-
-TCLSTUBOBJS = \
- $(TMPDIR)\tclStubLib.obj \
- $(TMPDIR)\tclTomMathStubLib.obj \
- $(TMPDIR)\tclOOStubLib.obj
-
-WINDIR = $(ROOT)\win
-GENERICDIR = $(ROOT)\generic
-
-TCL_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)"
-TCL_DEFINES = $(DEBUGDEFINES) $(THREADDEFINES) $(SYMDEFINES) \
- $(PROFDEFINES) $(OPTDEFINES) $(SIXFOURDEFINES) \
- -DTCL_CFGVAL_ENCODING=${CFG_ENCODING}
-### TODO: Add -DHAVE_ZLIB=1
-
-######################################################################
-# Compiler flags
-######################################################################
-
-!IF "$(NODEBUG)" == "1"
-# these macros cause maximum optimization and no symbols
-cdebug = -v- -vi- -O2 -D_DEBUG
-!ELSE
-# these macros enable debugging
-cdebug = -k -Od -r- -v -vi- -y
-!ENDIF
-
-SYSDEFINES = _MT;NO_STRICT;_NO_VCL
-
-# declarations common to all compiler options
-cbase = -c -q -3 -a4 -g0 -tWM -Ve -Vx -X-
-WARNINGS = -w-rch -w-pch -w-par -w-dup -w-pro -w-dpu
-
-ccons = -tWC
-
-INCLUDEPATH = $(include32) $(TCL_INCLUDES)
-
-CFLAGS = $(cdebug) $(cbase) $(INCLUDEPATH) $(WARNINGS) -D$(SYSDEFINES)
-TCL_CFLAGS = $(CFLAGS) $(TCL_DEFINES)
-CONS_CFLAGS = $(CFLAGS) $(TCL_DEFINES) $(ccons)
-
-######################################################################
-# Linker flags
-######################################################################
-
-!IF "$(NODEBUG)" == "1"
-ldebug =
-!ELSE
-ldebug = -v
-!ENDIF
-
-# declarations common to all linker options
-LNFLAGS = -D"" -Gn -I$(TMPDIR) -x $(ldebug) $(libpath32)
-# -Gi: create lib file (is -Gl in doc)
-# -aa: Windows app, -ap: Windows console app
-LNFLAGS_DLL = -ap -Gi -Tpd
-LNFLAGS_CONS = -ap -Tpe
-
-LNLIBS = import32 cw32mt
-
-
-######################################################################
-# Project specific targets
-######################################################################
-
-release: setup $(TCLSH) dlls
-dlls: setup $(TCLREGDLL) $(TCLDDEDLL)
-all: setup $(TCLSH) dlls $(CAT32)
-tcltest: setup $(TCLTEST) dlls $(CAT32)
-plugin: setup $(TCLPLUGINDLL) $(TCLSHP)
-install: install-binaries install-libraries
-
-test: setup $(TCLTEST) dlls $(CAT32)
- set TCL_LIBRARY=$(ROOT)/library
- $(TCLTEST) $(ROOT)/tests/all.tcl
-
-setup:
- @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR) &\
- echo *** Created directory '$(OUT_DIR)'
- @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR) &\
- echo *** Created directory '$(TMP_DIR)'
-
-
-$(TCLLIB): $(TCLDLL)
-
-$(TCLDLL): $(TCLOBJS) $(TMPDIR)\$(NAMEPREFIX).res
- $(link32) $(ldebug) $(LNFLAGS) $(LNFLAGS_DLL) $(TOOLS32)\lib\c0d32 @&&!
- $(TCLOBJS), $@, -x, $(LNLIBS),, $(TMPDIR)\$(NAMEPREFIX).res
-!
-
-$(TCLSTUBLIB): $(TCLSTUBOBJS)
- $(lib32) /u $@ $(TCLSTUBOBJS)
-
-$(TCLPLUGINLIB): $(TCLPLUGINDLL)
-
-$(TCLPLUGINDLL): $(TCLOBJS) $(TMPDIR)\tcl.res
- $(link32) $(ldebug) $(dlllflags) \
- -out:$@ $(TMPDIR)\tcl.res $(guilibsdll) @&&!
-$(TCLOBJS)
-!
-
-$(TCLSH): $(TCLSHOBJS) $(TCLLIB) $(TMPDIR)\$(NAMEPREFIX)sh.res
- $(link32) $(ldebug) -S:2400000 $(LNFLAGS) $(LNFLAGS_CONS) $(TOOLS32)\lib\c0x32 @&&!
- $(TCLSHOBJS), $@, -x, $(LNLIBS) $(TCLLIB),, $(TMPDIR)\$(NAMEPREFIX)sh.res
-!
-
-$(TCLSHP): $(TCLSHOBJS) $(TCLPLUGINLIB) $(TMPDIR)\tclsh.res
- $(link32) $(ldebug) $(conlflags) $(TMPDIR)\tclsh.res -stack:2300000 \
- -out:$@ $(conlibsdll) $(TCLPLUGINLIB) $(TCLSHOBJS)
-
-$(TCLTEST): $(TCLTESTOBJS) $(TCLLIB) $(TMPDIR)\$(NAMEPREFIX)sh.res
- $(link32) $(ldebug) -S:2400000 $(LNFLAGS) $(LNFLAGS_CONS) $(TOOLS32)\lib\c0x32 @&&!
- $(TCLTESTOBJS), $@, -x, $(LNLIBS) $(TCLLIB),, $(TMPDIR)\$(NAMEPREFIX)sh.res
-!
-
-$(TCLDDEDLL): $(TMPDIR)\tclWinDde.obj $(TCLSTUBLIB)
- $(link32) $(ldebug) $(LNFLAGS) $(LNFLAGS_DLL) $(TOOLS32)\lib\c0d32 \
- $(TMPDIR)\tclWinDde.obj, $@, -x, $(LNLIBS) $(TCLSTUBLIB),, \
- $(TMPDIR)\$(NAMEPREFIX).res
-
-$(TCLREGDLL): $(TMPDIR)\tclWinReg.obj $(TCLSTUBLIB)
- $(link32) $(ldebug) $(LNFLAGS) $(LNFLAGS_DLL) $(TOOLS32)\lib\c0d32 \
- $(TMPDIR)\tclWinReg.obj, $@, -x, $(LNLIBS) $(TCLSTUBLIB),, \
- $(TMPDIR)\$(NAMEPREFIX).res
-
-$(CAT32): $(WINDIR)\cat.c
- $(cc32) $(CONS_CFLAGS) -o$(TMPDIR)\cat.obj $?
- $(link32) $(ldebug) $(LNFLAGS) $(LNFLAGS_CONS) $(TOOLS32)\lib\c0x32 \
- $(TMPDIR)\cat.obj, $@, -x, $(LNLIBS),,
-
-install-binaries: $(TCLSH)
- $(MKDIR) "$(BIN_INSTALL_DIR)"
- $(MKDIR) "$(LIB_INSTALL_DIR)"
- @echo Installing $(TCLDLLNAME)
- @copy "$(TCLDLL)" "$(BIN_INSTALL_DIR)"
- @copy "$(TCLLIB)" "$(LIB_INSTALL_DIR)"
- @echo Installing "$(TCLSH)"
- @copy "$(TCLSH)" "$(BIN_INSTALL_DIR)"
- @echo Installing $(TCLSTUBLIBNAME)
- @copy "$(TCLSTUBLIB)" "$(LIB_INSTALL_DIR)"
- @echo Installing $(WINDIR)\tclooConfig.sh
- @copy "$(WINDIR)\tclooConfig.sh" "$(LIB_INSTALL_DIR)"
-
-install-libraries:
- -@$(MKDIR) "$(LIB_INSTALL_DIR)"
- -@$(MKDIR) "$(INCLUDE_INSTALL_DIR)"
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)"
- @echo Installing http1.0
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\http1.0"
- -@copy "$(ROOT)\library\http1.0\http.tcl" "$(SCRIPT_INSTALL_DIR)\http1.0"
- -@copy "$(ROOT)\library\http1.0\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\http1.0"
- @echo Installing http2.8
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\http2.8"
- -@copy "$(ROOT)\library\http\http.tcl" "$(SCRIPT_INSTALL_DIR)\http2.8"
- -@copy "$(ROOT)\library\http\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\http2.8"
- @echo Installing opt0.4
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\opt0.4"
- -@copy "$(ROOT)\library\opt\optparse.tcl" "$(SCRIPT_INSTALL_DIR)\opt0.4"
- -@copy "$(ROOT)\library\opt\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\opt0.4"
- @echo Installing msgcat1.5
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\msgcat1.5"
- -@copy "$(ROOT)\library\msgcat\msgcat.tcl" "$(SCRIPT_INSTALL_DIR)\msgcat1.5"
- -@copy "$(ROOT)\library\msgcat\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\msgcat1.5"
- @echo Installing tcltest2.3
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\tcltest2.3"
- -@copy "$(ROOT)\library\tcltest\tcltest.tcl" "$(SCRIPT_INSTALL_DIR)\tcltest2.3"
- -@copy "$(ROOT)\library\tcltest\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\tcltest2.3"
- @echo Installing platform1.0
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\platform1.0"
- -@copy "$(ROOT)\library\platform\platform.tcl" "$(SCRIPT_INSTALL_DIR)\platform1.0"
- -@copy "$(ROOT)\library\platform\shell.tcl" "$(SCRIPT_INSTALL_DIR)\platform1.0"
- -@copy "$(ROOT)\library\platform\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\platform1.0"
- @echo Installing $(TCLDDEDLLNAME)
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\dde1.3"
- -@copy "$(TCLDDEDLL)" "$(SCRIPT_INSTALL_DIR)\dde1.3"
- -@copy "$(ROOT)\library\dde\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\dde1.3"
- @echo Installing $(TCLREGDLLNAME)
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\reg1.2"
- -@copy "$(TCLREGDLL)" "$(SCRIPT_INSTALL_DIR)\reg1.3"
- -@copy "$(ROOT)\library\reg\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\reg1.2"
- @echo Installing encoding files
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\encoding"
- -@copy "$(ROOT)\library\encoding\*.enc" "$(SCRIPT_INSTALL_DIR)\encoding"
- @echo Installing library files
- -@copy "$(GENERICDIR)\tcl.h" "$(INCLUDE_INSTALL_DIR)"
- -@copy "$(GENERICDIR)\tclDecls.h" "$(INCLUDE_INSTALL_DIR)"
- -@copy "$(GENERICDIR)\tclOO.h" "$(INCLUDE_INSTALL_DIR)"
- -@copy "$(GENERICDIR)\tclOODecls.h" "$(INCLUDE_INSTALL_DIR)"
- -@copy "$(GENERICDIR)\tclPlatDecls.h" "$(INCLUDE_INSTALL_DIR)"
- -@copy "$(ROOT)\library\history.tcl" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\init.tcl" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\parray.tcl" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\safe.tcl" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\tclIndex" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\package.tcl" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\word.tcl" "$(SCRIPT_INSTALL_DIR)"
- -@copy "$(ROOT)\library\auto.tcl" "$(SCRIPT_INSTALL_DIR)"
-
-#
-# Regenerate the stubs files.
-#
-
-genstubs:
- tclsh$(VERSION) $(ROOT)\tools\genStubs.tcl $(GENERICDIR) \
- $(GENERICDIR)\tcl.decls $(GENERICDIR)\tclInt.decls
-
-#
-# Special case object file targets
-#
-$(TMPDIR)\tclWinInit.obj: $(WINDIR)\tclWinInit.c
- $(cc32) -DBUILD_tcl $(TCL_CFLAGS) -o$(TMPDIR)\$@ $?
-
-$(TMPDIR)\testMain.obj: $(WINDIR)\tclAppInit.c
- $(cc32) $(TCL_CFLAGS) -DTCL_TEST -o$(TMPDIR)\testMain.obj $?
-
-$(TMPDIR)\tclTest.obj: $(GENERICDIR)\tclTest.c
- $(cc32) $(TCL_CFLAGS) -o$(TMPDIR)\$@ $?
-
-$(TMPDIR)\tclTestObj.obj: $(GENERICDIR)\tclTestObj.c
- $(cc32) $(TCL_CFLAGS) -o$(TMPDIR)\$@ $?
-
-$(TMPDIR)\tclWinTest.obj: $(WINDIR)\tclWinTest.c
- $(cc32) $(TCL_CFLAGS) -o$(TMPDIR)\$@ $?
-
-$(TMP_DIR)\tclPkgConfig.obj: $(GENERICDIR)\tclPkgConfig.c
- $(cc32) $(TCL_CFLAGS) \
- -DCFG_INSTALL_EXEC_PREFIX=\"$(INSTALL_EXEC_PREFIX)\" \
- -DCFG_INSTALL_PREFIX=\"$(INSTALL_PREFIX)\" \
- -DCFG_RUNTIME_EXEC_PREFIX=\"$(RUNTIME_EXEC_PREFIX)\" \
- -DCFG_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" \
- -o$(TMPDIR)\$@ $?
-
-$(TMPDIR)\tclAppInit.obj : $(WINDIR)\tclAppInit.c
- $(cc32) $(TCL_CFLAGS) -o$(TMPDIR)\$@ $?
-
-# The following objects should be built using the stub interfaces
-
-# tclWinReg: Produces errors in ANSI mode
-$(TMPDIR)\tclWinReg.obj : $(WINDIR)\tclWinReg.c
- $(cc32) $(TCL_CFLAGS) -DUSE_TCL_STUBS -o$(TMPDIR)\$@ $?
-
-# tclWinDde: Produces errors in ANSI mode
-$(TMPDIR)\tclWinDde.obj : $(WINDIR)\tclWinDde.c
- $(cc32) $(TCL_CFLAGS) -DUSE_TCL_STUBS -o$(TMPDIR)\$@ $?
-
-
-# The following objects are part of the stub library and should not
-# be built as DLL objects but none of the symbols should be exported
-
-$(TMPDIR)\tclStubLib.obj : $(GENERICDIR)\tclStubLib.c
- $(cc32) $(TCL_CFLAGS) -DSTATIC_BUILD -o$(TMPDIR)\$@ $?
-
-$(TMPDIR)\tclTomMathStubLib.obj : $(GENERICDIR)\tclTomMathStubLib.c
- $(cc32) $(TCL_CFLAGS) -DSTATIC_BUILD -o$(TMPDIR)\$@ $?
-
-$(TMPDIR)\tclOOStubLib.obj : $(GENERICDIR)\tclOOStubLib.c
- $(cc32) $(TCL_CFLAGS) -DSTATIC_BUILD -o$(TMPDIR)\$@ $?
-
-
-# Dedependency rules
-
-$(GENERICDIR)\regcomp.c: \
- $(GENERICDIR)\regguts.h \
- $(GENERICDIR)\regc_lex.c \
- $(GENERICDIR)\regc_color.c \
- $(GENERICDIR)\regc_nfa.c \
- $(GENERICDIR)\regc_cvec.c \
- $(GENERICDIR)\regc_locale.c
-
-$(GENERICDIR)\regcustom.h: \
- $(GENERICDIR)\tclInt.h \
- $(GENERICDIR)\tclPort.h \
- $(GENERICDIR)\regex.h
-
-$(GENERICDIR)\regexec.c: \
- $(GENERICDIR)\rege_dfa.c \
- $(GENERICDIR)\regguts.h
-
-$(GENERICDIR)\regerror.c: $(GENERICDIR)\regguts.h
-$(GENERICDIR)\regfree.c: $(GENERICDIR)\regguts.h
-$(GENERICDIR)\regfronts.c: $(GENERICDIR)\regguts.h
-$(GENERICDIR)\regguts.h: $(GENERICDIR)\regcustom.h
-
-#
-# Implicit rules
-#
-
-{$(WINDIR)}.c{$(TMPDIR)}.obj:
- $(cc32) -DBUILD_tcl $(TCL_CFLAGS) -o$@ $<
-
-{$(GENERICDIR)}.c{$(TMPDIR)}.obj:
- $(cc32) -DBUILD_tcl $(TCL_CFLAGS) -o$@ $<
-
-{$(ROOT)\compat}.c{$(TMPDIR)}.obj:
- $(cc32) -DBUILD_tcl $(TCL_CFLAGS) -o$@ $<
-
-{$(WINDIR)}.rc{$(TMPDIR)}.res:
- $(rc32) $(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $<
-
-clean:
- -@$(RM) $(OUTDIR)\*.exp
- -@$(RM) $(OUTDIR)\*.lib
- -@$(RM) $(OUTDIR)\*.dll
- -@$(RM) $(OUTDIR)\*.exe
- -@$(RM) $(OUTDIR)\*.pdb
- -@$(RM) $(TMPDIR)\*.pch
- -@$(RM) $(TMPDIR)\*.obj
- -@$(RM) $(TMPDIR)\*.res
- -@$(RM) $(TMPDIR)\*.exe
- -@$(RMDIR) $(OUTDIR)
- -@$(RMDIR) $(TMPDIR)
-
-# Local Variables:
-# mode: makefile
-# End:
diff --git a/win/makefile.vc b/win/makefile.vc index d6de5e1..18f691b 100644 --- a/win/makefile.vc +++ b/win/makefile.vc @@ -879,6 +879,7 @@ $(OUT_DIR)\tclConfig.sh: $(WINDIR)\tclConfig.sh.in @SHLIB_SUFFIX@ .dll
@DL_LIBS@
@LDFLAGS@
+@TCL_CC_SEARCH_FLAGS@
@TCL_LD_SEARCH_FLAGS@
@LIBOBJS@
@RANLIB@
diff --git a/win/tcl.dsp b/win/tcl.dsp index cade8a7..48eae9d 100644 --- a/win/tcl.dsp +++ b/win/tcl.dsp @@ -1428,10 +1428,6 @@ SOURCE=.\configure.ac # End Source File
# Begin Source File
-SOURCE=.\makefile.bc
-# End Source File
-# Begin Source File
-
SOURCE=.\Makefile.in
# End Source File
# Begin Source File
diff --git a/win/tclConfig.sh.in b/win/tclConfig.sh.in index b060370..97670aa 100644 --- a/win/tclConfig.sh.in +++ b/win/tclConfig.sh.in @@ -92,10 +92,11 @@ TCL_DL_LIBS='@DL_LIBS@' # an executable tclsh or tcltest binary. TCL_LD_FLAGS='@LDFLAGS@' -# Flags to pass to ld, such as "-R /usr/local/tcl/lib", that tell the +# Flags to pass to cc/ld, such as "-R /usr/local/tcl/lib", that tell the # run-time dynamic linker where to look for shared libraries such as # libtcl.so. Used when linking applications. Only works if there # is a variable "LIB_RUNTIME_DIR" defined in the Makefile. +TCL_CC_SEARCH_FLAGS='@TCL_CC_SEARCH_FLAGS@' TCL_LD_SEARCH_FLAGS='@TCL_LD_SEARCH_FLAGS@' # Additional object files linked with Tcl to provide compatibility diff --git a/win/tclWinChan.c b/win/tclWinChan.c index 7518e3e..2898db0 100644 --- a/win/tclWinChan.c +++ b/win/tclWinChan.c @@ -25,7 +25,8 @@ #define FILE_TYPE_CONSOLE (FILE_TYPE_PIPE+2) /* - * The following structure contains per-instance data for a file based channel. + * The following structure contains per-instance data for a file based + * channel. */ typedef struct FileInfo { @@ -96,6 +97,7 @@ static int FileTruncateProc(ClientData instanceData, Tcl_WideInt length); static DWORD FileGetType(HANDLE handle); static int NativeIsComPort(const TCHAR *nativeName); + /* * This structure describes the channel type structure for file based IO. */ @@ -119,6 +121,14 @@ static const Tcl_ChannelType fileChannelType = { FileThreadActionProc, /* Thread action proc. */ FileTruncateProc /* Truncate proc. */ }; + +/* + * General useful clarification macros. + */ + +#define SET_FLAG(var, flag) ((var) |= (flag)) +#define CLEAR_FLAG(var, flag) ((var) &= ~(flag)) +#define TEST_FLAG(value, flag) (((value) & (flag)) != 0) /* *---------------------------------------------------------------------- @@ -140,7 +150,7 @@ static ThreadSpecificData * FileInit(void) { ThreadSpecificData *tsdPtr = - (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey); if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); @@ -201,7 +211,7 @@ FileSetupProc( Tcl_Time blockTime = { 0, 0 }; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!(flags & TCL_FILE_EVENTS)) { + if (!TEST_FLAG(flags, TCL_FILE_EVENTS)) { return; } @@ -244,7 +254,7 @@ FileCheckProc( FileInfo *infoPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!(flags & TCL_FILE_EVENTS)) { + if (!TEST_FLAG(flags, TCL_FILE_EVENTS)) { return; } @@ -255,8 +265,8 @@ FileCheckProc( for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) { - infoPtr->flags |= FILE_PENDING; + if (infoPtr->watchMask && !TEST_FLAG(infoPtr->flags, FILE_PENDING)) { + SET_FLAG(infoPtr->flags, FILE_PENDING); evPtr = ckalloc(sizeof(FileEvent)); evPtr->header.proc = FileEventProc; evPtr->infoPtr = infoPtr; @@ -296,7 +306,7 @@ FileEventProc( FileInfo *infoPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!(flags & TCL_FILE_EVENTS)) { + if (!TEST_FLAG(flags, TCL_FILE_EVENTS)) { return 0; } @@ -310,7 +320,7 @@ FileEventProc( for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (fileEvPtr->infoPtr == infoPtr) { - infoPtr->flags &= ~(FILE_PENDING); + CLEAR_FLAG(infoPtr->flags, FILE_PENDING); Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask); break; } @@ -350,9 +360,9 @@ FileBlockProc( */ if (mode == TCL_MODE_NONBLOCKING) { - infoPtr->flags |= FILE_ASYNC; + SET_FLAG(infoPtr->flags, FILE_ASYNC); } else { - infoPtr->flags &= ~(FILE_ASYNC); + CLEAR_FLAG(infoPtr->flags, FILE_ASYNC); } return 0; } @@ -472,7 +482,7 @@ FileSeekProc( oldPosHigh = 0; oldPos = SetFilePointer(infoPtr->handle, 0, &oldPosHigh, FILE_CURRENT); - if (oldPos == (LONG)INVALID_SET_FILE_POINTER) { + if (oldPos == (LONG) INVALID_SET_FILE_POINTER) { DWORD winError = GetLastError(); if (winError != NO_ERROR) { @@ -484,7 +494,7 @@ FileSeekProc( newPosHigh = (offset < 0 ? -1 : 0); newPos = SetFilePointer(infoPtr->handle, offset, &newPosHigh, moveMethod); - if (newPos == (LONG)INVALID_SET_FILE_POINTER) { + if (newPos == (LONG) INVALID_SET_FILE_POINTER) { DWORD winError = GetLastError(); if (winError != NO_ERROR) { @@ -547,7 +557,7 @@ FileWideSeekProc( newPosHigh = Tcl_WideAsLong(offset >> 32); newPos = SetFilePointer(infoPtr->handle, Tcl_WideAsLong(offset), &newPosHigh, moveMethod); - if (newPos == (LONG)INVALID_SET_FILE_POINTER) { + if (newPos == (LONG) INVALID_SET_FILE_POINTER) { DWORD winError = GetLastError(); if (winError != NO_ERROR) { @@ -556,7 +566,8 @@ FileWideSeekProc( return -1; } } - return (((Tcl_WideInt)((unsigned)newPos)) | (Tcl_LongAsWide(newPosHigh) << 32)); + return (((Tcl_WideInt)((unsigned)newPos)) + | (Tcl_LongAsWide(newPosHigh) << 32)); } /* @@ -589,8 +600,9 @@ FileTruncateProc( oldPosHigh = 0; oldPos = SetFilePointer(infoPtr->handle, 0, &oldPosHigh, FILE_CURRENT); - if (oldPos == (LONG)INVALID_SET_FILE_POINTER) { + if (oldPos == (LONG) INVALID_SET_FILE_POINTER) { DWORD winError = GetLastError(); + if (winError != NO_ERROR) { TclWinConvertError(winError); return errno; @@ -604,8 +616,9 @@ FileTruncateProc( newPosHigh = Tcl_WideAsLong(length >> 32); newPos = SetFilePointer(infoPtr->handle, Tcl_WideAsLong(length), &newPosHigh, FILE_BEGIN); - if (newPos == (LONG)INVALID_SET_FILE_POINTER) { + if (newPos == (LONG) INVALID_SET_FILE_POINTER) { DWORD winError = GetLastError(); + if (winError != NO_ERROR) { TclWinConvertError(winError); return errno; @@ -662,9 +675,9 @@ FileInputProc( *errorCode = 0; /* - * TODO: This comment appears to be out of date. We *do* have a - * console driver, over in tclWinConsole.c. After some Windows - * developer confirms, this comment should be revised. + * TODO: This comment appears to be out of date. We *do* have a console + * driver, over in tclWinConsole.c. After some Windows developer confirms, + * this comment should be revised. * * Note that we will block on reads from a console buffer until a full * line has been entered. The only way I know of to get around this is to @@ -721,7 +734,7 @@ FileOutputProc( * seek to the end of the file before writing the current buffer. */ - if (infoPtr->flags & FILE_APPEND) { + if (TEST_FLAG(infoPtr->flags, FILE_APPEND)) { SetFilePointer(infoPtr->handle, 0, NULL, FILE_END); } @@ -798,12 +811,12 @@ FileGetHandleProc( { FileInfo *infoPtr = instanceData; - if (direction & infoPtr->validMask) { - *handlePtr = (ClientData) infoPtr->handle; - return TCL_OK; - } else { + if (!TEST_FLAG(direction, infoPtr->validMask)) { return TCL_ERROR; } + + *handlePtr = (ClientData) infoPtr->handle; + return TCL_OK; } /* @@ -843,10 +856,10 @@ TclpOpenFileChannel( nativeName = Tcl_FSGetNativePath(pathPtr); if (nativeName == NULL) { - if (interp != (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "couldn't open \"", - TclGetString(pathPtr), "\": filename is invalid on this platform", - NULL); + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't open \"%s\": filename is invalid on this platform", + TclGetString(pathPtr))); } return NULL; } @@ -894,39 +907,40 @@ TclpOpenFileChannel( } /* - * [2413550] Avoid double-open of serial ports on Windows - * Special handling for Windows serial ports by a "name-hint" - * to directly open it with the OVERLAPPED flag set. + * [2413550] Avoid double-open of serial ports on Windows. Special + * handling for Windows serial ports by a "name-hint" to directly open it + * with the OVERLAPPED flag set. */ - if( NativeIsComPort(nativeName) ) { - + if (NativeIsComPort(nativeName)) { handle = TclWinSerialOpen(INVALID_HANDLE_VALUE, nativeName, accessMode); if (handle == INVALID_HANDLE_VALUE) { TclWinConvertError(GetLastError()); - if (interp != (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "couldn't open serial \"", - TclGetString(pathPtr), "\": ", - Tcl_PosixError(interp), NULL); + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't open serial \"%s\": %s", + TclGetString(pathPtr), Tcl_PosixError(interp))); } return NULL; } /* - * For natively named Windows serial ports we are done. - */ + * For natively named Windows serial ports we are done. + */ + channel = TclWinOpenSerialChannel(handle, channelName, channelPermissions); return channel; } + /* * If the file is being created, get the file attributes from the * permissions argument, else use the existing file attributes. */ - if (mode & O_CREAT) { - if (permissions & S_IWRITE) { + if (TEST_FLAG(mode, O_CREAT)) { + if (TEST_FLAG(permissions, S_IWRITE)) { flags = FILE_ATTRIBUTE_NORMAL; } else { flags = FILE_ATTRIBUTE_READONLY; @@ -955,10 +969,11 @@ TclpOpenFileChannel( DWORD err = GetLastError(); if ((err & 0xffffL) == ERROR_OPEN_FAILED) { - err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND; + err = TEST_FLAG(mode, O_CREAT) ? ERROR_FILE_EXISTS + : ERROR_FILE_NOT_FOUND; } TclWinConvertError(err); - if (interp != (Tcl_Interp *) NULL) { + if (interp) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "couldn't open \"%s\": %s", TclGetString(pathPtr), Tcl_PosixError(interp))); @@ -971,9 +986,9 @@ TclpOpenFileChannel( switch (FileGetType(handle)) { case FILE_TYPE_SERIAL: /* - * Natively named serial ports "com1-9", "\\\\.\\comXX" are - * already done with the code above. - * Here we handle all other serial port names. + * Natively named serial ports "com1-9", "\\\\.\\comXX" are already + * done with the code above. Here we handle all other serial port + * names. * * Reopen channel for OVERLAPPED operation. Normally this shouldn't * fail, because the channel exists. @@ -982,7 +997,7 @@ TclpOpenFileChannel( handle = TclWinSerialOpen(handle, nativeName, accessMode); if (handle == INVALID_HANDLE_VALUE) { TclWinConvertError(GetLastError()); - if (interp != (Tcl_Interp *) NULL) { + if (interp) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "couldn't reopen serial \"%s\": %s", TclGetString(pathPtr), Tcl_PosixError(interp))); @@ -997,10 +1012,10 @@ TclpOpenFileChannel( channelPermissions); break; case FILE_TYPE_PIPE: - if (channelPermissions & TCL_READABLE) { + if (TEST_FLAG(channelPermissions, TCL_READABLE)) { readFile = TclWinMakeFile(handle); } - if (channelPermissions & TCL_WRITABLE) { + if (TEST_FLAG(channelPermissions, TCL_WRITABLE)) { writeFile = TclWinMakeFile(handle); } channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL); @@ -1009,7 +1024,8 @@ TclpOpenFileChannel( case FILE_TYPE_DISK: case FILE_TYPE_UNKNOWN: channel = TclWinOpenFileChannel(handle, channelName, - channelPermissions, (mode & O_APPEND) ? FILE_APPEND : 0); + channelPermissions, + TEST_FLAG(mode, O_APPEND) ? FILE_APPEND : 0); break; default: @@ -1074,10 +1090,10 @@ Tcl_MakeFileChannel( channel = TclWinOpenConsoleChannel(handle, channelName, mode); break; case FILE_TYPE_PIPE: - if (mode & TCL_READABLE) { + if (TEST_FLAG(mode, TCL_READABLE)) { readFile = TclWinMakeFile(handle); } - if (mode & TCL_WRITABLE) { + if (TEST_FLAG(mode, TCL_WRITABLE)) { writeFile = TclWinMakeFile(handle); } channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL); @@ -1524,10 +1540,11 @@ FileGetType( * * NativeIsComPort -- * - * Determines if a path refers to a Windows serial port. - * A simple and efficient solution is to use a "name hint" to detect - * COM ports by their filename instead of resorting to a syscall - * to detect serialness after the fact. + * Determines if a path refers to a Windows serial port. A simple and + * efficient solution is to use a "name hint" to detect COM ports by + * their filename instead of resorting to a syscall to detect serialness + * after the fact. + * * The following patterns cover common serial port names: * COM[1-9] * \\.\COM[0-9]+ @@ -1549,12 +1566,12 @@ NativeIsComPort( * 1. Look for com[1-9]:? */ - if ( (len == 4) && (_wcsnicmp(p, L"com", 3) == 0) ) { + if ((len == 4) && (_wcsnicmp(p, L"com", 3) == 0)) { /* - * The 4th character must be a digit 1..9 - */ + * The 4th character must be a digit 1..9 + */ - if ( (p[3] < L'1') || (p[3] > L'9') ) { + if ((p[3] < L'1') || (p[3] > L'9')) { return 0; } return 1; @@ -1566,11 +1583,11 @@ NativeIsComPort( if ((len >= 8) && (_wcsnicmp(p, L"\\\\.\\com", 7) == 0)) { /* - * Charaters 8..end must be a digits 0..9 - */ + * Charaters 8..end must be a digits 0..9 + */ - for ( i=7; i<len; i++ ) { - if ( (p[i] < '0') || (p[i] > '9') ) { + for (i=7; i<len; i++) { + if ((p[i] < '0') || (p[i] > '9')) { return 0; } } diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index 5ce8a2b..d148872 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -54,11 +54,7 @@ typedef struct { HANDLE readyEvent; /* Manual-reset event to signal _to_ the main * thread when the worker thread has finished * waiting for its normal work to happen. */ - HANDLE startEvent; /* Auto-reset event used by the main thread to - * signal when the thread should attempt to do - * its normal work. */ - HANDLE stopEvent; /* Auto-reset event used by the main thread to - * signal when the thread should exit. */ + TclPipeThreadInfo *TI; /* Thread info structure of writer and reader. */ } ConsoleThreadInfo; /* @@ -82,16 +78,14 @@ typedef struct ConsoleInfo { * threads. */ ConsoleThreadInfo writer; /* A specialized thread for handling * asynchronous writes to the console; the - * waiting starts when a start event is sent, + * waiting starts when a control event is sent, * and a reset event is sent back to the main - * thread when the write is done. A stop event - * is used to terminate the thread. */ + * thread when the write is done. */ ConsoleThreadInfo reader; /* A specialized thread for handling * asynchronous reads from the console; the - * waiting starts when a start event is sent, + * waiting starts when a control event is sent, * and a reset event is sent back to the main - * thread when input is available. A stop - * event is used to terminate the thread. */ + * thread when input is available. */ DWORD writeError; /* An error caused by the last background * write. Set to 0 if no error has been * detected. This word is shared with the @@ -168,10 +162,6 @@ static BOOL ReadConsoleBytes(HANDLE hConsole, LPVOID lpBuffer, static BOOL WriteConsoleBytes(HANDLE hConsole, const void *lpBuffer, DWORD nbytes, LPDWORD nbyteswritten); -static void StartChannelThread(ConsoleInfo *infoPtr, - ConsoleThreadInfo *threadInfoPtr, - LPTHREAD_START_ROUTINE threadProc); -static void StopChannelThread(ConsoleThreadInfo *threadInfoPtr); /* * This structure describes the channel type structure for command console @@ -518,84 +508,6 @@ ConsoleBlockModeProc( /* *---------------------------------------------------------------------- * - * StartChannelThread, StopChannelThread -- - * - * Helpers that codify how to ask one of the console service threads to - * start and stop. - * - *---------------------------------------------------------------------- - */ - -static void -StartChannelThread( - ConsoleInfo *infoPtr, - ConsoleThreadInfo *threadInfoPtr, - LPTHREAD_START_ROUTINE threadProc) -{ - DWORD id; - - threadInfoPtr->readyEvent = CreateEvent(NULL, TRUE, TRUE, NULL); - threadInfoPtr->startEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - threadInfoPtr->stopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - threadInfoPtr->thread = CreateThread(NULL, 256, threadProc, infoPtr, 0, - &id); - SetThreadPriority(threadInfoPtr->thread, THREAD_PRIORITY_HIGHEST); -} - -static void -StopChannelThread( - ConsoleThreadInfo *threadInfoPtr) -{ - DWORD exitCode = 0; - - /* - * The thread may already have closed on it's own. Check it's exit - * code. - */ - - GetExitCodeThread(threadInfoPtr->thread, &exitCode); - if (exitCode == STILL_ACTIVE) { - /* - * Set the stop event so that if the reader thread is blocked in - * ConsoleReaderThread on WaitForMultipleEvents, it will exit cleanly. - */ - - SetEvent(threadInfoPtr->stopEvent); - - /* - * Wait at most 20 milliseconds for the reader thread to close. - */ - - if (WaitForSingleObject(threadInfoPtr->thread, 20) == WAIT_TIMEOUT) { - /* - * Forcibly terminate the background thread as a last resort. - * Note that we need to guard against terminating the thread while - * it is in the middle of Tcl_ThreadAlert because it won't be able - * to release the notifier lock. - */ - - Tcl_MutexLock(&consoleMutex); - /* BUG: this leaks memory. */ - TerminateThread(threadInfoPtr->thread, 0); - Tcl_MutexUnlock(&consoleMutex); - } - } - - /* - * Close all the handles associated with the thread, and set the thread - * handle field to NULL to mark that the thread has been cleaned up. - */ - - CloseHandle(threadInfoPtr->thread); - CloseHandle(threadInfoPtr->readyEvent); - CloseHandle(threadInfoPtr->startEvent); - CloseHandle(threadInfoPtr->stopEvent); - threadInfoPtr->thread = NULL; -} - -/* - *---------------------------------------------------------------------- - * * ConsoleCloseProc -- * * Closes a console based IO channel. @@ -626,7 +538,10 @@ ConsoleCloseProc( */ if (consolePtr->reader.thread) { - StopChannelThread(&consolePtr->reader); + TclPipeThreadStop(&consolePtr->reader.TI, consolePtr->reader.thread); + CloseHandle(consolePtr->reader.thread); + CloseHandle(consolePtr->reader.readyEvent); + consolePtr->reader.thread = NULL; } consolePtr->validMask &= ~TCL_READABLE; @@ -643,10 +558,13 @@ ConsoleCloseProc( * prevent infinite wait on exit. [Python Bug 216289] */ - WaitForSingleObject(consolePtr->writer.readyEvent, INFINITE); + WaitForSingleObject(consolePtr->writer.readyEvent, 5000); } - StopChannelThread(&consolePtr->writer); + TclPipeThreadStop(&consolePtr->writer.TI, consolePtr->writer.thread); + CloseHandle(consolePtr->writer.thread); + CloseHandle(consolePtr->writer.readyEvent); + consolePtr->writer.thread = NULL; } consolePtr->validMask &= ~TCL_WRITABLE; @@ -808,12 +726,15 @@ ConsoleOutputProc( int *errorCode) /* Where to store error code. */ { ConsoleInfo *infoPtr = instanceData; - ConsoleThreadInfo *threadInfo = &infoPtr->reader; + ConsoleThreadInfo *threadInfo = &infoPtr->writer; DWORD bytesWritten, timeout; *errorCode = 0; - timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE; - if (WaitForSingleObject(threadInfo->readyEvent,timeout) == WAIT_TIMEOUT) { + + /* avoid blocking if pipe-thread exited */ + timeout = (infoPtr->flags & CONSOLE_ASYNC) || !TclPipeThreadIsAlive(&threadInfo->TI) + || TclInExit() || TclInThreadExit() ? 0 : INFINITE; + if (WaitForSingleObject(threadInfo->readyEvent, timeout) == WAIT_TIMEOUT) { /* * The writer thread is blocked waiting for a write to complete and * the channel is in non-blocking mode. @@ -853,7 +774,7 @@ ConsoleOutputProc( memcpy(infoPtr->writeBuf, buf, (size_t) toWrite); infoPtr->toWrite = toWrite; ResetEvent(threadInfo->readyEvent); - SetEvent(threadInfo->startEvent); + TclPipeThreadSignal(&threadInfo->TI); bytesWritten = toWrite; } else { /* @@ -1090,9 +1011,10 @@ WaitForRead( * Synchronize with the reader thread. */ - timeout = blocking ? INFINITE : 0; - if (WaitForSingleObject(threadInfo->readyEvent, - timeout) == WAIT_TIMEOUT) { + /* avoid blocking if pipe-thread exited */ + timeout = (!blocking || !TclPipeThreadIsAlive(&threadInfo->TI) + || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; + if (WaitForSingleObject(threadInfo->readyEvent, timeout) == WAIT_TIMEOUT) { /* * The reader thread is blocked waiting for data and the channel * is in non-blocking mode. @@ -1152,7 +1074,7 @@ WaitForRead( */ ResetEvent(threadInfo->readyEvent); - SetEvent(threadInfo->startEvent); + TclPipeThreadSignal(&threadInfo->TI); } } @@ -1179,34 +1101,27 @@ static DWORD WINAPI ConsoleReaderThread( LPVOID arg) { - ConsoleInfo *infoPtr = arg; - HANDLE *handle = infoPtr->handle; - ConsoleThreadInfo *threadInfo = &infoPtr->reader; - DWORD waitResult; - HANDLE wEvents[2]; + TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; + ConsoleInfo *infoPtr = NULL; /* access info only after success init/wait */ + HANDLE *handle = NULL; + ConsoleThreadInfo *threadInfo = NULL; + int done = 0; - /* - * The first event takes precedence. - */ - - wEvents[0] = threadInfo->stopEvent; - wEvents[1] = threadInfo->startEvent; - - for (;;) { + while (!done) { /* - * Wait for the main thread to signal before attempting to wait. + * Wait for the main thread to signal before attempting to read. */ - waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); - - if (waitResult != (WAIT_OBJECT_0 + 1)) { - /* - * The start event was not signaled. It must be the stop event or - * an error, so exit this thread. - */ - + if (!TclPipeThreadWaitForSignal(&pipeTI)) { + /* exit */ break; } + if (!infoPtr) { + infoPtr = (ConsoleInfo *)pipeTI->clientData; + handle = infoPtr->handle; + threadInfo = &infoPtr->reader; + } + /* * Look for data on the console, but first ignore any events that are @@ -1226,6 +1141,7 @@ ConsoleReaderThread( if (err == (DWORD) EOF) { infoPtr->readFlags = CONSOLE_EOF; } + done = 1; } /* @@ -1253,6 +1169,9 @@ ConsoleReaderThread( Tcl_MutexUnlock(&consoleMutex); } + /* Worker exit, so inform the main thread or free TI-structure (if owned) */ + TclPipeThreadExit(&pipeTI); + return 0; } @@ -1279,35 +1198,27 @@ static DWORD WINAPI ConsoleWriterThread( LPVOID arg) { - ConsoleInfo *infoPtr = arg; - HANDLE *handle = infoPtr->handle; - ConsoleThreadInfo *threadInfo = &infoPtr->writer; - DWORD count, toWrite, waitResult; + TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; + ConsoleInfo *infoPtr = NULL; /* access info only after success init/wait */ + HANDLE *handle = NULL; + ConsoleThreadInfo *threadInfo = NULL; + DWORD count, toWrite; char *buf; - HANDLE wEvents[2]; - - /* - * The first event takes precedence. - */ + int done = 0; - wEvents[0] = threadInfo->stopEvent; - wEvents[1] = threadInfo->startEvent; - - for (;;) { + while (!done) { /* * Wait for the main thread to signal before attempting to write. */ - - waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); - - if (waitResult != (WAIT_OBJECT_0 + 1)) { - /* - * The start event was not signaled. It must be the stop event or - * an error, so exit this thread. - */ - + if (!TclPipeThreadWaitForSignal(&pipeTI)) { + /* exit */ break; } + if (!infoPtr) { + infoPtr = (ConsoleInfo *)pipeTI->clientData; + handle = infoPtr->handle; + threadInfo = &infoPtr->writer; + } buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; @@ -1320,6 +1231,7 @@ ConsoleWriterThread( if (WriteConsoleBytes(handle, buf, (DWORD) toWrite, &count) == FALSE) { infoPtr->writeError = GetLastError(); + done = 1; break; } toWrite -= count; @@ -1351,6 +1263,9 @@ ConsoleWriterThread( Tcl_MutexUnlock(&consoleMutex); } + /* Worker exit, so inform the main thread or free TI-structure (if owned) */ + TclPipeThreadExit(&pipeTI); + return 0; } @@ -1421,11 +1336,21 @@ TclWinOpenConsoleChannel( modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT); modes |= ENABLE_LINE_INPUT; SetConsoleMode(infoPtr->handle, modes); - StartChannelThread(infoPtr, &infoPtr->reader, ConsoleReaderThread); + + infoPtr->reader.readyEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + infoPtr->reader.thread = CreateThread(NULL, 256, ConsoleReaderThread, + TclPipeThreadCreateTI(&infoPtr->reader.TI, infoPtr, + infoPtr->reader.readyEvent), 0, NULL); + SetThreadPriority(infoPtr->reader.thread, THREAD_PRIORITY_HIGHEST); } if (permissions & TCL_WRITABLE) { - StartChannelThread(infoPtr, &infoPtr->writer, ConsoleWriterThread); + + infoPtr->writer.readyEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + infoPtr->writer.thread = CreateThread(NULL, 256, ConsoleWriterThread, + TclPipeThreadCreateTI(&infoPtr->writer.TI, infoPtr, + infoPtr->writer.readyEvent), 0, NULL); + SetThreadPriority(infoPtr->writer.thread, THREAD_PRIORITY_HIGHEST); } /* diff --git a/win/tclWinFile.c b/win/tclWinFile.c index e61d619..e61d619 100755..100644 --- a/win/tclWinFile.c +++ b/win/tclWinFile.c diff --git a/win/tclWinInit.c b/win/tclWinInit.c index d2ee7e1..c590865 100644 --- a/win/tclWinInit.c +++ b/win/tclWinInit.c @@ -76,6 +76,12 @@ typedef struct { #define PROCESSOR_ARCHITECTURE_UNKNOWN 0xFFFF #endif + +/* + * Windows version dependend functions + */ +TclWinProcs tclWinProcs; + /* * The following arrays contain the human readable strings for the Windows * platform and processor values. @@ -132,6 +138,7 @@ TclpInitPlatform(void) { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); + HMODULE handle; tclPlatform = TCL_PLATFORM_WINDOWS; @@ -150,6 +157,14 @@ TclpInitPlatform(void) TclWinInit(GetModuleHandle(NULL)); #endif + + /* + * Fill available functions depending on windows version + */ + handle = GetModuleHandle(TEXT("KERNEL32")); + tclWinProcs.cancelSynchronousIo = + (BOOL (WINAPI *)(HANDLE)) GetProcAddress(handle, + "CancelSynchronousIo"); } /* @@ -537,16 +552,13 @@ TclpSetVariables( TclGetProcessGlobalValue(&defaultLibraryDir), TCL_GLOBAL_ONLY); if (!osInfoInitialized) { - HANDLE handle = LoadLibraryW(L"NTDLL"); + HMODULE handle = GetModuleHandle(TEXT("NTDLL")); int(__stdcall *getversion)(void *) = (int(__stdcall *)(void *)) GetProcAddress(handle, "RtlGetVersion"); osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); if (!getversion || getversion(&osInfo)) { GetVersionExW(&osInfo); } - if (handle) { - FreeLibrary(handle); - } osInfoInitialized = 1; } GetSystemInfo(&sys.info); diff --git a/win/tclWinInt.h b/win/tclWinInt.h index 6b098f8..43799d0 100644 --- a/win/tclWinInt.h +++ b/win/tclWinInt.h @@ -32,6 +32,15 @@ typedef struct TCLEXCEPTION_REGISTRATION { #endif /* + * Windows version dependend functions + */ +typedef struct TclWinProcs { + BOOL (WINAPI *cancelSynchronousIo)(HANDLE); +} TclWinProcs; + +MODULE_SCOPE TclWinProcs tclWinProcs; + +/* * Some versions of Borland C have a define for the OSVERSIONINFO for * Win32s and for NT, but not for Windows 95. * Define VER_PLATFORM_WIN32_CE for those without newer headers. @@ -86,4 +95,70 @@ MODULE_SCOPE void TclpSetAllocCache(void *); #define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #endif +/* + *---------------------------------------------------------------------- + * Declarations of helper-workers threaded facilities for a pipe based channel. + * + * Corresponding functionality provided in "tclWinPipe.c". + *---------------------------------------------------------------------- + */ + +typedef struct TclPipeThreadInfo { + HANDLE evControl; /* Auto-reset event used by the main thread to + * signal when the pipe thread should attempt + * to do read/write operation. Additionally + * used as signal to stop (state set to -1) */ + volatile LONG state; /* Indicates current state of the thread */ + ClientData clientData; /* Referenced data of the main thread */ + HANDLE evWakeUp; /* Optional wake-up event worker set by shutdown */ +} TclPipeThreadInfo; + + +/* If pipe-workers will use some tcl subsystem, we can use ckalloc without + * more overhead for finalize thread (should be executed anyway) + * + * #define _PTI_USE_CKALLOC 1 + */ + +/* + * State of the pipe-worker. + * + * State PTI_STATE_STOP possible from idle state only, worker owns TI structure. + * Otherwise PTI_STATE_END used (main thread hold ownership of the TI). + */ + +#define PTI_STATE_IDLE 0 /* idle or not yet initialzed */ +#define PTI_STATE_WORK 1 /* in work */ +#define PTI_STATE_STOP 2 /* thread should stop work (owns TI structure) */ +#define PTI_STATE_END 4 /* thread should stop work (worker is busy) */ +#define PTI_STATE_DOWN 8 /* worker is down */ + + +MODULE_SCOPE +TclPipeThreadInfo * TclPipeThreadCreateTI(TclPipeThreadInfo **pipeTIPtr, + ClientData clientData, HANDLE wakeEvent); +MODULE_SCOPE int TclPipeThreadWaitForSignal(TclPipeThreadInfo **pipeTIPtr); + +static inline void +TclPipeThreadSignal( + TclPipeThreadInfo **pipeTIPtr) +{ + TclPipeThreadInfo *pipeTI = *pipeTIPtr; + if (pipeTI) { + SetEvent(pipeTI->evControl); + } +}; + +static inline int +TclPipeThreadIsAlive( + TclPipeThreadInfo **pipeTIPtr) +{ + TclPipeThreadInfo *pipeTI = *pipeTIPtr; + return (pipeTI && pipeTI->state != PTI_STATE_DOWN); +}; + +MODULE_SCOPE int TclPipeThreadStopSignal(TclPipeThreadInfo **pipeTIPtr, HANDLE wakeEvent); +MODULE_SCOPE void TclPipeThreadStop(TclPipeThreadInfo **pipeTIPtr, HANDLE hThread); +MODULE_SCOPE void TclPipeThreadExit(TclPipeThreadInfo **pipeTIPtr); + #endif /* _TCLWININT */ diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index 4666deb..5246d53 100644 --- a/win/tclWinPipe.c +++ b/win/tclWinPipe.c @@ -109,24 +109,17 @@ typedef struct PipeInfo { Tcl_ThreadId threadId; /* Thread to which events should be reported. * This value is used by the reader/writer * threads. */ + TclPipeThreadInfo *writeTI; /* Thread info of writer and reader, this */ + TclPipeThreadInfo *readTI; /* structure owned by corresponding thread. */ HANDLE writeThread; /* Handle to writer thread. */ HANDLE readThread; /* Handle to reader thread. */ + HANDLE writable; /* Manual-reset event to signal when the * writer thread has finished waiting for the * current buffer to be written. */ HANDLE readable; /* Manual-reset event to signal when the * reader thread has finished waiting for * input. */ - HANDLE startWriter; /* Auto-reset event used by the main thread to - * signal when the writer thread should - * attempt to write to the pipe. */ - HANDLE stopWriter; /* Manual-reset event used to alert the reader - * thread to fall-out and exit */ - HANDLE startReader; /* Auto-reset event used by the main thread to - * signal when the reader thread should - * attempt to read from the pipe. */ - HANDLE stopReader; /* Manual-reset event used to alert the reader - * thread to fall-out and exit */ DWORD writeError; /* An error caused by the last background * write. Set to 0 if no error has been * detected. This word is shared with the @@ -1571,7 +1564,6 @@ TclpCreateCommandChannel( Tcl_Pid *pidPtr) /* An array of process identifiers. */ { char channelName[16 + TCL_INTEGER_SPACE]; - DWORD id; PipeInfo *infoPtr = ckalloc(sizeof(PipeInfo)); PipeInit(); @@ -1599,13 +1591,13 @@ TclpCreateCommandChannel( */ infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); - infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); - infoPtr->stopReader = CreateEvent(NULL, TRUE, FALSE, NULL); infoPtr->readThread = CreateThread(NULL, 256, PipeReaderThread, - infoPtr, 0, &id); + TclPipeThreadCreateTI(&infoPtr->readTI, infoPtr, infoPtr->readable), + 0, NULL); SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_READABLE; } else { + infoPtr->readTI = NULL; infoPtr->readThread = 0; } if (writeFile != NULL) { @@ -1614,12 +1606,14 @@ TclpCreateCommandChannel( */ infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); - infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); - infoPtr->stopWriter = CreateEvent(NULL, TRUE, FALSE, NULL); infoPtr->writeThread = CreateThread(NULL, 256, PipeWriterThread, - infoPtr, 0, &id); - SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); + TclPipeThreadCreateTI(&infoPtr->writeTI, infoPtr, infoPtr->writable), + 0, NULL); + SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_WRITABLE; + } else { + infoPtr->writeTI = NULL; + infoPtr->writeThread = 0; } /* @@ -1805,12 +1799,12 @@ PipeClose2Proc( int errorCode, result; PipeInfo *infoPtr, **nextPtrPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - DWORD exitCode; + int inExit = (TclInExit() || TclInThreadExit()); errorCode = 0; result = 0; - if ((!flags || flags == TCL_CLOSE_READ) && (pipePtr->readFile != NULL)) { + if ((!flags || flags & TCL_CLOSE_READ) && (pipePtr->readFile != NULL)) { /* * Clean up the background thread if necessary. Note that this must be * done before we can close the file, since the thread may be blocking @@ -1818,55 +1812,10 @@ PipeClose2Proc( */ if (pipePtr->readThread) { - /* - * The thread may already have closed on its own. Check its exit - * code. - */ - - GetExitCodeThread(pipePtr->readThread, &exitCode); - - if (exitCode == STILL_ACTIVE) { - /* - * Set the stop event so that if the reader thread is blocked - * in PipeReaderThread on WaitForMultipleEvents, it will exit - * cleanly. - */ - - SetEvent(pipePtr->stopReader); - - /* - * Wait at most 20 milliseconds for the reader thread to - * close. - */ - - if (WaitForSingleObject(pipePtr->readThread, - 20) == WAIT_TIMEOUT) { - /* - * The thread must be blocked waiting for the pipe to - * become readable in ReadFile(). There isn't a clean way - * to exit the thread from this condition. We should - * terminate the child process instead to get the reader - * thread to fall out of ReadFile with a FALSE. (below) is - * not the correct way to do this, but will stay here - * until a better solution is found. - * - * Note that we need to guard against terminating the - * thread while it is in the middle of Tcl_ThreadAlert - * because it won't be able to release the notifier lock. - */ - - Tcl_MutexLock(&pipeMutex); - - /* BUG: this leaks memory */ - TerminateThread(pipePtr->readThread, 0); - Tcl_MutexUnlock(&pipeMutex); - } - } + TclPipeThreadStop(&pipePtr->readTI, pipePtr->readThread); CloseHandle(pipePtr->readThread); CloseHandle(pipePtr->readable); - CloseHandle(pipePtr->startReader); - CloseHandle(pipePtr->stopReader); pipePtr->readThread = NULL; } if (TclpCloseFile(pipePtr->readFile) != 0) { @@ -1875,80 +1824,34 @@ PipeClose2Proc( pipePtr->validMask &= ~TCL_READABLE; pipePtr->readFile = NULL; } - if ((!flags || flags & TCL_CLOSE_WRITE) - && (pipePtr->writeFile != NULL)) { + if ((!flags || flags & TCL_CLOSE_WRITE) && (pipePtr->writeFile != NULL)) { if (pipePtr->writeThread) { + /* * Wait for the writer thread to finish the current buffer, then * terminate the thread and close the handles. If the channel is - * nonblocking but blocked during exit, bail out since the worker + * nonblocking or may block during exit, bail out since the worker * thread is not interruptible and we want TIP#398-fast-exit. */ - if (TclInExit() - && (pipePtr->flags & PIPE_ASYNC)) { + if ((pipePtr->flags & PIPE_ASYNC) && inExit) { /* give it a chance to leave honorably */ - SetEvent(pipePtr->stopWriter); + TclPipeThreadStopSignal(&pipePtr->writeTI, pipePtr->writable); - if (WaitForSingleObject(pipePtr->writable, 0) == WAIT_TIMEOUT) { + if (WaitForSingleObject(pipePtr->writable, 20) == WAIT_TIMEOUT) { return EWOULDBLOCK; } } else { - WaitForSingleObject(pipePtr->writable, INFINITE); + WaitForSingleObject(pipePtr->writable, inExit ? 5000 : INFINITE); } - /* - * The thread may already have closed on it's own. Check its exit - * code. - */ - - GetExitCodeThread(pipePtr->writeThread, &exitCode); - - if (exitCode == STILL_ACTIVE) { - /* - * Set the stop event so that if the reader thread is blocked - * in PipeReaderThread on WaitForMultipleEvents, it will exit - * cleanly. - */ - - SetEvent(pipePtr->stopWriter); - - /* - * Wait at most 20 milliseconds for the reader thread to - * close. - */ + TclPipeThreadStop(&pipePtr->writeTI, pipePtr->writeThread); - if (WaitForSingleObject(pipePtr->writeThread, - 20) == WAIT_TIMEOUT) { - /* - * The thread must be blocked waiting for the pipe to - * consume input in WriteFile(). There isn't a clean way - * to exit the thread from this condition. We should - * terminate the child process instead to get the writer - * thread to fall out of WriteFile with a FALSE. (below) - * is not the correct way to do this, but will stay here - * until a better solution is found. - * - * Note that we need to guard against terminating the - * thread while it is in the middle of Tcl_ThreadAlert - * because it won't be able to release the notifier lock. - */ - - Tcl_MutexLock(&pipeMutex); - - /* BUG: this leaks memory */ - TerminateThread(pipePtr->writeThread, 0); - Tcl_MutexUnlock(&pipeMutex); - } - } - - CloseHandle(pipePtr->writeThread); CloseHandle(pipePtr->writable); - CloseHandle(pipePtr->startWriter); - CloseHandle(pipePtr->stopWriter); + CloseHandle(pipePtr->writeThread); pipePtr->writeThread = NULL; } if (TclpCloseFile(pipePtr->writeFile) != 0) { @@ -1983,7 +1886,7 @@ PipeClose2Proc( } } - if ((pipePtr->flags & PIPE_ASYNC) || TclInExit()) { + if ((pipePtr->flags & PIPE_ASYNC) || inExit) { /* * If the channel is non-blocking or Tcl is being cleaned up, just * detach the children PIDs, reap them (important if we are in a @@ -2161,7 +2064,10 @@ PipeOutputProc( DWORD bytesWritten, timeout; *errorCode = 0; - timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE; + + /* avoid blocking if pipe-thread exited */ + timeout = ((infoPtr->flags & PIPE_ASYNC) || !TclPipeThreadIsAlive(&infoPtr->writeTI) + || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { /* * The writer thread is blocked waiting for a write to complete and @@ -2202,7 +2108,7 @@ PipeOutputProc( memcpy(infoPtr->writeBuf, buf, (size_t) toWrite); infoPtr->toWrite = toWrite; ResetEvent(infoPtr->writable); - SetEvent(infoPtr->startWriter); + TclPipeThreadSignal(&infoPtr->writeTI); bytesWritten = toWrite; } else { /* @@ -2712,7 +2618,9 @@ WaitForRead( * Synchronize with the reader thread. */ - timeout = blocking ? INFINITE : 0; + /* avoid blocking if pipe-thread exited */ + timeout = (!blocking || !TclPipeThreadIsAlive(&infoPtr->readTI) + || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { /* * The reader thread is blocked waiting for data and the channel @@ -2786,7 +2694,7 @@ WaitForRead( */ ResetEvent(infoPtr->readable); - SetEvent(infoPtr->startReader); + TclPipeThreadSignal(&infoPtr->readTI); } } @@ -2814,33 +2722,27 @@ static DWORD WINAPI PipeReaderThread( LPVOID arg) { - PipeInfo *infoPtr = (PipeInfo *)arg; - HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; + TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; + PipeInfo *infoPtr = NULL; /* access info only after success init/wait */ + HANDLE handle = NULL; DWORD count, err; int done = 0; - HANDLE wEvents[2]; - DWORD waitResult; - - wEvents[0] = infoPtr->stopReader; - wEvents[1] = infoPtr->startReader; while (!done) { /* * Wait for the main thread to signal before attempting to wait on the * pipe becoming readable. */ - - waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); - - if (waitResult != (WAIT_OBJECT_0 + 1)) { - /* - * The start event was not signaled. It might be the stop event or - * an error, so exit. - */ - + if (!TclPipeThreadWaitForSignal(&pipeTI)) { + /* exit */ break; } + if (!infoPtr) { + infoPtr = (PipeInfo *)pipeTI->clientData; + handle = ((WinFile *) infoPtr->readFile)->handle; + } + /* * Try waiting for 0 bytes. This will block until some data is * available on NT, but will return immediately on Win 95. So, if no @@ -2860,7 +2762,7 @@ PipeReaderThread( infoPtr->readFlags |= PIPE_EOF; done = 1; } else if (err == ERROR_INVALID_HANDLE) { - break; + done = 1; } } else if (count == 0) { if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL) @@ -2882,12 +2784,11 @@ PipeReaderThread( infoPtr->readFlags |= PIPE_EOF; done = 1; } else if (err == ERROR_INVALID_HANDLE) { - break; + done = 1; } } } - /* * Signal the main thread by signalling the readable event and then * waking up the notifier thread. @@ -2913,6 +2814,12 @@ PipeReaderThread( Tcl_MutexUnlock(&pipeMutex); } + /* + * If state of thread was set to stop, we can sane free info structure, + * otherwise it is shared with main thread, so main thread will own it + */ + TclPipeThreadExit(&pipeTI); + return 0; } @@ -2937,37 +2844,27 @@ static DWORD WINAPI PipeWriterThread( LPVOID arg) { - PipeInfo *infoPtr = (PipeInfo *)arg; - HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle; + TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; + PipeInfo *infoPtr = NULL; /* access info only after success init/wait */ + HANDLE handle = NULL; DWORD count, toWrite; char *buf; int done = 0; - HANDLE wEvents[2]; - DWORD waitResult; - - wEvents[0] = infoPtr->stopWriter; - wEvents[1] = infoPtr->startWriter; while (!done) { /* * Wait for the main thread to signal before attempting to write. */ - - waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); - - if (waitResult != (WAIT_OBJECT_0 + 1)) { - /* - * The start event was not signaled. It might be the stop event or - * an error, so exit. - */ - - if (waitResult == WAIT_OBJECT_0) { - SetEvent(infoPtr->writable); - } - + if (!TclPipeThreadWaitForSignal(&pipeTI)) { + /* exit */ break; } + if (!infoPtr) { + infoPtr = (PipeInfo *)pipeTI->clientData; + handle = ((WinFile *) infoPtr->writeFile)->handle; + } + buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; @@ -3011,6 +2908,12 @@ PipeWriterThread( Tcl_MutexUnlock(&pipeMutex); } + /* + * If state of thread was set to stop, we can sane free info structure, + * otherwise it is shared with main thread, so main thread will own it. + */ + TclPipeThreadExit(&pipeTI); + return 0; } @@ -3159,6 +3062,395 @@ TclpOpenTemporaryFile( } /* + *---------------------------------------------------------------------- + * + * TclPipeThreadCreateTI -- + * + * Creates a thread info structure, can be owned by worker. + * + * Results: + * Pointer to created TI structure. + * + *---------------------------------------------------------------------- + */ + +TclPipeThreadInfo * +TclPipeThreadCreateTI( + TclPipeThreadInfo **pipeTIPtr, + ClientData clientData, + HANDLE wakeEvent) +{ + TclPipeThreadInfo *pipeTI; +#ifndef _PTI_USE_CKALLOC + pipeTI = malloc(sizeof(TclPipeThreadInfo)); +#else + pipeTI = ckalloc(sizeof(TclPipeThreadInfo)); +#endif + pipeTI->evControl = CreateEvent(NULL, FALSE, FALSE, NULL); + pipeTI->state = PTI_STATE_IDLE; + pipeTI->clientData = clientData; + pipeTI->evWakeUp = wakeEvent; + return (*pipeTIPtr = pipeTI); +} + +/* + *---------------------------------------------------------------------- + * + * TclPipeThreadWaitForSignal -- + * + * Wait for work/stop signals inside pipe worker. + * + * Results: + * 1 if signaled to work, 0 if signaled to stop. + * + * Side effects: + * If this function returns 0, TI-structure pointer given via pipeTIPtr + * may be NULL, so not accessible (can be owned by main thread). + * + *---------------------------------------------------------------------- + */ + +int +TclPipeThreadWaitForSignal( + TclPipeThreadInfo **pipeTIPtr) +{ + TclPipeThreadInfo *pipeTI = *pipeTIPtr; + LONG state; + DWORD waitResult; + HANDLE wakeEvent; + + if (!pipeTI) { + return 0; + } + + wakeEvent = pipeTI->evWakeUp; + /* + * Wait for the main thread to signal before attempting to do the work. + */ + + /* reset work state of thread (idle/waiting) */ + if ((state = InterlockedCompareExchange(&pipeTI->state, + PTI_STATE_IDLE, PTI_STATE_WORK)) & (PTI_STATE_STOP|PTI_STATE_END)) { + /* end of work, check the owner of structure */ + goto end; + } + /* entering wait */ + waitResult = WaitForSingleObject(pipeTI->evControl, INFINITE); + + if (waitResult != WAIT_OBJECT_0) { + + /* + * The control event was not signaled, so end of work (unexpected + * behaviour, main thread can be dead?). + */ + goto end; + } + + /* try to set work state of thread */ + if ((state = InterlockedCompareExchange(&pipeTI->state, + PTI_STATE_WORK, PTI_STATE_IDLE)) & (PTI_STATE_STOP|PTI_STATE_END)) { + /* end of work */ + goto end; + } + + /* signaled to work */ + return 1; + +end: + /* end of work, check the owner of the TI structure */ + if (state != PTI_STATE_STOP) { + *pipeTIPtr = NULL; + } else { + pipeTI->evWakeUp = NULL; + } + if (wakeEvent) { + SetEvent(wakeEvent); + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TclPipeThreadStopSignal -- + * + * Send stop signal to the pipe worker (without waiting). + * + * After calling of this function, TI-structure pointer given via pipeTIPtr + * may be NULL. + * + * Results: + * 1 if signaled (or pipe-thread is down), 0 if pipe thread still working. + * + *---------------------------------------------------------------------- + */ + +int +TclPipeThreadStopSignal( + TclPipeThreadInfo **pipeTIPtr, HANDLE wakeEvent) +{ + TclPipeThreadInfo *pipeTI = *pipeTIPtr; + HANDLE evControl; + int state; + + if (!pipeTI) { + return 1; + } + evControl = pipeTI->evControl; + pipeTI->evWakeUp = wakeEvent; + switch ( + (state = InterlockedCompareExchange(&pipeTI->state, + PTI_STATE_STOP, PTI_STATE_IDLE)) + ) { + + case PTI_STATE_IDLE: + + /* Thread was idle/waiting, notify it goes teardown */ + SetEvent(evControl); + + *pipeTIPtr = NULL; + + case PTI_STATE_DOWN: + + return 1; + + default: + /* + * Thread works currently, we should try to end it, own the TI structure + * (because of possible sharing the joint structures with thread) + */ + InterlockedExchange(&pipeTI->state, PTI_STATE_END); + break; + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TclPipeThreadStop -- + * + * Send stop signal to the pipe worker and wait for thread completion. + * + * May be combined with TclPipeThreadStopSignal. + * + * After calling of this function, TI-structure pointer given via pipeTIPtr + * is not accessible (owned by pipe worker or released here). + * + * Results: + * None. + * + * Side effects: + * Can terminate pipe worker (and / or stop its synchronous operations). + * + *---------------------------------------------------------------------- + */ + +void +TclPipeThreadStop( + TclPipeThreadInfo **pipeTIPtr, + HANDLE hThread) +{ + TclPipeThreadInfo *pipeTI = *pipeTIPtr; + HANDLE evControl; + int state; + + if (!pipeTI) { + return; + } + pipeTI = *pipeTIPtr; + evControl = pipeTI->evControl; + pipeTI->evWakeUp = NULL; + /* + * Try to sane stop the pipe worker, corresponding its current state + */ + switch ( + (state = InterlockedCompareExchange(&pipeTI->state, + PTI_STATE_STOP, PTI_STATE_IDLE)) + ) { + + case PTI_STATE_IDLE: + + /* Thread was idle/waiting, notify it goes teardown */ + SetEvent(evControl); + + /* we don't need to wait for it at all, thread frees himself (owns the TI structure) */ + pipeTI = NULL; + break; + + case PTI_STATE_STOP: + /* already stopped, thread frees himself (owns the TI structure) */ + pipeTI = NULL; + break; + case PTI_STATE_DOWN: + /* Thread already down (?), do nothing */ + + /* we don't need to wait for it, but we should free pipeTI */ + hThread = NULL; + break; + + /* case PTI_STATE_WORK: */ + default: + /* + * Thread works currently, we should try to end it, own the TI structure + * (because of possible sharing the joint structures with thread) + */ + if ((state = InterlockedCompareExchange(&pipeTI->state, + PTI_STATE_END, PTI_STATE_WORK)) == PTI_STATE_DOWN + ) { + /* we don't need to wait for it, but we should free pipeTI */ + hThread = NULL; + }; + break; + } + + if (pipeTI && hThread) { + DWORD exitCode; + + /* + * The thread may already have closed on its own. Check its exit + * code. + */ + + GetExitCodeThread(hThread, &exitCode); + + if (exitCode == STILL_ACTIVE) { + + int inExit = (TclInExit() || TclInThreadExit()); + /* + * Set the stop event so that if the pipe thread is blocked + * somewhere, it may hereafter sane exit cleanly. + */ + + SetEvent(evControl); + + /* + * Cancel all sync-IO of this thread (may be blocked there). + */ + if (tclWinProcs.cancelSynchronousIo) { + tclWinProcs.cancelSynchronousIo(hThread); + } + + /* + * Wait at most 20 milliseconds for the reader thread to + * close (regarding TIP#398-fast-exit). + */ + + /* if we want TIP#398-fast-exit. */ + if (WaitForSingleObject(hThread, inExit ? 0 : 20) == WAIT_TIMEOUT) { + + /* + * The thread must be blocked waiting for the pipe to + * become readable in ReadFile(). There isn't a clean way + * to exit the thread from this condition. We should + * terminate the child process instead to get the reader + * thread to fall out of ReadFile with a FALSE. (below) is + * not the correct way to do this, but will stay here + * until a better solution is found. + * + * Note that we need to guard against terminating the + * thread while it is in the middle of Tcl_ThreadAlert + * because it won't be able to release the notifier lock. + * + * Also note that terminating threads during their initialization or teardown phase + * may result in ntdll.dll's LoaderLock to remain locked indefinitely. + * This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock. + * LdrpInitializeThread() is executed within new threads to perform + * initialization and to execute DllMain() of all loaded dlls. + * As a result, all new threads are deadlocked in their initialization phase and never execute, + * even though CreateThread() reports successful thread creation. + * This results in a very weird process-wide behavior, which is extremely hard to debug. + * + * THREADS SHOULD NEVER BE TERMINATED. Period. + * + * But for now, check if thread is exiting, and if so, let it die peacefully. + * + * Also don't terminate if in exit (otherwise deadlocked in ntdll.dll's). + */ + + if ( pipeTI->state != PTI_STATE_DOWN + && WaitForSingleObject(hThread, + inExit ? 50 : 5000) != WAIT_OBJECT_0 + ) { + /* BUG: this leaks memory */ + if (inExit || !TerminateThread(hThread, 0)) { + /* in exit or terminate fails, just give thread a chance to exit */ + if (InterlockedExchange(&pipeTI->state, + PTI_STATE_STOP) != PTI_STATE_DOWN) { + pipeTI = NULL; + } + }; + } + } + } + } + + *pipeTIPtr = NULL; + if (pipeTI) { + if (pipeTI->evWakeUp) { + SetEvent(pipeTI->evWakeUp); + } + CloseHandle(pipeTI->evControl); + #ifndef _PTI_USE_CKALLOC + free(pipeTI); + #else + ckfree(pipeTI); + #endif + } +} + +/* + *---------------------------------------------------------------------- + * + * TclPipeThreadExit -- + * + * Clean-up for the pipe thread (removes owned TI-structure in worker). + * + * Should be executed on worker exit, to inform the main thread or + * free TI-structure (if owned). + * + * After calling of this function, TI-structure pointer given via pipeTIPtr + * is not accessible (owned by main thread or released here). + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TclPipeThreadExit( + TclPipeThreadInfo **pipeTIPtr) +{ + LONG state; + TclPipeThreadInfo *pipeTI = *pipeTIPtr; + /* + * If state of thread was set to stop (exactly), we can sane free its info + * structure, otherwise it is shared with main thread, so main thread will + * own it. + */ + if (!pipeTI) { + return; + } + *pipeTIPtr = NULL; + if ((state = InterlockedExchange(&pipeTI->state, + PTI_STATE_DOWN)) == PTI_STATE_STOP) { + CloseHandle(pipeTI->evControl); + if (pipeTI->evWakeUp) { + SetEvent(pipeTI->evWakeUp); + } + #ifndef _PTI_USE_CKALLOC + free(pipeTI); + #else + ckfree(pipeTI); + /* be sure all subsystems used are finalized */ + Tcl_FinalizeThread(); + #endif + } +} + +/* * Local Variables: * mode: c * c-basic-offset: 4 diff --git a/win/tclWinReg.c b/win/tclWinReg.c index 5f7fd31..de48b9b 100644 --- a/win/tclWinReg.c +++ b/win/tclWinReg.c @@ -1197,14 +1197,12 @@ RecursiveDeleteKey( */ if (mode && !checkExProc) { - HINSTANCE dllH; + HMODULE handle; checkExProc = 1; - dllH = LoadLibrary(TEXT("advapi32.dll")); - if (dllH) { - regDeleteKeyExProc = (FARPROC) - GetProcAddress(dllH, "RegDeleteKeyExW"); - } + handle = GetModuleHandle(TEXT("ADVAPI32")); + regDeleteKeyExProc = (FARPROC) + GetProcAddress(handle, "RegDeleteKeyExW"); } if (mode && regDeleteKeyExProc) { result = regDeleteKeyExProc(startKey, keyName, mode, 0); diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c index 0ce5f4d..ed1a8e5 100644 --- a/win/tclWinSerial.c +++ b/win/tclWinSerial.c @@ -93,17 +93,12 @@ typedef struct SerialInfo { * threads. */ OVERLAPPED osRead; /* OVERLAPPED structure for read operations. */ OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */ + TclPipeThreadInfo *writeTI; /* Thread info structure of writer worker. */ HANDLE writeThread; /* Handle to writer thread. */ CRITICAL_SECTION csWrite; /* Writer thread synchronisation. */ HANDLE evWritable; /* Manual-reset event to signal when the * writer thread has finished waiting for the * current buffer to be written. */ - HANDLE evStartWriter; /* Auto-reset event used by the main thread to - * signal when the writer thread should - * attempt to write to the serial. */ - HANDLE evStopWriter; /* Auto-reset event used by the main thread to - * signal when the writer thread should close. - */ DWORD writeError; /* An error caused by the last background * write. Set to 0 if no error has been * detected. This word is shared with the @@ -599,7 +594,6 @@ SerialCloseProc( int errorCode, result = 0; SerialInfo *infoPtr, **nextPtrPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - DWORD exitCode; errorCode = 0; @@ -609,56 +603,13 @@ SerialCloseProc( } serialPtr->validMask &= ~TCL_READABLE; - if (serialPtr->validMask & TCL_WRITABLE) { - /* - * Generally we cannot wait for a pending write operation because it - * may hang due to handshake - * WaitForSingleObject(serialPtr->evWritable, INFINITE); - */ - - /* - * The thread may have already closed on it's own. Check it's exit - * code. - */ - - GetExitCodeThread(serialPtr->writeThread, &exitCode); - - if (exitCode == STILL_ACTIVE) { - /* - * Set the stop event so that if the writer thread is blocked in - * SerialWriterThread on WaitForMultipleEvents, it will exit - * cleanly. - */ - - SetEvent(serialPtr->evStopWriter); - - /* - * Wait at most 20 milliseconds for the writer thread to close. - */ - - if (WaitForSingleObject(serialPtr->writeThread, - 20) == WAIT_TIMEOUT) { - /* - * Forcibly terminate the background thread as a last resort. - * Note that we need to guard against terminating the thread - * while it is in the middle of Tcl_ThreadAlert because it - * won't be able to release the notifier lock. - */ + if (serialPtr->writeThread) { - Tcl_MutexLock(&serialMutex); + TclPipeThreadStop(&serialPtr->writeTI, serialPtr->writeThread); - /* BUG: this leaks memory */ - TerminateThread(serialPtr->writeThread, 0); - - Tcl_MutexUnlock(&serialMutex); - } - } - - CloseHandle(serialPtr->writeThread); CloseHandle(serialPtr->osWrite.hEvent); CloseHandle(serialPtr->evWritable); - CloseHandle(serialPtr->evStartWriter); - CloseHandle(serialPtr->evStopWriter); + CloseHandle(serialPtr->writeThread); serialPtr->writeThread = NULL; PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR); @@ -1076,7 +1027,7 @@ SerialOutputProc( memcpy(infoPtr->writeBuf, buf, (size_t) toWrite); infoPtr->toWrite = toWrite; ResetEvent(infoPtr->evWritable); - SetEvent(infoPtr->evStartWriter); + TclPipeThreadSignal(&infoPtr->writeTI); bytesWritten = (DWORD) toWrite; } else { @@ -1313,34 +1264,21 @@ static DWORD WINAPI SerialWriterThread( LPVOID arg) { - SerialInfo *infoPtr = (SerialInfo *)arg; - DWORD bytesWritten, toWrite, waitResult; + TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; + SerialInfo *infoPtr = NULL; /* access info only after success init/wait */ + DWORD bytesWritten, toWrite; char *buf; OVERLAPPED myWrite; /* Have an own OVERLAPPED in this thread. */ - HANDLE wEvents[2]; - - /* - * The stop event takes precedence by being first in the list. - */ - - wEvents[0] = infoPtr->evStopWriter; - wEvents[1] = infoPtr->evStartWriter; for (;;) { /* * Wait for the main thread to signal before attempting to write. */ - - waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE); - - if (waitResult != (WAIT_OBJECT_0 + 1)) { - /* - * The start event was not signaled. It might be the stop event or - * an error, so exit. - */ - + if (!TclPipeThreadWaitForSignal(&pipeTI)) { + /* exit */ break; } + infoPtr = (SerialInfo *)pipeTI->clientData; buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; @@ -1404,6 +1342,9 @@ SerialWriterThread( Tcl_MutexUnlock(&serialMutex); } + /* Worker exit, so inform the main thread or free TI-structure (if owned) */ + TclPipeThreadExit(&pipeTI); + return 0; } @@ -1477,7 +1418,6 @@ TclWinOpenSerialChannel( int permissions) { SerialInfo *infoPtr; - DWORD id; SerialInit(); @@ -1529,10 +1469,9 @@ TclWinOpenSerialChannel( infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL); - infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL); - infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL); infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread, - infoPtr, 0, &id); + TclPipeThreadCreateTI(&infoPtr->writeTI, infoPtr, + infoPtr->evWritable), 0, NULL); } /* diff --git a/win/tclWinSock.c b/win/tclWinSock.c index 5e0d7c8..ee6be96 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -69,6 +69,7 @@ #define SET_BITS(var, bits) ((var) |= (bits)) #define CLEAR_BITS(var, bits) ((var) &= ~(bits)) +#define GOT_BITS(var, bits) (((var) & (bits)) != 0) /* "sock" + a pointer in hex + \0 */ #define SOCK_CHAN_LENGTH (4 + sizeof(void *) * 2 + 1) @@ -124,6 +125,8 @@ typedef struct TcpFdList { struct TcpState { Tcl_Channel channel; /* Channel associated with this socket. */ + int testFlags; /* bit field for tests. Is set by testsocket + * test procedure */ struct TcpFdList *sockets; /* Windows SOCKET handle. */ int flags; /* Bit field comprised of the flags described * below. */ @@ -184,6 +187,15 @@ struct TcpState { #define TCP_ASYNC_FAILED (1<<5) /* An async connect finally failed */ /* + * These bits may be ORed together into the "testFlags" field of a TcpState + * structure. + */ + +#define TCP_ASYNC_TEST_MODE (1<<0) /* Async testing activated. Do not + * automatically continue connection + * process */ + +/* * The following structure is what is added to the Tcl event queue when a * socket event occurs. */ @@ -292,22 +304,39 @@ static const Tcl_ChannelType tcpChannelType = { static TclInitProcessGlobalValueProc InitializeHostName; static ProcessGlobalValue hostName = {0, 0, NULL, NULL, InitializeHostName, NULL, NULL}; + +/* + * Simple wrapper round the SendMessage syscall. + */ + +#define SendSelectMessage(tsdPtr, message, payload) \ + SendMessage((tsdPtr)->hwnd, SOCKET_SELECT, \ + (WPARAM) (message), (LPARAM) (payload)) + /* * Address print debug functions */ #if 0 -void printaddrinfo(struct addrinfo *ai, char *prefix) +void +printaddrinfo( + struct addrinfo *ai, + char *prefix) { char host[NI_MAXHOST], port[NI_MAXSERV]; + getnameinfo(ai->ai_addr, ai->ai_addrlen, - host, sizeof(host), - port, sizeof(port), - NI_NUMERICHOST|NI_NUMERICSERV); + host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV); } -void printaddrinfolist(struct addrinfo *addrlist, char *prefix) + +void +printaddrinfolist( + struct addrinfo *addrlist, + char *prefix) { struct addrinfo *ai; + for (ai = addrlist; ai != NULL; ai = ai->ai_next) { printaddrinfo(ai, prefix); } @@ -524,9 +553,9 @@ TcpBlockModeProc( TcpState *statePtr = instanceData; if (mode == TCL_MODE_NONBLOCKING) { - statePtr->flags |= TCP_NONBLOCKING; + SET_BITS(statePtr->flags, TCP_NONBLOCKING); } else { - statePtr->flags &= ~(TCP_NONBLOCKING); + CLEAR_BITS(statePtr->flags, TCP_NONBLOCKING); } return 0; } @@ -536,29 +565,28 @@ TcpBlockModeProc( * * WaitForConnect -- * - * Check the state of an async connect process. If a connection - * attempt terminated, process it, which may finalize it or may - * start the next attempt. If a connect error occures, it is saved - * in statePtr->connectError to be reported by 'fconfigure -error'. + * Check the state of an async connect process. If a connection attempt + * terminated, process it, which may finalize it or may start the next + * attempt. If a connect error occures, it is saved in + * statePtr->connectError to be reported by 'fconfigure -error'. * * There are two modes of operation, defined by errorCodePtr: - * * non-NULL: Called by explicite read/write command. block if - * socket is blocking. + * * non-NULL: Called by explicite read/write command. Block if socket + * is blocking. * May return two error codes: * * EWOULDBLOCK: if connect is still in progress - * * ENOTCONN: if connect failed. This would be the error - * message of a rect or sendto syscall so this is - * emulated here. - * * Null: Called by a backround operation. Do not block and - * don't return any error code. + * * ENOTCONN: if connect failed. This would be the error message + * of a rect or sendto syscall so this is emulated here. + * * Null: Called by a backround operation. Do not block and don't + * return any error code. * * Results: - * 0 if the connection has completed, -1 if still in progress - * or there is an error. + * 0 if the connection has completed, -1 if still in progress or there is + * an error. * * Side effects: - * Processes socket events off the system queue. - * May process asynchroneous connect. + * Processes socket events off the system queue. May process + * asynchroneous connect. * *---------------------------------------------------------------------- */ @@ -566,20 +594,19 @@ TcpBlockModeProc( static int WaitForConnect( TcpState *statePtr, /* State of the socket. */ - int *errorCodePtr) /* Where to store errors? - * A passed null-pointer activates background mode. - */ + int *errorCodePtr) /* Where to store errors? A passed + * null-pointer activates background mode. */ { int result; int oldMode; ThreadSpecificData *tsdPtr; /* - * Check if an async connect failed already and error reporting is demanded, - * return the error ENOTCONN + * Check if an async connect failed already and error reporting is + * demanded, return the error ENOTCONN. */ - if (errorCodePtr != NULL && (statePtr->flags & TCP_ASYNC_FAILED)) { + if (errorCodePtr != NULL && GOT_BITS(statePtr->flags, TCP_ASYNC_FAILED)) { *errorCodePtr = ENOTCONN; return -1; } @@ -588,11 +615,26 @@ WaitForConnect( * Check if an async connect is running. If not return ok */ - if (!(statePtr->flags & TCP_ASYNC_CONNECT)) { + if (!GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT)) { return 0; } /* + * In socket test mode do not continue with the connect + * Exceptions are: + * - Call by recv/send and blocking socket + * (errorCodePtr != NULL && !GOT_BITS(flags, TCP_NONBLOCKING)) + * - Call by the event queue (errorCodePtr == NULL) + */ + + if (GOT_BITS(statePtr->testFlags, TCP_ASYNC_TEST_MODE) + && errorCodePtr != NULL + && GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) { + *errorCodePtr = EWOULDBLOCK; + return -1; + } + + /* * Be sure to disable event servicing so we are truly modal. */ @@ -603,36 +645,51 @@ WaitForConnect( */ while (1) { + /* + * Get the statePtr lock. + */ - /* get statePtr lock */ tsdPtr = TclThreadDataKeyGet(&dataKey); WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - /* Check for connect event */ - if (statePtr->readyEvents & FD_CONNECT) { + /* + * Check for connect event. + */ + + if (GOT_BITS(statePtr->readyEvents, FD_CONNECT)) { + /* + * Consume the connect event. + */ - /* Consume the connect event */ - statePtr->readyEvents &= ~(FD_CONNECT); + CLEAR_BITS(statePtr->readyEvents, FD_CONNECT); /* - * For blocking sockets and foreground processing - * disable async connect as we continue now synchoneously + * For blocking sockets and foreground processing, disable async + * connect as we continue now synchoneously. */ - if ( errorCodePtr != NULL && - ! (statePtr->flags & TCP_NONBLOCKING) ) { + + if (errorCodePtr != NULL && + !GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) { CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT); } - /* Free list lock */ + /* + * Free list lock. + */ + SetEvent(tsdPtr->socketListLock); /* - * Continue connect. - * If switched to synchroneous connect, the connect is terminated. + * Continue connect. If switched to synchroneous connect, the + * connect is terminated. */ + result = TcpConnect(NULL, statePtr); - /* Restore event service mode */ + /* + * Restore event service mode. + */ + (void) Tcl_SetServiceMode(oldMode); /* @@ -641,10 +698,11 @@ WaitForConnect( if (result == TCL_OK) { /* - * Check for async connect restart - * (not possible for foreground blocking operation) + * Check for async connect restart (not possible for + * foreground blocking operation) */ - if ( statePtr->flags & TCP_ASYNC_PENDING ) { + + if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) { if (errorCodePtr != NULL) { *errorCodePtr = EWOULDBLOCK; } @@ -654,8 +712,8 @@ WaitForConnect( } /* - * Connect finally failed. - * For foreground operation return ENOTCONN. + * Connect finally failed. For foreground operation return + * ENOTCONN. */ if (errorCodePtr != NULL) { @@ -664,7 +722,10 @@ WaitForConnect( return -1; } - /* Free list lock */ + /* + * Free list lock. + */ + SetEvent(tsdPtr->socketListLock); /* @@ -672,16 +733,16 @@ WaitForConnect( * event */ - if ( errorCodePtr == NULL ) { + if (errorCodePtr == NULL) { return -1; } /* - * A non blocking socket waiting for an asyncronous connect - * returns directly the error EWOULDBLOCK + * A non blocking socket waiting for an asyncronous connect returns + * directly the error EWOULDBLOCK. */ - if (statePtr->flags & TCP_NONBLOCKING) { + if (GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) { *errorCodePtr = EWOULDBLOCK; return -1; } @@ -745,7 +806,7 @@ TcpInputProc( * socket stack after the first time EOF is detected. */ - if (statePtr->flags & SOCKET_EOF) { + if (GOT_BITS(statePtr->flags, SOCKET_EOF)) { return 0; } @@ -768,18 +829,22 @@ TcpInputProc( */ while (1) { - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) UNSELECT, (LPARAM) statePtr); - /* single fd operation: this proc is only called for a connected socket. */ + SendSelectMessage(tsdPtr, UNSELECT, statePtr); + + /* + * Single fd operation: this proc is only called for a connected + * socket. + */ + bytesRead = recv(statePtr->sockets->fd, buf, bufSize, 0); - statePtr->readyEvents &= ~(FD_READ); + CLEAR_BITS(statePtr->readyEvents, FD_READ); /* * Check for end-of-file condition or successful read. */ if (bytesRead == 0) { - statePtr->flags |= SOCKET_EOF; + SET_BITS(statePtr->flags, SOCKET_EOF); } if (bytesRead != SOCKET_ERROR) { break; @@ -790,8 +855,8 @@ TcpInputProc( * error and report an EOF. */ - if (statePtr->readyEvents & FD_CLOSE) { - statePtr->flags |= SOCKET_EOF; + if (GOT_BITS(statePtr->readyEvents, FD_CLOSE)) { + SET_BITS(statePtr->flags, SOCKET_EOF); bytesRead = 0; break; } @@ -804,7 +869,7 @@ TcpInputProc( */ if (error == WSAECONNRESET) { - statePtr->flags |= SOCKET_EOF; + SET_BITS(statePtr->flags, SOCKET_EOF); bytesRead = 0; break; } @@ -813,7 +878,8 @@ TcpInputProc( * Check for error condition or underflow in non-blocking case. */ - if ((statePtr->flags & TCP_NONBLOCKING) || (error != WSAEWOULDBLOCK)) { + if (GOT_BITS(statePtr->flags, TCP_NONBLOCKING) + || (error != WSAEWOULDBLOCK)) { TclWinConvertError(error); *errorCodePtr = Tcl_GetErrno(); bytesRead = -1; @@ -831,7 +897,7 @@ TcpInputProc( } } - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr); + SendSelectMessage(tsdPtr, SELECT, statePtr); return bytesRead; } @@ -889,10 +955,13 @@ TcpOutputProc( } while (1) { - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) UNSELECT, (LPARAM) statePtr); + SendSelectMessage(tsdPtr, UNSELECT, statePtr); + + /* + * Single fd operation: this proc is only called for a connected + * socket. + */ - /* single fd operation: this proc is only called for a connected socket. */ written = send(statePtr->sockets->fd, buf, toWrite, 0); if (written != SOCKET_ERROR) { /* @@ -901,8 +970,9 @@ TcpOutputProc( * until the condition changes. */ - if (statePtr->watchEvents & FD_WRITE) { + if (GOT_BITS(statePtr->watchEvents, FD_WRITE)) { Tcl_Time blockTime = { 0, 0 }; + Tcl_SetMaxBlockTime(&blockTime); } break; @@ -917,8 +987,8 @@ TcpOutputProc( error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) { - statePtr->readyEvents &= ~(FD_WRITE); - if (statePtr->flags & TCP_NONBLOCKING) { + CLEAR_BITS(statePtr->readyEvents, FD_WRITE); + if (GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) { *errorCodePtr = EWOULDBLOCK; written = -1; break; @@ -941,7 +1011,7 @@ TcpOutputProc( } } - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr); + SendSelectMessage(tsdPtr, SELECT, statePtr); return written; } @@ -988,10 +1058,10 @@ TcpCloseProc( * background. */ - while ( statePtr->sockets != NULL ) { + while (statePtr->sockets != NULL) { TcpFdList *thisfd = statePtr->sockets; - statePtr->sockets = thisfd->next; + statePtr->sockets = thisfd->next; if (closesocket(thisfd->fd) == SOCKET_ERROR) { TclWinConvertError((DWORD) WSAGetLastError()); errorCode = Tcl_GetErrno(); @@ -1009,18 +1079,25 @@ TcpCloseProc( /* * Clear an eventual tsd info list pointer. + * * This may be called, if an async socket connect fails or is closed * between connect and thread action callback. */ + if (tsdPtr->pendingTcpState != NULL && tsdPtr->pendingTcpState == statePtr) { + /* + * Get infoPtr lock, because this concerns the notifier thread. + */ - /* get infoPtr lock, because this concerns the notifier thread */ WaitForSingleObject(tsdPtr->socketListLock, INFINITE); tsdPtr->pendingTcpState = NULL; - /* Free list lock */ + /* + * Free list lock. + */ + SetEvent(tsdPtr->socketListLock); } @@ -1081,8 +1158,11 @@ TcpClose2Proc( return TCL_ERROR; } - /* single fd operation: Tcl_OpenTcpServer() does not set TCL_READABLE or - * TCL_WRITABLE so this should never be called for a server socket. */ + /* + * Single fd operation: Tcl_OpenTcpServer() does not set TCL_READABLE or + * TCL_WRITABLE so this should never be called for a server socket. + */ + if (shutdown(statePtr->sockets->fd, sd) == SOCKET_ERROR) { TclWinConvertError((DWORD) WSAGetLastError()); errorCode = Tcl_GetErrno(); @@ -1134,7 +1214,7 @@ TcpSetOptionProc( } #ifdef TCL_FEATURE_KEEPALIVE_NAGLE - #error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat statePtr->sockets as single fd or list" +#error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat statePtr->sockets as single fd or list" sock = statePtr->sockets->fd; if (!strcasecmp(optionName, "-keepalive")) { @@ -1243,9 +1323,14 @@ TcpGetOptionProc( /* * Go one step in async connect - * If any error is thrown save it as backround error to report eventually below + * + * If any error is thrown save it as backround error to report eventually + * below. */ - WaitForConnect(statePtr, NULL); + + if (!GOT_BITS(statePtr->testFlags, TCP_ASYNC_TEST_MODE)) { + WaitForConnect(statePtr, NULL); + } sock = statePtr->sockets->fd; if (optionName != NULL) { @@ -1254,31 +1339,26 @@ TcpGetOptionProc( if ((len > 1) && (optionName[1] == 'e') && (strncmp(optionName, "-error", len) == 0)) { - /* - * Do not return any errors if async connect is running - */ - if ( ! (statePtr->flags & TCP_ASYNC_PENDING) ) { - - - if ( statePtr->flags & TCP_ASYNC_FAILED ) { + * Do not return any errors if async connect is running. + */ + if (!GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) { + if (GOT_BITS(statePtr->flags, TCP_ASYNC_FAILED)) { /* * In case of a failed async connect, eventually report the - * connect error only once. - * Do not report the system error, as this comes again and again. + * connect error only once. Do not report the system error, + * as this comes again and again. */ - if ( statePtr->connectError != 0 ) { + if (statePtr->connectError != 0) { Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(statePtr->connectError), -1); statePtr->connectError = 0; } - } else { - /* - * Report an eventual last error of the socket system + * Report an eventual last error of the socket system. */ int optlen; @@ -1286,24 +1366,30 @@ TcpGetOptionProc( DWORD err; /* - * Populater the err Variable with a possix error + * Populate the err variable with a POSIX error */ + optlen = sizeof(int); ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &optlen); + /* - * The error was not returned directly but should be - * taken from WSA + * The error was not returned directly but should be taken + * from WSA. */ + if (ret == SOCKET_ERROR) { err = WSAGetLastError(); } + /* - * Return error message + * Return error message. */ + if (err) { TclWinConvertError(err); - Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1); + Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), + -1); } } } @@ -1312,14 +1398,14 @@ TcpGetOptionProc( if ((len > 1) && (optionName[1] == 'c') && (strncmp(optionName, "-connecting", len) == 0)) { - Tcl_DStringAppend(dsPtr, - (statePtr->flags & TCP_ASYNC_PENDING) + GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING) ? "1" : "0", -1); return TCL_OK; } - if (interp != NULL && Tcl_GetVar2(interp, SUPPRESS_RDNS_VAR, NULL, 0) != NULL) { + if (interp != NULL + && Tcl_GetVar2(interp, SUPPRESS_RDNS_VAR, NULL, 0) != NULL) { reverseDNS = NI_NUMERICHOST; } @@ -1328,20 +1414,23 @@ TcpGetOptionProc( address peername; socklen_t size = sizeof(peername); - if ( (statePtr->flags & TCP_ASYNC_PENDING) ) { + if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) { /* * In async connect output an empty string */ + if (len == 0) { Tcl_DStringAppendElement(dsPtr, "-peername"); Tcl_DStringAppendElement(dsPtr, ""); } else { return TCL_OK; } - } else if ( getpeername(sock, (LPSOCKADDR) &(peername.sa), &size) == 0) { + } else if (getpeername(sock, (LPSOCKADDR) &(peername.sa), + &size) == 0) { /* * Peername fetch succeeded - output list */ + if (len == 0) { Tcl_DStringAppendElement(dsPtr, "-peername"); Tcl_DStringStartSublist(dsPtr); @@ -1390,11 +1479,12 @@ TcpGetOptionProc( Tcl_DStringAppendElement(dsPtr, "-sockname"); Tcl_DStringStartSublist(dsPtr); } - if ( (statePtr->flags & TCP_ASYNC_PENDING ) ) { + if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) { /* * In async connect output an empty string */ - found = 1; + + found = 1; } else { for (fds = statePtr->sockets; fds != NULL; fds = fds->next) { sock = fds->fd; @@ -1408,9 +1498,11 @@ TcpGetOptionProc( Tcl_DStringAppendElement(dsPtr, host); /* - * We don't want to resolve INADDR_ANY and sin6addr_any; they - * can sometimes cause problems (and never have a name). + * We don't want to resolve INADDR_ANY and sin6addr_any; + * they can sometimes cause problems (and never have a + * name). */ + flags |= NI_NUMERICSERV; if (sockname.sa.sa_family == AF_INET) { if (sockname.sa4.sin_addr.s_addr == INADDR_ANY) { @@ -1494,7 +1586,8 @@ TcpGetOptionProc( return Tcl_BadChannelOption(interp, optionName, "connecting peername sockname keepalive nagle"); #else - return Tcl_BadChannelOption(interp, optionName, "connecting peername sockname"); + return Tcl_BadChannelOption(interp, optionName, + "connecting peername sockname"); #endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/ } @@ -1535,11 +1628,11 @@ TcpWatchProc( if (!statePtr->acceptProc) { statePtr->watchEvents = 0; - if (mask & TCL_READABLE) { - statePtr->watchEvents |= (FD_READ|FD_CLOSE); + if (GOT_BITS(mask, TCL_READABLE)) { + SET_BITS(statePtr->watchEvents, FD_READ | FD_CLOSE); } - if (mask & TCL_WRITABLE) { - statePtr->watchEvents |= (FD_WRITE|FD_CLOSE); + if (GOT_BITS(mask, TCL_WRITABLE)) { + SET_BITS(statePtr->watchEvents, FD_WRITE | FD_CLOSE); } /* @@ -1630,13 +1723,13 @@ TcpConnect( TcpState *statePtr) { DWORD error; - /* - * We are started with async connect and the connect notification - * was not jet received - */ - int async_connect = statePtr->flags & TCP_ASYNC_CONNECT; - /* We were called by the event procedure and continue our loop */ - int async_callback = statePtr->flags & TCP_ASYNC_PENDING; + int async_connect = GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT); + /* We are started with async connect and the + * connect notification was not yet + * received. */ + int async_callback = GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING); + /* We were called by the event procedure and + * continue our loop. */ ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey); if (async_callback) { @@ -1644,11 +1737,10 @@ TcpConnect( } for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL; - statePtr->addr = statePtr->addr->ai_next) { - - for (statePtr->myaddr = statePtr->myaddrlist; statePtr->myaddr != NULL; - statePtr->myaddr = statePtr->myaddr->ai_next) { - + statePtr->addr = statePtr->addr->ai_next) { + for (statePtr->myaddr = statePtr->myaddrlist; + statePtr->myaddr != NULL; + statePtr->myaddr = statePtr->myaddr->ai_next) { /* * No need to try combinations of local and remote addresses * of different families. @@ -1662,25 +1754,37 @@ TcpConnect( * Close the socket if it is still open from the last unsuccessful * iteration. */ + if (statePtr->sockets->fd != INVALID_SOCKET) { closesocket(statePtr->sockets->fd); } - /* get statePtr lock */ + /* + * Get statePtr lock. + */ + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); /* * Reset last error from last try */ + statePtr->notifierConnectError = 0; Tcl_SetErrno(0); - statePtr->sockets->fd = socket(statePtr->myaddr->ai_family, SOCK_STREAM, 0); + statePtr->sockets->fd = socket(statePtr->myaddr->ai_family, + SOCK_STREAM, 0); + + /* + * Free list lock. + */ - /* Free list lock */ SetEvent(tsdPtr->socketListLock); - /* continue on socket creation error */ + /* + * Continue on socket creation error. + */ + if (statePtr->sockets->fd == INVALID_SOCKET) { TclWinConvertError((DWORD) WSAGetLastError()); continue; @@ -1691,31 +1795,39 @@ TcpConnect( * processes by default. Turn off the inherit bit. */ - SetHandleInformation((HANDLE) statePtr->sockets->fd, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation((HANDLE) statePtr->sockets->fd, + HANDLE_FLAG_INHERIT, 0); /* * Set kernel space buffering */ - TclSockMinimumBuffers((void *) statePtr->sockets->fd, TCP_BUFFER_SIZE); + TclSockMinimumBuffers((void *) statePtr->sockets->fd, + TCP_BUFFER_SIZE); /* * Try to bind to a local port. */ if (bind(statePtr->sockets->fd, statePtr->myaddr->ai_addr, - statePtr->myaddr->ai_addrlen) == SOCKET_ERROR) { + statePtr->myaddr->ai_addrlen) == SOCKET_ERROR) { TclWinConvertError((DWORD) WSAGetLastError()); continue; } + /* * For asyncroneous connect set the socket in nonblocking mode * and activate connect notification */ + if (async_connect) { TcpState *statePtr2; int in_socket_list = 0; - /* get statePtr lock */ + + /* + * Get statePtr lock. + */ + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); /* @@ -1725,8 +1837,8 @@ TcpConnect( * It is set after this call by TcpThreadActionProc and is set * on a second round. * - * If not, we buffer my statePtr in the tsd memory so it is not - * lost by the event procedure + * If not, we buffer my statePtr in the tsd memory so it is + * not lost by the event procedure */ for (statePtr2 = tsdPtr->socketList; statePtr2 != NULL; @@ -1739,21 +1851,27 @@ TcpConnect( if (!in_socket_list) { tsdPtr->pendingTcpState = statePtr; } + /* * Set connect mask to connect events - * This is activated by a SOCKET_SELECT message to the notifier - * thread. + * + * This is activated by a SOCKET_SELECT message to the + * notifier thread. */ - statePtr->selectEvents |= FD_CONNECT; + + SET_BITS(statePtr->selectEvents, FD_CONNECT); /* - * Free list lock + * Free list lock. */ + SetEvent(tsdPtr->socketListLock); - /* activate accept notification */ - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, - (LPARAM) statePtr); + /* + * Activate accept notification. + */ + + SendSelectMessage(tsdPtr, SELECT, statePtr); } /* @@ -1769,12 +1887,11 @@ TcpConnect( if (async_connect && error == WSAEWOULDBLOCK) { /* * Asynchroneous connect - */ - - /* + * * Remember that we jump back behind this next round */ - statePtr->flags |= TCP_ASYNC_PENDING; + + SET_BITS(statePtr->flags, TCP_ASYNC_PENDING); return TCL_OK; reenter: @@ -1784,21 +1901,39 @@ TcpConnect( * * Clear the reenter flag */ - statePtr->flags &= ~(TCP_ASYNC_PENDING); - /* get statePtr lock */ + + CLEAR_BITS(statePtr->flags, TCP_ASYNC_PENDING); + + /* + * Get statePtr lock. + */ + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - /* Get signaled connect error */ + + /* + * Get signaled connect error. + */ + TclWinConvertError((DWORD) statePtr->notifierConnectError); - /* Clear eventual connect flag */ - statePtr->selectEvents &= ~(FD_CONNECT); - /* Free list lock */ + + /* + * Clear eventual connect flag. + */ + + CLEAR_BITS(statePtr->selectEvents, FD_CONNECT); + + /* + * Free list lock. + */ + SetEvent(tsdPtr->socketListLock); } /* - * Clear the tsd socket list pointer if we did not wait for - * the FD_CONNECT asyncroneously + * Clear the tsd socket list pointer if we did not wait for the + * FD_CONNECT asynchronously. */ + tsdPtr->pendingTcpState = NULL; if (Tcl_GetErrno() == 0) { @@ -1807,7 +1942,7 @@ TcpConnect( } } -out: + out: /* * Socket connected or connection failed */ @@ -1818,13 +1953,13 @@ out: CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT); - if ( Tcl_GetErrno() == 0 ) { + if (Tcl_GetErrno() == 0) { /* * Succesfully connected - */ - /* + * * Set up the select mask for read/write events. */ + statePtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE; /* @@ -1832,35 +1967,56 @@ out: * automatically places the socket into non-blocking mode. */ - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, - (LPARAM) statePtr); + SendSelectMessage(tsdPtr, SELECT, statePtr); } else { /* * Connect failed - */ - - /* + * * For async connect schedule a writable event to report the fail. */ + if (async_callback) { /* * Set up the select mask for read/write events. */ + statePtr->selectEvents = FD_WRITE|FD_READ; - /* get statePtr lock */ + + /* + * Get statePtr lock. + */ + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - /* Signal ready readable and writable events */ - statePtr->readyEvents |= FD_WRITE | FD_READ; - /* Flag error to event routine */ - statePtr->flags |= TCP_ASYNC_FAILED; - /* Save connect error to be reported by 'fconfigure -error' */ + + /* + * Signal ready readable and writable events. + */ + + SET_BITS(statePtr->readyEvents, FD_WRITE | FD_READ); + + /* + * Flag error to event routine. + */ + + SET_BITS(statePtr->flags, TCP_ASYNC_FAILED); + + /* + * Save connect error to be reported by 'fconfigure -error'. + */ + statePtr->connectError = Tcl_GetErrno(); - /* Free list lock */ + + /* + * Free list lock. + */ + SetEvent(tsdPtr->socketListLock); } + /* * Error message on syncroneous connect */ + if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "couldn't open socket: %s", Tcl_PosixError(interp))); @@ -1938,7 +2094,7 @@ Tcl_OpenTcpClient( statePtr->addrlist = addrlist; statePtr->myaddrlist = myaddrlist; if (async) { - statePtr->flags |= TCP_ASYNC_CONNECT; + SET_BITS(statePtr->flags, TCP_ASYNC_CONNECT); } /* @@ -2008,7 +2164,7 @@ Tcl_MakeTcpClientChannel( */ statePtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE; - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr); + SendSelectMessage(tsdPtr, SELECT, statePtr); sprintf(channelName, SOCK_TEMPLATE, statePtr); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, @@ -2078,7 +2234,8 @@ Tcl_OpenTcpServerEx( goto error; } - if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) { + if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, + &errorMsg)) { goto error; } @@ -2117,13 +2274,14 @@ Tcl_OpenTcpServerEx( } /* - * The SO_REUSEADDR option on Windows behaves like SO_REUSEPORT on unix - * systems. + * The SO_REUSEADDR option on Windows behaves like SO_REUSEPORT on + * unix systems. */ - if (flags & TCL_TCPSERVER_REUSEPORT) { + + if (GOT_BITS(flags, TCL_TCPSERVER_REUSEPORT)) { optvalue = 1; (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char *) &optvalue, sizeof(optvalue)); + (char *) &optvalue, sizeof(optvalue)); } /* @@ -2134,8 +2292,8 @@ Tcl_OpenTcpServerEx( * place to look for bugs. */ - if (bind(sock, addrPtr->ai_addr, addrPtr->ai_addrlen) - == SOCKET_ERROR) { + if (bind(sock, addrPtr->ai_addr, + addrPtr->ai_addrlen) == SOCKET_ERROR) { TclWinConvertError((DWORD) WSAGetLastError()); closesocket(sock); continue; @@ -2170,13 +2328,14 @@ Tcl_OpenTcpServerEx( /* * Add this socket to the global list of sockets. */ + statePtr = NewSocketInfo(sock); } else { - AddSocketInfoFd( statePtr, sock ); + AddSocketInfoFd(statePtr, sock); } } -error: + error: if (addrlist != NULL) { freeaddrinfo(addrlist); } @@ -2201,8 +2360,7 @@ error: */ ioctlsocket(sock, (long) FIONBIO, &flag); - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, - (LPARAM) statePtr); + SendSelectMessage(tsdPtr, SELECT, statePtr); if (Tcl_SetChannelOption(interp, statePtr->channel, "-eofchar", "") == TCL_ERROR) { Tcl_Close(NULL, statePtr->channel); @@ -2271,8 +2429,7 @@ TcpAccept( */ newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE); - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, - (LPARAM) newInfoPtr); + SendSelectMessage(tsdPtr, SELECT, newInfoPtr); sprintf(channelName, SOCK_TEMPLATE, newInfoPtr); newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, @@ -2503,7 +2660,7 @@ SocketSetupProc( Tcl_Time blockTime = { 0, 0 }; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!(flags & TCL_FILE_EVENTS)) { + if (!GOT_BITS(flags, TCL_FILE_EVENTS)) { return; } @@ -2513,9 +2670,8 @@ SocketSetupProc( WaitForSingleObject(tsdPtr->socketListLock, INFINITE); for (statePtr = tsdPtr->socketList; statePtr != NULL; statePtr = statePtr->nextPtr) { - if (statePtr->readyEvents & - (statePtr->watchEvents | FD_CONNECT | FD_ACCEPT) - ) { + if (GOT_BITS(statePtr->readyEvents, + statePtr->watchEvents | FD_CONNECT | FD_ACCEPT)) { Tcl_SetMaxBlockTime(&blockTime); break; } @@ -2549,7 +2705,7 @@ SocketCheckProc( SocketEvent *evPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!(flags & TCL_FILE_EVENTS)) { + if (!GOT_BITS(flags, TCL_FILE_EVENTS)) { return; } @@ -2562,11 +2718,10 @@ SocketCheckProc( WaitForSingleObject(tsdPtr->socketListLock, INFINITE); for (statePtr = tsdPtr->socketList; statePtr != NULL; statePtr = statePtr->nextPtr) { - if ((statePtr->readyEvents & - (statePtr->watchEvents | FD_CONNECT | FD_ACCEPT)) - && !(statePtr->flags & SOCKET_PENDING) - ) { - statePtr->flags |= SOCKET_PENDING; + if (GOT_BITS(statePtr->readyEvents, + statePtr->watchEvents | FD_CONNECT | FD_ACCEPT) + && !GOT_BITS(statePtr->flags, SOCKET_PENDING)) { + SET_BITS(statePtr->flags, SOCKET_PENDING); evPtr = ckalloc(sizeof(SocketEvent)); evPtr->header.proc = SocketEventProc; evPtr->socket = statePtr->sockets->fd; @@ -2612,7 +2767,7 @@ SocketEventProc( address addr; int len; - if (!(flags & TCL_FILE_EVENTS)) { + if (!GOT_BITS(flags, TCL_FILE_EVENTS)) { return 0; } @@ -2641,29 +2796,26 @@ SocketEventProc( * Clear flag that (this) event is pending */ - statePtr->flags &= ~SOCKET_PENDING; + CLEAR_BITS(statePtr->flags, SOCKET_PENDING); /* * Continue async connect if pending and ready */ - if ( statePtr->readyEvents & FD_CONNECT ) { - if ( statePtr->flags & TCP_ASYNC_PENDING ) { - + if (GOT_BITS(statePtr->readyEvents, FD_CONNECT)) { + if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) { /* * Do one step and save eventual connect error */ SetEvent(tsdPtr->socketListLock); WaitForConnect(statePtr,NULL); - } else { - /* * No async connect reenter pending. Just clear event. */ - statePtr->readyEvents &= ~(FD_CONNECT); + CLEAR_BITS(statePtr->readyEvents, FD_CONNECT); SetEvent(tsdPtr->socketListLock); } return 1; @@ -2672,20 +2824,23 @@ SocketEventProc( /* * Handle connection requests directly. */ - if (statePtr->readyEvents & FD_ACCEPT) { - for (fds = statePtr->sockets; fds != NULL; fds = fds->next) { + if (GOT_BITS(statePtr->readyEvents, FD_ACCEPT)) { + for (fds = statePtr->sockets; fds != NULL; fds = fds->next) { /* - * Accept the incoming connection request. - */ - len = sizeof(address); + * Accept the incoming connection request. + */ + len = sizeof(address); newSocket = accept(fds->fd, &(addr.sa), &len); - /* On Tcl server sockets with multiple OS fds we loop over the fds trying - * an accept() on each, so we expect INVALID_SOCKET. There are also other - * network stack conditions that can result in FD_ACCEPT but a subsequent - * failure on accept() by the time we get around to it. + /* + * On Tcl server sockets with multiple OS fds we loop over the fds + * trying an accept() on each, so we expect INVALID_SOCKET. There + * are also other network stack conditions that can result in + * FD_ACCEPT but a subsequent failure on accept() by the time we + * get around to it. + * * Access to sockets (acceptEventCount, readyEvents) in socketList * is still protected by the lock (prevents reintroduction of * SF Tcl Bug 3056775. @@ -2697,35 +2852,40 @@ SocketEventProc( } /* - * It is possible that more than one FD_ACCEPT has been sent, so an extra - * count must be kept. Decrement the count, and reset the readyEvent bit - * if the count is no longer > 0. + * It is possible that more than one FD_ACCEPT has been sent, so + * an extra count must be kept. Decrement the count, and reset the + * readyEvent bit if the count is no longer > 0. */ + statePtr->acceptEventCount--; if (statePtr->acceptEventCount <= 0) { - statePtr->readyEvents &= ~(FD_ACCEPT); + CLEAR_BITS(statePtr->readyEvents, FD_ACCEPT); } SetEvent(tsdPtr->socketListLock); - /* Caution: TcpAccept() has the side-effect of evaluating the server - * accept script (via AcceptCallbackProc() in tclIOCmd.c), which can - * close the server socket and invalidate statePtr and fds. - * If TcpAccept() accepts a socket we must return immediately and let - * SocketCheckProc queue additional FD_ACCEPT events. + /* + * Caution: TcpAccept() has the side-effect of evaluating the + * server accept script (via AcceptCallbackProc() in tclIOCmd.c), + * which can close the server socket and invalidate statePtr and + * fds. If TcpAccept() accepts a socket we must return immediately + * and let SocketCheckProc queue additional FD_ACCEPT events. */ + TcpAccept(fds, newSocket, addr); return 1; } - /* Loop terminated with no sockets accepted; clear the ready mask so + /* + * Loop terminated with no sockets accepted; clear the ready mask so * we can detect the next connection request. Note that connection * requests are level triggered, so if there is a request already * pending, a new event will be generated. */ + statePtr->acceptEventCount = 0; - statePtr->readyEvents &= ~(FD_ACCEPT); + CLEAR_BITS(statePtr->readyEvents, FD_ACCEPT); SetEvent(tsdPtr->socketListLock); return 1; @@ -2740,7 +2900,7 @@ SocketEventProc( events = statePtr->readyEvents & statePtr->watchEvents; - if (events & FD_CLOSE) { + if (GOT_BITS(events, FD_CLOSE)) { /* * If the socket was closed and the channel is still interested in * read events, then we need to ensure that we keep polling for this @@ -2754,17 +2914,14 @@ SocketEventProc( Tcl_Time blockTime = { 0, 0 }; Tcl_SetMaxBlockTime(&blockTime); - mask |= TCL_READABLE|TCL_WRITABLE; - } else if (events & FD_READ) { - + SET_BITS(mask, TCL_READABLE | TCL_WRITABLE); + } else if (GOT_BITS(events, FD_READ)) { /* * Throw the readable event if an async connect failed. */ - if ( statePtr->flags & TCP_ASYNC_FAILED ) { - - mask |= TCL_READABLE; - + if (GOT_BITS(statePtr->flags, TCP_ASYNC_FAILED)) { + SET_BITS(mask, TCL_READABLE); } else { fd_set readFds; struct timeval timeout; @@ -2777,8 +2934,7 @@ SocketEventProc( * async select handler and keep waiting. */ - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) UNSELECT, (LPARAM) statePtr); + SendSelectMessage(tsdPtr, UNSELECT, statePtr); FD_ZERO(&readFds); FD_SET(statePtr->sockets->fd, &readFds); @@ -2786,11 +2942,10 @@ SocketEventProc( timeout.tv_sec = 0; if (select(0, &readFds, NULL, NULL, &timeout) != 0) { - mask |= TCL_READABLE; + SET_BITS(mask, TCL_READABLE); } else { - statePtr->readyEvents &= ~(FD_READ); - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) SELECT, (LPARAM) statePtr); + CLEAR_BITS(statePtr->readyEvents, FD_READ); + SendSelectMessage(tsdPtr, SELECT, statePtr); } } } @@ -2799,8 +2954,8 @@ SocketEventProc( * writable event */ - if (events & FD_WRITE) { - mask |= TCL_WRITABLE; + if (GOT_BITS(events, FD_WRITE)) { + SET_BITS(mask, TCL_WRITABLE); } /* @@ -2837,13 +2992,19 @@ AddSocketInfoFd( { TcpFdList *fds = statePtr->sockets; - if ( fds == NULL ) { - /* Add the first FD */ + if (fds == NULL) { + /* + * Add the first FD. + */ + statePtr->sockets = ckalloc(sizeof(TcpFdList)); fds = statePtr->sockets; } else { - /* Find end of list and append FD */ - while ( fds->next != NULL ) { + /* + * Find end of list and append FD. + */ + + while (fds->next != NULL) { fds = fds->next; } @@ -2851,7 +3012,10 @@ AddSocketInfoFd( fds = fds->next; } - /* Populate new FD */ + /* + * Populate new FD. + */ + fds->fd = socket; fds->statePtr = statePtr; fds->next = NULL; @@ -2921,6 +3085,7 @@ WaitForSocketEvent( int result = 1; int oldMode; ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey); + /* * Be sure to disable event servicing so we are truly modal. */ @@ -2931,29 +3096,42 @@ WaitForSocketEvent( * Reset WSAAsyncSelect so we have a fresh set of events pending. */ - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT, - (LPARAM) statePtr); - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, - (LPARAM) statePtr); + SendSelectMessage(tsdPtr, UNSELECT, statePtr); + SendSelectMessage(tsdPtr, SELECT, statePtr); while (1) { int event_found; - /* get statePtr lock */ + /* + * Get statePtr lock. + */ + WaitForSingleObject(tsdPtr->socketListLock, INFINITE); - /* Check if event occured */ - event_found = (statePtr->readyEvents & events); + /* + * Check if event occured. + */ + + event_found = GOT_BITS(statePtr->readyEvents, events); + + /* + * Free list lock. + */ - /* Free list lock */ SetEvent(tsdPtr->socketListLock); - /* exit loop if event occured */ + /* + * Exit loop if event occured. + */ + if (event_found) { break; } - /* Exit loop if event did not occur but this is a non-blocking channel */ + /* + * Exit loop if event did not occur but this is a non-blocking channel + */ + if (statePtr->flags & TCP_NONBLOCKING) { *errorCodePtr = EWOULDBLOCK; result = 0; @@ -3110,55 +3288,59 @@ SocketProc( for (statePtr = tsdPtr->socketList; statePtr != NULL; statePtr = statePtr->nextPtr) { - if ( FindFDInList(statePtr,socket) ) { + if (FindFDInList(statePtr, socket)) { info_found = 1; break; } } + /* - * Check if there is a pending info structure not jet in the - * list + * Check if there is a pending info structure not jet in the list. */ - if ( !info_found + + if (!info_found && tsdPtr->pendingTcpState != NULL - && FindFDInList(tsdPtr->pendingTcpState,socket) ) { + && FindFDInList(tsdPtr->pendingTcpState, socket)) { statePtr = tsdPtr->pendingTcpState; info_found = 1; } if (info_found) { - /* * Update the socket state. * * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event - * happens, then clear the FD_ACCEPT count. Otherwise, - * increment the count if the current event is an FD_ACCEPT. + * happens, then clear the FD_ACCEPT count. Otherwise, increment + * the count if the current event is an FD_ACCEPT. */ - if (event & FD_CLOSE) { + if (GOT_BITS(event, FD_CLOSE)) { statePtr->acceptEventCount = 0; - statePtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT); - } else if (event & FD_ACCEPT) { + CLEAR_BITS(statePtr->readyEvents, FD_WRITE | FD_ACCEPT); + } else if (GOT_BITS(event, FD_ACCEPT)) { statePtr->acceptEventCount++; } - if (event & FD_CONNECT) { + if (GOT_BITS(event, FD_CONNECT)) { /* * Remember any error that occurred so we can report * connection failures. */ + if (error != ERROR_SUCCESS) { statePtr->notifierConnectError = error; } } + /* * Inform main thread about signaled events */ - statePtr->readyEvents |= event; + + SET_BITS(statePtr->readyEvents, event); /* * Wake up the Main Thread. */ + SetEvent(tsdPtr->readyEvent); Tcl_ThreadAlert(tsdPtr->threadId); } @@ -3239,6 +3421,7 @@ FindFDInList( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED #undef TclWinGetSockOpt int TclWinGetSockOpt( @@ -3278,6 +3461,7 @@ TclWinGetServByName( { return getservbyname(name, proto); } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -3367,8 +3551,7 @@ TcpThreadActionProc( * thread. */ - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) notifyCmd, (LPARAM) statePtr); + SendSelectMessage(tsdPtr, notifyCmd, statePtr); } /* diff --git a/win/tclWinTime.c b/win/tclWinTime.c index 7cbc1ba..d123985 100644 --- a/win/tclWinTime.c +++ b/win/tclWinTime.c @@ -27,6 +27,7 @@ * month, where index 1 is January. */ +#ifndef TCL_NO_DEPRECATED static const int normalDays[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; @@ -40,6 +41,7 @@ typedef struct { struct tm tm; /* time information */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; +#endif /* TCL_NO_DEPRECATED */ /* * Data for managing high-resolution timers. @@ -124,7 +126,9 @@ static struct { * Declarations for functions defined later in this file. */ +#ifndef TCL_NO_DEPRECATED static struct tm * ComputeGMT(const time_t *tp); +#endif /* TCL_NO_DEPRECATED */ static void StopCalibration(ClientData clientData); static DWORD WINAPI CalibrationThread(LPVOID arg); static void UpdateTimeEachSecond(void); @@ -715,6 +719,7 @@ StopCalibration( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED struct tm * TclpGetDate( const time_t *t, @@ -917,6 +922,7 @@ ComputeGMT( return tmPtr; } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -1261,6 +1267,7 @@ AccumulateSample( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED struct tm * TclpGmtime( const time_t *timePtr) /* Pointer to the number of seconds since the @@ -1305,6 +1312,7 @@ TclpLocaltime( return localtime(timePtr); } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- |