diff --git a/CHANGES b/CHANGES index f2795b2..c62a185 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 0.19: add io_socketpair add io_passfd and io_receivefd (and test/fdpassing.c) + io_trywrite and io_waitwrite not ignore SIGPIPE 0.18: make libowfat compile on BSD again (sorry, and thanks to everyone who diff --git a/io/io_canread.3 b/io/io_canread.3 new file mode 100644 index 0000000..b40a7df --- /dev/null +++ b/io/io_canread.3 @@ -0,0 +1,28 @@ +.TH io_canread 3 +.SH NAME +io_canread \- return a file descriptor that can be read from +.SH SYNTAX +.B #include + +int64 \fBio_canread\fP(); +.SH DESCRIPTION +io_canread returns the next file descriptor that can be read from. +You have to have used io_wantread() on the file descriptor earlier, and +you have to have called io_wait() or io_waituntil(). + +These functions then keep an internal data structure on which +descriptors were reported readable by the operating system. + +Please note that there is no guarantee that there still is data that can +be read from the descriptor, just that there was data when io_wait() or +io_waituntil() were called. Another process could have read the data +before you. Look at the result from io_tryread(). + +If there are no more descriptors that you can write to without blocking, +io_canwrite will return -1. In this case you should call io_wait() or +io_waituntil() again. + +You should use io_tryread(3) to read from the descriptor, not plain +read(2). If you use read(2) and you get EAGAIN, call io_eagain(3). +.SH "SEE ALSO" +io_wait(3), io_canwrite(3), io_eagain(3) diff --git a/io/io_canwrite.3 b/io/io_canwrite.3 new file mode 100644 index 0000000..e57c9a3 --- /dev/null +++ b/io/io_canwrite.3 @@ -0,0 +1,29 @@ +.TH io_canwrite 3 +.SH NAME +io_canwrite \- return a file descriptor that can be written to +.SH SYNTAX +.B #include + +int64 \fBio_canwrite\fP(); +.SH DESCRIPTION +io_canwrite returns the next file descriptor that can be written to. +You have to have used io_wantwrite() on the file descriptor earlier, and +you have to have called io_wait() or io_waituntil(). + +These functions then keep an internal data structure on which +descriptors were reported writable by the operating system. + +Please note that there is no guarantee that you can still write data +without blocking to that descriptor, just that you could when io_wait() +or io_waituntil() were called. Another process could have written +something before you. Look at the result from io_trywrite(). + +If there are no more descriptors that you can write to without blocking, +io_canwrite will return -1. In this case you should call io_wait() or +io_waituntil() again. + +You should only use io_trywrite(3), io_sendfile(3) or iob_send(3) to +write to the file, not plain write(2). If you use write(2) and get +EAGAIN, call io_eagain(3). +.SH "SEE ALSO" +io_wait(3), io_canwrite(3) diff --git a/io/io_check.3 b/io/io_check.3 new file mode 100644 index 0000000..14ee3b0 --- /dev/null +++ b/io/io_check.3 @@ -0,0 +1,11 @@ +.TH io_check 3 +.SH NAME +io_check \- check for new readable or writable descriptors +.SH SYNTAX +.B #include + +int64 \fBio_check\fP(); +.SH DESCRIPTION +io_check is like io_waituntil() with a deadline of now. +.SH "SEE ALSO" +io_wait(3), io_waituntil(3) diff --git a/io/io_close.3 b/io/io_close.3 new file mode 100644 index 0000000..fce20af --- /dev/null +++ b/io/io_close.3 @@ -0,0 +1,24 @@ +.TH io_close 3 +.SH NAME +io_close \- close a file descriptor +.SH SYNTAX +.B #include + +void \fBio_close\fP(int64 fd); +.SH DESCRIPTION +io_close eliminates the descriptor numbered \fIfd\fR. This usually does not +mean eliminating the object that the descriptor is talking to. +(For example, if a descriptor writes to a named disk file, closing the +descriptor will not remove the file; it simply removes one way of +writing to the file. On the other hand, a pipe disappears as soon as no +descriptors refer to it.) + +io_close has no return value; it always succeeds in deallocating the +memory used for the descriptor. If \fIfd\fR is not the number of a +descriptor, io_close has no effect. + +io_close() is like close(), but it also removes the descriptor from the +internal io_wait() data structures. If you called io_fd on a +descriptor, you need to use io_close to close it, not just close(). +.SH "SEE ALSO" +io_wait(3), io_canwrite(3) diff --git a/io/io_closeonexec.3 b/io/io_closeonexec.3 new file mode 100644 index 0000000..2e528de --- /dev/null +++ b/io/io_closeonexec.3 @@ -0,0 +1,14 @@ +.TH io_closeonexec 3 +.SH NAME +io_closeonexec \- mark a file descriptor non-inheritable +.SH SYNTAX +.B #include + +void \fBio_closeonexec\fP(int64 fd); +.SH DESCRIPTION +io_closeonexec marks a file descriptor non-inheritable. It will be +automatically closed if the process executes a different one. The +descriptor will not be automatically closed when you fork() a new child +process, though. +.SH "SEE ALSO" +io_wait(3), io_canwrite(3) diff --git a/io/io_createfile.3 b/io/io_createfile.3 new file mode 100644 index 0000000..9267935 --- /dev/null +++ b/io/io_createfile.3 @@ -0,0 +1,17 @@ +.TH io_createfile 3 +.SH NAME +io_createfile \- create a file +.SH SYNTAX +.B #include + +int \fBio_createfile\fP(int64* d,const char* s); +.SH DESCRIPTION +io_createfile sets d to the number of a new descriptor writing to the disk file +named \fIs\fR, and returns 1. If \fIs\fR already existed, it is truncated to length 0; +otherwise, it is created, with mode 0600. + +If something goes wrong, io_createfile sets \fIerrno\fR to indicate the error, and +returns 0; it does not create a new descriptor, and it does not touch d. +(However, it may have truncated or created the file.) +.SH "SEE ALSO" +io_readfile(3) diff --git a/io/io_dontwantread.3 b/io/io_dontwantread.3 new file mode 100644 index 0000000..cdf4e0e --- /dev/null +++ b/io/io_dontwantread.3 @@ -0,0 +1,18 @@ +.TH io_dontwantread 3 +.SH NAME +io_dontwantread \- signal that you do not want to read from a descriptor +.SH SYNTAX +.B #include + +void \fBio_dontwantread\fP(int64 fd); +.SH DESCRIPTION +io_dontwantread tells the next io_wait() that you don't want to read +from this descriptor for now. Call io_wantread() again if you change +your mind. + +You have to have called io_fd on the descriptor first (io_pipe and +io_socketpair do this for you). Waiting on descriptors only works for +sockets, fifos and pipes. It may also work on devices and TTYs, but +that is platform dependent -- you should not rely on that. +.SH "SEE ALSO" +io_wait(3), io_canread(3), io_wantread(3), io_fd(3) diff --git a/io/io_dontwantwrite.3 b/io/io_dontwantwrite.3 new file mode 100644 index 0000000..24e9941 --- /dev/null +++ b/io/io_dontwantwrite.3 @@ -0,0 +1,18 @@ +.TH io_dontwantwrite 3 +.SH NAME +io_dontwantwrite \- signal that you do not want to write to a descriptor +.SH SYNTAX +.B #include + +void \fBio_dontwantwrite\fP(int64 fd); +.SH DESCRIPTION +io_dontwantwrite tells the next io_wait() that you don't want to write +to this descriptor for now. Call io_wantwrite() again if you change +your mind. + +You have to have called io_fd on the descriptor first (io_pipe and +io_socketpair do this for you). Waiting on descriptors only works for +sockets, fifos and pipes. It may also work on devices and TTYs, but +that is platform dependent -- you should not rely on that. +.SH "SEE ALSO" +io_wait(3), io_canwrite(3), io_wantwrite(3), io_fd(3) diff --git a/io/io_eagain.3 b/io/io_eagain.3 new file mode 100644 index 0000000..c582c87 --- /dev/null +++ b/io/io_eagain.3 @@ -0,0 +1,20 @@ +.TH io_eagain 3 +.SH NAME +io_eagain \- tell io_wait that you got an EAGAIN +.SH SYNTAX +.B #include + +void \fBio_eagain\fP(int64 fd); +.SH DESCRIPTION +If io_wait() said that you can read from a descriptor, you try to read +from it, and you get EAGAIN, you need to tell io_wait(). This is +important for edge triggered event notification schemes like Linux 2.4's +SIGIO, or you will lose events. + +io_tryread(), io_trywrite() and io_sendfile() and iob_send() already +take care of this for you. + +This function is only of interest if you integrate io_wait() with legacy +code that uses read/write directly. +.SH "SEE ALSO" +io_wait(3) diff --git a/io/io_fd.3 b/io/io_fd.3 new file mode 100644 index 0000000..5cfa801 --- /dev/null +++ b/io/io_fd.3 @@ -0,0 +1,19 @@ +.TH io_fd 3 +.SH NAME +io_fd \- prepare descriptor for io_wait +.SH SYNTAX +.B #include + +void \fBio_fd\fP(int64 fd); +.SH DESCRIPTION +If you want to use io_canread() and io_canwrite() on a descriptor, you +have to use io_wait() on it first, and io_wait() has to know which +descriptors you are interested in. Use io_fd() for this. + +io_pipe and io_socketpair already call io_fd for you. + +Waiting on descriptors only works for sockets, fifos and pipes. It may +also work on devices and TTYs, but that is platform dependent -- you +should not rely on that. It does not work on files. +.SH "SEE ALSO" +io_wait(3), io_wantread(3), io_canread(3), io_eagain(3), io_nonblock(3) diff --git a/io/io_finishandshutdown.3 b/io/io_finishandshutdown.3 new file mode 100644 index 0000000..16da9f0 --- /dev/null +++ b/io/io_finishandshutdown.3 @@ -0,0 +1,14 @@ +.TH io_finishandshutdown 3 +.SH NAME +io_finishandshutdown \- deallocate internal data structures +.SH SYNTAX +.B #include + +void \fBio_finishandshutdown\fP(); +.SH DESCRIPTION +io_finishandshutdown deallocates the internal data structures of +io_wait(). This only makes sense if you run your program in a malloc +checker and want to eliminate the false alarms. Your OS will free data +structures automatically on process termination. +.SH "SEE ALSO" +io_wait(3) diff --git a/io/io_getcookie.3 b/io/io_getcookie.3 new file mode 100644 index 0000000..7847dca --- /dev/null +++ b/io/io_getcookie.3 @@ -0,0 +1,23 @@ +.TH io_getcookie 3 +.SH NAME +io_getcookie \- retrieve cookie +.SH SYNTAX +.B #include + +void* \fBio_getcookie\fP(int64 fd); +.SH DESCRIPTION +io_getcookie retrieves a cookie (pointer to some anonymous data +structure you associated with this descriptor). + +Use io_setcookie(3) to associate a cookie with a descriptor. + +If you did not associate a cookie with this descriptor, io_getcookie +returns NULL. + +The idea is that you put the state associated with a TCP connection you +serve in a common struct. Then you do not need to have your own data +structure to retrieve the state for a connection, you can just use the +data structure io_wait already maintains. The lookup works in constant +time and should not cause any cache misses. +.SH "SEE ALSO" +io_wait(3), io_setcookie(3) diff --git a/io/io_nonblock.3 b/io/io_nonblock.3 new file mode 100644 index 0000000..b9ac3ff --- /dev/null +++ b/io/io_nonblock.3 @@ -0,0 +1,43 @@ +.TH io_nonblock 3 +.SH NAME +io_nonblock \- switch to non-blocking I/O +.SH SYNTAX +.B #include + +void \fBio_nonblock\fP(int64 fd); +.SH DESCRIPTION +io_nonblock puts UNIX descriptor fd into ``non-blocking mode.'' Calling +io_nonblock(\fIfd\fR) before io_fd(\fIfd\fR) makes io_tryread and +io_trywrite faster and more efficient. + +Actually, current UNIX kernels do not support non-blocking descriptors; they +support non-blocking open files. Furthermore, many programs will break if they +encounter non-blocking mode. This means that you must not use io_nonblock for a +descriptor inherited from another program. + +io_nonblock has no return value; it always succeeds. If d is not the number of +a UNIX descriptor, io_nonblock has no effect. + +If io_fd is given a descriptor in blocking mode, io_tryread and io_trywrite go +through the following contortions to avoid blocking: + +.RS 0 +.nr step 1 1 +.IP \n[step] 3 +Stop if poll says that the descriptor is not ready. Otherwise there's a good +chance, but not a guarantee: even if poll says the descriptor is ready, the +descriptor might not be ready a moment later. (Furthermore, poll can fail on +some systems.) +.IP \n+[step] +Catch SIGALRM. SIGALRM must not be blocked, and must not be used elsewhere in +the program. +.IP \n+[step] +Set an interval timer so that any blocking call will be interrupted by SIGALRM +within 10 milliseconds. (Current UNIX kernels do not allow any shorter +interval.) Of course, this may still mean a 10-millisecond delay. +.RE + +If io_fd is given a descriptor in non-blocking mode (or a descriptor for a +regular disk file), io_tryread and io_trywrite avoid these contortions. +.SH "SEE ALSO" +io_wait(3), io_canwrite(3) diff --git a/io/io_passfd.3 b/io/io_passfd.3 new file mode 100644 index 0000000..ce345f9 --- /dev/null +++ b/io/io_passfd.3 @@ -0,0 +1,19 @@ +.TH io_passfd 3 +.SH NAME +io_passfd \- pass a file descriptor over a Unix Domain socket +.SH SYNTAX +.B #include + +int \fBio_passfd\fP(int64 sock,int64 fd); +.SH DESCRIPTION +io_passfd transfers the file descriptor \fIfd\fR over the Unix Domain +socket \fIsock\fR. This works much like dup(2), only that the copy of +the descriptor appears not in this process but at the other end of the +Unix Domain socket (which therefore must be a process on the same +system). + +The peer can then use io_receivefd(3) to receive the file descriptor. + +Note that the passed descriptor stays open in the sending process. +.SH "SEE ALSO" +io_receivefd(3) diff --git a/io/io_pipe.3 b/io/io_pipe.3 new file mode 100644 index 0000000..03af8be --- /dev/null +++ b/io/io_pipe.3 @@ -0,0 +1,28 @@ +.TH io_pipe 3 +.SH NAME +io_pipe \- create a Unix pipe +.SH SYNTAX +.B #include + +int \fBio_pipe\fP(int64 pfd[2]); +.SH DESCRIPTION +io_pipe creates a new UNIX ``pipe.'' The pipe can receive data and provide +data; any bytes written to the pipe can then be read from the pipe in the same +order. + +A pipe is typically stored in an 8192-byte memory buffer; the exact number +depends on the UNIX kernel. Bytes are written to the end of the buffer and read +from the beginning of the buffer. Once a byte has been read, it is eliminated +from the buffer, making space for another byte to be written; readers cannot +``rewind'' a pipe to read old data. Once 8192 bytes have been written to the +buffer, the pipe will not be ready for further writing until some of the bytes +have been read. Once all the bytes written have been read, the pipe will not be +ready for further reading until more bytes are written. + +io_pipe sets \fId\fR[0] to the number of a new descriptor reading from the pipe, and +sets \fId\fR[1] to the number of a new descriptor writing to the pipe. It then +returns 1 to indicate success. If something goes wrong, io_pipe returns 0, +setting errno to indicate the error; in this case it frees any memory that it +allocated for the new pipe, and it leaves \fId\fR alone. +.SH "SEE ALSO" +io_readfile(3), io_createfile(3), io_socketpair(3) diff --git a/io/io_readfile.3 b/io/io_readfile.3 new file mode 100644 index 0000000..37a69e8 --- /dev/null +++ b/io/io_readfile.3 @@ -0,0 +1,15 @@ +.TH io_readfile 3 +.SH NAME +io_readfile \- open a file for reading +.SH SYNTAX +.B #include + +int \fBio_readfile\fP(int64* d,const char* s); +.SH DESCRIPTION +io_readfile sets d to the number of a new descriptor reading from the +disk file named \fIs\fR, and returns 1. + +If something goes wrong, io_readfile sets \fIerrno\fR to indicate the error, and +returns 0; it does not create a new descriptor, and it does not touch d. +.SH "SEE ALSO" +io_readfile(3) diff --git a/io/io_receivefd.3 b/io/io_receivefd.3 new file mode 100644 index 0000000..b30fffb --- /dev/null +++ b/io/io_receivefd.3 @@ -0,0 +1,18 @@ +.TH io_receivefd 3 +.SH NAME +io_receivefd \- receive a file descriptor over a Unix Domain socket +.SH SYNTAX +.B #include + +int64 \fBio_receivefd\fP(int64 sock); +.SH DESCRIPTION +io_receivefd receives a file descriptor from the Unix Domain socket +\fIsock\fR. You can send a descriptor using io_passfd(3). + +io_receivefd returns -1 on error or the file descriptor. + +The underlying Unix API can send more than one descriptor at the time. +This function expects only one descriptor and will return the first +one if more than one were sent. +.SH "SEE ALSO" +io_passfd(3) diff --git a/io/io_sendfile.3 b/io/io_sendfile.3 new file mode 100644 index 0000000..ccaf53c --- /dev/null +++ b/io/io_sendfile.3 @@ -0,0 +1,20 @@ +.TH io_sendfile 3 +.SH NAME +io_sendfile \- send data from a file to a socket +.SH SYNTAX +.B #include + +int64 \fBio_sendfile\fP(int64 sock,int64 fd,uint64 off,uint64 n); +.SH DESCRIPTION +io_sendfile sends data from a file to a socket. This function tries to +do this in a way that allows high quality operating systems to do +zero-copy TCP (serving the data without copying the file contents to +user space or inside kernel space). This function will use special +operating system "sendfile" primitives where available and use memory +mapped I/O otherwise. It should always be faster than using read() and +write() to copy the data yourself. + +io_sendfile will return the number of bytes sent. If an error occurred, +it will return -1 for EAGAIN, or -3 otherwise. +.SH "SEE ALSO" +io_wait(3), io_waituntil(3) diff --git a/io/io_sendfile.c b/io/io_sendfile.c index 5efd288..b66696b 100644 --- a/io/io_sendfile.c +++ b/io/io_sendfile.c @@ -28,7 +28,9 @@ int64 io_sendfile(int64 s,int64 fd,uint64 off,uint64 n) { #include int64 io_sendfile(int64 out,int64 in,uint64 off,uint64 bytes) { - return sendfile64(out,in,off,bytes,0,0); + long long r=sendfile64(out,in,off,bytes,0,0); + if (r==-1 && errno!=EAGAIN) r=-3; + return r; } #elif defined (__sun__) && defined(__svr4__) @@ -40,7 +42,9 @@ int64 io_sendfile(int64 out,int64 in,uint64 off,uint64 bytes) { int64 io_sendfile(int64 out,int64 in,uint64 off,uint64 bytes) { off64_t o=off; - return sendfile64(out,in,&o,bytes); + long long r=sendfile64(out,in,&o,bytes); + if (r==-1 && errno!=EAGAIN) r=-3; + return r; } #elif defined(_AIX) diff --git a/io/io_setcookie.3 b/io/io_setcookie.3 new file mode 100644 index 0000000..f9d1b36 --- /dev/null +++ b/io/io_setcookie.3 @@ -0,0 +1,25 @@ +.TH io_setcookie 3 +.SH NAME +io_setcookie \- associate cookie with descriptor +.SH SYNTAX +.B #include + +void \fBio_setcookie\fP(int64 fd,void* cookie); +.SH DESCRIPTION +io_setcookie associates a cookie (pointer to some anonymous data +structure) with this descriptor. Only one cookie can be associated with +a descriptor. + +Use io_getcookie(3) to retrieve the cookie for a descriptor (usually +after io_canread or io_canwrite brought it to your attention). + +Please note that io_close does not deallocate your cookie. You need to +do that yourself. + +The idea is that you put the state associated with a TCP connection you +serve in a common struct. Then you do not need to have your own data +structure to retrieve the state for a connection, you can just use the +data structure io_wait already maintains. The lookup works in constant +time and should not cause any cache misses. +.SH "SEE ALSO" +io_wait(3), io_getcookie(3) diff --git a/io/io_socketpair.3 b/io/io_socketpair.3 new file mode 100644 index 0000000..42c52e1 --- /dev/null +++ b/io/io_socketpair.3 @@ -0,0 +1,18 @@ +.TH io_socketpair 3 +.SH NAME +io_socketpair \- create a pair of sockets +.SH SYNTAX +.B #include + +int \fBio_socketpair\fP(int64 pfd[2]); +.SH DESCRIPTION +io_socketpair creates a new UNIX socket pair and writes both descriptors +to \fId\fR. The socket pair works much like a pipe, but it is +bidirectional (i.e. both descriptors are for reading and writing). + +io_socketpair returns 1 to indicate success. If something goes wrong, +io_socketpair returns 0, setting errno to indicate the error; in this +case it frees any memory that it allocated for the new socketpair, and +it leaves \fId\fR alone. +.SH "SEE ALSO" +io_readfile(3), io_createfile(3), io_pipe(3) diff --git a/io/io_timeout.3 b/io/io_timeout.3 new file mode 100644 index 0000000..d05fbb4 --- /dev/null +++ b/io/io_timeout.3 @@ -0,0 +1,18 @@ +.TH io_timeout 3 +.SH NAME +io_timeout \- set time limit on descriptor +.SH SYNTAX +.B #include + +void \fBio_timeout\fP(int64 fd,tai6464 deadline); +.SH DESCRIPTION +The io library keeps track of an optional ``timeout'' for each descriptor. The +timeout is a specific moment in time, stored in a tai6464 variable. + +io_timeout(\fId\fR,\fIt\fR) sets the timeout for descriptor \fId\fR to \fIt\fR. + +io_timeout has no return value; it always succeeds. (Space to store the timeout +was already allocated as part of the descriptor.) It has no effect if \fId\fR is not +the number of a descriptor. +.SH "SEE ALSO" +io_waituntil(3), io_timeouted(3) diff --git a/io/io_timeouted.3 b/io/io_timeouted.3 new file mode 100644 index 0000000..e2b2af6 --- /dev/null +++ b/io/io_timeouted.3 @@ -0,0 +1,17 @@ +.TH io_timeouted 3 +.SH NAME +io_timeouted \- return a file descriptor over deadline +.SH SYNTAX +.B #include + +int64 \fBio_timeouted\fP(); +.SH DESCRIPTION +io_timeouted returns the next file descriptor that is past it's deadline. +If no descriptors are past their deadlines, it returns -1. + +After io_timeouted() returned -1, you should wait a second before +calling it again. Checking for connections past their deadline involves +walking through the whole data structure, which may thrash the CPU +cache needlessly. Treat it as an expensive operation. +.SH "SEE ALSO" +io_wait(3), io_timeout(3) diff --git a/io/io_tryread.3 b/io/io_tryread.3 new file mode 100644 index 0000000..ff353f2 --- /dev/null +++ b/io/io_tryread.3 @@ -0,0 +1,42 @@ +.TH io_tryread 3 +.SH NAME +io_tryread \- read from a descriptor without blocking +.SH SYNTAX +.B #include + +int \fBio_tryread\fP(int64 fd,char* buf,int64 len); +.SH DESCRIPTION +io_tryread tries to read \fIlen\fR bytes of data from descriptor +\fIfd\fR into buf[0], buf[1], ..., buf[len-1]. (The effects are +undefined if \fIlen\fR is 0 or smaller.) There are several possible +results: + +.RS 0 +.IP \[bu] 3 +o_tryread returns an integer between 1 and \fIlen\fR: This number of bytes was +available for immediate reading; the bytes were read into the beginning +of \fIbuf\fR. Note that this number can be, and often is, smaller than \fIlen\fR; +you must not assume that io_tryread always succeeds in reading exactly +\fIlen\fR bytes. +.IP \[bu] +io_tryread returns 0: No bytes were read, because the descriptor is at +end of file. For example, this descriptor has reached the end of a disk +file, or is reading an empty pipe that has been closed by all writers. +.IP \[bu] +io_tryread returns -1, setting \fIerrno\fR to EAGAIN: No bytes were read, +because the descriptor is not ready. For example, the descriptor is +reading an empty pipe that could still be written to. +.IP \[bu] +io_tryread returns -3, setting \fIerrno\fR to something other than +EAGAIN: No bytes were read, because the read attempt encountered a +persistent error, such as a serious disk failure (EIO), an unreachable +network (ENETUNREACH), or an invalid descriptor number (EBADF). +.RE + +io_tryread does not pause waiting for a descriptor that is not ready. +If you want to pause, use io_waitread or io_wait. + +You can make io_tryread faster and more efficient by making +the socket non-blocking with io_nonblock(). +.SH "SEE ALSO" +io_nonblock(3), io_waitread(3), io_tryreadtimeout(3) diff --git a/io/io_tryreadtimeout.3 b/io/io_tryreadtimeout.3 new file mode 100644 index 0000000..c0b73e7 --- /dev/null +++ b/io/io_tryreadtimeout.3 @@ -0,0 +1,23 @@ +.TH io_tryreadtimeout 3 +.SH NAME +io_tryreadtimeout \- read from a descriptor without blocking +.SH SYNTAX +.B #include + +int \fBio_tryreadtimeout\fP(int64 fd,char* buf,int64 len); +.SH DESCRIPTION +io_tryreadtimeout is identical to io_tryread, with the following +exception: if + +.RS 0 +.IP \[bu] 3 +io_tryread returns -1 (the descriptor is not ready for reading), and +.IP \[bu] +the descriptor has a timeout, and +.IP \[bu] +the read attempt was after the descriptor's timeout, +.RE + +then io_tryreadtimeout instead returns -2, with errno set to ETIMEDOUT. +.SH "SEE ALSO" +io_nonblock(3), io_waitread(3), io_tryread(3) diff --git a/io/io_trywrite.3 b/io/io_trywrite.3 new file mode 100644 index 0000000..95835f4 --- /dev/null +++ b/io/io_trywrite.3 @@ -0,0 +1,51 @@ +.TH io_trywrite 3 +.SH NAME +io_trywrite \- write to a descriptor without blocking +.SH SYNTAX +.B #include + +int \fBio_trywrite\fP(int64 fd,const char* buf,int64 len); +.SH DESCRIPTION +io_trywrite tries to write \fIlen\fR bytes of data from +buf[0], buf[1], ..., buf[len-1] to descriptor \fIfd\fR. (The effects are +undefined if \fIlen\fR is 0 or smaller.) There are several possible +results: + +.RS 0 +.IP \[bu] 3 +o_trywrite returns an integer between 1 and \fIlen\fR: This number of bytes was +immediately written from the beginning of \fIbuf\fR. +Note that this number can be, and often is, smaller than \fIlen\fR; +you must not assume that io_trywrite always succeeds in writing exactly +\fIlen\fR bytes. +.IP \[bu] +io_trywrite returns -1, setting \fIerrno\fR to EAGAIN: No bytes were +written, because the descriptor is not ready. For example, the +descriptor is writing to a full pipe that could still be read. +.IP \[bu] +io_trywrite returns -3, setting \fIerrno\fR to something other than +EAGAIN: No bytes were written, because the write attempt encountered a +persistent error, such as a serious disk failure (EIO), an unreachable +network (ENETUNREACH), or an invalid descriptor number (EBADF). +.RE + +io_trywrite does not pause waiting for a descriptor that is not ready. +If you want to pause, use io_waitread or io_wait. + +You can make io_trywrite faster and more efficient by making +the socket non-blocking with io_nonblock(). + +Once upon a time, many UNIX programs neglected to check the success of +their writes. They would often encounter EPIPE, and would blithely +continue writing, rather than exiting with an appropriate exit code. The +UNIX kernel developers decided to send a SIGPIPE signal, which +terminates the process by default, along with returning EPIPE. This +papers over the problem without fixing it: the same programs ignore +other errors such as EIO. One hopes that the programs have been fixed by +now; kernels nevertheless continue to generate the SIGPIPE signal. The +first time io_trywrite or io_waitwrite is called, it arranges for +SIGPIPE to be ignored. (Technically, for SIGPIPE to be caught by an +empty signal handler, so this doesn't affect child processes.) Do not +use SIGPIPE elsewhere in the program. +.SH "SEE ALSO" +io_nonblock(3), io_waitread(3), io_trywritetimeout(3) diff --git a/io/io_trywrite.c b/io/io_trywrite.c index 820d03f..c9b2cd5 100644 --- a/io/io_trywrite.c +++ b/io/io_trywrite.c @@ -9,6 +9,7 @@ int64 io_trywrite(int64 d,const char* buf,int64 len) { struct itimerval old,new; struct pollfd p; io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + io_sigpipe(); if (!e) { errno=EBADF; return -3; } if (!e->nonblock) { p.fd=d; diff --git a/io/io_trywritetimeout.3 b/io/io_trywritetimeout.3 new file mode 100644 index 0000000..fe479b0 --- /dev/null +++ b/io/io_trywritetimeout.3 @@ -0,0 +1,23 @@ +.TH io_trywritetimeout 3 +.SH NAME +io_trywritetimeout \- write to a descriptor without blocking +.SH SYNTAX +.B #include + +int \fBio_trywritetimeout\fP(int64 fd,const char* buf,int64 len); +.SH DESCRIPTION +io_trywritetimeout is identical to io_trywrite, with the following +exception: if + +.RS 0 +.IP \[bu] 3 +io_trywrite returns -1 (the descriptor is not ready for writing), and +.IP \[bu] +the descriptor has a timeout, and +.IP \[bu] +the write attempt was after the descriptor's timeout, +.RE + +then io_trywritetimeout instead returns -2, with errno set to ETIMEDOUT. +.SH "SEE ALSO" +io_nonblock(3), io_waitwrite(3), io_trywrite(3) diff --git a/io/io_waitwrite.c b/io/io_waitwrite.c index 6c434c6..a187826 100644 --- a/io/io_waitwrite.c +++ b/io/io_waitwrite.c @@ -7,6 +7,7 @@ int64 io_waitwrite(int64 d,const char* buf,int64 len) { long r; struct pollfd p; io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + io_sigpipe(); if (!e) { errno=EBADF; return -3; } if (e->nonblock) { again: diff --git a/io/io_wantread.3 b/io/io_wantread.3 new file mode 100644 index 0000000..f98e194 --- /dev/null +++ b/io/io_wantread.3 @@ -0,0 +1,21 @@ +.TH io_wantread 3 +.SH NAME +io_wantread \- signal that you want to read from a descriptor +.SH SYNTAX +.B #include + +void \fBio_wantread\fP(int64 fd); +.SH DESCRIPTION +io_wantread tells the next io_wait() that you want to read from this +descriptor. Call io_dontwantread() again if you change your mind. + +The next time you call io_wait(), it will look whether this descriptor +becomes readable, too. You can then use io_canread() to check whether +the descriptor has become readable. + +You have to have called io_fd on the descriptor first (io_pipe and +io_socketpair do this for you). Waiting on descriptors only works for +sockets, fifos and pipes. It may also work on devices and TTYs, but +that is platform dependent -- you should not rely on that. +.SH "SEE ALSO" +io_wait(3), io_canread(3), io_wantread(3), io_fd(3) diff --git a/io/io_wantwrite.3 b/io/io_wantwrite.3 new file mode 100644 index 0000000..78b0dd1 --- /dev/null +++ b/io/io_wantwrite.3 @@ -0,0 +1,21 @@ +.TH io_wantwrite 3 +.SH NAME +io_wantwrite \- signal that you want to write to a descriptor +.SH SYNTAX +.B #include + +void \fBio_wantwrite\fP(int64 fd); +.SH DESCRIPTION +io_wantwrite tells the next io_wait() that you want to write to this +descriptor. Call io_dontwantwrite() again if you change your mind. + +The next time you call io_wait(), it will look whether this descriptor +becomes writeable, too. You can then use io_canwrite() to check whether +the descriptor has become writable. + +You have to have called io_fd on the descriptor first (io_pipe and +io_socketpair do this for you). Waiting on descriptors only works for +sockets, fifos and pipes. It may also work on devices and TTYs, but +that is platform dependent -- you should not rely on that. +.SH "SEE ALSO" +io_wait(3), io_canwrite(3), io_wantwrite(3), io_fd(3) diff --git a/io_internal.h b/io_internal.h index 170892b..57cb5c0 100644 --- a/io_internal.h +++ b/io_internal.h @@ -71,4 +71,6 @@ extern long alt_firstwrite; int64 io_waituntil2(int64 milliseconds); +void io_sigpipe(void); + #define debug_printf(x)