On Friday 17 October 2008 04:30:48 am Rob Landley wrote:
> > If you got an ESC, next char is delayed by 8ms by serial line and you
> > are scheduled away in poll(), there is no guarantee you come back
> > 25ms and not 25s later.
> 
> Sure, but the data will have come in to the kernel and be queued, so when you 
> _do_ get scheduled again the poll() will return because there's data pending, 
> not because of the timeout.

Yes, I was confused. Scheduling on the receive side can't hurt us.

But scheduling in the sender can. If sender is not a hardware
but a process (say, sitting on the other side of ssh) and it
takes input from, say, serial line, it may read only one char, ESC,
and send it to us over ssh. Then it will read more and send more to us.
Delay between ESC and the rest here is affected by how *that process*
is scheduled, and there scheduling delays can not be predicted.

Your qemu case can be such example too. What is injecting "serial"
input? It's a host process. Can it be delayed? Yes.
If qemu is clever enough to have many threads, it can have one process
to run quest OS and another process to do IO. There is no guarantee
second process won't experience delay between it pushed ESC
down the "serial line" and it pushed the rest.

> > Kernel simply gives no such promises. 
> 
> It checks for pending data before it checks for the timeout.  The kernel 
> accepting data from the serial port and queuing it to the tty happens in 
> interrupt context, and that interrupt handling takes priority over handling 
> the timer interrupt that causes the poll to expire.  (That was the test I did 
>  
> by repeatedly suspending qemu for more than the timeout period.)

I don't think this is how it works. poll() just blocks on _both_ timeout
and data being available. When block is gone and poll() continues inside kernel,
it checks why it was able to continue. That's where it checks
"do we have data available" and returns the number of fd's with data.

Interrupt priority has nothing to do with it.

> > In-kernel stuff (IRQs from gigabit ethernet which is flooded
> > with UDP packets) also can delay processing of next character
> > from serial line.
> 
> Yes, but it would delay the timer expiration more.  They still get done in 
> sequence;

Yes, you are right here.

> > > I trimmed it
> > > down to 25 miliseconds, which should be plenty for the next character to
> > > come in at 1200 bps, and is way the heck below any human perceptual
> > > threshold.
> >
> > Couldn't resist, joined bikeshed painting :| and bumped it to 50.
> 
> *shrug*  I note that 25 miliseconds is already about 3 times what a 1200 bps 
> connection needs, but the perceptual threshold of "sluggish UI" is something 
> like 1/8th of a second according to the human factors people (I'd look up the 
> actual reference, but don't actually care), so we're still good.
> 
> > It's 5 scheduling intervals on a 100 Hz system, I consider system
> > which does not deprive vi from scheduling for 5 timer tick intervals
> > as a "system not yet too overloaded". 25ms is only 2 intervals.
> 
> It's not scheduling.  It has nothing to do with scheduling.  That's what I 
> ran 
> a test to prove.)  The point is that poll() is a syscall, and the kernel 
> flags it to return to userspace when it either gets data from the device or 
> when the timer it requested expires, neither of which has anything to do with 
> the process scheduler.

I think scheduler is at play here.
Imagine a low-end system (say ~100MHz cpu) with rather poor capabilities
wrt measuring time. No microsecond clock, just timer interrupts.

If one runs Linux on such a system with HZ=100, poll() with timeout of 10ms
is basically a "one timer tick timeout". I don't know how precise it can be.
To be exact, can kernel ensure that it won't be significantly _smaller_
than 10ms? What if poll() was called soon before our 10ms timeslice
was going to expire anyway? When the timeout will expire? Next timeslice?
Then it can be smaller than 10ms. Two timeslices from now?
This means almost always poll(10) will do ~20ms waits.

Therefore, I think poll(10ms) might be somewhat iffy wrt timeout duration.
If we want to err on the safe side, we'd better use at the very least 20ms.

> It has nothing to do with timer ticks.  (And the comment that 50ms is somehow 
> 5 timer ticks is actively wrong; if you look at the stupid chunk of perl 
> peter anvin added to the kernel build in 2.6.25, there's apparently a 
> platform out there using 24/second.

Even worse than my example. I would be comfortable with at least
poll(50) on such a system.

> Also, the current scheduler doesn't  
> switch _every_ timer tick, it's more complicated than that.

I know.

> > I also increased a count in read(). There is a non-obvious reason
> > why bumping it past 3 would require more surgery, or
> > "hold down Page Down" starts editing the file on it's own :)
> 
> I don't understand what you're talking about here.
> 
> > (hint: assigning "n = 0;" is wrong in this case).
> 
> I changed the code to only pre-read one character at a time, and only when 
> another character was needed to match a potential escape sequence.

> That means that if we matched the entire escape sequence, we read _exactly_ 
> as 
> much data as we needed to do so, and have thus consumed exactly the contents 
> of the buffer.  That's why I set n=0.  Why is this wrong?

This is ok. Think what happens if match failed.
Failure to match may leave up to 4 chars buffered in readbuffer[].
Then on the next call we try to interpret *them*
as a potential ESC sequence. This works correctly
because there are no sequences like "ESC ESC x y".

If I want to not be sadistic towards poor kernel and retrieve more chars
at once, I can do read(fd,buf,5) from the start. It's the same, right?
After all, if it's not ESC seq, we will buffer up to 4 chars,
which is exactly what we do after failed match anyway.

Well, it's wrong. Example: x ESC O A ESC O A - it's keys
x, cursor_up, cursor_up. read(fd,buf,5) will read x ESC O A ESC.

We eat x. We come back later and eat "ESC O A", and here's the problem -
we lose next ESC because code just sets chars_to_parse to 0
if successful ESC sequence match happened.

So, in short, the code is correct as-is. But trying to enlarge the first read()
in readit() past 4 bytes will not work, in a rather non-obvious way.

I felt this is worth mentioning in the comments, so I did so.
--
vda
_______________________________________________
busybox mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/busybox

Reply via email to