This time looking into non-blocking file access. I realise that the term has wider
application, but right now my scope is “communication files” (tty’s, pipes, network
connections).
As far as I can tell, prior to 1979 non-blocking access did not appear in the Spider
lineage, nor did it appear in the NCP Unix lineage. First appearance of non-blocking
behaviour seems to have been with Chesson’s multiplexed files where it is marked
experimental (an experiment within an experiment, so to say) in 1979.
The first appearance resembling the modern form appears to have been with SysIII in 1980,
where open() gains a O_NDELAY flag and appears to have had two uses: (i) when used on TTY
devices it makes open() return without waiting for a carrier signal (and subsequent read()
/ write() calls on the descriptor return with 0, until the carrier/data is there); and
(ii) on pipes and fifo’s, read() and write() will not block on an empty/full pipe, but
return 0 instead. This behaviour seems to have continued into SysVR1, I’m not sure when
EAGAIN came into use as a return value for this use case in the SysV lineage. Maybe with
SysVR3 networking?
In the Research lineage, the above SysIII approach does not seem to exist, although the V8
manual page for open() says under BUGS "It should be possible [...] to optionally
call open without the possibility of hanging waiting for carrier on communication lines.”
In the same location for V10 it reads "It should be possible to call open without
waiting for carrier on communication lines.”
The July 1981 design proposals for 4.2BSD note that SysIII non-blocking files are a useful
feature and should be included in the new system. In Jan/Feb 1982 this appears to be coded
up, although not all affected files are under SCCS tracking at that point in time.
Non-blocking behaviour is changed from the SysIII semantics, in that EWOULDBLOCK is
returned instead of 0 when progress is not possible. The non-blocking behaviour is
extended beyond TTY’s and pipes to sockets, with additional errors (such as EINPROGRESS).
At this time EWOULDBLOCK is not the same error number as EGAIN.
It would seem that the differences between the BSD and SysV lineages in this area
persisted until around 2000 or so.
Is that a fair summary?
- - -
I’m not quite sure why the Research lineage did not include non-blocking behaviour,
especially in view of the man page comments. Maybe it was seen as against the Unix
philosophy, with select() offering sufficient mechanism to avoid blocking (with open() the
hard corner case)?
In the SysIII code base, the FNDELAY flag is stored on the file pointer (i.e. with struct
file). This has the effect that the flag is shared between processes using the same
pointer, but can be changed in one process (using fcntl) without the knowledge of others.
It seems more logical to me to have made it a per-process flag (i.e. with struct user)
instead. In this aspect the SysIII semantics carry through to today’s Unix/Linux. Was this
semantic a deliberate design choice, or simply an overlooked complication?