Условия ошибки модульного тестирования - EINTR - PullRequest
7 голосов
/ 07 октября 2008

Короче говоря, как выполнить модульное тестирование состояния ошибки, такого как EINTR, при системном вызове.

Один конкретный пример, над которым я работаю, который мог бы быть сам по себе, заключается в том, нужно ли снова вызывать fclose, когда он возвращает EOF с помощью (errno == EINTR). Поведение зависит от реализации fclose:

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

Этот вызов может быть небезопасным, если fp освобожден, когда происходит EINTR. Как я могу проверить обработку ошибок, когда (errno == EINTR)?

Ответы [ 8 ]

4 голосов
/ 07 октября 2008

В этом конкретном случае небезопасно снова вызывать fclose (), так как стандарт C говорит, что поток не связан с файлом (и становится неопределенным), даже если вызов не удался.

3 голосов
/ 07 октября 2008

Глядя на исходный код ядра Linux, я не могу найти драйверы, которые даже возвращают EINTR при закрытии файла.

Если вам absosmurfly пришлось воспроизвести этот случай, вы можете написать свой собственный драйвер в Linux, чтобы он возвращал -EINTR для метода .release. Взгляните на пример кода из книги O'Reilly's Linux Device Drivers. Проект scull - один из самых простых. Вы бы изменили это так:

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

Опять же, просматривая дерево исходных текстов Linux, я не могу найти драйвер, который бы возвращал EINTR при закрытии.

РЕДАКТИРОВАТЬ - хорошо, это похоже на предохранитель - файловая система пользовательского пространства может быть в состоянии. Это используется для таких вещей, как sshfs. Тем не менее, крайний случай.

3 голосов
/ 07 октября 2008

Используйте структуру указателей на функции, чтобы абстрагировать системные функции. Замените вызов flcose (fp) на что-то вроде sys-> fclose (fp). В своем модульном тесте создайте реализацию fclose, которая всегда возвращает EINTR, а затем установите sys-> fclose в эту версию.

2 голосов
/ 07 октября 2008

Я не думаю, что есть простой способ проверить это по своему желанию.

EINTR будет сгенерирован, если операция fclose прервана сигналом. Это означает, что fclose находился в фоновом потоке, который получил сигнал во время обработки запроса на закрытие.

Удачи в попытках воспроизвести этот набор обстоятельств.

1 голос
/ 26 февраля 2011

Я думаю, что может быть проблема с одновременной обработкой сигналов и подтверждением условий ошибки.

1 голос
/ 02 июля 2009

Хм, я думаю, что вы все игнорируете что-то очень важное.

fclose () не может вернуть EINTR. Ни один не может fopen (), fwrite (), fread (), или любая из стандартных функций ввода-вывода C.

Только когда вы балуетесь низкоуровневыми вызовами ввода-вывода, такими как open (2), write (2) и выбираете (2), вам нужно обрабатывать EINTR.

Читать [смешные] справочные страницы

1 голос
/ 10 ноября 2008

Вот как я бы это протестировал, просто ради тестирования, поскольку fizzer напомнил нам, что дважды вызывать fclose() небезопасно.

Возможно переопределить fclose() (или любую другую функцию libc) в вашей программе с вашим собственным поведением . В Unix-подобных системах компоновщик не жалуется - никогда не пробовал в Windows, но с Cygwin. Конечно, это не позволяет вашим другим тестам использовать настоящий fclose(), поэтому такой тест должен быть помещен в отдельный исполняемый файл теста.

Вот пример «все в одном» с 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 голос
/ 07 октября 2008

Как подсказывает Крис, временно замените вызовы на "реальные" fclose() вашей собственной реализацией, определенной в вашем коде модульного теста. Ваша реализация может сделать что-то простое:

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

Но, как отмечает fizzer, вы не должны снова вызывать fclose(), поскольку поведение не определено, поэтому вам не нужно даже проверять условие.

Другой вопрос, который нужно задать, - действительно ли вам нужно беспокоиться об этом; если бы код вашего приложения блокировал все возможные сигналы (кроме SIGKILL и SIGSTOP) во время вашего fclose(), вы бы не получили EINTR и не должны были бы беспокоиться вообще.

...