在非阻塞时处理tcl readhandler中的非持久性eof

时间:2013-10-27 23:10:04

标签: windows tcl tk ftdi

我在Windows上运行tcl8.5 / tk8.5应用程序(在XP,7和8上发生问题),在BitBash模式下使用FTDI D2xx驱动程序。还使用ftd2xx c扩展来访问FTDI dll。

我有一个用户的报告该应用程序最初工作正常,但在一天不使用后,生活在后台,它突然从5M增长到100M并开始消耗99%的CPU。 (这不好!)

在此之前我遇到过USB问题,特别是如果USB是“热插拔”的话。它可能导致应用程序阻止,Windows无法杀死它。我的应用程序永远不需要读取USB,只是写入它(控制它),但我发现在BitBash模式下,FTDI芯片会发送连续的数据流。处理读取会清除readbuffer,因此如果USB已拔下,则没有挂起的读取阻止,我可以正常退出。

但现在,我认为readhandler给了我一些问题。我编写了一小段测试代码,我模仿了我认为可以解决问题根源的实际应用程序。无论如何,我不理解tcl的行为。

以下是代码:

    package require -exact ftd2xx 1.2.1

    # define a read handler
    # gets called by a fileevent readable
    #
    proc readchan {} {
        variable cnt
        set len [gets $::handle buf]

        # buffer is non zero

        if {$len > 0} {
            incr cnt
            set end [eof $::handle]
            puts "$len ($cnt) eof $end"; 
            # if it wasn't an eof output the buffer
            if {!$end} {
                    puts $buf
            }
        } elseif {$len == 0} {
            # was a zero length read
            puts -nonewline "0"
            set end [eof $::handle]
            if { !$end } {
                    # eof makes the $len invalid?
                    puts "-0"
                    #puts "\nlen is 0 and eof is $end - exit!";
                    #exit
            }
        } else {
            # len was negative (-1) so data in buffer but no end of line (in binary mode)
            if { [eof $::handle] } {
                    puts "EOF w/len 0"
            } else {
                    puts -nonewline "."
            }
        }
}

#  main code 
#
# find the usb device and open it
set usb [ftd2xx list]
lassign $usb d
lassign $d d id e loc f serial
puts "found USB $serial"
set handle [ftd2xx open -serial $serial]
puts "Opened USB $handle"
#
# configure it for bitbash mode and for binary, non blocking
#
set bitmode 0xFF01
chan configure $handle -tranlation binary -bitmode $bitmode -blocking 0
fileevent $handle readable readchan

# output something to the usb every 100ms or so required to cause failure
#
while {1} {
    set continue 0
    after 100 {set continue 1}

    # putting:
    # "a" made it crash after 245 "timeouts" - no flush
    # null made it past 255 - no flush
    # "aa" made it crash after 164 "timeouts - no flush

    puts $handle "aa"

    #flushing $handle makes it crash after first flush
    #flush $handle
    puts -nonewline "w"
    vwait continue
}

以下是上述代码的输出:

$ tclsh85 usbfailtest.tcl
found USB AH009L40
Opened USB ftd2xx0
w...w....w....w....w....w....w...w....w.126976 (1) eof 1  <<< 126K length with eof true
..w....w....w....w...w....w....w....w..126976 (2) eof 1   <<< "w" output for usb write
.w....w....w...w....w....w....w....w...126976 (3) eof 1   <<< this is 3rd non-zero read
w...w....w....w....w....w...w....w....w.126976 (4) eof 1  <<< "." output when len -1
..w....w...w....w....w....w....w...w...126976 (5) eof 1   <<< line takes about 1 sec 
w....w....w....w....w...w....w....w....w126976 (6) eof 1
.
. (output skipped)
.
.w....w....w....w...w....w....w....w...126976 (156) eof 1
w....w....w...w....w....w....w....w....w126976 (157) eof 1
..w....w....w....w....w....w...w....w..126976 (158) eof 1
.w....w....w...w....w....w....w....w...126976 (159) eof 1
w....w...w....w....w....w....w....w...w.126976 (160) eof 1
..w....w....w....w....w...w....w....w..126976 (161) eof 1
.w....w....w....w...w....w....w....w...126976 (162) eof 1
w....w...w....w....w....w....w....w...w.126976 (163) eof 1
..w....w....w....w....w...w....w102695 (164) eof 0  << a 102K buffer with EOF false
aa  << all but two of the 102K buffer were null?
0-0 << zero length buffer with no eof
2 (165) eof 0
aa  << two length buffer, no eof, and we got two chars
3 (166) eof 0
aaa << three length bufffer, no eof, and we got three chars
2 (167) eof 0
aa  << etc.
0-0  << another zero zero  these scroll out *very* fast.
0-0
0-0
0-0
2 (168) eof 0
aa  << an so forth
0-0
0-0
.
. (output skipped)
.
43 (268) eof 0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
..w...22192 (269) eof 1
w...w....w....w....w....w...w....w....w.126976 (270) eof 1  << revert to "slow" mode
..w....w....w....w...w....w....w....w..126976 (271) eof 1
.w....w....w...w....w....w....w....w...126976 (272) eof 1
w....w...w....w....w....w....w....w...w.126976 (273) eof 1
..w....w....w....w....w...w....w....w..126976 (274) eof 1

输出的解释:

首先,使用不完整的缓冲区(长度为-1)并且没有eof条件多次调用读取处理程序(每个“。”)。最终会出现超时或内部缓冲区限制,并且强制读取完成。同时提出了EOF。 “...... 126987”的每一行都需要大约一秒的时间来写。

经过一定数量的EOF(即...... 126987的行 - 在这种情况下为164 - 非常可重复),取决于写入通道的数据量以及是否刷新了通道而没有EOF的读取完成(第164行)。

到目前为止,读取事件中断率是可以容忍的,但是,它会飙升并消耗大量处理它的周期。在较慢的机器上,没有时间做任何有用的事情。

我对此有很多疑问,难以表达。但首先要说:

我不明白为什么读取处理程序将被调用0字节挂起和非eof条件。缓冲区中是否必须至少有一个字节是“可读”的?

我不明白为什么EOF是短暂的。我希望USB端口上的EOF意味着它被拔掉,但事实并非如此。

如果我在写入USB后刷新通道,我得到的是0长度读取,即使我杀死应用程序并重新启动它,这仍然存在。清除此设置(进入慢速模式)的唯一方法是从USB拔下设备并重新启动(无需刷新)。我不知道该怎么做。我必须刷新通道才能将写入实际转到FTDI芯片。

我应该使用固定长度的读取而不是获取此读取吗?

2 个答案:

答案 0 :(得分:1)

我使用read而不是gets来实现实际代码。使用我的客户作为测试平台,他报告问题已得到解决。从现在开始,我将使用read而不是获取二进制通道。

答案 1 :(得分:0)

我没有具体了解ftd2xx包,但我看到一些奇怪的东西。通常,当eof返回1时,您最好关闭处理程序中的通道。只要数据准备就绪,或者当通道在外部关闭时(可能是驱动程序或操作系统关闭它),就会调用fileevent处理程序,如果你不告诉TCL关闭处理程序中的通道,TCL会一直反复调用它永远(告诉你做某事)。 来自fileevent手册:

  

&#34;如果基础文件或设备上存在文件结尾或错误情况,则通道也被视为可读。脚本检查这些条件并对其进行适当处理非常重要;例如,如果没有对文件结尾进行特殊检查,则可能会发生无限循环,其中脚本不读取数据,返回并立即再次调用。&#34;