Mike Ditto writes:> I just got caught up on 3 days worth of interesting discussion; here are> some points that I didn't see made. Everything I say here about poll()> applies equally to select(), of course.

> Someone said that if regular files supported poll(), "tail -f" could use> poll() instead of sleep(). This is not true; it is a common> misconception about what poll actually does. A poll for reading doesn't> mean "wake me when there are data which I can read". It means "wake me> when a read() would not block interruptibly. A read() on a regular file> will never block interruptibly (at least by tradition, interruptible NFS> mounts notwithstanding). Regular files already support poll() fully and> correctly.

Yep.

> Someone said that poll on regular files is the same as async I/O. It> isn't. poll isn't I/O at all. Operations under any async I/O paradigm> actually perform I/O and notify asynchronously of completion. poll> never performs I/O, it only gives the caller stale information about> whether certain hypothetical I/O operations would block. This is one of> the flaws in select, poll, ioctl(FIONREAD) and similar kludges. Between> the time that the call returns and the time you try to do something> about it, things can change. If you are correctly using non-blocking> operations, this is probably only a performance problem (you would just> loop back into another poll), but if you assume that the information> from poll is more than a hint, you can reach deadlock or some other> failure.> > True async I/O requires that the actual I/O be asynchronous, not just a> mechanism for waiting until it is safe to do synchronous I/O.

Yep.

> Someone claimed that poll() could benefit from a wake-one implementation> or option. This doesn't make sense for two reasons. First, it just> doesn't make sense to have more than one context polling for the same> readiness condition, for the reasons above. Second, after the chosen> thread is awoken, what should happen to the other polling threads? They> are still in a poll() system call, one of the file descriptors they are> polling is ready, and there is no real guarantee that the awoken thread> will do anything with the ready fd any time soon. It may have other> ready fds or other internal processing to do first, or just be hosed in> general. The situation is one where the semantics of poll() say that> the other threads should wake up, but you "don't want" them to, but> there is no reasonable condition defined for when this special exception> period should end.> > poll() is a substitute for multithreading and just wasn't meant to work> well in conjunction with multithreading. Because of the nature of> poll() it is most robust to multiplex your polling of any one condition> through one thread and pass off the events to other threads, even though> this has unfortunate performance costs.

Yep.

> Someone wondered what it would mean to be polling a file descriptor and> have that file descriptor become closed (either by closing it in another> thread, or by using some new asynchronous form of polling). The proper> semantics of poll() say that the poll should immediately report the> closed file descriptor as "ready" because a read() would not block, it> would immediately return (with EBADF).

I don't recall seeing that. I did speculate what should happen if a FDis ready, causing a event message to be sent, and then the FD isclosed by a different thread. Different story from poll(2) and closingFDs, though.

> Some people proposed various forms of asynchronous I/O or asynchronous> polling. Asynchronous I/O is not a bad idea, but it is a very different> paradigm from that of Unix. I find the idea of asynchronous polling,> however, absolutely hideous. It just adds a messy, complex interface on> top of a flawed incomplete alternative to true threads. An asynchronous> programming model would be much better based on true async I/O, with> completion messages indicating that the I/O is complete.

Don't recall seeing that one.

> But all currently implemented or proposed asynchronous I/O schemes (of> course there probably some of which I am not aware) are still incomplete> alternatives to threads. There are many operations for which one needs> to wait that are not strictly I/O operations, or at least do not use the> read/write/file descriptor model. Examples: wait(), connect(),> pause(). Threads are really the only way to get everything right.

We want to be careful not to create zillions of threads, though, asthey have a cost too.

> The idea of implementing async I/O on top of threads seems completely> backward to me. If I were designing and implementing a OS from scratch,> I would have the kernel provide only asynchronous I/O, and no threads.> Every single system call would be asynchronous in structure, although> most operations would no doubt be marked as already completed when the> trap returned. One version of libc would wrap these asynchronous calls> inside synchronous wrappers, doing an explicit wait for completion after> every call, providing a traditional Unix-like model. Another version of> libc would implement threads, doing a wait for completion only when all> threads were sleeping. Another version of libc could make the> asynchronous interfaces themselves available.

If userland AIO (using threads) works just as well as a kernel-spaceAIO implementation, then there is no need for a kernel-spaceimplementation. We'll only know if both scheme are implemented andbenchmarked.

> Note that the above hypothetical system fails to give any parallelism> benefit to a single running process on a MP system. This is fine by me.> Threads are a programming paradigm, not a perfomance tool. If you want> parallelism, use fork().