IOPROC(3)

πŸ“¦ version 0_git20260208, in alpine 3.24, arch rolling, nixos 26.05, nixpkgs unstable

πŸ’… IOPROC(3) β€” Library Functions Manual πŸŽ€

okay bestie, listen up, because this one is literally the blueprint for making your threaded programs not choke on I/O 🌸 this is from the Library Functions Manual, and it's all about those little helper procs that do the boring waiting so your main threads can keep slaying ✨


🌈 NAME 🌈

closeioproc, iocall, ioclose, iointerrupt, iodial, ioopen, ioproc, ioread, ioread9pmsg, ioreadn, iorecvfd, iosendfd, iosleep, iowrite β€” the slave I/O processes for your threaded programs, no cap 🫢


✨ SYNOPSIS ✨

#include <u.h>
#include <libc.h>
#include <thread.h>

typedef struct Ioproc Ioproc;

Ioproc* ioproc(void);
int     ioclose(Ioproc *io, int fd);
int     iodial(Ioproc *io, char *addr, char *local, char *dir, char *cdfp);
int     ioopen(Ioproc *io, char *file, int omode);
long    ioread(Ioproc *io, int fd, void *a, long n);
int     ioread9pmsg(Ioproc *io, int fd, void *a, uint n);
long    ioreadn(Ioproc *io, int fd, void *a, long n);
int     iorecvfd(Ioproc *io, int socket);
int     iosendfd(Ioproc *io, int socket, int fd);
int     iosleep(Ioproc *io, long milli);
long    iowrite(Ioproc *io, int fd, void *a, long n);
void    iointerrupt(Ioproc *io);
void    closeioproc(Ioproc *io);
long    iocall(Ioproc *io, long (*op)(va_list *arg), ...);

πŸ’– DESCRIPTION πŸ’–

so these routines give you access to I/O inside slave procs, and the whole point is main character energy for your threads β€” because the actual I/O happens in a separate slave proc, all the other threads in the calling proc can keep running while your calling thread is just sitting there waiting for the I/O to finish. it's giving "multitasking queen" πŸ‘‘

Ioproc forks a brand-new slave proc and hands you back a pointer to the Ioproc associated with it. it uses mallocz and proccreate; and if either of those fails, it calls sysfatal instead of returning an error β€” so yeah, no graceful error return here, it just full-on dies πŸ’€

Ioclose, iodial, ioopen, ioread, ioread9pmsg, ioreadn, iorecvfd, iosendfd, iosleep, and iowrite all execute the similarly named library or system calls in the slave process associated with io (peep close(2), dial(3), open(3), read(3), fcall(3), sendfd(3), and sleep(3) for the deets). and bestie, hear me: it is an error to execute more than one call at a time in an I/O proc β€” one at a time, periodt πŸ™…β€β™€οΈ

Iointerrupt interrupts the call that's currently executing in the I/O proc. if no call is executing? iointerrupt is a no-op, literally does nothing, a little nothingburger πŸ”

Closeioproc terminates the I/O proc and frees the associated Ioproc πŸ¦‹

Iocall is the primitive you can use to implement more slave I/O routines. it arranges for op to be called in io's proc, with arg set to the variable parameter list, and returns the value that op returns. iconic little building block fr fr 🧚


πŸŽ€ EXAMPLE πŸŽ€

relay messages between two file descriptors while counting the total number of bytes seen β€” here's the whole vibe:

int tot;

void
relaythread(void *v)
{
    int *fd, n;
    char buf[1024];
    Ioproc *io;

    fd = v;
    io = ioproc();
    while((n = ioread(io, fd[0], buf, sizeof buf)) > 0){
        if(iowrite(io, fd[1], buf, n) != n)
            sysfatal("iowrite: %r");
        tot += n;
    }
    closeioproc(io);
}

void
relay(int fd0, int fd1)
{
    int fd[4];

    fd[0] = fd[3] = fd0;
    fd[1] = fd[2] = fd1;
    threadcreate(relaythread, fd, 8192);
    threadcreate(relaythread, fd+2, 8192);
}

the two relaythread instances are running in the same proc, so the common access to tot is safe β€” no race condition, we love to see it πŸ’…

and if you ever wondered how ioread itself is implemented, boom:

static long
_ioread(va_list *arg)
{
    int fd;
    void *a;
    long n;

    fd = va_arg(*arg, int);
    a = va_arg(*arg, void*);
    n = va_arg(*arg, long);
    return read(fd, a, n);
}

long
ioread(Ioproc *io, int fd, void *a, long n)
{
    return iocall(io, _ioread, fd, a, n);
}

πŸ”₯ SOURCE πŸ”₯

/src/libthread β€” that's where she lives 🏠


πŸ‘‘ SEE ALSO πŸ‘‘

dial(3), open(3), read(3), thread(3) β€” go read these too, iykyk πŸ“š


πŸ’€ BUGS πŸ’€

Iointerrupt is currently unimplemented β€” yeah, she's not done yet, don't come for her πŸ™ˆ

also, C99 disallows the use of pointers to va_list. so this interface will have to change to use pointers to a structure containing a va_list. future-us is gonna have to deal with that, bestie 😭


and that's the whole page, bestie β€” go forth and let your threads slay without ever blocking on I/O βœ¨πŸ’–πŸ¦‹

signing off from IOPROC(3), brought to you by Library Functions Manual β€” mwah, ily, see u in the next man page πŸ«ΆπŸŽ€