Закрытие опрашиваемого файлового дескриптора

Если у меня есть два потока (Linux, NPTL), и один поток опрашивает один или несколько файловых дескрипторов, а другой закрывает один из них, разумно ли это? Делаю ли я что-то, чего не должен делать в среде MT?

Основная причина, по которой я думаю сделать это, заключается в том, что я не обязательно хочу связываться с потоком опроса, прерывать его и т. Д., Вместо этого я хотел бы просто закрыть дескриптор по любым причинам, и когда поток опроса просыпается, Я ожидаю, что отчеты будут содержать POLLNVAL, что будет означать, что дескриптор файла должен быть просто выброшен потоком перед следующим опросом.

Я составил простой тест, который показывает, что POLLNVAL — это именно то, что должно произойти. Однако в этом случае POLLNVAL устанавливается только по истечении времени ожидания, закрытие сокета, похоже, не приводит к возврату poll(). Если это так, я могу убить поток, чтобы перезапустить poll() для возврата.

#define _GNU_SOURCE

#include <stdio.h>
#include <pthread.h>
#include <poll.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static pthread_t main_thread;

void * close_some(void*a) {

    printf("thread #2 (%d) is sleeping\n", getpid());
    sleep(2);
    close(0);
    printf("socket closed\n");
    // comment out the next line to not forcefully interrupt
    pthread_kill(main_thread, SIGUSR1);
    return 0;

}

void on_sig(int s) {
    printf("signal recieved\n");
}

int main(int argc, char ** argv) {

    pthread_t two;
    struct pollfd pfd;
    int rc;

    struct sigaction act;
    act.sa_handler = on_sig;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGUSR1, &act, 0);

    main_thread = pthread_self();

    pthread_create(&two, 0, close_some, 0);

    pfd.fd = 0;
    pfd.events = POLLIN | POLLRDHUP;

    printf("thread 0 (%d) polling\n", getpid());

    rc = poll(&pfd, 1, 7000);

    if (rc < 0) {
        printf("error : %s\n", strerror(errno));
    } else if (!rc) {
        printf("time out!\n");
    } else {
        printf("revents = %x\n", pfd.revents);
    }
    return 0;

}

person Pawel Veselov    schedule 12.05.2012    source источник


Ответы (1)


По крайней мере, для Linux это кажется рискованным. Страница руководства для close предупреждает:

Вероятно, неразумно закрывать файловые дескрипторы, пока они могут использоваться системными вызовами в других потоках того же процесса. Поскольку файловый дескриптор может использоваться повторно, существуют некоторые неясные условия гонки, которые могут вызвать непреднамеренные побочные эффекты.

Поскольку вы работаете в Linux, вы можете сделать следующее:

  • Настройте eventfd и добавьте его в опрос
  • Подайте сигнал eventfd (напишите на него), когда вы хотите закрыть fd
  • В опросе, когда вы видите активность на eventfd, вы можете немедленно закрыть fd и удалить его из poll

В качестве альтернативы вы можете просто установить обработчик signal и проверить наличие errno == EINTR, когда poll вернется. Обработчику сигнала нужно будет только установить некоторую глобальную переменную в значение закрываемого вами fd.


Поскольку вы работаете в Linux, возможно, вы захотите рассмотреть epoll в качестве превосходной, хотя и нестандартной альтернативы poll.

person cnicutar    schedule 12.05.2012
comment
спасибо, этот фрагмент именно то, что я должен был прочитать :) Повторное использование вызовет у меня проблемы. Я не могу писать в фд сам, так как он будет подключен к удаленному, так что придется придумывать кастомную сигнализацию. - person Pawel Veselov; 12.05.2012