Обработка EINTR (с goto?)

История вопроса: это дополнительный вопрос к этой теме о обработка EINTR для системных вызовов в C ++ (Linux / GCC). Независимо от того, собираюсь ли я профилировать свое приложение, мне кажется, что я должен обрабатывать системные вызовы, устанавливая errno на EINTR как особый случай. Есть много, многие, многие мнения об использовании goto.

Мой вопрос: является ли системный вызов errno на EINTR случаем, когда goto считается номинальным? Если нет, то как бы вы предложили преобразовать следующий код для обработки EINTR?

if ( ( sock_fd = ::socket( domain, type, protocol ) ) < 0 ) {
  throw SocketException( "Socket::Socket() -> ::socket()", errno );
}

Заранее спасибо!
Ура,
-Крис

ОБНОВЛЕНИЕ. На основании ответов ниже я написал следующий макрос:

#define SOCK_SYSCALL_TRY(call,error)              \
  while ( (call) < 0 ) {                          \
    switch ( errno ) {                            \
      case EINTR:                                 \
        continue;                                 \
      default:                                    \
        throw SocketException( (error), errno );  \
    }                                             \
  }                                               \

Что используется для преобразования моего исходного фрагмента в этот пример:

SOCK_SYSCALL_TRY( sock_fd = ::socket( domain, type, protocol ), "Socket::Socket() -> ::socket()" )

Надеюсь, это поможет кому-то другому!


person Chris Tonkinson    schedule 02.06.2010    source источник
comment
Крис, возьми эту книгу: unpbook.com - будь рад, что ты это сделал. Исходный код находится в Интернете unpbook.com/src.html - см. Там примеры того, как обрабатывать EINTR.   -  person Nikolai Fetissov    schedule 02.06.2010
comment
Никогда не думал использовать while / continue - отличные идеи!   -  person Chris Tonkinson    schedule 02.06.2010


Ответы (2)


Насколько мне известно, системный вызов сокета не может вернуться, если для errno установлено значение EINTR. В остальных случаях я использую цикл:

while ((::connect(sock, (struct sockaddr *)&destAddress, sizeof(struct sockaddr))) == -1) {
    if (errno == EINTR) {
        LOGERROR("connect interrupted, retry");
        continue;
    } else if (errno == EINPROGRESS) {
        break;
    } else {
        LOGERROR("connect failed, errno: " << errno);
        return -1;
    }
}
person macgarden    schedule 02.06.2010

Я запрограммировал FTP-сервер, и мне никогда не приходилось использовать goto. Обычно я создавал прерываемые системные вызовы следующим образом:

    while( (ret = 
        splice_stream( data, NULL, file, &block_offset, 
            XFER_BLOCK_SIZE )) == -1 )
    {
        switch( errno )
        {
        case EINTR:
            if( server_handle_signal() )
                return FTP_QUIT;
            else
                continue;
            break;
        case EPIPE:
        case ECONNRESET:
            return FTP_ABOR;
        default:
            log_fatal("Splice error: %m\n");
            return FTP_ERROR;
        }
    }

EINTR означает, что ваш сервер поймал сигнал, и большую часть времени важно обработать этот сигнал.

person Kasper    schedule 02.06.2010
comment
Я не думаю, что вы правильно обрабатываете сигнал. После того, как вы вернетесь из системного вызова с -1 и EINTR, signal_handler уже был вызван асинхронно. Вам нужно обработать сигнал в signal_handler или использовать signal_fd. - person WiSaGaN; 05.02.2015