Menu

#176 Shutdown one side of a socket/pipe channel.

obsolete: 8.0.4
closed-fixed
nobody
2
2001-03-31
2000-10-26
Anonymous
No

OriginalBugID: 5339 RFE
Version: 8.0.4
SubmitDate: '2000-05-03'
LastModified: '2000-05-04'
Severity: MED
Status: Closed
Submitter: techsupp
ChangedBy: hobbs
RelatedBugIDs: 1429
OS: Solaris
OSVersion: Solaris 2.6
Machine: Standard Sun UltraSparc architecture
FixedDate: '2000-10-25'
ClosedDate: '2000-05-04'

Name: Kevin L. Sitze

DesiredBehavior:
There are times when a Tcl script needs to be able to shutdown only
one side of a channel. Currently Tcl does not allow this
functionality. Here is an example of how this can cause problems.

If a Tcl script sets up a pipeline wherein it is both the consumer and
the producer, it is incapable of properly cleaning up because it
doesn't know when to close the channel. It cannot detect the EOF on
the channel because it must first send an EOF to the channel, which it
cannot do without resorting to C.

Here's some example code.

#! /usr/local/bin/tclsh

####
# This is just to get the data, the data may be produced
# in some fashion (say, pulled from an internal database)
# which prevents us from

set fname "some.tar.gz"
set fd [open $fname "r"]
set gzdata [read $fd [file size $fname]]
close $fd

####
# Simple callback for pushing pipe output to stdout.

proc gzrecv {fd} {
while {[gets $fd line] > 0} {
puts $line
}

if {[eof $fd]} {
global forever
set forever true
}
}

####
# Setup the processing pipeline.

set gunzip [open "| gunzip | tar tf -" "r+"]
fileevent $gunzip readable "gzrecv $gunzip"
fconfigure $gunzip -blocking false

puts $gunzip $gzdata
set forever false
vwait forever

####
# Must close socket after data has been read.

close $gunzip

exit 0

There are actually two problems here. The first problem is in the
"fconfigure $gunzip -blocking false" statement. Setting this file
descriptor in non-blocking mode apparently causes "EAGAIN" errors to
be returned from the "gunzip" command (I don't understand why, but it
does, on Solaris anyway). This command expects a blocking socket and
doesn't handle this error properly, eventually causing the application
to exit incorrectly.

The second problem is: when does the EOF get to the output side of the
socket? Typically, the EOF propagates through the pipeline from the
producer (writer) to the consumer (reader). As Tcl views a pipeline
channel as a single file, there is no way whereby a pipeline is
capable of closing down its input without also closing down its output
(which may still have data pending in the pipeline).

There are several workarounds:
1) Spawn off a secondary script to act as either the producer or
consumer (as may be required).
2) Generate a C hack to perform the shutdown.
3) Write the data into a temporary file and feed the pipeline on
this file. This may result in certain performance issues if
attempting to run in real time.

The preferred solution is to provide a Tcl command which allows
splitting a single read/write channel into two channels. One for
reading and one for writing.

set rwlist [fsplit $channel]
set recvfd [lindex $rwlist 0]
set sendfd [lindex $rwlist 1]

It is debatable as to whether the original channel is valid after
splitting it out in this fashion.

The alternate solution is to modify the "fconfigure" and "close"
commands to allow for performing certain operations on only one "side"
of the channel. Something like:

fconfigure $fd -side read -blocking false
close -side write $fd

The above code could then be written:

#! /usr/local/bin/tclsh

####
# This is just to get the data, the data may be produced
# in some fashion (say, pulled from an internal database)
# which prevents us from

set fname "some.tar.gz"
set fd [open $fname "r"]
set gzdata [read $fd [file size $fname]]
close $fd

####
# Simple callback for pushing pipe output to stdout.

proc gzrecv {fd} {
while {[gets $fd line] > 0} {
puts $line
}

if {[eof $fd]} {
global forever
set forever true
}
}

####
# Setup the processing pipeline.

set gunzip [open "| gunzip | tar tf -" "r+"]
fconfigure $gunzip -side read -blocking false
fileevent $gunzip readable "gzrecv $gunzip"

puts $gunzip $gzdata
close -side write $gunzip
set forever false
vwait forever

####
# Must close socket after data has been read.

close $gunzip

exit 0

Note that such operations are also useful for handling socket
channels. The shutdown(3n) interface allows one end of the
file/socket descriptor to be closed. This functionality can be useful
in handling advanced Tcl client/server problems.

Discussion

  • Brent B. Welch

    Brent B. Welch - 2000-10-26
    • priority: 5 --> 2
    • status: open --> closed-fixed
     
  • Don Porter

    Don Porter - 2001-03-31
    • labels: 104250 --> 27. Channel Types