Reputation: 71
In short, how do you unit test an error condition such as EINTR on a system call.
One particular example I'm working on, which could be a case all by itself, is whether it's necessary to call fclose again when it returns EOF with (errno==EINTR). The behavior depends on the implementation of fclose:
// Given an open FILE *fp
while (fclose(fp)==EOF && errno==EINTR) {
errno = 0;
}
This call can be unsafe if fp freed when EINTR occurs. How can I test the error handling for when (errno==EINTR)?
Upvotes: 7
Views: 6518
Reputation: 9
I think that there might be an issue with the simultaneous processing of signals and confirming error conditions.
Upvotes: 1
Reputation:
Um, I think all of you are ignoring something pretty important.
fclose() cannot return EINTR. Neither can fopen(), fwrite(), fread(), or any of the standard C I/O functions.
It is only when you dabble in the low-level I/O calls like open(2), write(2), and select(2) that you need to handle EINTR.
Read the [funny] man pages
Upvotes: 1
Reputation: 35836
Here is how I would test it, just for the sake of testing since fizzer reminded us calling fclose()
twice is unsafe.
It is possible to redefine fclose()
(or any other function of libc) in your program with your own behavior. On Unix-like systems, the linker does not complain - never tried on Windows but with cygwin. Of course this prevents your other tests to use the real fclose()
, therefore such a test has to be put in a separate test executable.
Here is an all-in-one example with minunit.
#include <errno.h>
#include <stdio.h>
#include <stdbool.h>
/* from minunit.h : http://www.jera.com/techinfo/jtns/jtn002.html */
#define mu_assert(message, test) do { if (!(test)) return message; } while (0)
#define mu_run_test(test) do { char *message = test(); tests_run++; \
if (message) return message; } while (0)
int tests_run = 0;
bool fclose_shall_fail_on_EINTR = false;
//--- our implemention of fclose()
int fclose(FILE *fp) {
if (fclose_shall_fail_on_EINTR) {
errno = EINTR;
fclose_shall_fail_on_EINTR = false; //--- reset for next call
return EOF;
} else { return 0; }
}
//--- this is the "production" function to be tested
void uninterruptible_close(FILE *fp) {
// Given an open FILE *fp
while (fclose(fp)==EOF && errno==EINTR) {
errno = 0;
}
}
char *test_non_interrupted_fclose(void) {
FILE *theHandle = NULL; //--- don't care here
uninterruptible_close(theHandle);
mu_assert("test fclose wo/ interruption", 0 == errno);
return 0;
}
char *test_interrupted_fclose(void) {
FILE *theHandle = NULL; //--- don't care here
fclose_shall_fail_on_EINTR = true;
uninterruptible_close(theHandle);
mu_assert("test fclose wo/ interruption", 0 == errno);
return 0;
}
char *test_suite(void)
{
mu_run_test(test_non_interrupted_fclose);
mu_run_test(test_interrupted_fclose);
return 0;
}
int main(int ac, char **av)
{
char *result = test_suite();
printf("number of tests run: %d\n", tests_run);
if (result) { printf("FAIL: %s\n", result); }
return 0 != result;
}
Upvotes: 1
Reputation: 87124
As Kris suggests, temporarily replace calls to the "real" fclose()
with your own implementation defined in your unit test code. Your implementation could do something as simple as:
int my_fclose(FILE *fp)
{
errno = EINTR;
return EOF;
}
But, as fizzer points out, you shouldn't call fclose()
again as the behavior is undefined, so you needn't bother even checking for the condition.
The other question to ask is whether you really need to worry about this; if your application code were to block all possible signals (except SIGKILL and SIGSTOP) during your fclose()
then you wouldn't get a EINTR and wouldn't need to worry at all.
Upvotes: 1
Reputation: 16421
Looking at the linux kernel source code, I'm unable to find any drivers that even return EINTR upon file close.
If you absosmurfly had to reproduce this case, you could write your own driver in Linux to return -EINTR on the .release method. Take a look at the example code from O'Reilly's Linux Device Drivers book. The scull project is one of the simplest ones. You'd change it to be like this:
int scull_release(struct inode *inode, struct file *filp)
{
return -EINTR;
}
Again though, grepping through the linux source tree, I can't find any driver that would return EINTR on close.
EDIT - ok it looks like fuse - the userspace filesystem might be able to. This is used for things like sshfs. Still an edge case though.
Upvotes: 3
Reputation: 13806
In this particular case, it's not safe to call fclose() again, as the C standard says the stream is disassociated from the file (and becomes indeterminate) even if the call fails.
Upvotes: 4
Reputation:
Use a struct of function pointers to abstract out the system functions. Replace the call to flcose(fp) with something like sys->fclose(fp). In your unit test, create an implementation of fclose that always returns EINTR then set sys->fclose to that version.
Upvotes: 3
Reputation: 39084
I don't think there is an easy way to actually test this at will.
EINTR will be generated if the fclose operation is interrupted by a signal. This implies that fclose was in a background thread which received a signal as it was processing the close request.
Good luck trying to reproduce that set of circumstances.
Upvotes: 2