Tcl Source Code

View Ticket
Login
Ticket UUID: 2176669
Title: Strange seek bug
Type: Bug Version: obsolete: 8.4.19
Submitter: nobody Created on: 2008-10-18 11:57:20
Subsystem: 25. Channel System Assigned To: andreas_kupries
Priority: 8 Severity:
Status: Closed Last Modified: 2009-12-10 06:05:51
Resolution: Duplicate Closed By:
    Closed on:
Description:
I've got strange seek behavior executing this simple script (Windows
XP, tcl 8.4.19/8.5.2/8.6a1):

--- exp.tcl ---
set fd [open hash.db r+]

seek $fd 100
puts [tell $fd]
puts -nonewline $fd 0
puts [tell $fd]
read $fd 2
seek $fd 0
puts [tell $fd]
---
Result is:

100
101
error during seek on "filea3bf40": bad address in system call argument
    while executing
"seek $fd 0"
    (file "exp.tcl" line 8)

------------
It seems that it originates from here:

----- tclIO.c -----
...
In Tcl_Seek:

/*
 * Compute how much input and output is buffered. If both input and
output
 * is buffered, cannot compute the current position.
 */

inputBuffered = Tcl_InputBuffered(chan);
outputBuffered = Tcl_OutputBuffered(chan);

if ((inputBuffered != 0) && (outputBuffered != 0)) {
    Tcl_SetErrno(EFAULT);
    return Tcl_LongAsWide(-1);

}

--------

So, [flush]-ing channel before seeking helps.
I didn't find this documented anywhere.
Moreover, as Alexandre Ferrieux said, [seek] is documented to flush the output side.
User Comments: andreas_kupries added on 2009-12-10 06:05:33:
This artifact has been marked as a duplicate of artifact 2901998 with reason:
Same thing internally, buffered output was not flushed before read

ferrieux added on 2009-11-22 17:51:02:
Previous comment now promoted to a bug by itself: 2901998.
The reason is that it is simpler and more spectacular.
Note that the fix I propose (assuming the potential incompatibility is validated as benign) would solve both.

ferrieux added on 2009-11-22 06:04:02:
Looking in detail at your example, I'm a bit surprised by yet another effect of this internal I/O conflict:   after the 1-byte [puts], if we ask [tell] (which your example doesn't do), we get 1 of course. But then, at script level, how do we explain the fact that the subsequent [read] starts from the beginning of the file ?

In other words, if we forget about the underlying OS file position and concentrate on the only Tcl-visible thing which is what [tell/seek] manipulate, it seems only logical to assume it is *both* a read and write pointer (otherwise we'd need extra API to separate them). Then after writing one byte we're at position 1, and the subsequent [read 2] should read from that position, and hence return the 2nd and 3rd byte of the file ("0a" here).

Of course the implication of such a semantics is that a read might incur a flush (otherwise costly overlap computations are needed). So what ?

Apart from the POTENTIAL INCOMPATIBILITY,  what's wrong with such a simplified "Tcl r/w file pointer" concept ?

andreas_kupries added on 2009-11-21 04:35:18:
I believe this is a limitation which we should just document somewhere (seek ?), with example.

andreas_kupries added on 2009-11-21 02:49:35:
I have attached 2 scripts, one generating the test input file for the other.
The other, wr.tcl, should explain why having both input and output buffered is a bad thing regarding seek and tell, i.e. why they are reported as error. See the comments in the script.

andreas_kupries added on 2009-11-21 02:48:01:

File Added - 351874: wr.tcl

andreas_kupries added on 2009-11-21 02:47:12:

File Added - 351873: maketestfile.tcl

andreas_kupries added on 2009-11-21 02:03:20:
Notes ... Regarding "[seek] is documented to flush the output side. "
Yes. That happens too. It happens quite a bit after the check which fails here.

ferrieux added on 2008-10-21 06:28:49:

File Added - 298169: seek.diff

ferrieux added on 2008-10-21 06:28:40:
Oops I forgot both to login and to attach the file, sorry ;-)

[email protected] added on 2008-10-21 05:37:28:
The attached patch fixes that strange behavior *and* passes the test suite.
The idea is that the check was useless because of the DiscardInputQueued().
The important part which I kept is the

 if (mode == SEEK_CUR) {
  offset -= inputBuffered;
 }

What is unclear to me though, is the reason for this check...

ferrieux added on 2008-10-20 04:54:44:
Looking at the code in Tcl_Seek, there's something I don't understand: a few lines below the above test against simultaneous input and output data buffered, there is:

  DiscardInputQueued(statePtr, 0);

whose effect is to guaranteed nothing's left in the input buffer chain.
So, why not do it first, and avoid the error ?

Attachments: