Ticket UUID: | 2407783 | |||
Title: | Seek causes IO channel to be shared between all interps | |||
Type: | Bug | Version: | obsolete: 8.5.5 | |
Submitter: | sbron | Created on: | 2008-12-08 22:44:14 | |
Subsystem: | 25. Channel System | Assigned To: | andreas_kupries | |
Priority: | 9 Immediate | Severity: | ||
Status: | Closed | Last Modified: | 2008-12-12 00:31:26 | |
Resolution: | Fixed | Closed By: | andreas_kupries | |
Closed on: | 2008-12-11 17:31:26 | |||
Description: |
I noticed that 'seek 0' on a file handle makes the handle accessible in all interps. I can't find any evidence in the docs that this is the intended behavior. Example: The following script will dump 2 x four bytes from /dev/random: interp create foo interp create bar set f [open /dev/random rb] interp eval foo { proc bytes {fh} { binary scan [read $fh 4] H* hex puts $hex close $fh } } interp eval bar { proc bytes {fh} { binary scan [read $fh 4] H* hex puts $hex close $fh } } seek $f 0 interp eval foo [list bytes $f] interp eval bar [list bytes $f] close $f Without the seek command it reports an error as expected: can not find channel named "file5" while executing "read $fh 4" (procedure "bytes" line 2) invoked from within "bytes file5" invoked from within "interp eval foo [list byte $f]" SuSE linux 11.0 Kernel 2.6.25.18-0.2-pae Tcl 8.5.5 | |||
User Comments: |
andreas_kupries added on 2008-12-12 00:31:16:
Applied patches to both 8.5 and head. sbron added on 2008-12-11 15:44:12: I don't know enough of the internals of Tcl to be able to give a theoretical opinion about the patch. I have confirmed that it works in practice with the application where I found the issue. One additional remark: I found that this bug was not present in Tcl 8.5.2. It must have been introduced in Tcl 8.5.3 or 8.5.4. andreas_kupries added on 2008-12-11 07:37:52: File Added - 304859: tcl86.diff-wu andreas_kupries added on 2008-12-11 07:37:09: File Added - 304858: tcl85.diff-wu andreas_kupries added on 2008-12-11 00:09:44: @dgp: A bit of clarification. The functions of tclChannelType do indeed Tcl_Preserve/Release ChannelState pointers. This is however not because of my fix. They did that even before I put my hands into this particular pie. As such it is an integral part of this TclObjTyp I was not really willing to change for my fix. What I did was to extend this scheme to the Interp pointer I added to the intrep. It might actually not be necessary, as the Interp pointer is only copied and compared, never de-referenced, contrary to the ChannelState. I should further note that ChannelState structures actually have a refCount. This however is used to count how many interpreters have the the channel Tcl_RegisterChannel()'ed in them, and a refCount > 0 prevents fully closing the channel. I do not believe that it is a good idea to use this refCount to count how many Tcl_Obj refer to this channel. As it means that a 'close $f' will never really close the channel, but only at some arbitrary time later when the last intrep goes away. Which actually may be never during the lifetime of an interp. I will see that I can get a test done for 8.5 and 8.6 in the next days, and then check if may Preseve/Release of the Interp* was overkill. Likely yes. If so I will remove that part of the patch. dgp added on 2008-12-10 21:11:15: comments on the chat indicated the solution here involved having the intrep of a Tcl_Obj set a Tcl_Preserve on a struct, with a matching Tcl_Release when the intrep is shimmered away. That's fine as a stopgap, but if at all possible, the need to do that means the preserved struct really needs its own refCount field. dkf added on 2008-12-10 17:07:58: If interpreter boundaries are the only concern (the "preservation within an interp" issue is a great thing to have a test case on in any case) then the patch is good. Needs a test, but that can probably be adapted from the bug report. andreas_kupries added on 2008-12-10 06:48:35: I do not believe that an epoch is required. SCFA checks the flags of the preserved channelState for CHANNEL_CLOSED. If that is true it already invalidated and refetched the intrep. This should handle the close/reopen case. dkf added on 2008-12-10 06:28:49: sounds like a reasonable fix. You also probably need to check an epoch counter or something like that so that if the channel is deleted and reallocated, that can be handled too... andreas_kupries added on 2008-12-10 06:27:58: Added patch containing my proposed fix. dkf, schelte, please review. andreas_kupries added on 2008-12-10 06:27:30: File Added - 304737: channel-intrep.patch andreas_kupries added on 2008-12-10 06:06:03: Ok, I was possibly not too clear. SetChannelFromAny (SCFA) uses Tcl_GetChannel. However if and only if the intrep is not already tclChannelType. If the intrep is properly tclChannelType it pulls the ChannelState* directly out of the intrep, and now we have direct access to a channel which may not be known to the interpreter we are in, because the intrep was set in a different interpreter. So, even with a properly typed intrep we have to recheck that the channel we got is actually known in the current interp, and the same. Best would be to not only store channelstate*, but also interp* of the interp which set the intrep. The moment we are in a different interp we invalidate and recompute the intrep. sort of like cmd resolution intreps. andreas_kupries added on 2008-12-10 05:58:06: @jeffh: The intrep of tclChannelType is the statePtr (ChannelState) of a channel, without an association to an interp. This means a Tcl_Obj with vlaid intrep can carry a channel into an interp which actually doesn't know, and it will be properly used by direct access to its state, without looking it up anywhere. andreas_kupries added on 2008-12-10 05:55:11: Note 1: There is no Tcl_GetChannelFromObj(). Only a TclGetChannelFromObj(). I.e. internal, not public. Note 2: Trying to trace where the fix is in all the unrelated changes is ... not fun. There do not seem to be any changes to fix it. Tracing the TclGetChannelFromObj a bit it seems that in 8.6 something invalidates the intrep after the seek, before the 'puts' in interp foo is reached. So far no idea what. hobbs added on 2008-12-10 05:54:02: Donal's changes do not look like they intentionally changed any behavior. I'm skeptical that TclGetChannelFromObj is the source of the issue as it layers over Tcl_GetChannel, which maintains a table in the assocData of a single interp, and uses Tcl_Objs, which should not be shared. Am I missing something? andreas_kupries added on 2008-12-10 03:47:33: Ok, coming to this I see the problem in 8.5, but not 8.6/Head. As I also saw a commit by dkf to tclIO.c I am assuming right now that the 'Refactoring tclIO.c' commit was (intended as) a fix for this. Meaning that I 'just' have to back port it. Is that correct, Donal ? dkf added on 2008-12-09 18:47:32: Diagnosis: leakage of file handle through Tcl_Obj internal rep. Problem is not [seek] itself, but rather Tcl_GetChannelFromObj. |