tclwinpipe.c

来自「tcl是工具命令语言」· C语言 代码 · 共 2,329 行 · 第 1/5 页

C
2,329
字号
 * Side effects: *	Closes the physical channel. * *---------------------------------------------------------------------- */static intPipeClose2Proc(    ClientData instanceData,	/* Pointer to PipeInfo structure. */    Tcl_Interp *interp,		/* For error reporting. */    int flags)			/* Flags that indicate which side to close. */{    PipeInfo *pipePtr = (PipeInfo *) instanceData;    Tcl_Channel errChan;    int errorCode, result;    PipeInfo *infoPtr, **nextPtrPtr;    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);    DWORD exitCode;    errorCode = 0;    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 trying to read from the pipe.	 */	if (pipePtr->readThread) {	    /*	     * The thread may already have closed on it's own.  Check it's	     * 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);		}	    }	    CloseHandle(pipePtr->readThread);	    CloseHandle(pipePtr->readable);	    CloseHandle(pipePtr->startReader);	    CloseHandle(pipePtr->stopReader);	    pipePtr->readThread = NULL;	}	if (TclpCloseFile(pipePtr->readFile) != 0) {	    errorCode = errno;	}	pipePtr->validMask &= ~TCL_READABLE;	pipePtr->readFile = 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);	    /*	     * The thread may already have closed on it's own.  Check it's	     * 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.		 */		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);	    pipePtr->writeThread = NULL;	}	if (TclpCloseFile(pipePtr->writeFile) != 0) {	    if (errorCode == 0) {		errorCode = errno;	    }	}	pipePtr->validMask &= ~TCL_WRITABLE;	pipePtr->writeFile = NULL;    }    pipePtr->watchMask &= pipePtr->validMask;    /*     * Don't free the channel if any of the flags were set.     */    if (flags) {	return errorCode;    }    /*     * Remove the file from the list of watched files.     */    for (nextPtrPtr = &(tsdPtr->firstPipePtr), infoPtr = *nextPtrPtr;	    infoPtr != NULL;	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {	if (infoPtr == (PipeInfo *)pipePtr) {	    *nextPtrPtr = infoPtr->nextPtr;	    break;	}    }    /*     * Wrap the error file into a channel and give it to the cleanup     * routine.     */    if (pipePtr->errorFile) {	WinFile *filePtr;	filePtr = (WinFile*)pipePtr->errorFile;	errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,		TCL_READABLE);	ckfree((char *) filePtr);    } else {        errChan = NULL;    }    result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,            errChan);    if (pipePtr->numPids > 0) {        ckfree((char *) pipePtr->pidPtr);    }    if (pipePtr->writeBuf != NULL) {	ckfree(pipePtr->writeBuf);    }    ckfree((char*) pipePtr);    if (errorCode == 0) {        return result;    }    return errorCode;}/* *---------------------------------------------------------------------- * * PipeInputProc -- * *	Reads input from the IO channel into the buffer given. Returns *	count of how many bytes were actually read, and an error indication. * * Results: *	A count of how many bytes were read is returned and an error *	indication is returned in an output argument. * * Side effects: *	Reads input from the actual channel. * *---------------------------------------------------------------------- */static intPipeInputProc(    ClientData instanceData,		/* Pipe state. */    char *buf,				/* Where to store data read. */    int bufSize,			/* How much space is available                                         * in the buffer? */    int *errorCode)			/* Where to store error code. */{    PipeInfo *infoPtr = (PipeInfo *) instanceData;    WinFile *filePtr = (WinFile*) infoPtr->readFile;    DWORD count, bytesRead = 0;    int result;    *errorCode = 0;    /*     * Synchronize with the reader thread.     */    result = WaitForRead(infoPtr, (infoPtr->flags & PIPE_ASYNC) ? 0 : 1);    /*     * If an error occurred, return immediately.     */    if (result == -1) {	*errorCode = errno;	return -1;    }    if (infoPtr->readFlags & PIPE_EXTRABYTE) {	/*	 * The reader thread consumed 1 byte as a side effect of	 * waiting so we need to move it into the buffer.	 */	*buf = infoPtr->extraByte;	infoPtr->readFlags &= ~PIPE_EXTRABYTE;	buf++;	bufSize--;	bytesRead = 1;	/*	 * If further read attempts would block, return what we have.	 */	if (result == 0) {	    return bytesRead;	}    }    /*     * Attempt to read bufSize bytes.  The read will return immediately     * if there is any data available.  Otherwise it will block until     * at least one byte is available or an EOF occurs.     */    if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,	    (LPOVERLAPPED) NULL) == TRUE) {	return bytesRead + count;    } else if (bytesRead) {	/*	 * Ignore errors if we have data to return.	 */	return bytesRead;    }    TclWinConvertError(GetLastError());    if (errno == EPIPE) {	infoPtr->readFlags |= PIPE_EOF;	return 0;    }    *errorCode = errno;    return -1;}/* *---------------------------------------------------------------------- * * PipeOutputProc -- * *	Writes the given output on the IO channel. Returns count of how *	many characters were actually written, and an error indication. * * Results: *	A count of how many characters were written is returned and an *	error indication is returned in an output argument. * * Side effects: *	Writes output on the actual channel. * *---------------------------------------------------------------------- */static intPipeOutputProc(    ClientData instanceData,		/* Pipe state. */    CONST char *buf,			/* The data buffer. */    int toWrite,			/* How many bytes to write? */    int *errorCode)			/* Where to store error code. */{    PipeInfo *infoPtr = (PipeInfo *) instanceData;    WinFile *filePtr = (WinFile*) infoPtr->writeFile;    DWORD bytesWritten, timeout;        *errorCode = 0;    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 = EAGAIN;	goto error;    }        /*     * Check for a background error on the last write.     */    if (infoPtr->writeError) {	TclWinConvertError(infoPtr->writeError);	infoPtr->writeError = 0;	goto error;    }    if (infoPtr->flags & PIPE_ASYNC) {	/*	 * The pipe is non-blocking, so copy the data into the output	 * buffer and restart the writer thread.	 */	if (toWrite > infoPtr->writeBufLen) {	    /*	     * Reallocate the buffer to be large enough to hold the data.	     */	    if (infoPtr->writeBuf) {		ckfree(infoPtr->writeBuf);	    }	    infoPtr->writeBufLen = toWrite;	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite);	}	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);	infoPtr->toWrite = toWrite;	ResetEvent(infoPtr->writable);	SetEvent(infoPtr->startWriter);	bytesWritten = toWrite;    } else {	/*	 * In the blocking case, just try to write the buffer directly.	 * This avoids an unnecessary copy.	 */	if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite,		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {	    TclWinConvertError(GetLastError());	    goto error;	}    }    return bytesWritten;    error:    *errorCode = errno;    return -1;}/* *---------------------------------------------------------------------- * * PipeEventProc -- * *	This function is invoked by Tcl_ServiceEvent when a file event *	reaches the front of the event queue.  This procedure invokes *	Tcl_NotifyChannel on the pipe. * * Results: *	Returns 1 if the event was handled, meaning it should be removed *	from the queue.  Returns 0 if the event was not handled, meaning *	it should stay on the queue.  The only time the event isn't *	handled is if the TCL_FILE_EVENTS flag bit isn't set. * * Side effects: *	Whatever the notifier callback does. * *---------------------------------------------------------------------- */static intPipeEventProc(    Tcl_Event *evPtr,		/* Event to service. */    int flags)			/* Flags that indicate what events to				 * handle, such as TCL_FILE_EVENTS. */{    PipeEvent *pipeEvPtr = (PipeEvent *)evPtr;    PipeInfo *infoPtr;    WinFile *filePtr;    int mask;    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);    if (!(flags & TCL_FILE_EVENTS)) {	return 0;    }    /*     * Search through the list of watched pipes for the one whose handle     * matches the event.  We do this rather than simply dereferencing     * the handle in the event so that pipes can be deleted while the     * event is in the queue.     */    for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL;	    infoPtr = infoPtr->nextPtr) {	if (pipeEvPtr->infoPtr == infoPtr) {	    infoPtr->flags &= ~(PIPE_PENDING);	    break;	}    }    /*     * Remove stale events.     */    if (!infoPtr) {	return 1;    }    /*     * Check to see if the pipe is readable.  Note     * that we can't tell if a pipe is writab

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?