Do Files want to be Actors?

In the world of high performance linux apps, io_uring is changing how we communicate with the operating system.

The io_uring model is based on two queues:

Combined with an event loop, we have a new way of doing IO on linux without waiting on syscalls to complete before yielding back control to the program. You simply put things on the queue, and then you get right back to computing.

The async request sent to the kernel has this structure (see man 7 io_uring):

      struct io_uring_sqe {
                   __u8    opcode;         /* type of operation for this sqe */
                   __s32   fd;             /* file descriptor to do IO on */
                   __u64   off;            /* offset into file */
                   __u64   addr;           /* pointer to buffer or iovecs */
                   __u32   len;            /* buffer size or number of iovecs */
                   __u64   user_data;      /* data to be passed back at completion time */
                   __u8    flags;          /* IOSQE_ flags */
                   ...
           };

The opcode represents the OS level syscall, ie open, read, write etc. So even function call semantics are abstracted away by this OS interface.

Re-wind back to the 1970s, where Carl Hewitt and Henry Baker wrote the first paper about the actor model; Laws for Communicating Parallel Processes. In it they describe a base model for concurrent computations:

The action in the actor model consists of objects called actors sending messages to other actors, called the targets of those messages...an event E is the receipt of the message "message(E)" by the actor "target(E)". Upon receipt of this message in the event E, the target consults its script (the actor analogue of program text), and using its current local state and the message as parameters, sends new messages to other actors and computes a new local state for itself.

Is it just me, or are these two seemingly unrelated schools of computing converging on the exact same idea? You send messages to some target (ie, the file descriptor). You receive (or maybe you don't receive) responses back asynchronously. We're not concerned with function calls, or mutexes, or spinlocks - they're implementation details. We're concerned with sending messages concurrently.

The game has changed. Our operating systems want to do things asynchronously, on their own terms. They'll tell you when they're done. Maybe this is a new era. Maybe making syscalls from 1970s Unix directly is like a remote procedure call to another machine - a leaky abstraction, a feeble attempt to impose your old mental model onto a new reality.