Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You can siglongjump out of a signal handler [1]. If you sigsetjump right before doing a blocking call, you can reliably detect signals.

Another way to avoid the race condition in poll/select, before p{poll/select} were standardized, was to store the timeout parameter in a global variable and have the signal handler set it to zero. Finally there is the self pipe trick, which admittedly doesn't require EINTR at all.

[1] This is historical unix behaviour. At one time it was specified by the SUS, but it seems that it was dropped from more recent SUS/Posix standards.



> You can siglongjump out of a signal handler [1]. If you sigsetjump right before doing a blocking call, you can reliably detect signals.

The problem with that approach is that if the system call has already returned by the time the signal handler runs and jumps, the system call's return gets clobbered. So if for example you're doing blocking reads/writes, you don't know how many bytes you read or wrote.

If your only blocking syscall is level-driven polling this approach is fine but the self-pipe trick is easier.

I wrote (10 years ago) a library to do something similar reliably. It required custom wrappers for every system call of interest so I could know by the instruction pointer in the ucontext_t whether the system call had actually run yet or not. http://www.slamb.org/projects/sigsafe/ The library's a bit stale now; it doesn't do the vsyscall thing for example.


Duh! You are right, losing the results of partial read/writes is not acceptable. I guess on x86, completely unportably, you could check whether the current ip is pointing to a syscall/int instruction.


You probably want to jump if you're "just before" the syscall, too, though. So you end up with basically this:

syscall wrappers: https://github.com/scottlamb/sigsafe/blob/master/src/x86_64-... (probably could do something better with that thread local; and as I mentioned this isn't using vsyscall)

signal handler: https://github.com/scottlamb/sigsafe/blob/master/src/x86_64-... (although it bugs me now that I iterate the whole array if the instruction pointer's not in any of the syscalls)

and just to be sure, a race checker: https://github.com/scottlamb/sigsafe/blob/master/tests/race_...


> Another way to avoid the race condition in poll/select, before p{poll/select} were standardized, was to store the timeout parameter in a global variable and have the signal handler set it to zero.

That doesn't work. At some point, the userspace code has to copy the value from the global variable to the location where the system call calling convention expects it, some time later followed by execution of the system call trap (and usually there is even the libc system call wrapper in between the two that takes care of adapting the userspace calling convention to the system call calling convention). If your signal handler gets to run in between the two, the system call timeout will remain unchanged.

Or in other words: Yes, be afraid of signals, whatever clever scheme you come up with to handle them probably is wrong.


It works with select because it takes a pointer to the timespec object, which is read kernel side [1]. I misremembered poll doing the same thing but it takes a plain integer parameter.

[1] not guaranteed of course, but it is historical Unix behaviour

Edit: btw, i do agree with your final sentence!




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: