When a channel is closed for writing, any buffered output on the channel is flushed. When a channel is closed for reading, any buffered input is discarded. When a channel is destroyed the underlying resource is closed and the channel is thereafter unavailable.
chan close fully flushes any output before closing the write side of a channel unless it is non-blocking mode, where it returns immediately and the channel is flushed in the background before finally being closed.
chan close may return an error if an error occurs while flushing output. If a process in a command pipeline created by open returns an error (either by returning a non-zero exit code or writing to its standard error file descriptor), chan close generates an error in the same manner as exec.
Closing one side of a socket or command pipeline may lead to the shutdown() or close() of the underlying system resource, leading to a reaction from whatever is on the other side of the pipeline or socket.
If the channel for a command pipeline is in blocking mode, chan close waits for the connected processes to complete.
chan close only affects the current interpreter. If the channel is open in any other interpreter, its state is unchanged there. See interp for a description of channel sharing.
When the last interpreter sharing a channel is destroyed, the channel is switched to blocking mode and fully flushed and then closed.
Channels are automatically closed when an interpreter is destroyed and when the process exits. From 8.6 on (TIP#398), nonblocking channels are no longer switched to blocking mode when exiting; this guarantees a timely exit even when the peer or a communication channel is stalled. To ensure proper flushing of stalled nonblocking channels on exit, one must now either (a) actively switch them back to blocking or (b) use the environment variable TCL_FLUSH_NONBLOCKING_ON_EXIT, which when set and not equal to “0” restores the previous behavior.
If no optionName or value arguments are given, chan configure returns a dictionary of option names and values for the channel. If optionName is supplied without a value, chan configure returns the current value of the named option. If one or more pairs of optionName and value are supplied, chan configure sets each of the named options to the corresponding value and returns the empty string.
The options described below are supported for all channels. Each type of channel may provide additional options. Those options are described in the relevant documentation. For example, additional options are documented for socket, and also for serial devices at open.
binary is an alias for iso8859-1. This alone is not sufficient for working with binary data. Use -translation binary instead.
The encoding of a new channel is the value of encoding system, which returns the platform- and locale-dependent system encoding used to interface with the operating system,
Returns the input translation for a read-only channel, the output translation for a write-only channel, and both the input translation and the the output translation for a read-write channel. When two translations are given, they are the input and output translation, respectively. When only one translation is given for a read-write channel, it is the translation for both input and output. The following values are currently supported:
-size limits the number of characters copied.
If -command is given, chan copy returns immediately, works in the background, and calls callback when the copy completes, providing as an additional argument the number of characters written to outputChan. If an error occurs during the background copy, another argument provides message for the error. inputChan and outputChan are automatically configured for non-blocking mode if needed. Background copying only works correctly if events are being processed, e.g. via vwait or Tk.
During a background copy no other read operation may be performed on inputChan, and no write operation may be performed on outputChan. However, write operations may by performed on inputChan and read operations may be performed on outputChan, as exhibited by the bidirectional copy example below.
If either inputChan or outputChan is closed while the copy is in progress, copying ceases and no callback is made. If inputChan is closed all data already queued is written to outputChan.
There should be no event handler established for inputChan because it may become readable during a background copy. An attempt to read or write from within an event handler results result in the error, "channel busy". Any wrong-sided I/O attempted (by a fileevent handler or otherwise) results in a “channel busy” error.
The first example transfers the contents of one channel exactly to another. Note that when copying one file to another, it is better to use file copy which also copies file metadata (e.g. the file access permissions) where possible.
fconfigure $in -translation binary fconfigure $out -translation binary fcopy $in $out
This second example shows how the callback gets passed the number of bytes transferred. It also uses vwait to put the application into the event loop. Of course, this simplified example could be done without the command callback.
proc Cleanup {in out bytes {error {}}} { global total set total $bytes close $in close $out if {[string length $error] != 0} { # error occurred during the copy } } set in [open $file1] set out [socket $server $port] fcopy $in $out -command [list Cleanup $in $out] vwait total
The third example copies in chunks and tests for end of file in the command callback.
proc CopyMore {in out chunk bytes {error {}}} { global total done incr total $bytes if {([string length $error] != 0) || [eof $in]} { set done $total close $in close $out } else { fcopy $in $out -size $chunk \ -command [list CopyMore $in $out $chunk] } } set in [open $file1] set out [socket $server $port] set chunk 1024 set total 0 fcopy $in $out -size $chunk \ -command [list CopyMore $in $out $chunk] vwait done
The fourth example starts an asynchronous, bidirectional fcopy between two sockets. Those could also be pipes from two [open "|hal 9000" r+] (though their conversation would remain secret to the script, since all four fileevent slots are busy).
set flows 2 proc Done {dir args} { global flows done puts "$dir is over." incr flows -1 if {$flows<=0} {set done 1} } fcopy $sok1 $sok2 -command [list Done UP] fcopy $sok2 $sok1 -command [list Done DOWN] vwait done
Imode is a list of one or more of the strings “read” or “write”, indicating whether the channel is a read channel, a write channel, or both. It is an error if the handler does not support the chosen mode.
The handler is called as needed from the global namespace at the top level, and command resolution happens there at the time of the call. If the handler is renamed or deleted any subsequent attempt to call it is an error, which may not be able to describe the failure.
The handler is always called in the interpreter and thread it was created in, even if the channel was shared with or moved into a different interpreter in a different thread. This is achieved through event dispatch, so if the event loop is not entered, e.g. by calling Tcl_DoOneEvent or vwait or using Tk, the thread performing the channel operation blocks indefinitely, resulting in deadlock.
One side of a channel may be in one thread while the other side is in a different thread, providing a stream-oriented bridge between the threads. This provides a method for regular stream communication between threads as an alternative to sending commands.
When the interpreter the handler is in is deleted each channel associated with the handler is deleted as well, regardless of which interpreter or thread it is currently in or shared with.
chan create is safe and is accessible to safe interpreters. The handler is always called in the safe interpreter it was created in.
script is evaluated at the global level in the interpreter it was established in. Any resulting error is handled in the background, i.e. via interp bgerror. In order to prevent an endless loop due to a buggy handler, the handler is deleted if script returns an error so that it is not evaluated again.
Without an event handler, chan gets or chan read on a channel in blocking mode may block until data becomes available, become during which the thread is unable to perform other work or respond to events on other channels. This could cause the application to appear to “freeze up” . Channel event handlers allow events on the channel to direct channel handling so that the reader or writer can continue to perform other processing while waiting for a channel to become available and then handle channel operations when the channel is ready for the operation.
A “readable” event occurs when there is data that can be read from the channel and also when there is an error on the channel. The handler must check for these conditions and handle them appropriately. For example, a handler that does not check whether the end of the data has been reached may be repeatedly evaluated in a busy loop until the channel is closed.
A “writable” event occurs when at least one byte of data can be written, or if there is an error on the channel. A client socket opened in non-blocking mode becomes writable when it becomes connected or if the connection fails.
Event-driven channel handling works best for channels in non-blocking mode. A channel in blocking mode blocks when chan puts writes more data than the channel can accept at the moment, and when chan gets or chan read requests more data than is currently available. When a channel blocks, the thread can not do any other processing or service any other events. A channel in non-blocking mode allows a thread to carry on with other work and get back to the channel at the right time.
If the end of the channel is reached the data read so far is returned or assigned to varName. When varName is not given, chan eof may indicate that the empty string means that the end of the data has been reached, and chan blocked may indicate that that the empty string means there isn't currently enough data do return the next line.
Due to buffering, data written to one side of a pipe might not immediately become available on the other side. Tcl's own buffers can be configured via chan configure -buffering, but overall behaviour still depends on operating system buffers outside of Tcl's control. Once the write side of the channel is closed, any data remaining in the buffers is flushed through to the read side. It may be useful to arrange for the connected process to flush at some point after writing to the channel or to have it use some system-provided mechanism to configure buffering. When two pipes are connected to the same process, one to send data to the process, and one to read data from the process, a deadlock may occur if the channels are in blocking mode: If reading, the channel may block waiting for data that can never come because buffers are only flushed on subsequent writes, and if writing, the channel may block while waiting for the buffers to become free, which can never happen because the reader can not read while the writer is blocking. To avoid this issue, either put the channels into non-blocking mode and use event handlers, or place the read channel and the write channel in separate interpreters in separate threads.
For use only by handlers for a channel created by chan create. It is an error to post an event for any other channel.
Since only the handler for a reflected channel channel should post events it is an error to post an event from any interpreter other than the interpreter that created the channel.
It is an error to post an event that the channel has no interest in. See watch in the refchan documentation for more information
chan postevent is available in safe interpreters, as any handler for a reflected channel would have been created, and will be evaluated in that interpreter as well.
Each line feed in the output is translated according to the configuration of -translation.
Because Tcl internally buffers output, characters written to a channel may not immediately be available at the destination. Tcl normally delays output until the buffer is full or the channel is closed. chan flush forces output in the direction of the destination.
When the output for a channel in blocking mode fills up, chan puts blocks until space in the buffer is available again, but for a channel in non-blocking mode, it returns immediately and the data is written in the background as fast possible, constrained by the speed at which as the destination accepts it. Output to a channel in non-blocking mode only works properly when the application enters the event loop, giving Tcl a chance to find out that the destination is ready to accept more data. When a channel is in non-blocking mode, Tcl's internal buffers can hold an arbitrary amount of data, possibly consuming a large amount of memory. To avoid wasting memory, channels in non-blocking mode should normally be handled using chan event, where the application only invokes chan puts after being recently notified through a file event handler that the channel is ready for more output data.
If the channel is in non-blocking mode, fewer characters than requested may be returned. If the channel is configured to use a multi-byte encoding, bytes that do not form a complete character are retained in the buffers until enough bytes to complete the character accumulate, or the end of the data is reached. -nonewline is ignored if characters are returned before reaching the end of the file.
Each end-of-line sequence according to the value of -translation is translated into a line feed.
When reading from a serial port, most applications should configure the serial port channel to be in non-blocking mode, but not necessarily use an event handler since most serial ports are comparatively slow. It is entirely possible to get a readable event for each individual character. In blocking mode, chan read blocks forever when reading to the end of the data if there is no chan configure -eofchar configured for the channel.
Chan seek flushes all buffered output even if the channel is in non-blocking mode, discards any buffered and unread input, and returns the empty string or an error if the channel does not support seeking.
offset values are byte offsets, not character offsets. Unlike chan read, both chan seek and chan tell operate in terms of bytes, not characters,
set f [open somefile.txt r+] chan configure $f -encoding cp1252 set offset 0 # Search for string "FOOBAR" in the file while {[chan gets $f line] >= 0} { set idx [string first FOOBAR $line] if {$idx >= 0} { # Found it; rewrite line chan seek $f [expr {$offset + $idx}] chan puts -nonewline $f BARFOO # Skip to end of following line, and truncate chan gets $f chan gets $f chan truncate $f # Stop searching the file now break } # Save offset of start of next line for later set offset [chan tell $f] } chan close $f
A network server that echoes its input line-by-line without preventing servicing of other connections at the same time:
# This is a very simple logger... proc log message { chan puts stdout $message } # This is called whenever a new client connects to the server proc connect {chan host port} { set clientName [format <%s:%d> $host $port] log "connection from $clientName" chan configure $chan -blocking 0 -buffering line chan event $chan readable [list echoLine $chan $clientName] } # This is called whenever either at least one byte of input # data is available, or the channel was closed by the client. proc echoLine {chan clientName} { chan gets $chan line if {[chan eof $chan]} { log "finishing connection from $clientName" chan close $chan } elseif {![chan blocked $chan]} { # Didn't block waiting for end-of-line log "$clientName - $line" chan puts $chan $line } } # Create the server socket and enter the event-loop to wait # for incoming connections... socket -server connect 12345 vwait forever