While working on the homework problem 8.25 in CSAPP, I referred to its solution available at https://dreamanddead.github.io/CSAPP-3e-Solutions/. The provided solution includes the following C code:
#include <stdio.h>
#include "csapp.h"
sigjmp_buf buf;
void handler(int sig) {
/* jump */
siglongjmp(buf, 1);
}
char* tfgets(char* s, int size, FILE* stream) {
char* result;
if (!sigsetjmp(buf, 1)) {
alarm(5);
if (signal(SIGALRM, handler) == SIG_ERR)
unix_error("set alarm handler error");
return fgets(s, size, stream);
} else {
/* run out of time */
return NULL;
}
}
The tfgets function is fgets with a timeout feature. However, a concern arises regarding the safety of using siglongjmp to jump out of IO operations.
Is it safe to use siglongjmp to interrupt IO operations and exit them abruptly?
I wasn't able to find any document or related questions on stackoverflow.
No! It is not safe to do
siglongjmpfrom this handler. You can corrupt the state of the stream thatfgetsis operating on.Instead of doing
sigsetjmp/siglongjmp, have the signal handler set a global (e.g.volatile int alarm_fired;).Then, if the handler fires,
fgetsshould returnNULLanderrnoshould be set toEINTR.You can (safely) check for the return value and
errnovalue. You can also checkalarm_fired.However ...
Without the
siglongjmp,fgetswill (under linux/glibc, at least) absorb the signal.And, it's unclear what
errnowould be set to (e.g.EINTRvsEAGAIN) even if it did return immediately.So, you may have to configure the stream to allow this. Some possibilities:
cfmakerawFIONREADioctl.selectand/orpollonfileno(stream).readdirectly, possibly with some of the other items above.This seems primarily useful to get a timeout from a TTY device (e.g.
stdin) or a pipe/socket rather than a file. In those cases, to me,readseems to be the better bet.Side note: You have a race condition.
You set the signal handler (with
signal) after doingalarm. Under heavy system loading, the signal could fire before you have a chance to set the handler.Put the
alarmafter thesignalcall.