imlib2 doesn't handle EINTR #5
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
EINTR is a recoverable error, but imlib2 hard errors whenever it's encountered.
Here's one instance with write() which is also present in other places, use grep:
5c226954c1/src/modules/loaders/loader_bz2.c (L39-L40)
Here's one instance with fopen(), also present in other places:
5c226954c1/src/lib/image.c (L107-L109)
Here's a more complicated case, opendir() isn't defined to return EINTR by POSIX, but on OpenBSD it can:
5c226954c1/src/lib/file.c (L211-L216)
http://man.openbsd.org/opendir.3
I think the dir needs to be closed here, too:
5c226954c1/src/lib/file.c (L226-L229)
@guijan
In theory it looks like you are right. However, I have not been able to code something which demonstrates the problem (provoke EINTR errors) on my linux box.
If you can provide such an example I'll take a look at it. Needs to be on linux, I have no xyzBSD.
This issue has to my knowledge not come up before in the lifetime of imlib2 (20+ years), so I'd like an actual test case before starting to fix it.
And how about EAGAIN - shouldn't there be similar problems with that?
@NRK Should be fixed now.
EINTR on Linux and OpenBSD is only returned if the underlying medium is considered blocking, blocking means syscalls that use the fd could theoretically pause forever. The disk isn't considered blocking, pipes and FIFOs are, mounted network storage should be (I haven't checked). Even if you receive an interrupt while a syscall is operating on a fd backed by a medium that isn't considered blocking, it won't return. I don't know about other unices; the other BSDs are probably similar, and I don't know how Illumos, HaikuOS, Cygwin, and the proprietary unices behave.
Here's a program that triggers EINTR for write():
eintr.c:
Here's an example invocation of that program:
Here's a program that triggers EINTR inside an imlib function:
imlib2-eintr.c:
Here's an example invocation of that program:
EAGAIN is a thing, yes. I've heard some hearsay that the Linux sockets code does return EAGAIN. I haven't seen any code handle EAGAIN out there or run into it myself, so I'm not sure how to proceed.
Right, writing to a pipe :) It didn't cross my mind to check that path.
I tend to focus on loaders and forget about savers.
I'll fix writing to pipes in the savers.
All write() calls (e.g. the one in the bz2 loader) are to files created by mkstemp() in /tmp. I'd expect the underlying medium there to be non-blocking (RAM or local disk). So that should fail only on very peculiar setups, I think.
As things are, imlib2 will only load images from mmapable files, so if (in image.c) we should succeed in fopen()ing a pipe, mmap() will fail shortly after. Maybe it should be prevented to go there entirely by checking if we are dealing with a pipe (like it already is checked that it isn't a directory).
opendir() etc. is only called when loading modules (or fonts).
If there are pipes in that path then IMO the system is broken.
So maybe there could be a potential issue if network file systems are involved?
I don't know. I also think that is a highly unlikely scenario in the real world today.
So, for cases other than saving to a pipe I'll again await an actual test case.
Thanks for your input so far :)
https://linux.die.net/man/5/nfs (ctrl+f noeintr)
That manual says making NFS return EINTR was an optional and nondefault option in Linux kernels older than version 2.6.25, and that 2.6.25 and later versions never return EINTR.
https://manpages.ubuntu.com/manpages/bionic/man4/fuse.4.html (ctrl+f FUSE_INTERRUPT)
The FUSE API seems to leave continuing or returning EINTR or a short read/write up to the individual filesystem.
I don't think poking into the current implementation details of one kernel and writing programs according to those details and a particular set of use cases is the right way to do things. Handling EINTR where POSIX or OS-specific documentation mentions it is the right approach. I don't have a test case.
While I agree in general - other than the
write
I don't think it's worth dealing withEINTR
on the other cases - it's just busywork.And EINTR in specific is an insane headace - most kernels seem to have done the "right thing" by making EINTR as rare as possible. Here's one of my faviourite - the fact that every single
close
call is "incorrect" under current POSIX. From the linux manpage:EINTR is now handled in connection with fopen() in savers (and loaders).
With this I see no problems with EINTR.
In theory there may well be more problems, but I'm not convinced that there are in practice.
Until I am convinced that there are actual problems I'm not going to try to fix things that may not be a problem.
But I probably won't object much to others doing so :)
Could just set SA_RESTART to avoid all the pain. Personally, I use the following function at the top of my mains: https://git.sr.ht/~q3cpma/misc-tools/tree/master/item/misc.c#L45
That's something that should be done on the application side. Altering global state behind the user's back is NOT something I like to see in libraries.
(More generally, I prefer libraries that don't have any hidden global states at all. Imlib2 is guilty of it unfortunately - but it's not a dealbreaker - just a nitpick).
Right, forgot we were talking about a library for a second.
I'll leave things as they are for now.