📄 tclwinpipe.c
字号:
SetEvent(pipePtr->stopReader); /* * Wait at most 10 milliseconds for the reader thread to close. */ WaitForSingleObject(pipePtr->readThread, 10); GetExitCodeThread(pipePtr->readThread, &exitCode); if (exitCode == STILL_ACTIVE) { /* * 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); /* Wait for the thread to terminate. */ WaitForSingleObject(pipePtr->readThread, INFINITE); 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)) { /* * 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. */ if (pipePtr->writeThread) { WaitForSingleObject(pipePtr->writable, INFINITE); /* * Forcibly terminate the background thread. We cannot rely on the * thread to cleanly terminate itself because we have no way of * closing the pipe handle without blocking in the case where the * thread is in the middle of an I/O operation. 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); TerminateThread(pipePtr->writeThread, 0); /* * Wait for the thread to terminate. This ensures that we are * completely cleaned up before we leave this function. */ WaitForSingleObject(pipePtr->writeThread, INFINITE); Tcl_MutexUnlock(&pipeMutex); CloseHandle(pipePtr->writeThread); CloseHandle(pipePtr->writable); CloseHandle(pipePtr->startWriter); 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 writable, so we always report it * as being writable unless we have detected EOF. */ filePtr = (WinFile*) ((PipeInfo*)infoPtr)->writeFile; mask = 0; if ((infoPtr->watchMask & TCL_WRITABLE) && (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { mask = TCL_WRITABLE; } filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile; if ((infoPtr->watchMask & TCL_READABLE) && (WaitForRead(infoPtr, 0) >= 0)) { if (infoPtr->readFlags & PIPE_EOF) { mask = TCL_READABLE; } else { mask |= TCL_READABLE; } } /* * Inform the channel of the events. */ Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask); return 1;}/* *---------------------------------------------------------------------- * * PipeWatchProc -- * * Called by the notifier to set up to watch for events on this * channel. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidPipeWatchProc( ClientData instanceData, /* Pipe state. */ int mask) /* What events to watch for, OR-ed * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */{ PipeInfo **nextPtrPtr, *ptr; PipeInfo *infoPtr = (PipeInfo *) instanceData; int oldMask = infoPtr->watchMask; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * Since most of the work is handled by the background threads, * we just need to update the watchMask and then force the notifier * to poll once. */ infoPtr
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -