📄 tclwinpipe.c
字号:
/* * tclWinPipe.c -- * * This file implements the Windows-specific exec pipeline functions, * the "pipe" channel driver, and the "pid" Tcl command. * * 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. * * RCS: @(#) $Id: tclWinPipe.c,v 1.25 2002/06/17 20:05:49 andreas_kupries Exp $ */#include "tclWinInt.h"#include <fcntl.h>#include <io.h>#include <sys/stat.h>/* * The following variable is used to tell whether this module has been * initialized. */static int initialized = 0;/* * The pipeMutex locks around access to the initialized and procList variables, * and it is used to protect background threads from being terminated while * they are using APIs that hold locks. */TCL_DECLARE_MUTEX(pipeMutex)/* * The following defines identify the various types of applications that * run under windows. There is special case code for the various types. */#define APPL_NONE 0#define APPL_DOS 1#define APPL_WIN3X 2#define APPL_WIN32 3/* * The following constants and structures are used to encapsulate the state * of various types of files used in a pipeline. * This used to have a 1 && 2 that supported Win32s. */#define WIN_FILE 3 /* Basic Win32 file. *//* * This structure encapsulates the common state associated with all file * types used in a pipeline. */typedef struct WinFile { int type; /* One of the file types defined above. */ HANDLE handle; /* Open file handle. */} WinFile;/* * This list is used to map from pids to process handles. */typedef struct ProcInfo { HANDLE hProcess; DWORD dwProcessId; struct ProcInfo *nextPtr;} ProcInfo;static ProcInfo *procList;/* * Bit masks used in the flags field of the PipeInfo structure below. */#define PIPE_PENDING (1<<0) /* Message is pending in the queue. */#define PIPE_ASYNC (1<<1) /* Channel is non-blocking. *//* * Bit masks used in the sharedFlags field of the PipeInfo structure below. */#define PIPE_EOF (1<<2) /* Pipe has reached EOF. */#define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. *//* * This structure describes per-instance data for a pipe based channel. */typedef struct PipeInfo { struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */ Tcl_Channel channel; /* Pointer to channel structure. */ int validMask; /* OR'ed combination of TCL_READABLE, * TCL_WRITABLE, or TCL_EXCEPTION: indicates * which operations are valid on the file. */ int watchMask; /* OR'ed combination of TCL_READABLE, * TCL_WRITABLE, or TCL_EXCEPTION: indicates * which events should be reported. */ int flags; /* State flags, see above for a list. */ TclFile readFile; /* Output from pipe. */ TclFile writeFile; /* Input from pipe. */ TclFile errorFile; /* Error output from 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. */ 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 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. */ char *writeBuf; /* Current background output buffer. * Access is synchronized with the writable * object. */ int writeBufLen; /* Size of write buffer. Access is * synchronized with the writable * object. */ int toWrite; /* Current amount to be written. Access is * synchronized with the writable object. */ int readFlags; /* Flags that are shared with the reader * thread. Access is synchronized with the * readable object. */ char extraByte; /* Buffer for extra character consumed by * reader thread. This byte is shared with * the reader thread so access must be * synchronized with the readable object. */} PipeInfo;typedef struct ThreadSpecificData { /* * The following pointer refers to the head of the list of pipes * that are being watched for file events. */ PipeInfo *firstPipePtr;} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;/* * The following structure is what is added to the Tcl event queue when * pipe events are generated. */typedef struct PipeEvent { Tcl_Event header; /* Information that is standard for * all events. */ PipeInfo *infoPtr; /* Pointer to pipe info structure. Note * that we still have to verify that the * pipe exists before dereferencing this * pointer. */} PipeEvent;/* * Declarations for functions used only in this file. */static int ApplicationType(Tcl_Interp *interp, const char *fileName, char *fullName);static void BuildCommandLine(const char *executable, int argc, CONST char **argv, Tcl_DString *linePtr);static BOOL HasConsole(void);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 void PipeExitHandler(ClientData clientData);static int PipeGetHandleProc(ClientData instanceData, int direction, ClientData *handlePtr);static void PipeInit(void);static int PipeInputProc(ClientData instanceData, char *buf, int toRead, int *errorCode);static int PipeOutputProc(ClientData instanceData, CONST char *buf, int toWrite, int *errorCode);static DWORD WINAPI PipeReaderThread(LPVOID arg);static void PipeSetupProc(ClientData clientData, int flags);static void PipeWatchProc(ClientData instanceData, int mask);static DWORD WINAPI PipeWriterThread(LPVOID arg);static void ProcExitHandler(ClientData clientData);static int TempFileName(WCHAR name[MAX_PATH]);static int WaitForRead(PipeInfo *infoPtr, int blocking);/* * This structure describes the channel type structure for command pipe * based IO. */static Tcl_ChannelType pipeChannelType = { "pipe", /* Type name. */ TCL_CHANNEL_VERSION_2, /* v2 channel */ TCL_CLOSE2PROC, /* Close proc. */ PipeInputProc, /* Input proc. */ PipeOutputProc, /* Output proc. */ NULL, /* Seek proc. */ NULL, /* Set option proc. */ NULL, /* Get option proc. */ PipeWatchProc, /* Set up notifier to watch the channel. */ PipeGetHandleProc, /* Get an OS handle from channel. */ PipeClose2Proc, /* close2proc */ PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */};/* *---------------------------------------------------------------------- * * PipeInit -- * * This function initializes the static variables for this file. * * Results: * None. * * Side effects: * Creates a new event source. * *---------------------------------------------------------------------- */static voidPipeInit(){ ThreadSpecificData *tsdPtr; /* * Check the initialized flag first, then check again in the mutex. * This is a speed enhancement. */ if (!initialized) { Tcl_MutexLock(&pipeMutex); if (!initialized) { initialized = 1; procList = NULL; Tcl_CreateExitHandler(ProcExitHandler, NULL); } Tcl_MutexUnlock(&pipeMutex); } tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->firstPipePtr = NULL; Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL); Tcl_CreateThreadExitHandler(PipeExitHandler, NULL); }}/* *---------------------------------------------------------------------- * * PipeExitHandler -- * * This function is called to cleanup the pipe module before * Tcl is unloaded. * * Results: * None. * * Side effects: * Removes the pipe event source. * *---------------------------------------------------------------------- */static voidPipeExitHandler( ClientData clientData) /* Old window proc */{ Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL);}/* *---------------------------------------------------------------------- * * ProcExitHandler -- * * This function is called to cleanup the process list before * Tcl is unloaded. * * Results: * None. * * Side effects: * Resets the process list. * *---------------------------------------------------------------------- */static voidProcExitHandler( ClientData clientData) /* Old window proc */{ Tcl_MutexLock(&pipeMutex); initialized = 0; Tcl_MutexUnlock(&pipeMutex);}/* *---------------------------------------------------------------------- * * PipeSetupProc -- * * This procedure is invoked before Tcl_DoOneEvent blocks waiting * for an event. * * Results: * None. * * Side effects: * Adjusts the block time if needed. * *---------------------------------------------------------------------- */voidPipeSetupProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ PipeInfo *infoPtr; Tcl_Time blockTime = { 0, 0 }; int block = 1; WinFile *filePtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Look to see if any events are already pending. If they are, poll. */ for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->watchMask & TCL_WRITABLE) { filePtr = (WinFile*) infoPtr->writeFile; if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { block = 0; } } if (infoPtr->watchMask & TCL_READABLE) { filePtr = (WinFile*) infoPtr->readFile; if (WaitForRead(infoPtr, 0) >= 0) { block = 0; } } } if (!block) { Tcl_SetMaxBlockTime(&blockTime); }}/* *---------------------------------------------------------------------- * * PipeCheckProc -- * * This procedure is called by Tcl_DoOneEvent to check the pipe * event source for events. * * Results: * None. * * Side effects: * May queue an event. * *---------------------------------------------------------------------- */static voidPipeCheckProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ PipeInfo *infoPtr; PipeEvent *evPtr; WinFile *filePtr; int needEvent; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Queue events for any ready pipes that don't already have events * queued. */ for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->flags & PIPE_PENDING) { continue; } /* * Queue an event if the pipe is signaled for reading or writing. */ needEvent = 0; filePtr = (WinFile*) infoPtr->writeFile; if ((infoPtr->watchMask & TCL_WRITABLE) && (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { needEvent = 1; } filePtr = (WinFile*) infoPtr->readFile; if ((infoPtr->watchMask & TCL_READABLE) && (WaitForRead(infoPtr, 0) >= 0)) { needEvent = 1; } if (needEvent) { infoPtr->flags |= PIPE_PENDING; evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent)); evPtr->header.proc = PipeEventProc; evPtr->infoPtr = infoPtr; Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); } }}/* *---------------------------------------------------------------------- * * TclWinMakeFile -- * * This function constructs a new TclFile from a given data and * type value. * * Results: * Returns a newly allocated WinFile as a TclFile. * * Side effects: * None. * *---------------------------------------------------------------------- */TclFileTclWinMakeFile( HANDLE handle) /* Type-specific data. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -