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 siglongjmp
from this handler. You can corrupt the state of the stream that fgets
is 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, fgets
should return NULL
and errno
should be set to EINTR
.
You can (safely) check for the return value and errno
value. You can also check alarm_fired
.
However ...
Without the siglongjmp
, fgets
will (under linux/glibc, at least) absorb the signal.
And, it's unclear what errno
would be set to (e.g. EINTR
vs EAGAIN
) even if it did return immediately.
So, you may have to configure the stream to allow this. Some possibilities:
cfmakeraw
FIONREAD
ioctl.select
and/or poll
on fileno(stream)
.read
directly, 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, read
seems to be the better bet.
Side note: You have a race condition.
You set the signal handler (with signal
) after doing alarm
. Under heavy system loading, the signal could fire before you have a chance to set the handler.
Put the alarm
after the signal
call.