IOPROC(3)
π 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 π«Άπ
π¦ where this page comes from
- alpine 3.24: plan9port-doc 0_git20260208-r0 Β· LPL-1.02
- arch rolling: plan9port 20261021-2 Β· custom
- nixos 26.05: plan9port 0-unstable-2025-11-10
- nixos 26.05: plan9port-wayland 0-unstable-2025-11-26
- nixpkgs unstable: plan9port 0-unstable-2025-11-10
- nixpkgs unstable: plan9port-wayland 0-unstable-2025-11-26
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms