Version 3 of read_with_timeout

Updated 2005-11-14 15:34:28

[Group project!]

    ########
    #
    # After at most $timeout milliseconds, return a string of at most $number_of_characters.
    # 
    # [Hide stuff inside some namespace.]
    #
    ########
    proc read_with_timeout {channel number_of_characters timeout} {
        # Preserve existing fileevent on $channel in order to restore it on return.
        # Define timeout handler.
        # Create character-at-a-time fileevent handler which accumulates result string.
        # Restore original fileevent.
    }


# Variation, from entirely different direction: Expect's timeout.


# Also note that some channels (serial lines?) already admit timeout configuration. # Is it time for a TIP to propose that Tcl do this for all platforms/devices/...?


Not sure why you only want to have a "character-at-a-time" fileevent handler, but if you care to do this more efficiently (reading in chunks), does something like the following do what you want? (forgive the verbosity and lack of testing, I just coded this up in a few minutes)- Todd Coram

 namespace eval ::timed {
    array set orig_event [list]
    array set orig_config [list]
    array set result [list]
    array set error [list]

    proc read {channel count timeout} {
        variable orig_event
        variable orig_config
        variable error
        variable result

        set orig_event($channel) [fileevent $channel readable]
        set orig_config($channel) [fconfigure $channel]
        set result($channel) ""
        set error($channel) ""

        fconfigure $channel -blocking 0

        set timer_id [after $timeout \
                    [namespace code [list cancel $channel "timeout"]]]
        fileevent $channel readable\
            [namespace code [list read_accum $channel $count ""]]

        vwait ::timed::result($channel)
        after cancel $timer_id

        if {[llength $orig_event($channel)] > 0} {
            fileevent $channel readable $orig_event($channel)
        }
        eval fconfigure $channel $orig_config($channel)

        if {$error($channel) != ""}  {
            error $error($channel)
        }
        return $result($channel)
    }

    proc read_accum {channel count accum} {
        variable result

        set bytes [::read $channel $count]
        if {[eof $channel]} {
            cancel $channel "eof"
            return
        }
        append accum $bytes
        incr count -[string bytelength $bytes]
        if {$count > 0} {
            fileevent $channel readable \
                [namespace code [list read_accum $channel $count $accum]]
        }
        set result($channel) $accum
    }

    proc cancel {channel reason} {
        variable result
        variable error

        set result($channel) ""
        set error($channel) "[namespace current]::read failed: $reason"
    }
 }