diff options
| author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2017-12-06 12:27:50 (GMT) |
|---|---|---|
| committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2017-12-06 12:27:50 (GMT) |
| commit | fe4ff7a3fb60d7dd8c75e1b33dae1fc70f15fdc0 (patch) | |
| tree | 4425148c353ff24118c096bbba98806625bf463e /win | |
| parent | d2ef001c4971191280470eca5f1cf9dc1e2d8070 (diff) | |
| parent | 72f0f0b3468809e3a3a26e448b3bd3be8a8398a6 (diff) | |
| download | tcl-fe4ff7a3fb60d7dd8c75e1b33dae1fc70f15fdc0.zip tcl-fe4ff7a3fb60d7dd8c75e1b33dae1fc70f15fdc0.tar.gz tcl-fe4ff7a3fb60d7dd8c75e1b33dae1fc70f15fdc0.tar.bz2 | |
merge core-8-branch
Diffstat (limited to 'win')
| -rw-r--r-- | win/Makefile.in | 13 | ||||
| -rwxr-xr-x | win/configure | 4 | ||||
| -rw-r--r-- | win/configure.ac | 3 | ||||
| -rw-r--r-- | win/makefile.vc | 19 | ||||
| -rw-r--r-- | win/nmakehlp.c | 10 | ||||
| -rw-r--r-- | win/tcl.dsp | 4 | ||||
| -rw-r--r-- | win/tcl.hpj.in | 4 | ||||
| -rw-r--r-- | win/tclAppInit.c | 4 | ||||
| -rw-r--r-- | win/tclConfig.sh.in | 3 | ||||
| -rw-r--r-- | win/tclWin32Dll.c | 4 | ||||
| -rw-r--r-- | win/tclWinChan.c | 145 | ||||
| -rw-r--r-- | win/tclWinConsole.c | 225 | ||||
| -rw-r--r-- | win/tclWinError.c | 2 | ||||
| -rw-r--r-- | win/tclWinFCmd.c | 4 | ||||
| -rw-r--r-- | win/tclWinFile.c | 2 | ||||
| -rw-r--r-- | win/tclWinInit.c | 20 | ||||
| -rw-r--r-- | win/tclWinInt.h | 75 | ||||
| -rw-r--r-- | win/tclWinLoad.c | 38 | ||||
| -rw-r--r-- | win/tclWinPipe.c | 626 | ||||
| -rw-r--r-- | win/tclWinReg.c | 10 | ||||
| -rw-r--r-- | win/tclWinSerial.c | 95 | ||||
| -rw-r--r-- | win/tclWinThrd.c | 2 | ||||
| -rw-r--r-- | win/tclooConfig.sh | 2 |
23 files changed, 811 insertions, 503 deletions
diff --git a/win/Makefile.in b/win/Makefile.in index e967ef3..a3275ba 100644 --- a/win/Makefile.in +++ b/win/Makefile.in @@ -329,6 +329,9 @@ TOMMATH_OBJS = \ bn_mp_exch.${OBJEXT} \ bn_mp_expt_d.${OBJEXT} \ bn_mp_expt_d_ex.${OBJEXT} \ + bn_mp_get_int.${OBJEXT} \ + bn_mp_get_long.${OBJEXT} \ + bn_mp_get_long_long.${OBJEXT} \ bn_mp_grow.${OBJEXT} \ bn_mp_init.${OBJEXT} \ bn_mp_init_copy.${OBJEXT} \ @@ -353,6 +356,8 @@ TOMMATH_OBJS = \ bn_mp_rshd.${OBJEXT} \ bn_mp_set.${OBJEXT} \ bn_mp_set_int.${OBJEXT} \ + bn_mp_set_long.${OBJEXT} \ + bn_mp_set_long_long.${OBJEXT} \ bn_mp_shrink.${OBJEXT} \ bn_mp_sqr.${OBJEXT} \ bn_mp_sqrt.${OBJEXT} \ @@ -652,15 +657,15 @@ 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.12 as a Tcl Module"; + @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.8.12.tm; @echo "Installing library opt0.4 directory"; @for j in $(ROOT_DIR)/library/opt/*.tcl; \ do \ $(COPY) "$$j" "$(SCRIPT_INSTALL_DIR)/opt0.4"; \ done; - @echo "Installing package msgcat 1.6.0 as a Tcl Module"; - @$(COPY) $(ROOT_DIR)/library/msgcat/msgcat.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/msgcat-1.6.0.tm; + @echo "Installing package msgcat 1.6.1 as a Tcl Module"; + @$(COPY) $(ROOT_DIR)/library/msgcat/msgcat.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/msgcat-1.6.1.tm; @echo "Installing package tcltest 2.4.0 as a Tcl Module"; @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/tcltest-2.4.0.tm; @echo "Installing package platform 1.0.14 as a Tcl Module"; diff --git a/win/configure b/win/configure index 147252c..fdd3adb 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 @@ -2101,7 +2102,7 @@ SHELL=/bin/sh TCL_VERSION=8.7 TCL_MAJOR_VERSION=8 TCL_MINOR_VERSION=7 -TCL_PATCH_LEVEL="a0" +TCL_PATCH_LEVEL="a2" VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION TCL_DDE_VERSION=1.4 @@ -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..d03695c 100644 --- a/win/configure.ac +++ b/win/configure.ac @@ -14,7 +14,7 @@ SHELL=/bin/sh TCL_VERSION=8.7 TCL_MAJOR_VERSION=8 TCL_MINOR_VERSION=7 -TCL_PATCH_LEVEL="a0" +TCL_PATCH_LEVEL="a2" VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION TCL_DDE_VERSION=1.4 @@ -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.vc b/win/makefile.vc index d6de5e1..2bff871 100644 --- a/win/makefile.vc +++ b/win/makefile.vc @@ -121,7 +121,7 @@ the build instructions. # nodep = Turns off compatibility macros to ensure the core
# isn't being built with deprecated functions.
#
-# MACHINE=(ALPHA|AMD64|IA64|IX86)
+# MACHINE=(ARM|AMD64|IA64|IX86)
# Set the machine type used for the compiler, linker, and
# resource compiler. This hook is needed to tell the tools
# when alternate platforms are requested. IX86 is the default
@@ -384,6 +384,9 @@ TOMMATHOBJS = \ $(TMP_DIR)\bn_mp_exch.obj \
$(TMP_DIR)\bn_mp_expt_d.obj \
$(TMP_DIR)\bn_mp_expt_d_ex.obj \
+ $(TMP_DIR)\bn_mp_get_int.obj \
+ $(TMP_DIR)\bn_mp_get_long.obj \
+ $(TMP_DIR)\bn_mp_get_long_long.obj \
$(TMP_DIR)\bn_mp_grow.obj \
$(TMP_DIR)\bn_mp_init.obj \
$(TMP_DIR)\bn_mp_init_copy.obj \
@@ -408,6 +411,8 @@ TOMMATHOBJS = \ $(TMP_DIR)\bn_mp_rshd.obj \
$(TMP_DIR)\bn_mp_set.obj \
$(TMP_DIR)\bn_mp_set_int.obj \
+ $(TMP_DIR)\bn_mp_set_long.obj \
+ $(TMP_DIR)\bn_mp_set_long_long.obj \
$(TMP_DIR)\bn_mp_shrink.obj \
$(TMP_DIR)\bn_mp_sqr.obj \
$(TMP_DIR)\bn_mp_sqrt.obj \
@@ -485,9 +490,16 @@ cdebug = -Zi -Od $(DEBUGFLAGS) cdebug = -Zi -WX $(DEBUGFLAGS)
!endif
+### Common compiler options that are architecture specific
+!if "$(MACHINE)" == "ARM"
+carch = -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
+!else
+carch =
+!endif
+
### Declarations common to all compiler options
cwarn = $(WARNINGS) -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE
-cflags = -nologo -c $(COMPILERFLAGS) $(cwarn) -Fp$(TMP_DIR)^\
+cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\
!if $(MSVCRT)
!if $(DEBUG) && !$(UNCHECKED)
@@ -879,6 +891,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@
@@ -1222,6 +1235,8 @@ clean: clean-pkgs @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
@echo Cleaning $(WINDIR)\nmakehlp.exe ...
@if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
+ @echo Cleaning $(WINDIR)\nmhlp-out.txt ...
+ @if exist $(WINDIR)\nmhlp-out.txt del $(WINDIR)\nmhlp-out.txt
@echo Cleaning $(WINDIR)\_junk.pch ...
@if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch
@echo Cleaning $(WINDIR)\vercl.x ...
diff --git a/win/nmakehlp.c b/win/nmakehlp.c index 22b7b06..0439d1c 100644 --- a/win/nmakehlp.c +++ b/win/nmakehlp.c @@ -74,6 +74,7 @@ main( char msg[300]; DWORD dwWritten; int chars; + char *s; /* * Make sure children (cl.exe and link.exe) are kept quiet. @@ -153,8 +154,13 @@ main( &dwWritten, NULL); return 0; } - printf("%s\n", GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0')); - return 0; + s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0'); + if (s && *s) { + printf("%s\n", s); + return 0; + } else + return 1; /* Version not found. Return non-0 exit code */ + case 'Q': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, diff --git a/win/tcl.dsp b/win/tcl.dsp index 48eae9d..ad9c764 100644 --- a/win/tcl.dsp +++ b/win/tcl.dsp @@ -152,10 +152,6 @@ SOURCE=..\compat\fixstrtod.c # End Source File
# Begin Source File
-SOURCE=..\compat\float.h
-# End Source File
-# Begin Source File
-
SOURCE=..\compat\gettod.c
# End Source File
# Begin Source File
diff --git a/win/tcl.hpj.in b/win/tcl.hpj.in index a94cea6..08d411d 100644 --- a/win/tcl.hpj.in +++ b/win/tcl.hpj.in @@ -5,9 +5,9 @@ HCW=0 LCID=0x409 0x0 0x0 ;English (United States)
REPORT=Yes
TITLE=Tcl/Tk Reference Manual
-CNT=tcl86.cnt
+CNT=tcl87.cnt
COPYRIGHT=Copyright © 2000 Ajuba Solutions
-HLP=tcl86.hlp
+HLP=tcl87.hlp
[FILES]
tcl.rtf
diff --git a/win/tclAppInit.c b/win/tclAppInit.c index e06eaf5..ef9f98b 100644 --- a/win/tclAppInit.c +++ b/win/tclAppInit.c @@ -263,8 +263,8 @@ setargv( } /* Make sure we don't call ckalloc through the (not yet initialized) stub table */ - #undef Tcl_Alloc - #undef Tcl_DbCkalloc +# undef Tcl_Alloc +# undef Tcl_DbCkalloc argSpace = ckalloc(size * sizeof(char *) + (_tcslen(cmdLine) * sizeof(TCHAR)) + sizeof(TCHAR)); 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/tclWin32Dll.c b/win/tclWin32Dll.c index e4adb1d..1d6cb2b 100644 --- a/win/tclWin32Dll.c +++ b/win/tclWin32Dll.c @@ -29,7 +29,7 @@ static int platformId; /* Running under NT, or 95/98? */ * VC++ 5.x has no 'cpuid' assembler instruction, so we must emulate it */ -#if defined(_MSC_VER) && (_MSC_VER <= 1100) +#if defined(_MSC_VER) && (_MSC_VER <= 1100) && defined (_M_IX86) #define cpuid __asm __emit 0fh __asm __emit 0a2h #endif @@ -735,7 +735,7 @@ TclWinCPUID( __cpuid(regsPtr, index); status = TCL_OK; -# else +# elif defined (_M_IX86) /* * Define a structure in the stack frame to hold the registers. */ 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/tclWinError.c b/win/tclWinError.c index 30079b9..fea4b0f 100644 --- a/win/tclWinError.c +++ b/win/tclWinError.c @@ -30,7 +30,7 @@ static const unsigned char errorTable[] = { ENOEXEC, /* ERROR_BAD_FORMAT 11 */ EACCES, /* ERROR_INVALID_ACCESS 12 */ EINVAL, /* ERROR_INVALID_DATA 13 */ - EFAULT, /* ERROR_OUT_OF_MEMORY 14 */ + ENOMEM, /* ERROR_OUT_OF_MEMORY 14 */ ENOENT, /* ERROR_INVALID_DRIVE 15 */ EACCES, /* ERROR_CURRENT_DIRECTORY 16 */ EXDEV, /* ERROR_NOT_SAME_DEVICE 17 */ diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c index 01af950..319ecf9 100644 --- a/win/tclWinFCmd.c +++ b/win/tclWinFCmd.c @@ -1611,13 +1611,11 @@ ConvertFileNameFormat( for (i = 0; i < pathc; i++) { Tcl_Obj *elt; char *pathv; - size_t pathLen; Tcl_ListObjIndex(NULL, splitPath, i, &elt); pathv = TclGetString(elt); - pathLen = elt->length; - if ((pathv[0] == '/') || ((pathLen == 3) && (pathv[1] == ':')) + if ((pathv[0] == '/') || ((elt->length == 3) && (pathv[1] == ':')) || (strcmp(pathv, ".") == 0) || (strcmp(pathv, "..") == 0)) { /* * Handle "/", "//machine/export", "c:/", "." or ".." by just diff --git a/win/tclWinFile.c b/win/tclWinFile.c index e61d619..700e3c8 100644 --- a/win/tclWinFile.c +++ b/win/tclWinFile.c @@ -828,7 +828,7 @@ tclWinDebugPanic( __builtin_trap(); #elif defined(_WIN64) __debugbreak(); -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && defined (_M_IX86) _asm {int 3} #else DebugBreak(); 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/tclWinLoad.c b/win/tclWinLoad.c index 26512b1..69263e9 100644 --- a/win/tclWinLoad.c +++ b/win/tclWinLoad.c @@ -63,9 +63,10 @@ TclpDlopen( * file. */ int flags) { - HINSTANCE hInstance; + HINSTANCE hInstance = NULL; const TCHAR *nativeName; Tcl_LoadHandle handlePtr; + DWORD firstError; /* * First try the full path the user gave us. This is particularly @@ -74,7 +75,10 @@ TclpDlopen( */ nativeName = Tcl_FSGetNativePath(pathPtr); - hInstance = LoadLibraryEx(nativeName,NULL,LOAD_WITH_ALTERED_SEARCH_PATH); + if (nativeName != NULL) { + hInstance = LoadLibraryEx(nativeName, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + } if (hInstance == NULL) { /* * Let the OS loader examine the binary search path for whatever @@ -84,6 +88,13 @@ TclpDlopen( Tcl_DString ds; + /* + * Remember the first error on load attempt to be used if the + * second load attempt below also fails. + */ + firstError = (nativeName == NULL) ? + ERROR_MOD_NOT_FOUND : GetLastError(); + nativeName = Tcl_WinUtfToTChar(Tcl_GetString(pathPtr), -1, &ds); hInstance = LoadLibraryEx(nativeName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); @@ -91,8 +102,21 @@ TclpDlopen( } if (hInstance == NULL) { - DWORD lastError = GetLastError(); - Tcl_Obj *errMsg = Tcl_ObjPrintf("couldn't load library \"%s\": ", + DWORD lastError; + Tcl_Obj *errMsg; + + /* + * We choose to only use the error from the second call if the first + * call failed due to the file not being found. Else stick to the + * first error for reporting purposes. + */ + if (firstError == ERROR_MOD_NOT_FOUND || + firstError == ERROR_DLL_NOT_FOUND) + lastError = GetLastError(); + else + lastError = firstError; + + errMsg = Tcl_ObjPrintf("couldn't load library \"%s\": ", Tcl_GetString(pathPtr)); /* @@ -129,7 +153,11 @@ TclpDlopen( Tcl_AppendToObj(errMsg, "the library initialization" " routine failed", -1); break; - default: + case ERROR_BAD_EXE_FORMAT: + Tcl_SetErrorCode(interp, "WIN_LOAD", "BAD_EXE_FORMAT", NULL); + Tcl_AppendToObj(errMsg, "Bad exe format. Possibly a 32/64-bit mismatch.", -1); + break; + default: TclWinConvertError(lastError); Tcl_AppendToObj(errMsg, Tcl_PosixError(interp), -1); } diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index 4666deb..5f1aa70 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 @@ -1486,10 +1479,10 @@ BuildCommandLine( quote = 1; } else { int count; - Tcl_UniChar ch; + Tcl_UniChar ch = 0; for (start = arg; *start != '\0'; start += count) { - count = Tcl_UtfToUniChar(start, &ch); + count = TclUtfToUniChar(start, &ch); if (Tcl_UniCharIsSpace(ch)) { /* INTL: ISO space. */ quote = 1; break; @@ -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..acfeecb 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); } /* @@ -1799,7 +1738,7 @@ SerialSetOptionProc( dcb.XonChar = argv[0][0]; dcb.XoffChar = argv[1][0]; if (argv[0][0] & 0x80 || argv[1][0] & 0x80) { - Tcl_UniChar character; + Tcl_UniChar character = 0; int charLen; charLen = Tcl_UtfToUniChar(argv[0], &character); diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index b9cde72..8c130a7 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -13,8 +13,6 @@ #include "tclWinInt.h" -#include <float.h> - /* Workaround for mingw versions which don't provide this in float.h */ #ifndef _MCW_EM # define _MCW_EM 0x0008001F /* Error masks */ diff --git a/win/tclooConfig.sh b/win/tclooConfig.sh index ee10b81..4c2068c 100644 --- a/win/tclooConfig.sh +++ b/win/tclooConfig.sh @@ -16,4 +16,4 @@ TCLOO_STUB_LIB_SPEC="" TCLOO_INCLUDE_SPEC="" TCLOO_PRIVATE_INCLUDE_SPEC="" TCLOO_CFLAGS="" -TCLOO_VERSION=1.0.4 +TCLOO_VERSION=1.2.0 |
