Ticket #3442 defect new
t.i.stdio must not set nonblocking on stdio FDs
| Reported by: | jknight | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | core | Keywords: | |
| Cc: | exarkun, itamar, jknight, glyph, ezyang | Branch: | |
| Author: | Launchpad Bug: |
Description
It seems that O_NONBLOCK is a property of file descriptions, not of file descriptors. This means that it's incorrect for any code to ever set it on a file descriptor that it did not just freshly create with open(), socket(), etc.
t.i.stdio needs to instead do a workaround like http://cr.yp.to/unix/nonblock.html describes. That is: use select/etc as usual to find when the fd is probably readable/writable. Then, try to read/write it. However, if it turns out select lied, you need a way to escape from the blocking read/write. So, you have to set a timer to send a signal to yourself (e.g. SIGALARM) around the read/write, so that it interrupts the syscall for you after a short period of time, in case select lied.
This is totally sucky but I can't see a correct alternative (other than using threads, like the windows version does).
<foom> yeah, so t.i.stdio is totally broken, it can't set nonblocking on random fds it inherits, because that's a property of the file description not file descriptor.
<exarkun> Okay, that's a property of the file description. So?
<foom> you run a t.i.stdio app. it sets stdout to nonblocking mode. Now, stdout is nonblocking for all processes that share that same stream.
<exarkun> How many people want to share stdout among multiple processes?
<foom> e.g. you return back to the shell, now the shell's stdout is nonblocking
<exarkun> That'd be easily fixed by making them blocking again before exiting
<foom> unless you don't exit cleanly
<foom> or unless someone else is running concurrently
<exarkun> If you don't exit cleanly, you might leave stdout in an inconsistent state anyway
<exarkun> And concurrency is a different case :)
<foom> you might? how?
<glyph> foom: sys.stdout.write("\x1b")
<foom> at least random other programs won't fail with EAGAIN
<exarkun> I've never wanted to programmatically interact with stdout that was in concurrent use by another process
<exarkun> That's why I said it was an uncommon case
<foom> you've never ran program & in a shell?
<foom> and had it print something?
<exarkun> not when `program´ wanted to play with stdout
<exarkun> well, not that's not true
<exarkun> I have
<exarkun> But whenever the program prints to stdout I get pissed up, foreground it and kill it.
<exarkun> Why would I want a background process talking to my stdio? It's *background*! The foreground process is the one that gets to talk to stdio.
<foom> so, in the meantime, since the shell printed something, it set the file back to blocking mode
<exarkun> Anyway, that's irrelevant - it's *possible* to share, I'm not denying that
<exarkun> But I really think it's very uncommon.
<foom> and now your twisted program is blocking on stdio
<foom> for the rest of its life
<foom> well, I just ran into this in real life.
<exarkun> Okay, so you probably want a fix :)
<foom> and it took quite a while to realize what the hell was going on
<exarkun> Before I was thinking that twisted.internet.stdio didn't need to change completely to accomodate an uncommon use-case
<foom> because I didn't even realize nonblockingness was a property of the file description
<exarkun> But now I'm thinking it can accomodate this use-case without changing completely
<exarkun> What if it could dup stdio and make the copies non-blocking?
<exarkun> Is that a fix?
<foom> no
<foom> dup doesn't copy the file description
<foom> it copies the file descriptor
<exarkun> okay
<foom> see, this is really evil.
<exarkun> shared mutable state usually is ;)
<itamar> what do other applications do?
<exarkun> itamar: I bet they break and die.
<exarkun> foom just said what shells do - set the fd back to blocking, but I bet that's not a reliable fix for them
<exarkun> (ie, what if twisted sets it non-blocking in between doing that and doing a read)
<foom> they do that in response to EAGAIN I believe
<foom> so it should work out okay for them. :)
<exarkun> around every read we do, we could lock the fd and set it non-blocking :)
<foom> lock it? how?
<exarkun> mandatory non-posix locking of course
<exarkun> (this is a joke)
<exarkun> bbiaf, coffee
<itamar> http://cr.yp.to/unix/nonblock.html
<foom> man, that sucks, but it sure would work.
<foom> select() would usually give the right answer, if it doesn't, the signal will break you out of the read.
<foom> it shouldn't even be that hard to fix t.i.stdio to do that.
<foom> sucksucksucksuck
<itamar> do we still want to set non-block?
<foom> no
<foom> you can't
<foom> it's basically incorrect to set nonblockingness on any fd you didn't create
<itamar> :(
