diff options
Diffstat (limited to 'win/tclWinPipe.c')
| -rw-r--r-- | win/tclWinPipe.c | 1607 |
1 files changed, 486 insertions, 1121 deletions
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index bb4983e..ee088a5 100644 --- a/win/tclWinPipe.c +++ b/win/tclWinPipe.c @@ -4,7 +4,7 @@ * This file implements the Windows-specific exec pipeline functions, the * "pipe" channel driver, and the "pid" Tcl command. * - * Copyright © 1996-1997 Sun Microsystems, Inc. + * Copyright (c) 1996-1997 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -50,7 +50,7 @@ TCL_DECLARE_MUTEX(pipeMutex) * used in a pipeline. */ -typedef struct { +typedef struct WinFile { int type; /* One of the file types defined above. */ HANDLE handle; /* Open file handle. */ } WinFile; @@ -61,7 +61,7 @@ typedef struct { typedef struct ProcInfo { HANDLE hProcess; - TCL_HASH_TYPE dwProcessId; + DWORD dwProcessId; struct ProcInfo *nextPtr; } ProcInfo; @@ -82,12 +82,6 @@ static ProcInfo *procList; #define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */ /* - * TODO: It appears the whole EXTRABYTE machinery is in place to support - * outdated Win 95 systems. If this can be confirmed, much code can be - * deleted. - */ - -/* * This structure describes per-instance data for a pipe based channel. */ @@ -104,27 +98,35 @@ typedef struct PipeInfo { TclFile readFile; /* Output from pipe. */ TclFile writeFile; /* Input from pipe. */ TclFile errorFile; /* Error output from pipe. */ - TCL_HASH_TYPE numPids; /* Number of processes attached to pipe. */ + int numPids; /* Number of processes attached to pipe. */ Tcl_Pid *pidPtr; /* Pids of attached processes. */ 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 * writer thread so access must be - * synchronized with the writable object. */ + * synchronized with the writable object. + */ char *writeBuf; /* Current background output buffer. Access is * synchronized with the writable object. */ int writeBufLen; /* Size of write buffer. Access is @@ -140,7 +142,7 @@ typedef struct PipeInfo { * synchronized with the readable object. */ } PipeInfo; -typedef struct { +typedef struct ThreadSpecificData { /* * The following pointer refers to the head of the list of pipes that are * being watched for file events. @@ -156,7 +158,7 @@ static Tcl_ThreadDataKey dataKey; * events are generated. */ -typedef struct { +typedef struct PipeEvent { Tcl_Event header; /* Information that is standard for all * events. */ PipeInfo *infoPtr; /* Pointer to pipe info structure. Note that @@ -171,28 +173,28 @@ typedef struct { static int ApplicationType(Tcl_Interp *interp, const char *fileName, char *fullName); -static void BuildCommandLine(const char *executable, Tcl_Size argc, +static void BuildCommandLine(const char *executable, int argc, const char **argv, Tcl_DString *linePtr); static BOOL HasConsole(void); -static int PipeBlockModeProc(void *instanceData, int mode); -static void PipeCheckProc(void *clientData, int flags); -static int PipeClose2Proc(void *instanceData, +static int PipeBlockModeProc(ClientData instanceData, int mode); +static void PipeCheckProc(ClientData clientData, int flags); +static int PipeClose2Proc(ClientData instanceData, Tcl_Interp *interp, int flags); static int PipeEventProc(Tcl_Event *evPtr, int flags); -static int PipeGetHandleProc(void *instanceData, - int direction, void **handlePtr); +static int PipeGetHandleProc(ClientData instanceData, + int direction, ClientData *handlePtr); static void PipeInit(void); -static int PipeInputProc(void *instanceData, char *buf, +static int PipeInputProc(ClientData instanceData, char *buf, int toRead, int *errorCode); -static int PipeOutputProc(void *instanceData, +static int PipeOutputProc(ClientData instanceData, const char *buf, int toWrite, int *errorCode); static DWORD WINAPI PipeReaderThread(LPVOID arg); -static void PipeSetupProc(void *clientData, int flags); -static void PipeWatchProc(void *instanceData, int mask); +static void PipeSetupProc(ClientData clientData, int flags); +static void PipeWatchProc(ClientData instanceData, int mask); static DWORD WINAPI PipeWriterThread(LPVOID arg); static int TempFileName(WCHAR name[MAX_PATH]); static int WaitForRead(PipeInfo *infoPtr, int blocking); -static void PipeThreadActionProc(void *instanceData, +static void PipeThreadActionProc(ClientData instanceData, int action); /* @@ -200,7 +202,7 @@ static void PipeThreadActionProc(void *instanceData, * I/O. */ -static const Tcl_ChannelType pipeChannelType = { +static Tcl_ChannelType pipeChannelType = { "pipe", /* Type name. */ TCL_CHANNEL_VERSION_5, /* v5 channel */ TCL_CLOSE2PROC, /* Close proc. */ @@ -217,7 +219,7 @@ static const Tcl_ChannelType pipeChannelType = { NULL, /* handler proc. */ NULL, /* wide seek proc */ PipeThreadActionProc, /* thread action proc */ - NULL /* truncate */ + NULL, /* truncate */ }; /* @@ -310,7 +312,7 @@ TclpFinalizePipes(void) void PipeSetupProc( - TCL_UNUSED(void *), + ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { PipeInfo *infoPtr; @@ -363,7 +365,7 @@ PipeSetupProc( static void PipeCheckProc( - TCL_UNUSED(void *), + ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { PipeInfo *infoPtr; @@ -402,7 +404,7 @@ PipeCheckProc( if (needEvent) { infoPtr->flags |= PIPE_PENDING; - evPtr = (PipeEvent *)ckalloc(sizeof(PipeEvent)); + evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent)); evPtr->header.proc = PipeEventProc; evPtr->infoPtr = infoPtr; Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); @@ -433,7 +435,7 @@ TclWinMakeFile( { WinFile *filePtr; - filePtr = (WinFile *)ckalloc(sizeof(WinFile)); + filePtr = (WinFile *) ckalloc(sizeof(WinFile)); filePtr->type = WIN_FILE; filePtr->handle = handle; @@ -465,15 +467,24 @@ TempFileName( WCHAR name[MAX_PATH]) /* Buffer in which name for temporary file * gets stored. */ { - const WCHAR *prefix = L"TCL"; - if (GetTempPathW(MAX_PATH, name) != 0) { - if (GetTempFileNameW(name, prefix, 0, name) != 0) { + TCHAR *prefix; + + prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL"; + if ((*tclWinProcs->getTempPathProc)(MAX_PATH, name) != 0) { + if ((*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, + name) != 0) { return 1; } } - name[0] = '.'; - name[1] = '\0'; - return GetTempFileNameW(name, prefix, 0, name); + if (tclWinProcs->useWide) { + ((WCHAR *) name)[0] = '.'; + ((WCHAR *) name)[1] = '\0'; + } else { + ((char *) name)[0] = '.'; + ((char *) name)[1] = '\0'; + } + return (*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, + name); } /* @@ -500,7 +511,7 @@ TclpMakeFile( HANDLE handle; if (Tcl_GetChannelHandle(channel, direction, - (void **) &handle) == TCL_OK) { + (ClientData *) &handle) == TCL_OK) { return TclWinMakeFile(handle); } else { return (TclFile) NULL; @@ -532,7 +543,7 @@ TclpOpenFile( HANDLE handle; DWORD accessMode, createMode, shareMode, flags; Tcl_DString ds; - const WCHAR *nativePath; + const TCHAR *nativePath; /* * Map the access bits to the NT access mode. @@ -549,7 +560,7 @@ TclpOpenFile( accessMode = (GENERIC_READ | GENERIC_WRITE); break; default: - Tcl_WinConvertError(ERROR_INVALID_FUNCTION); + TclWinConvertError(ERROR_INVALID_FUNCTION); return NULL; } @@ -577,8 +588,7 @@ TclpOpenFile( break; } - Tcl_DStringInit(&ds); - nativePath = Tcl_UtfToWCharDString(path, TCL_INDEX_NONE, &ds); + nativePath = Tcl_WinUtfToTChar(path, -1, &ds); /* * If the file is not being created, use the existing file attributes. @@ -586,7 +596,7 @@ TclpOpenFile( flags = 0; if (!(mode & O_CREAT)) { - flags = GetFileAttributesW(nativePath); + flags = (*tclWinProcs->getFileAttributesProc)(nativePath); if (flags == 0xFFFFFFFF) { flags = 0; } @@ -602,18 +612,18 @@ TclpOpenFile( * Now we get to create the file. */ - handle = CreateFileW(nativePath, accessMode, shareMode, - NULL, createMode, flags, NULL); + handle = (*tclWinProcs->createFileProc)(nativePath, accessMode, + shareMode, NULL, createMode, flags, NULL); Tcl_DStringFree(&ds); if (handle == INVALID_HANDLE_VALUE) { DWORD err; err = GetLastError(); - if ((err & 0xFFFFL) == ERROR_OPEN_FAILED) { + if ((err & 0xffffL) == ERROR_OPEN_FAILED) { err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND; } - Tcl_WinConvertError(err); + TclWinConvertError(err); return NULL; } @@ -659,7 +669,7 @@ TclpCreateTempFile( return NULL; } - handle = CreateFileW(name, + handle = (*tclWinProcs->createFileProc)((TCHAR *) name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL); if (handle == INVALID_HANDLE_VALUE) { @@ -679,7 +689,7 @@ TclpCreateTempFile( * Convert the contents from UTF to native encoding */ - native = Tcl_UtfToExternalDString(NULL, contents, TCL_INDEX_NONE, &dstring); + native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); toCopy = Tcl_DStringLength(&dstring); for (p = native; toCopy > 0; p++, toCopy--) { @@ -719,9 +729,9 @@ TclpCreateTempFile( Tcl_DStringFree(&dstring); } - Tcl_WinConvertError(GetLastError()); + TclWinConvertError(GetLastError()); CloseHandle(handle); - DeleteFileW(name); + (*tclWinProcs->deleteFileProc)((TCHAR *) name); return NULL; } @@ -750,7 +760,7 @@ TclpTempFileName(void) return NULL; } - return TclpNativeToNormalized(fileName); + return TclpNativeToNormalized((ClientData) fileName); } /* @@ -784,7 +794,7 @@ TclpCreatePipe( return 1; } - Tcl_WinConvertError(GetLastError()); + TclWinConvertError(GetLastError()); return 0; } @@ -825,8 +835,8 @@ TclpCloseFile( && (GetStdHandle(STD_ERROR_HANDLE) != filePtr->handle))) { if (filePtr->handle != NULL && CloseHandle(filePtr->handle) == FALSE) { - Tcl_WinConvertError(GetLastError()); - ckfree(filePtr); + TclWinConvertError(GetLastError()); + ckfree((char *) filePtr); return -1; } } @@ -836,7 +846,7 @@ TclpCloseFile( Tcl_Panic("TclpCloseFile: unexpected file type"); } - ckfree(filePtr); + ckfree((char *) filePtr); return 0; } @@ -851,7 +861,7 @@ TclpCloseFile( * Results: * Returns the process id for the child process. If the pid was not known * by Tcl, either because the pid was not created by Tcl or the child - * process has already been reaped, TCL_INDEX_NONE is returned. + * process has already been reaped, -1 is returned. * * Side effects: * None. @@ -859,7 +869,7 @@ TclpCloseFile( *-------------------------------------------------------------------------- */ -Tcl_Size +int TclpGetPid( Tcl_Pid pid) /* The HANDLE of the child process. */ { @@ -869,13 +879,13 @@ TclpGetPid( Tcl_MutexLock(&pipeMutex); for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->dwProcessId == PTR2UINT(pid)) { + if (infoPtr->hProcess == (HANDLE) pid) { Tcl_MutexUnlock(&pipeMutex); return infoPtr->dwProcessId; } } Tcl_MutexUnlock(&pipeMutex); - return TCL_INDEX_NONE; + return (unsigned long) -1; } /* @@ -890,7 +900,7 @@ TclpGetPid( * * The complete Windows search path is searched to find the specified * executable. If an executable by the given name is not found, - * automatically tries appending standard extensions to the + * automatically tries appending ".com", ".exe", and ".bat" to the * executable name. * * Results: @@ -911,7 +921,7 @@ TclpCreateProcess( * occurred when creating the child process. * Error messages from the child process * itself are sent to errorFile. */ - Tcl_Size argc, /* Number of arguments in following array. */ + int argc, /* Number of arguments in following array. */ const char **argv, /* Array of argument strings. argv[0] contains * the name of the executable converted to * native format (using the @@ -923,12 +933,12 @@ TclpCreateProcess( * receive no standard input. */ TclFile outputFile, /* If non-NULL, gives the file that receives * output from the child process. If - * outputFile file is not writable or is + * outputFile file is not writeable or is * NULL, output from the child will be * discarded. */ TclFile errorFile, /* If non-NULL, gives the file that receives * errors from the child process. If errorFile - * file is not writable or is NULL, errors + * file is not writeable or is NULL, errors * from the child will be discarded. errorFile * may be the same as outputFile. */ Tcl_Pid *pidPtr) /* If this function is successful, pidPtr is @@ -936,12 +946,12 @@ TclpCreateProcess( * process. */ { int result, applType, createFlags; - Tcl_DString cmdLine; /* Complete command line (WCHAR). */ - STARTUPINFOW startInfo; + Tcl_DString cmdLine; /* Complete command line (TCHAR). */ + STARTUPINFOA startInfo; PROCESS_INFORMATION procInfo; SECURITY_ATTRIBUTES secAtts; HANDLE hProcess, h, inputHandle, outputHandle, errorHandle; - char execPath[MAX_PATH * 3]; + char execPath[MAX_PATH * TCL_UTF_MAX]; WinFile *filePtr; PipeInit(); @@ -1026,10 +1036,9 @@ TclpCreateProcess( 0, TRUE, DUPLICATE_SAME_ACCESS); } if (startInfo.hStdInput == INVALID_HANDLE_VALUE) { - Tcl_WinConvertError(GetLastError()); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't duplicate input handle: %s", - Tcl_PosixError(interp))); + TclWinConvertError(GetLastError()); + Tcl_AppendResult(interp, "couldn't duplicate input handle: ", + Tcl_PosixError(interp), (char *) NULL); goto end; } @@ -1048,17 +1057,23 @@ TclpCreateProcess( * sink. */ - startInfo.hStdOutput = CreateFileW(L"NUL:", GENERIC_WRITE, 0, - &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) + && (applType == APPL_DOS)) { + if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) { + CloseHandle(h); + } + } else { + startInfo.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0, + &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } } else { DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); } if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) { - Tcl_WinConvertError(GetLastError()); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't duplicate output handle: %s", - Tcl_PosixError(interp))); + TclWinConvertError(GetLastError()); + Tcl_AppendResult(interp, "couldn't duplicate output handle: ", + Tcl_PosixError(interp), (char *) NULL); goto end; } @@ -1068,17 +1083,16 @@ TclpCreateProcess( * sink. */ - startInfo.hStdError = CreateFileW(L"NUL:", GENERIC_WRITE, 0, + startInfo.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0, &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } else { DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS); } if (startInfo.hStdError == INVALID_HANDLE_VALUE) { - Tcl_WinConvertError(GetLastError()); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't duplicate error handle: %s", - Tcl_PosixError(interp))); + TclWinConvertError(GetLastError()); + Tcl_AppendResult(interp, "couldn't duplicate error handle: ", + Tcl_PosixError(interp), (char *) NULL); goto end; } @@ -1095,23 +1109,110 @@ TclpCreateProcess( * detached processes. The GUI window will still pop up to the foreground. */ - if (HasConsole()) { - createFlags = 0; - } else if (applType == APPL_DOS) { - /* - * Under NT, 16-bit DOS applications will not run unless they can - * be attached to a console. If we are running without a console, - * run the 16-bit program as an normal process inside of a hidden - * console application, and then run that hidden console as a - * detached process. - */ + if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) { + if (HasConsole()) { + createFlags = 0; + } else if (applType == APPL_DOS) { + /* + * Under NT, 16-bit DOS applications will not run unless they can + * be attached to a console. If we are running without a console, + * run the 16-bit program as an normal process inside of a hidden + * console application, and then run that hidden console as a + * detached process. + */ - startInfo.wShowWindow = SW_HIDE; - startInfo.dwFlags |= STARTF_USESHOWWINDOW; - createFlags = CREATE_NEW_CONSOLE; - TclDStringAppendLiteral(&cmdLine, "cmd.exe /c"); + startInfo.wShowWindow = SW_HIDE; + startInfo.dwFlags |= STARTF_USESHOWWINDOW; + createFlags = CREATE_NEW_CONSOLE; + Tcl_DStringAppend(&cmdLine, "cmd.exe /c", -1); + } else { + createFlags = DETACHED_PROCESS; + } } else { - createFlags = DETACHED_PROCESS; + if (HasConsole()) { + createFlags = 0; + } else { + createFlags = DETACHED_PROCESS; + } + + if (applType == APPL_DOS) { + /* + * Under Windows 95, 16-bit DOS applications do not work well with + * pipes: + * + * 1. EOF on a pipe between a detached 16-bit DOS application and + * another application is not seen at the other end of the pipe, + * so the listening process blocks forever on reads. This inablity + * to detect EOF happens when either a 16-bit app or the 32-bit + * app is the listener. + * + * 2. If a 16-bit DOS application (detached or not) blocks when + * writing to a pipe, it will never wake up again, and it + * eventually brings the whole system down around it. + * + * The 16-bit application is run as a normal process inside of a + * hidden helper console app, and this helper may be run as a + * detached process. If any of the stdio handles is a pipe, the + * helper application accumulates information into temp files and + * forwards it to or from the DOS application as appropriate. + * This means that DOS apps must receive EOF from a stdin pipe + * before they will actually begin, and must finish generating + * stdout or stderr before the data will be sent to the next stage + * of the pipe. + * + * The helper app should be located in the same directory as the + * tcl dll. + */ + Tcl_Obj *tclExePtr, *pipeDllPtr; + char *start, *end; + int i, fileExists; + Tcl_DString pipeDll; + + if (createFlags != 0) { + startInfo.wShowWindow = SW_HIDE; + startInfo.dwFlags |= STARTF_USESHOWWINDOW; + createFlags = CREATE_NEW_CONSOLE; + } + + Tcl_DStringInit(&pipeDll); + Tcl_DStringAppend(&pipeDll, TCL_PIPE_DLL, -1); + tclExePtr = TclGetObjNameOfExecutable(); + Tcl_IncrRefCount(tclExePtr); + start = Tcl_GetStringFromObj(tclExePtr, &i); + for (end = start + (i-1); end > start; end--) { + if (*end == '/') { + break; + } + } + if (*end != '/') { + Tcl_AppendResult(interp, "no / in executable path name \"", + start, "\"", (char *) NULL); + Tcl_DecrRefCount(tclExePtr); + Tcl_DStringFree(&pipeDll); + goto end; + } + i = (end - start) + 1; + pipeDllPtr = Tcl_NewStringObj(start, i); + Tcl_AppendToObj(pipeDllPtr, Tcl_DStringValue(&pipeDll), -1); + Tcl_IncrRefCount(pipeDllPtr); + if (Tcl_FSConvertToPathType(interp, pipeDllPtr) != TCL_OK) { + Tcl_Panic("Tcl_FSConvertToPathType failed"); + } + fileExists = (Tcl_FSAccess(pipeDllPtr, F_OK) == 0); + if (!fileExists) { + Tcl_AppendResult(interp, "Tcl pipe dll \"", + Tcl_DStringValue(&pipeDll), "\" not found", + (char *) NULL); + Tcl_DecrRefCount(tclExePtr); + Tcl_DecrRefCount(pipeDllPtr); + Tcl_DStringFree(&pipeDll); + goto end; + } + Tcl_DStringAppend(&cmdLine, Tcl_DStringValue(&pipeDll), -1); + Tcl_DecrRefCount(tclExePtr); + Tcl_DecrRefCount(pipeDllPtr); + Tcl_DStringFree(&pipeDll); + } } /* @@ -1134,12 +1235,12 @@ TclpCreateProcess( BuildCommandLine(execPath, argc, argv, &cmdLine); - if (CreateProcessW(NULL, (WCHAR *) Tcl_DStringValue(&cmdLine), - NULL, NULL, TRUE, (DWORD) createFlags, NULL, NULL, &startInfo, - &procInfo) == 0) { - Tcl_WinConvertError(GetLastError()); - Tcl_SetObjResult(interp, Tcl_ObjPrintf("couldn't execute \"%s\": %s", - argv[0], Tcl_PosixError(interp))); + if ((*tclWinProcs->createProcessProc)(NULL, + (TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE, + (DWORD) createFlags, NULL, NULL, &startInfo, &procInfo) == 0) { + TclWinConvertError(GetLastError()); + Tcl_AppendResult(interp, "couldn't execute \"", argv[0], + "\": ", Tcl_PosixError(interp), (char *) NULL); goto end; } @@ -1156,14 +1257,14 @@ TclpCreateProcess( * will be created for each process but the previous instances may not be * cleaned up. This results in a significant virtual memory loss each time * the process is spawned. If there is a WaitForInputIdle() call between - * CreateProcessW() and CloseHandle(), the problem does not occur." PSS ID + * CreateProcess() and CloseHandle(), the problem does not occur." PSS ID * Number: Q124121 */ WaitForInputIdle(procInfo.hProcess, 5000); CloseHandle(procInfo.hThread); - *pidPtr = (Tcl_Pid)INT2PTR(procInfo.dwProcessId); + *pidPtr = (Tcl_Pid) procInfo.hProcess; if (*pidPtr != 0) { TclWinAddProcess(procInfo.hProcess, procInfo.dwProcessId); } @@ -1205,7 +1306,7 @@ HasConsole(void) { HANDLE handle; - handle = CreateFileW(L"CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, + handle = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { @@ -1260,22 +1361,22 @@ ApplicationType( { int applType, i, nameLen, found; HANDLE hFile; - WCHAR *rest; + TCHAR *rest; char *ext; char buf[2]; DWORD attr, read; IMAGE_DOS_HEADER header; Tcl_DString nameBuf, ds; - const WCHAR *nativeName; + const TCHAR *nativeName; WCHAR nativeFullPath[MAX_PATH]; - static const char extensions[][5] = {"", ".com", ".exe", ".bat", ".cmd"}; + static const char extensions[][5] = {"", ".com", ".exe", ".bat"}; /* * Look for the program as an external program. First try the name as it - * is, then try adding .com, .exe, .bat and .cmd, in that order, to the name, + * is, then try adding .com, .exe, and .bat, in that order, to the name, * looking for an executable. * - * Using the raw SearchPathW() function doesn't do quite what is necessary. + * Using the raw SearchPath() function doesn't do quite what is necessary. * If the name of the executable already contains a '.' character, it will * not try appending the specified extension when searching (in other * words, SearchPath will not find the program "a.b.exe" if the arguments @@ -1285,17 +1386,16 @@ ApplicationType( applType = APPL_NONE; Tcl_DStringInit(&nameBuf); - Tcl_DStringAppend(&nameBuf, originalName, TCL_INDEX_NONE); + Tcl_DStringAppend(&nameBuf, originalName, -1); nameLen = Tcl_DStringLength(&nameBuf); for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { Tcl_DStringSetLength(&nameBuf, nameLen); - Tcl_DStringAppend(&nameBuf, extensions[i], TCL_INDEX_NONE); - Tcl_DStringInit(&ds); - nativeName = Tcl_UtfToWCharDString(Tcl_DStringValue(&nameBuf), + Tcl_DStringAppend(&nameBuf, extensions[i], -1); + nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf), Tcl_DStringLength(&nameBuf), &ds); - found = SearchPathW(NULL, nativeName, NULL, MAX_PATH, - nativeFullPath, &rest); + found = (*tclWinProcs->searchPathProc)(NULL, nativeName, NULL, + MAX_PATH, nativeFullPath, &rest); Tcl_DStringFree(&ds); if (found == 0) { continue; @@ -1306,22 +1406,20 @@ ApplicationType( * known type. */ - attr = GetFileAttributesW(nativeFullPath); - if ((attr == 0xFFFFFFFF) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { + attr = (*tclWinProcs->getFileAttributesProc)((TCHAR *) nativeFullPath); + if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { continue; } - Tcl_DStringInit(&ds); - strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds)); + strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); Tcl_DStringFree(&ds); ext = strrchr(fullName, '.'); - if ((ext != NULL) && - (strcasecmp(ext, ".cmd") == 0 || strcasecmp(ext, ".bat") == 0)) { + if ((ext != NULL) && (strcasecmp(ext, ".bat") == 0)) { applType = APPL_DOS; break; } - hFile = CreateFileW(nativeFullPath, + hFile = (*tclWinProcs->createFileProc)((TCHAR *) nativeFullPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { @@ -1387,13 +1485,13 @@ ApplicationType( Tcl_DStringFree(&nameBuf); if (applType == APPL_NONE) { - Tcl_WinConvertError(GetLastError()); - Tcl_SetObjResult(interp, Tcl_ObjPrintf("couldn't execute \"%s\": %s", - originalName, Tcl_PosixError(interp))); + TclWinConvertError(GetLastError()); + Tcl_AppendResult(interp, "couldn't execute \"", originalName, + "\": ", Tcl_PosixError(interp), (char *) NULL); return APPL_NONE; } - if (applType == APPL_WIN3X) { + if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { /* * Replace long path name of executable with short path name for * 16-bit applications. Otherwise the application may not be able to @@ -1401,9 +1499,9 @@ ApplicationType( * application name from the arguments. */ - GetShortPathNameW(nativeFullPath, nativeFullPath, MAX_PATH); - Tcl_DStringInit(&ds); - strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds)); + (*tclWinProcs->getShortPathNameProc)((TCHAR *) nativeFullPath, + nativeFullPath, MAX_PATH); + strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); Tcl_DStringFree(&ds); } return applType; @@ -1415,7 +1513,7 @@ ApplicationType( * BuildCommandLine -- * * The command line arguments are stored in linePtr separated by spaces, - * in a form that CreateProcessW() understands. Special characters in + * in a form that CreateProcess() understands. Special characters in * individual arguments from argv[] must be quoted when being stored in * cmdLine. * @@ -1428,144 +1526,18 @@ ApplicationType( *---------------------------------------------------------------------- */ -static const char * -BuildCmdLineBypassBS( - const char *current, - const char **bspos) -{ - /* - * Mark first backslash position. - */ - - if (!*bspos) { - *bspos = current; - } - do { - current++; - } while (*current == '\\'); - return current; -} - -static void -QuoteCmdLineBackslash( - Tcl_DString *dsPtr, - const char *start, - const char *current, - const char *bspos) -{ - if (!bspos) { - if (current > start) { /* part before current (special) */ - Tcl_DStringAppend(dsPtr, start, (int) (current - start)); - } - } else { - if (bspos > start) { /* part before first backslash */ - Tcl_DStringAppend(dsPtr, start, (int) (bspos - start)); - } - while (bspos++ < current) { /* each backslash twice */ - TclDStringAppendLiteral(dsPtr, "\\\\"); - } - } -} - -static const char * -QuoteCmdLinePart( - Tcl_DString *dsPtr, - const char *start, - const char *special, - const char *specMetaChars, - const char **bspos) -{ - if (!*bspos) { - /* - * Rest before special (before quote). - */ - - QuoteCmdLineBackslash(dsPtr, start, special, NULL); - start = special; - } else { - /* - * Rest before first backslash and backslashes into new quoted block. - */ - - QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL); - start = *bspos; - } - - /* - * escape all special chars enclosed in quotes like `"..."`, note that - * here we don't must escape `\` (with `\`), because it's outside of the - * main quotes, so `\` remains `\`, but important - not at end of part, - * because results as before the quote, so `%\%\` should be escaped as - * `"%\%"\\`). - */ - - TclDStringAppendLiteral(dsPtr, "\""); /* opening escape quote-char */ - do { - *bspos = NULL; - special++; - if (*special == '\\') { - /* - * Bypass backslashes (and mark first backslash position). - */ - - special = BuildCmdLineBypassBS(special, bspos); - if (*special == '\0') { - break; - } - } - } while (*special && strchr(specMetaChars, *special)); - if (!*bspos) { - /* - * Unescaped rest before quote. - */ - - QuoteCmdLineBackslash(dsPtr, start, special, NULL); - } else { - /* - * Unescaped rest before first backslash (rather belongs to the main - * block). - */ - - QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL); - } - TclDStringAppendLiteral(dsPtr, "\""); /* closing escape quote-char */ - return special; -} - static void BuildCommandLine( const char *executable, /* Full path of executable (including * extension). Replacement for argv[0]. */ - Tcl_Size argc, /* Number of arguments. */ + int argc, /* Number of arguments. */ const char **argv, /* Argument strings in UTF. */ Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the - * command line (WCHAR). */ + * command line (TCHAR). */ { - const char *arg, *start, *special, *bspos; - int quote = 0; - Tcl_Size i; + const char *arg, *start, *special; + int quote, i; Tcl_DString ds; -#ifdef TCL_WIN_PIPE_FULLESC - /* full escape inclusive %-subst avoidance */ - static const char specMetaChars[] = "&|^<>!()%"; - /* Characters to enclose in quotes if unpaired - * quote flag set. */ - static const char specMetaChars2[] = "%"; - /* Character to enclose in quotes in any case - * (regardless of unpaired-flag). */ -#else - /* escape considering quotation only (no %-subst avoidance) */ - static const char specMetaChars[] = "&|^<>!()"; - /* Characters to enclose in quotes if unpaired - * quote flag set. */ -#endif - /* - * Quote flags: - * CL_ESCAPE - escape argument; - * CL_QUOTE - enclose in quotes; - * CL_UNPAIRED - previous arguments chain contains unpaired quote-char; - */ - enum {CL_ESCAPE = 1, CL_QUOTE = 2, CL_UNPAIRED = 4}; Tcl_DStringInit(&ds); @@ -1573,9 +1545,9 @@ BuildCommandLine( * Prime the path. Add a space separator if we were primed with something. */ - TclDStringAppendDString(&ds, linePtr); + Tcl_DStringAppend(&ds, Tcl_DStringValue(linePtr), -1); if (Tcl_DStringLength(linePtr) > 0) { - TclDStringAppendLiteral(&ds, " "); + Tcl_DStringAppend(&ds, " ", 1); } for (i = 0; i < argc; i++) { @@ -1583,166 +1555,68 @@ BuildCommandLine( arg = executable; } else { arg = argv[i]; - TclDStringAppendLiteral(&ds, " "); + Tcl_DStringAppend(&ds, " ", 1); } - quote &= ~(CL_ESCAPE|CL_QUOTE); /* reset escape flags */ - bspos = NULL; + quote = 0; if (arg[0] == '\0') { - quote = CL_QUOTE; + quote = 1; } else { - for (start = arg; - *start != '\0' && - (quote & (CL_ESCAPE|CL_QUOTE)) != (CL_ESCAPE|CL_QUOTE); - start++) { - if (*start & 0x80) { - continue; - } - if (TclIsSpaceProc(*start)) { - quote |= CL_QUOTE; /* quote only */ - if (bspos) { /* if backslash found, escape & quote */ - quote |= CL_ESCAPE; - break; - } - continue; - } - if (strchr(specMetaChars, *start)) { - quote |= (CL_ESCAPE|CL_QUOTE); /* escape & quote */ + int count; + Tcl_UniChar ch; + for (start = arg; *start != '\0'; start += count) { + count = Tcl_UtfToUniChar(start, &ch); + if (Tcl_UniCharIsSpace(ch)) { /* INTL: ISO space. */ + quote = 1; break; } - if (*start == '"') { - quote |= CL_ESCAPE; /* escape only */ - continue; - } - if (*start == '\\') { - bspos = start; - if (quote & CL_QUOTE) { /* if quote, escape & quote */ - quote |= CL_ESCAPE; - break; - } - continue; - } } - bspos = NULL; - } - if (quote & CL_QUOTE) { - /* - * Start of argument (main opening quote-char). - */ - - TclDStringAppendLiteral(&ds, "\""); } - if (!(quote & CL_ESCAPE)) { - /* - * Nothing to escape. - */ - - Tcl_DStringAppend(&ds, arg, TCL_INDEX_NONE); - } else { - start = arg; - for (special = arg; *special != '\0'; ) { - /* - * Position of `\` is important before quote or at end (equal - * `\"` because quoted). - */ - - if (*special == '\\') { - /* - * Bypass backslashes (and mark first backslash position) - */ + if (quote) { + Tcl_DStringAppend(&ds, "\"", 1); + } + start = arg; + for (special = arg; ; ) { + if ((*special == '\\') && (special[1] == '\\' || + special[1] == '"' || (quote && special[1] == '\0'))) { + Tcl_DStringAppend(&ds, start, (int) (special - start)); + start = special; + while (1) { + special++; + if (*special == '"' || (quote && *special == '\0')) { + /* + * N backslashes followed a quote -> insert N * 2 + 1 + * backslashes then a quote. + */ - special = BuildCmdLineBypassBS(special, &bspos); - if (*special == '\0') { + Tcl_DStringAppend(&ds, start, + (int) (special - start)); + break; + } + if (*special != '\\') { break; } } - /* ["] */ - if (*special == '"') { - /* - * Invert the unpaired flag - observe unpaired quotes - */ - - quote ^= CL_UNPAIRED; - - /* - * Add part before (and escape backslashes before quote). - */ - - QuoteCmdLineBackslash(&ds, start, special, bspos); - bspos = NULL; - - /* - * Escape using backslash - */ - - TclDStringAppendLiteral(&ds, "\\\""); - start = ++special; - continue; - } - - /* - * Unpaired (escaped) quote causes special handling on - * meta-chars - */ - - if ((quote & CL_UNPAIRED) && strchr(specMetaChars, *special)) { - special = QuoteCmdLinePart(&ds, start, special, - specMetaChars, &bspos); - - /* - * Start to current or first backslash - */ - - start = !bspos ? special : bspos; - continue; - } -#ifdef TCL_WIN_PIPE_FULLESC - /* - * Special case for % - should be enclosed always (paired - * also) - */ - - if (strchr(specMetaChars2, *special)) { - special = QuoteCmdLinePart(&ds, start, special, - specMetaChars2, &bspos); - - /* - * Start to current or first backslash. - */ - - start = !bspos ? special : bspos; - continue; - } -#endif - - /* - * Other not special (and not meta) character - */ - - bspos = NULL; /* reset last backslash position (not - * interesting) */ - special++; + Tcl_DStringAppend(&ds, start, (int) (special - start)); + start = special; } - - /* - * Rest of argument (and escape backslashes before closing main - * quote) - */ - - QuoteCmdLineBackslash(&ds, start, special, - (quote & CL_QUOTE) ? bspos : NULL); + if (*special == '"') { + Tcl_DStringAppend(&ds, start, (int) (special - start)); + Tcl_DStringAppend(&ds, "\\\"", 2); + start = special + 1; + } + if (*special == '\0') { + break; + } + special++; } - if (quote & CL_QUOTE) { - /* - * End of argument (main closing quote-char) - */ - - TclDStringAppendLiteral(&ds, "\""); + Tcl_DStringAppend(&ds, start, (int) (special - start)); + if (quote) { + Tcl_DStringAppend(&ds, "\"", 1); } } Tcl_DStringFree(linePtr); - Tcl_DStringInit(linePtr); - Tcl_UtfToWCharDString(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr); + Tcl_WinUtfToTChar(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr); Tcl_DStringFree(&ds); } @@ -1769,11 +1643,12 @@ TclpCreateCommandChannel( TclFile writeFile, /* If non-null, gives the file for writing. */ TclFile errorFile, /* If non-null, gives the file where errors * can be read. */ - Tcl_Size numPids, /* The number of pids in the pid array. */ + int numPids, /* The number of pids in the pid array. */ Tcl_Pid *pidPtr) /* An array of process identifiers. */ { char channelName[16 + TCL_INTEGER_SPACE]; - PipeInfo *infoPtr = (PipeInfo *)ckalloc(sizeof(PipeInfo)); + DWORD id; + PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo)); PipeInit(); @@ -1788,7 +1663,7 @@ TclpCreateCommandChannel( infoPtr->writeBuf = 0; infoPtr->writeBufLen = 0; infoPtr->writeError = 0; - infoPtr->channel = NULL; + infoPtr->channel = (Tcl_Channel) NULL; infoPtr->validMask = 0; @@ -1799,14 +1674,14 @@ TclpCreateCommandChannel( * Start the background reader thread. */ - infoPtr->readable = CreateEventW(NULL, TRUE, TRUE, NULL); + 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, - TclPipeThreadCreateTI(&infoPtr->readTI, infoPtr, infoPtr->readable), - 0, NULL); + infoPtr, 0, &id); SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_READABLE; } else { - infoPtr->readTI = NULL; infoPtr->readThread = 0; } if (writeFile != NULL) { @@ -1814,15 +1689,13 @@ TclpCreateCommandChannel( * Start the background writer thread. */ - infoPtr->writable = CreateEventW(NULL, TRUE, TRUE, NULL); + 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, - TclPipeThreadCreateTI(&infoPtr->writeTI, infoPtr, infoPtr->writable), - 0, NULL); - SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST); + infoPtr, 0, &id); + SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); infoPtr->validMask |= TCL_WRITABLE; - } else { - infoPtr->writeTI = NULL; - infoPtr->writeThread = 0; } /* @@ -1832,9 +1705,9 @@ TclpCreateCommandChannel( * unique, in case channels share handles (stdin/stdout). */ - TclWinGenerateChannelName(channelName, "file", infoPtr); + sprintf(channelName, "file%" TCL_I_MODIFIER "x", (size_t)infoPtr); infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, - infoPtr, infoPtr->validMask); + (ClientData) infoPtr, infoPtr->validMask); /* * Pipes have AUTO translation mode on Windows and ^Z eof char, which @@ -1842,58 +1715,16 @@ TclpCreateCommandChannel( * Windows programs that expect a ^Z at EOF. */ - Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto"); - Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\x1A {}"); + Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, + "-translation", "auto"); + Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, + "-eofchar", "\032 {}"); return infoPtr->channel; } /* *---------------------------------------------------------------------- * - * Tcl_CreatePipe -- - * - * System dependent interface to create a pipe for the [chan pipe] - * command. Stolen from TclX. - * - * Results: - * TCL_OK or TCL_ERROR. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_CreatePipe( - Tcl_Interp *interp, /* Errors returned in result.*/ - Tcl_Channel *rchan, /* Where to return the read side. */ - Tcl_Channel *wchan, /* Where to return the write side. */ - TCL_UNUSED(int) /*flags*/) /* Reserved for future use. */ -{ - HANDLE readHandle, writeHandle; - SECURITY_ATTRIBUTES sec; - - sec.nLength = sizeof(SECURITY_ATTRIBUTES); - sec.lpSecurityDescriptor = NULL; - sec.bInheritHandle = FALSE; - - if (!CreatePipe(&readHandle, &writeHandle, &sec, 0)) { - Tcl_WinConvertError(GetLastError()); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "pipe creation failed: %s", Tcl_PosixError(interp))); - return TCL_ERROR; - } - - *rchan = Tcl_MakeFileChannel((void *) readHandle, TCL_READABLE); - Tcl_RegisterChannel(interp, *rchan); - - *wchan = Tcl_MakeFileChannel((void *) writeHandle, TCL_WRITABLE); - Tcl_RegisterChannel(interp, *wchan); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * * TclGetAndDetachPids -- * * Stores a list of the command PIDs for a command channel in the @@ -1915,8 +1746,8 @@ TclGetAndDetachPids( { PipeInfo *pipePtr; const Tcl_ChannelType *chanTypePtr; - Tcl_Obj *pidsObj, *elemPtr; - TCL_HASH_TYPE i; + int i; + char buf[TCL_INTEGER_SPACE]; /* * Punt if the channel is not a command channel. @@ -1927,16 +1758,14 @@ TclGetAndDetachPids( return; } - pipePtr = (PipeInfo *)Tcl_GetChannelInstanceData(chan); - TclNewObj(pidsObj); + pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); for (i = 0; i < pipePtr->numPids; i++) { - TclNewIntObj(elemPtr, TclpGetPid(pipePtr->pidPtr[i])); - Tcl_ListObjAppendElement(NULL, pidsObj, elemPtr); - Tcl_DetachPids(1, &pipePtr->pidPtr[i]); + wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); + Tcl_AppendElement(interp, buf); + Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); } - Tcl_SetObjResult(interp, pidsObj); if (pipePtr->numPids > 0) { - ckfree(pipePtr->pidPtr); + ckfree((char *) pipePtr->pidPtr); pipePtr->numPids = 0; } } @@ -1959,7 +1788,7 @@ TclGetAndDetachPids( static int PipeBlockModeProc( - void *instanceData, /* Instance data for channel. */ + ClientData instanceData, /* Instance data for channel. */ int mode) /* TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */ { @@ -1998,7 +1827,7 @@ PipeBlockModeProc( static int PipeClose2Proc( - void *instanceData, /* Pointer to PipeInfo structure. */ + ClientData instanceData, /* Pointer to PipeInfo structure. */ Tcl_Interp *interp, /* For error reporting. */ int flags) /* Flags that indicate which side to close. */ { @@ -2007,12 +1836,12 @@ PipeClose2Proc( int errorCode, result; PipeInfo *infoPtr, **nextPtrPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - int inExit = (TclInExit() || TclInThreadExit()); + DWORD exitCode; 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 @@ -2020,10 +1849,55 @@ 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) { @@ -2032,34 +1906,66 @@ 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, there should be no pending write operations. + */ + + WaitForSingleObject(pipePtr->writable, INFINITE); /* - * Wait for the writer thread to finish the current buffer, then - * terminate the thread and close the handles. If the channel is - * nonblocking or may block during exit, bail out since the worker - * thread is not interruptible and we want TIP#398-fast-exit. + * The thread may already have closed on it's own. Check its exit + * code. */ - if ((pipePtr->flags & PIPE_ASYNC) && inExit) { - /* give it a chance to leave honorably */ - TclPipeThreadStopSignal(&pipePtr->writeTI, pipePtr->writable); + GetExitCodeThread(pipePtr->writeThread, &exitCode); - if (WaitForSingleObject(pipePtr->writable, 20) == WAIT_TIMEOUT) { - return EWOULDBLOCK; - } + if (exitCode == STILL_ACTIVE) { + /* + * Set the stop event so that if the reader thread is blocked + * in PipeReaderThread on WaitForMultipleEvents, it will exit + * cleanly. + */ - } else { + SetEvent(pipePtr->stopWriter); - WaitForSingleObject(pipePtr->writable, inExit ? 5000 : INFINITE); + /* + * Wait at most 20 milliseconds for the reader thread to + * close. + */ - } + 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. + */ - TclPipeThreadStop(&pipePtr->writeTI, pipePtr->writeThread); + Tcl_MutexLock(&pipeMutex); + + /* BUG: this leaks memory */ + TerminateThread(pipePtr->writeThread, 0); + Tcl_MutexUnlock(&pipeMutex); + } + } - CloseHandle(pipePtr->writable); CloseHandle(pipePtr->writeThread); + CloseHandle(pipePtr->writable); + CloseHandle(pipePtr->startWriter); + CloseHandle(pipePtr->stopWriter); pipePtr->writeThread = NULL; } if (TclpCloseFile(pipePtr->writeFile) != 0) { @@ -2094,7 +2000,7 @@ PipeClose2Proc( } } - if ((pipePtr->flags & PIPE_ASYNC) || inExit) { + if ((pipePtr->flags & PIPE_ASYNC) || TclInExit()) { /* * 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 @@ -2119,11 +2025,12 @@ PipeClose2Proc( */ if (pipePtr->errorFile) { - WinFile *filePtr = (WinFile *) pipePtr->errorFile; + WinFile *filePtr; - errChan = Tcl_MakeFileChannel((void *)filePtr->handle, + filePtr = (WinFile*)pipePtr->errorFile; + errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle, TCL_READABLE); - ckfree(filePtr); + ckfree((char *) filePtr); } else { errChan = NULL; } @@ -2133,14 +2040,14 @@ PipeClose2Proc( } if (pipePtr->numPids > 0) { - ckfree(pipePtr->pidPtr); + ckfree((char *) pipePtr->pidPtr); } if (pipePtr->writeBuf != NULL) { ckfree(pipePtr->writeBuf); } - ckfree(pipePtr); + ckfree((char*) pipePtr); if (errorCode == 0) { return result; @@ -2168,7 +2075,7 @@ PipeClose2Proc( static int PipeInputProc( - void *instanceData, /* Pipe state. */ + ClientData instanceData, /* Pipe state. */ char *buf, /* Where to store data read. */ int bufSize, /* How much space is available in the * buffer? */ @@ -2233,7 +2140,7 @@ PipeInputProc( return bytesRead; } - Tcl_WinConvertError(GetLastError()); + TclWinConvertError(GetLastError()); if (errno == EPIPE) { infoPtr->readFlags |= PIPE_EOF; return 0; @@ -2262,7 +2169,7 @@ PipeInputProc( static int PipeOutputProc( - void *instanceData, /* Pipe state. */ + ClientData instanceData, /* Pipe state. */ const char *buf, /* The data buffer. */ int toWrite, /* How many bytes to write? */ int *errorCode) /* Where to store error code. */ @@ -2272,18 +2179,14 @@ PipeOutputProc( DWORD bytesWritten, timeout; *errorCode = 0; - - /* avoid blocking if pipe-thread exited */ - timeout = ((infoPtr->flags & PIPE_ASYNC) - || !TclPipeThreadIsAlive(&infoPtr->writeTI) - || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; + timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE; if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { /* * The writer thread is blocked waiting for a write to complete and * the channel is in non-blocking mode. */ - errno = EWOULDBLOCK; + errno = EAGAIN; goto error; } @@ -2292,7 +2195,7 @@ PipeOutputProc( */ if (infoPtr->writeError) { - Tcl_WinConvertError(infoPtr->writeError); + TclWinConvertError(infoPtr->writeError); infoPtr->writeError = 0; goto error; } @@ -2312,12 +2215,12 @@ PipeOutputProc( ckfree(infoPtr->writeBuf); } infoPtr->writeBufLen = toWrite; - infoPtr->writeBuf = (char *)ckalloc(toWrite); + infoPtr->writeBuf = ckalloc((unsigned int) toWrite); } - memcpy(infoPtr->writeBuf, buf, toWrite); + memcpy(infoPtr->writeBuf, buf, (size_t) toWrite); infoPtr->toWrite = toWrite; ResetEvent(infoPtr->writable); - TclPipeThreadSignal(&infoPtr->writeTI); + SetEvent(infoPtr->startWriter); bytesWritten = toWrite; } else { /* @@ -2327,7 +2230,7 @@ PipeOutputProc( if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) { - Tcl_WinConvertError(GetLastError()); + TclWinConvertError(GetLastError()); goto error; } } @@ -2444,7 +2347,7 @@ PipeEventProc( static void PipeWatchProc( - void *instanceData, /* Pipe state. */ + ClientData instanceData, /* Pipe state. */ int mask) /* What events to watch for, OR-ed combination * of TCL_READABLE, TCL_WRITABLE and * TCL_EXCEPTION. */ @@ -2462,7 +2365,6 @@ PipeWatchProc( infoPtr->watchMask = mask & infoPtr->validMask; if (infoPtr->watchMask) { Tcl_Time blockTime = { 0, 0 }; - if (!oldMask) { infoPtr->nextPtr = tsdPtr->firstPipePtr; tsdPtr->firstPipePtr = infoPtr; @@ -2506,21 +2408,21 @@ PipeWatchProc( static int PipeGetHandleProc( - void *instanceData, /* The pipe state. */ + ClientData instanceData, /* The pipe state. */ int direction, /* TCL_READABLE or TCL_WRITABLE */ - void **handlePtr) /* Where to store the handle. */ + ClientData *handlePtr) /* Where to store the handle. */ { PipeInfo *infoPtr = (PipeInfo *) instanceData; WinFile *filePtr; if (direction == TCL_READABLE && infoPtr->readFile) { filePtr = (WinFile*) infoPtr->readFile; - *handlePtr = (void *) filePtr->handle; + *handlePtr = (ClientData) filePtr->handle; return TCL_OK; } if (direction == TCL_WRITABLE && infoPtr->writeFile) { filePtr = (WinFile*) infoPtr->writeFile; - *handlePtr = (void *) filePtr->handle; + *handlePtr = (ClientData) filePtr->handle; return TCL_OK; } return TCL_ERROR; @@ -2574,7 +2476,7 @@ Tcl_WaitPid( prevPtrPtr = &procList; for (infoPtr = procList; infoPtr != NULL; prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) { - if (infoPtr->dwProcessId == PTR2UINT(pid)) { + if (infoPtr->hProcess == (HANDLE) pid) { *prevPtrPtr = infoPtr->nextPtr; break; } @@ -2692,7 +2594,7 @@ Tcl_WaitPid( */ CloseHandle(infoPtr->hProcess); - ckfree(infoPtr); + ckfree((char*)infoPtr); return result; } @@ -2718,9 +2620,9 @@ Tcl_WaitPid( void TclWinAddProcess( void *hProcess, /* Handle to process */ - Tcl_Size id) /* Global process identifier */ + unsigned long id) /* Global process identifier */ { - ProcInfo *procPtr = (ProcInfo *)ckalloc(sizeof(ProcInfo)); + ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo)); PipeInit(); @@ -2749,9 +2651,10 @@ TclWinAddProcess( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_PidObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const *objv) /* Argument strings. */ @@ -2759,18 +2662,19 @@ Tcl_PidObjCmd( Tcl_Channel chan; const Tcl_ChannelType *chanTypePtr; PipeInfo *pipePtr; - TCL_HASH_TYPE i; - Tcl_Obj *resultPtr, *elemPtr; + int i; + Tcl_Obj *resultPtr; + char buf[TCL_INTEGER_SPACE]; if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); return TCL_ERROR; } if (objc == 1) { - TclNewIntObj(elemPtr, getpid()); - Tcl_SetObjResult(interp, elemPtr); + wsprintfA(buf, "%lu", (unsigned long) getpid()); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); } else { - chan = Tcl_GetChannel(interp, TclGetString(objv[1]), + chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL); if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; @@ -2781,10 +2685,11 @@ Tcl_PidObjCmd( } pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); - TclNewObj(resultPtr); + resultPtr = Tcl_NewObj(); for (i = 0; i < pipePtr->numPids; i++) { - TclNewIntObj(elemPtr, TclpGetPid(pipePtr->pidPtr[i])); - Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr, elemPtr); + wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); + Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr, + Tcl_NewStringObj(buf, -1)); } Tcl_SetObjResult(interp, resultPtr); } @@ -2820,23 +2725,21 @@ WaitForRead( * or not. */ { DWORD timeout, count; - HANDLE handle = ((WinFile *) infoPtr->readFile)->handle; + HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; while (1) { /* * Synchronize with the reader thread. */ - /* avoid blocking if pipe-thread exited */ - timeout = (!blocking || !TclPipeThreadIsAlive(&infoPtr->readTI) - || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; + timeout = blocking ? INFINITE : 0; if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { /* * The reader thread is blocked waiting for data and the channel * is in non-blocking mode. */ - errno = EWOULDBLOCK; + errno = EAGAIN; return -1; } @@ -2859,7 +2762,7 @@ WaitForRead( if (PeekNamedPipe(handle, (LPVOID) NULL, (DWORD) 0, (LPDWORD) NULL, &count, (LPDWORD) NULL) != TRUE) { - Tcl_WinConvertError(GetLastError()); + TclWinConvertError(GetLastError()); /* * Check to see if the peek failed because of EOF. @@ -2903,7 +2806,7 @@ WaitForRead( */ ResetEvent(infoPtr->readable); - TclPipeThreadSignal(&infoPtr->readTI); + SetEvent(infoPtr->startReader); } } @@ -2931,11 +2834,15 @@ static DWORD WINAPI PipeReaderThread( LPVOID arg) { - TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *) arg; - PipeInfo *infoPtr = NULL; /* access info only after success init/wait */ - HANDLE handle = NULL; + PipeInfo *infoPtr = (PipeInfo *)arg; + HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; DWORD count, err; int done = 0; + HANDLE wEvents[2]; + DWORD waitResult; + + wEvents[0] = infoPtr->stopReader; + wEvents[1] = infoPtr->startReader; while (!done) { /* @@ -2943,14 +2850,15 @@ PipeReaderThread( * pipe becoming readable. */ - if (!TclPipeThreadWaitForSignal(&pipeTI)) { - /* exit */ - break; - } + 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 (!infoPtr) { - infoPtr = (PipeInfo *) pipeTI->clientData; - handle = ((WinFile *) infoPtr->readFile)->handle; + break; } /* @@ -2972,7 +2880,7 @@ PipeReaderThread( infoPtr->readFlags |= PIPE_EOF; done = 1; } else if (err == ERROR_INVALID_HANDLE) { - done = 1; + break; } } else if (count == 0) { if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL) @@ -2994,11 +2902,12 @@ PipeReaderThread( infoPtr->readFlags |= PIPE_EOF; done = 1; } else if (err == ERROR_INVALID_HANDLE) { - done = 1; + break; } } } + /* * Signal the main thread by signalling the readable event and then * waking up the notifier thread. @@ -3024,12 +2933,6 @@ 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; } @@ -3054,25 +2957,31 @@ static DWORD WINAPI PipeWriterThread( LPVOID arg) { - TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; - PipeInfo *infoPtr = NULL; /* access info only after success init/wait */ - HANDLE handle = NULL; + PipeInfo *infoPtr = (PipeInfo *)arg; + HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle; 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. */ - if (!TclPipeThreadWaitForSignal(&pipeTI)) { - /* exit */ - break; - } - if (!infoPtr) { - infoPtr = (PipeInfo *)pipeTI->clientData; - handle = ((WinFile *) infoPtr->writeFile)->handle; + 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. + */ + + break; } buf = infoPtr->writeBuf; @@ -3118,12 +3027,6 @@ 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; } @@ -3145,7 +3048,7 @@ PipeWriterThread( static void PipeThreadActionProc( - void *instanceData, + ClientData instanceData, int action) { PipeInfo *infoPtr = (PipeInfo *) instanceData; @@ -3153,7 +3056,7 @@ PipeThreadActionProc( /* * We do not access firstPipePtr in the thread structures. This is not for * all pipes managed by the thread, but only those we are watching. - * Removal of the fileevent handlers before transfer thus takes care of + * Removal of the filevent handlers before transfer thus takes care of * this structure. */ @@ -3178,544 +3081,6 @@ PipeThreadActionProc( } /* - *---------------------------------------------------------------------- - * - * TclpOpenTemporaryFile -- - * - * Creates a temporary file, possibly based on the supplied bits and - * pieces of template supplied in the first three arguments. If the - * fourth argument is non-NULL, it contains a Tcl_Obj to store the name - * of the temporary file in (and it is caller's responsibility to clean - * up). If the fourth argument is NULL, try to arrange for the temporary - * file to go away once it is no longer needed. - * - * Results: - * A read-write Tcl Channel open on the file. - * - *---------------------------------------------------------------------- - */ - -Tcl_Channel -TclpOpenTemporaryFile( - TCL_UNUSED(Tcl_Obj *) /*dirObj*/, - Tcl_Obj *basenameObj, - TCL_UNUSED(Tcl_Obj *) /*extensionObj*/, - Tcl_Obj *resultingNameObj) -{ - WCHAR name[MAX_PATH]; - char *namePtr; - HANDLE handle; - DWORD flags = FILE_ATTRIBUTE_TEMPORARY; - Tcl_Size length; - int counter, counter2; - Tcl_DString buf; - - if (!resultingNameObj) { - flags |= FILE_FLAG_DELETE_ON_CLOSE; - } - - namePtr = (char *) name; - length = GetTempPathW(MAX_PATH, name); - if (length == 0) { - goto gotError; - } - namePtr += length * sizeof(WCHAR); - if (basenameObj) { - const char *string = TclGetStringFromObj(basenameObj, &length); - - Tcl_DStringInit(&buf); - Tcl_UtfToWCharDString(string, length, &buf); - memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf)); - namePtr += Tcl_DStringLength(&buf); - Tcl_DStringFree(&buf); - } else { - const WCHAR *baseStr = L"TCL"; - length = 3 * sizeof(WCHAR); - - memcpy(namePtr, baseStr, length); - namePtr += length; - } - counter = TclpGetClicks() % 65533; - counter2 = 1024; /* Only try this many times! Prevents - * an infinite loop. */ - - do { - char number[TCL_INTEGER_SPACE + 4]; - - snprintf(number, sizeof(number), "%d.TMP", counter); - counter = (unsigned short) (counter + 1); - Tcl_DStringInit(&buf); - Tcl_UtfToWCharDString(number, strlen(number), &buf); - Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf) + 1); - memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf) + 1); - Tcl_DStringFree(&buf); - - handle = CreateFileW(name, - GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, flags, NULL); - } while (handle == INVALID_HANDLE_VALUE - && --counter2 > 0 - && GetLastError() == ERROR_FILE_EXISTS); - if (handle == INVALID_HANDLE_VALUE) { - goto gotError; - } - - if (resultingNameObj) { - Tcl_Obj *tmpObj = TclpNativeToNormalized(name); - - Tcl_AppendObjToObj(resultingNameObj, tmpObj); - TclDecrRefCount(tmpObj); - } - - return Tcl_MakeFileChannel((void *) handle, - TCL_READABLE|TCL_WRITABLE); - - gotError: - Tcl_WinConvertError(GetLastError()); - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * TclPipeThreadCreateTI -- - * - * Creates a thread info structure, can be owned by worker. - * - * Results: - * Pointer to created TI structure. - * - *---------------------------------------------------------------------- - */ - -TclPipeThreadInfo * -TclPipeThreadCreateTI( - TclPipeThreadInfo **pipeTIPtr, - void *clientData, - HANDLE wakeEvent) -{ - TclPipeThreadInfo *pipeTI; -#ifndef _PTI_USE_CKALLOC - pipeTI = (TclPipeThreadInfo *)malloc(sizeof(TclPipeThreadInfo)); -#else - pipeTI = (TclPipeThreadInfo *)ckalloc(sizeof(TclPipeThreadInfo)); -#endif /* !_PTI_USE_CKALLOC */ - pipeTI->evControl = CreateEventW(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) - */ - - state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_IDLE, - PTI_STATE_WORK); - if (state & (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 - */ - - state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_WORK, - PTI_STATE_IDLE); - if (state & (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; - state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_STOP, - PTI_STATE_IDLE); - switch (state) { - case PTI_STATE_IDLE: - /* - * Thread was idle/waiting, notify it goes teardown - */ - - SetEvent(evControl); - *pipeTIPtr = NULL; - /* FALLTHRU */ - 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 - */ - - state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_STOP, - PTI_STATE_IDLE); - switch (state) { - 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) - */ - - state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_END, - PTI_STATE_WORK); - if (state == 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). - */ - - 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 /* !_PTI_USE_CKALLOC */ - } -} - -/* - *---------------------------------------------------------------------- - * - * 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; - state = InterlockedExchange(&pipeTI->state, PTI_STATE_DOWN); - if (state == 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 /* !_PTI_USE_CKALLOC */ - } -} - -/* * Local Variables: * mode: c * c-basic-offset: 4 |
