diff options
Diffstat (limited to 'doc/internals/notes-polling.txt')
-rw-r--r-- | doc/internals/notes-polling.txt | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/doc/internals/notes-polling.txt b/doc/internals/notes-polling.txt new file mode 100644 index 0000000..e7741a6 --- /dev/null +++ b/doc/internals/notes-polling.txt @@ -0,0 +1,192 @@ +2019-09-03 + +u8 fd.state; +u8 fd.ev; + + +ev = one of : + #define FD_POLL_IN 0x01 + #define FD_POLL_PRI 0x02 + #define FD_POLL_OUT 0x04 + #define FD_POLL_ERR 0x08 + #define FD_POLL_HUP 0x10 + +Could we instead have : + + FD_WAIT_IN 0x01 + FD_WAIT_OUT 0x02 + FD_WAIT_PRI 0x04 + FD_SEEN_HUP 0x08 + FD_SEEN_HUP 0x10 + FD_WAIT_CON 0x20 <<= shouldn't this be in the connection itself in fact ? + +=> not needed, covered by the state instead. + +What is missing though is : + - FD_DATA_PENDING -- overlaps with READY_R, OK if passed by pollers only + - FD_EOI_PENDING + - FD_ERR_PENDING + - FD_EOI + - FD_SHW + - FD_ERR + +fd_update_events() could do that : + + if ((fd_data_pending|fd_eoi_pending|fd_err_pending) && !(fd_err|fd_eoi)) + may_recv() + + if (fd_send_ok && !(fd_err|fd_shw)) + may_send() + + if (fd_err) + wake() + +the poller could do that : + HUP+OUT => always indicates a failed connect(), it should not lack ERR. Is this err_pending ? + + ERR HUP OUT IN + 0 0 0 0 => nothing + 0 0 0 1 => FD_DATA_PENDING + 0 0 1 0 => FD_SEND_OK + 0 0 1 1 => FD_DATA_PENDING|FD_SEND_OK + 0 1 0 0 => FD_EOI (|FD_SHW) + 0 1 0 1 => FD_DATA_PENDING|FD_EOI_PENDING (|FD_SHW) + 0 1 1 0 => FD_EOI |FD_ERR (|FD_SHW) + 0 1 1 1 => FD_EOI_PENDING (|FD_ERR_PENDING) |FD_DATA_PENDING (|FD_SHW) + 1 X 0 0 => FD_ERR | FD_EOI (|FD_SHW) + 1 X X 1 => FD_ERR_PENDING | FD_EOI_PENDING | FD_DATA_PENDING (|FD_SHW) + 1 X 1 0 => FD_ERR | FD_EOI (|FD_SHW) + + OUT+HUP,OUT+HUP+ERR => FD_ERR + +This reorders to: + + IN ERR HUP OUT + 0 0 0 0 => nothing + 0 0 0 1 => FD_SEND_OK + 0 0 1 0 => FD_EOI (|FD_SHW) + + 0 X 1 1 => FD_ERR | FD_EOI (|FD_SHW) + 0 1 X 0 => FD_ERR | FD_EOI (|FD_SHW) + 0 1 X 1 => FD_ERR | FD_EOI (|FD_SHW) + + 1 0 0 0 => FD_DATA_PENDING + 1 0 0 1 => FD_DATA_PENDING|FD_SEND_OK + 1 0 1 0 => FD_DATA_PENDING|FD_EOI_PENDING (|FD_SHW) + 1 0 1 1 => FD_EOI_PENDING (|FD_ERR_PENDING) |FD_DATA_PENDING (|FD_SHW) + 1 1 X X => FD_ERR_PENDING | FD_EOI_PENDING | FD_DATA_PENDING (|FD_SHW) + +Regarding "|SHW", it's normally useless since it will already have been done, +except on connect() error where this indicates there's no need for SHW. + +FD_EOI and FD_SHW could be part of the state (FD_EV_SHUT_R, FD_EV_SHUT_W). +Then all states having these bit and another one would be transient and need +to resync. We could then have "fd_shut_recv" and "fd_shut_send" to turn these +states. + +The FD's ev then only needs to update EOI_PENDING, ERR_PENDING, ERR, DATA_PENDING. +With this said, these are not exactly polling states either, as err/eoi/shw are +orthogonal to the other states and are required to update them so that the polling +state really is DISABLED in the end. So we need more of an operational status for +the FD containing EOI_PENDING, EOI, ERR_PENDING, ERR, SHW, CLO?. These could be +classified in 3 categories: read:(OPEN, EOI_PENDING, EOI); write:(OPEN,SHW), +ctrl:(OPEN,ERR_PENDING,ERR,CLO). That would be 2 bits for R, 1 for W, 2 for ctrl +or total 5 vs 6 for individual ones, but would be harder to manipulate. + +Proposal: + - rename fdtab[].state to "polling_state" + - rename fdtab[].ev to "status" + +Note: POLLHUP is also reported is a listen() socket has gone in shutdown() +TEMPORARILY! Thus we may not always consider this as a final error. + + +Work hypothesis: + +SHUT RDY ACT + 0 0 0 => disabled + 0 0 1 => active + 0 1 0 => stopped + 0 1 1 => ready + 1 0 0 => final shut + 1 0 1 => shut pending without data + 1 1 0 => shut pending, stopped + 1 1 1 => shut pending + +PB: we can land into final shut if one thread disables the FD while another + one that was waiting on it reports it as shut. Theorically it should be + implicitly ready though, since reported. But if no data is reported, it + will be reportedly shut only. And no event will be reported then. This + might still make sense since it's not active, thus we don't want events. + But it will not be enabled later either in this case so the shut really + risks not to be properly reported. The issue is that there's no difference + between a shut coming from the bottom and a shut coming from the top, and + we need an event to report activity here. Or we may consider that a poller + never leaves a final shut by itself (100) and always reports it as + shut+stop (thus ready) if it was not active. Alternately, if active is + disabled, shut should possibly be ignored, then a poller cannot report + shut. But shut+stopped seems the most suitable as it corresponds to + disabled->stopped transition. + +Now let's add ERR. ERR necessarily implies SHUT as there doesn't seem to be a +valid case of ERR pending without shut pending. + +ERR SHUT RDY ACT + 0 0 0 0 => disabled + 0 0 0 1 => active + 0 0 1 0 => stopped + 0 0 1 1 => ready + + 0 1 0 0 => final shut, no error + 0 1 0 1 => shut pending without data + 0 1 1 0 => shut pending, stopped + 0 1 1 1 => shut pending + + 1 0 X X => invalid + + 1 1 0 0 => final shut, error encountered + 1 1 0 1 => error pending without data + 1 1 1 0 => error pending after data, stopped + 1 1 1 1 => error pending + +So the algorithm for the poller is: + - if (shutdown_pending or error) reported and ACT==0, + report SHUT|RDY or SHUT|ERR|RDY + +For read handlers : + - if (!(flags & (RDY|ACT))) + return + - if (ready) + try_to_read + - if (err) + report error + - if (shut) + read0 + +For write handlers: + - if (!(flags & (RDY|ACT))) + return + - if (err||shut) + report error + - if (ready) + try_to_write + +For listeners: + - if (!(flags & (RDY|ACT))) + return + - if (err||shut) + pause + - if (ready) + try_to_accept + +Kqueue reports events differently, it says EV_EOF() on READ or WRITE, that +we currently map to FD_POLL_HUP and FD_POLL_ERR. Thus kqueue reports only +POLLRDHUP and not POLLHUP, so for now a direct mapping of POLLHUP to +FD_POLL_HUP does NOT imply write closed with kqueue while it does for others. + +Other approach, use the {RD,WR}_{ERR,SHUT,RDY} flags to build a composite +status in each poller and pass this to fd_update_events(). We normally +have enough to be precise, and this latter will rework the events. + +FIXME: Normally on KQUEUE we're supposed to look at kev[].fflags to get the error +on EV_EOF() on read or write. |