2008-10-06 6 views
6

En resumen, ¿cómo probar la unidad una condición de error como EINTR en una llamada al sistema?Condiciones de error de prueba de la unidad - EINTR

Un ejemplo particular en el que estoy trabajando, que podría ser un caso en sí mismo, es si es necesario llamar a fclose nuevamente cuando devuelve EOF con (errno == EINTR). El comportamiento depende de la implementación de fclose:

// Given an open FILE *fp 
while (fclose(fp)==EOF && errno==EINTR) { 
    errno = 0; 
} 

Esta llamada puede ser peligroso si fp liberado cuando se produce EINTR. ¿Cómo puedo probar el manejo de errores para cuándo (errno == EINTR)?

Respuesta

2

No creo que haya una manera fácil de probar esto a voluntad.

EINTR se generará si se interrumpe el funcionamiento de fclose por una señal. Esto implica que fclose estaba en un hilo de fondo que recibió una señal cuando procesaba la solicitud de cierre.

Buena suerte tratando de reproducir ese conjunto de circunstancias.

3

Utilice una estructura de punteros de función para abstraer las funciones del sistema. Reemplace la llamada a flcose (fp) con algo como sys-> fclose (fp). En su prueba unitaria, cree una implementación de fclose que siempre devuelva EINTR y luego configure sys-> fclose en esa versión.

+0

Básicamente está hablando de burlarse de las llamadas al sistema, que es lo que hay que hacer en este caso. – tvanfosson

+0

Lamentablemente, se estaría burlando de la carne de lo que quiere probar. La pregunta es: ¿cuál es el estado de fp cuando la llamada al sistema * real * se interrumpe durante el cierre. – bmdhacks

4

En este caso particular, no es seguro volver a llamar a fclose(), ya que el estándar C dice que la secuencia está desasociada del archivo (y se vuelve indeterminada) incluso si la llamada falla.

3

Al mirar el código fuente del kernel de Linux, no puedo encontrar ningún controlador que incluso devuelva EINTR al archivo cerrado.

Si tuviera que reproducir este caso, podría escribir su propio controlador en Linux para devolver -EINTR en el método .release. Eche un vistazo al example code del libro Controladores de dispositivos Linux de O'Reilly. El proyecto Scull es uno de los más simples. Se podría cambiarlo por qué ser así:

int scull_release(struct inode *inode, struct file *filp) 
{ 
    return -EINTR; 
} 

Otra vez sin embargo, grepping a través del árbol de código fuente de Linux, no puedo encontrar cualquier conductor que devolvería EINTR en una estrecha.

EDIT - bien parece fusible - el sistema de archivos espacio-de-usuario podría ser capaz. Esto se usa para cosas como sshfs. Sin embargo, sigue siendo un caso marginal.

1

Como Kris sugiere, reemplace temporalmente las llamadas al "real" fclose() con su propia implementación definida en el código de prueba de su unidad. Su aplicación podría hacer algo tan simple como:

int my_fclose(FILE *fp) 
{ 
    errno = EINTR; 
    return EOF; 
} 

Pero, como señala Efervescencia, no debe llamar fclose() de nuevo como el comportamiento no está definido, por lo que no tiene que molestarse siquiera la comprobación de la condición.

La otra pregunta es si realmente debe preocuparse por esto; si su código de aplicación bloqueara todas las señales posibles (excepto SIGKILL y SIGSTOP) durante su fclose(), entonces no obtendría un EINTR y no tendría que preocuparse en absoluto.

1

Así es como lo probaría, por el mero hecho de probarlo, ya que fizzer nos recordó que llamar al fclose() dos veces no es seguro.

Es posible redefinir fclose() (o cualquier otra función de libc) en su programa con su propio comportamiento. En los sistemas tipo Unix, el enlazador no se queja, nunca lo intentó en Windows pero con cygwin. Por supuesto, esto evita que sus otras pruebas utilicen el fclose() real, por lo tanto, dicha prueba debe colocarse en un archivo ejecutable de prueba separado.

Aquí hay un ejemplo todo-en-uno con 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; 
} 
1

Um, creo que todos ustedes están ignorando algo muy importante.

fclose() no puede devolver EINTR. Ninguno puede fopen(), fwrite(), fread() o cualquiera de las funciones C I/O estándar.

Es solo cuando usted incursiona en las llamadas de E/S de bajo nivel como abrir (2), escribir (2) y seleccionar (2) que necesita manejar EINTR.

Leer las [Humor] páginas man

+9

http://linux.die.net/man/3/fclose dice: "La función fclose() también puede fallar y establecer errno para cualquiera de los errores especificados para las rutinas close (2), write (2) o fflush (3). " close (2) puede devolver EINTR. – user9876

1

creo que puede haber un problema con el procesamiento simultáneo de las señales y las condiciones de error que confirman.