📄 tclio.c
字号:
(chanPtr->flags & BG_FLUSH_SCHEDULED)) { return 0; } /* * If the output queue is still empty, break out of the while loop. */ if (bufPtr == (ChannelBuffer *) NULL) { break; /* Out of the "while (1)". */ } /* * Produce the output on the channel. */ toWrite = bufPtr->nextAdded - bufPtr->nextRemoved; written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData, bufPtr->buf + bufPtr->nextRemoved, toWrite, &errorCode); /* * If the write failed completely attempt to start the asynchronous * flush mechanism and break out of this loop - do not attempt to * write any more output at this time. */ if (written < 0) { /* * If the last attempt to write was interrupted, simply retry. */ if (errorCode == EINTR) { errorCode = 0; continue; } /* * If the channel is non-blocking and we would have blocked, * start a background flushing handler and break out of the loop. */ if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) { if (chanPtr->flags & CHANNEL_NONBLOCKING) { if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) { chanPtr->flags |= BG_FLUSH_SCHEDULED; UpdateInterest(chanPtr); } errorCode = 0; break; } else { panic("Blocking channel driver did not block on output"); } } /* * Decide whether to report the error upwards or defer it. */ if (calledFromAsyncFlush) { if (chanPtr->unreportedError == 0) { chanPtr->unreportedError = errorCode; } } else { Tcl_SetErrno(errorCode); if (interp != NULL) { Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_VOLATILE); } } /* * When we get an error we throw away all the output * currently queued. */ DiscardOutputQueued(chanPtr); continue; } bufPtr->nextRemoved += written; /* * If this buffer is now empty, recycle it. */ if (bufPtr->nextRemoved == bufPtr->nextAdded) { chanPtr->outQueueHead = bufPtr->nextPtr; if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) { chanPtr->outQueueTail = (ChannelBuffer *) NULL; } RecycleBuffer(chanPtr, bufPtr, 0); } } /* Closes "while (1)". */ /* * If the queue became empty and we have the asynchronous flushing * mechanism active, cancel the asynchronous flushing. */ if ((chanPtr->outQueueHead == (ChannelBuffer *) NULL) && (chanPtr->flags & BG_FLUSH_SCHEDULED)) { chanPtr->flags &= (~(BG_FLUSH_SCHEDULED)); (chanPtr->typePtr->watchProc)(chanPtr->instanceData, chanPtr->interestMask); } /* * If the channel is flagged as closed, delete it when the refCount * drops to zero, the output queue is empty and there is no output * in the current output buffer. */ if ((chanPtr->flags & CHANNEL_CLOSED) && (chanPtr->refCount <= 0) && (chanPtr->outQueueHead == (ChannelBuffer *) NULL) && ((chanPtr->curOutPtr == (ChannelBuffer *) NULL) || (chanPtr->curOutPtr->nextAdded == chanPtr->curOutPtr->nextRemoved))) { return CloseChannel(interp, chanPtr, errorCode); } return errorCode;}/* *---------------------------------------------------------------------- * * CloseChannel -- * * Utility procedure to close a channel and free its associated * resources. * * Results: * 0 on success or a POSIX error code if the operation failed. * * Side effects: * May close the actual channel; may free memory. * *---------------------------------------------------------------------- */static intCloseChannel(interp, chanPtr, errorCode) Tcl_Interp *interp; /* For error reporting. */ Channel *chanPtr; /* The channel to close. */ int errorCode; /* Status of operation so far. */{ int result = 0; /* Of calling driver close * operation. */ Channel *prevChanPtr; /* Preceding channel in list of * all channels - used to splice a * channel out of the list on close. */ if (chanPtr == NULL) { return result; } /* * No more input can be consumed so discard any leftover input. */ DiscardInputQueued(chanPtr, 1); /* * Discard a leftover buffer in the current output buffer field. */ if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) { ckfree((char *) chanPtr->curOutPtr); chanPtr->curOutPtr = (ChannelBuffer *) NULL; } /* * The caller guarantees that there are no more buffers * queued for output. */ if (chanPtr->outQueueHead != (ChannelBuffer *) NULL) { panic("TclFlush, closed channel: queued output left"); } /* * If the EOF character is set in the channel, append that to the * output device. */ if ((chanPtr->outEofChar != 0) && (chanPtr->flags & TCL_WRITABLE)) { int dummy; char c; c = (char) chanPtr->outEofChar; (chanPtr->typePtr->outputProc) (chanPtr->instanceData, &c, 1, &dummy); } /* * Remove TCL_READABLE and TCL_WRITABLE from chanPtr->flags, so * that close callbacks can not do input or output (assuming they * squirreled the channel away in their clientData). This also * prevents infinite loops if the callback calls any C API that * could call FlushChannel. */ chanPtr->flags &= (~(TCL_READABLE|TCL_WRITABLE)); /* * Splice this channel out of the list of all channels. */ if (chanPtr == firstChanPtr) { firstChanPtr = chanPtr->nextChanPtr; } else { for (prevChanPtr = firstChanPtr; (prevChanPtr != (Channel *) NULL) && (prevChanPtr->nextChanPtr != chanPtr); prevChanPtr = prevChanPtr->nextChanPtr) { /* Empty loop body. */ } if (prevChanPtr == (Channel *) NULL) { panic("FlushChannel: damaged channel list"); } prevChanPtr->nextChanPtr = chanPtr->nextChanPtr; } /* * OK, close the channel itself. */ result = (chanPtr->typePtr->closeProc) (chanPtr->instanceData, interp); if (chanPtr->channelName != (char *) NULL) { ckfree(chanPtr->channelName); } /* * If we are being called synchronously, report either * any latent error on the channel or the current error. */ if (chanPtr->unreportedError != 0) { errorCode = chanPtr->unreportedError; } if (errorCode == 0) { errorCode = result; if (errorCode != 0) { Tcl_SetErrno(errorCode); } } /* * Cancel any outstanding timer. */ Tcl_DeleteTimerHandler(chanPtr->timer); /* * Mark the channel as deleted by clearing the type structure. */ chanPtr->typePtr = NULL; Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC); return errorCode;}/* *---------------------------------------------------------------------- * * Tcl_Close -- * * Closes a channel. * * Results: * A standard Tcl result. * * Side effects: * Closes the channel if this is the last reference. * * NOTE: * Tcl_Close removes the channel as far as the user is concerned. * However, it may continue to exist for a while longer if it has * a background flush scheduled. The device itself is eventually * closed and the channel record removed, in CloseChannel, above. * *---------------------------------------------------------------------- */ /* ARGSUSED */intTcl_Close(interp, chan) Tcl_Interp *interp; /* Interpreter for errors. */ Tcl_Channel chan; /* The channel being closed. Must * not be referenced in any * interpreter. */{ ChannelHandler *chPtr, *chNext; /* Iterate over channel handlers. */ CloseCallback *cbPtr; /* Iterate over close callbacks * for this channel. */ EventScriptRecord *ePtr, *eNextPtr; /* Iterate over eventscript records. */ Channel *chanPtr; /* The real IO channel. */ int result; /* Of calling FlushChannel. */ NextChannelHandler *nhPtr; if (chan == (Tcl_Channel) NULL) { return TCL_OK; } /* * Perform special handling for standard channels being closed. If the * refCount is now 1 it means that the last reference to the standard * channel is being explicitly closed, so bump the refCount down * artificially to 0. This will ensure that the channel is actually * closed, below. Also set the static pointer to NULL for the channel. */ CheckForStdChannelsBeingClosed(chan); chanPtr = (Channel *) chan; if (chanPtr->refCount > 0) { panic("called Tcl_Close on channel with refCount > 0"); } /* * Remove any references to channel handlers for this channel that * may be about to be invoked. */ for (nhPtr = nestedHandlerPtr; nhPtr != (NextChannelHandler *) NULL; nhPtr = nhPtr->nestedHandlerPtr) { if (nhPtr->nextHandlerPtr && (nhPtr->nextHandlerPtr->chanPtr == chanPtr)) { nhPtr->nextHandlerPtr = NULL; } } /* * Remove all the channel handler records attached to the channel * itself. */ for (chPtr = chanPtr->chPtr; chPtr != (ChannelHandler *) NULL; chPtr = chNext) { chNext = chPtr->nextPtr; ckfree((char *) chPtr); } chanPtr->chPtr = (ChannelHandler *) NULL; /* * Cancel any pending copy operation. */ StopCopy(chanPtr->csPtr); /* * Must set the interest mask now to 0, otherwise infinite loops * will occur if Tcl_DoOneEvent is called before the channel is * finally deleted in FlushChannel. This can happen if the channel * has a background flush active. */ chanPtr->interestMask = 0; /* * Remove any EventScript records for this channel. */ for (ePtr = chanPtr->scriptRecordPtr; ePtr != (EventScriptRecord *) NULL; ePtr = eNextPtr) { eNextPtr = ePtr->nextPtr; ckfree(ePtr->script); ckfree((char *) ePtr); } chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL; /* * Invoke the registered close callbacks and delete their records. */ while (chanPtr->closeCbPtr != (CloseCallback *) NULL) { cbPtr = chanPtr->closeCbPtr; chanPtr->closeCbPtr = cbPtr->nextPtr; (cbPtr->proc) (cbPtr->clientData); ckfree((char *) cbPtr); } /* * Ensure that the last output buffer will be flushed. */ if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) && (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) { chanPtr->flags |= BUFFER_READY; } /* * The call to FlushChannel will flush any queued output and invoke * the close function of the channel driver, or it will set up the * channel to be flushed and closed asynchronously. */ chanPtr->flags |= CHANNEL_CLOSED; result = Flu
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -