Cygwin — блокировка при открытии именованного fifo приводит к блокировке другого потока при открытии обычного файла

#c #multithreading #cygwin #named-pipes

#c #многопоточность #cygwin #именованные каналы

Вопрос:

Заблокированное открытие именованного канала в Cygwin приводит к зависанию другого потока при попытке открыть любой файл, включая простой текстовый файл. Приведенный ниже код воспроизводит проблему в cygwin 3.1.6(0.340/5/3 ) и отлично работает (не зависает) на RHEL 7.

 #include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <thread>
#include <sys/stat.h>
#include <fcntl.h>

void openFile() {
    int fd;

    printf("inside new threadn");
    sleep(10); // Ensure main thread reaches call to open()
    
    printf("opening a simple filen");
    if((fd = open("simpleFile", 0600)) == -1) { // simpleFile is a simple text file in the filesystem
        printf("failed opening a simple filen");
    }
    printf("simple file opened successfullyn");
    close(fd);
    printf("simple file closedn");
}

int main(int argc, char *argv[]) {
    int fd;
    char readBuffer[PIPE_BUF];
    
    printf("creating named pipen");
    if (mkfifo("namedPipe", 0600)) {
        printf("creating named pipe failedn");
    }
    
    printf("creating threadn");
    std::thread pipeCreator = std::thread(openFile);
    
    printf("opening named pipe for readn");
    fd = open("namedPipe", O_RDONLY); // Block will only release when we echo something into namedPipe
    printf("reading from named pipen");
    if (read(fd, readBuffer, PIPE_BUF) == -1) {
        printf("error reading from pipen");
    }
    printf("read successfully from named pipen");
    
    pipeCreator.join();
    
    return 0;
}
  

Запуск этого печатает:

 creating named pipe
creating thread
opening named pipe for read
inside new thread
opening a simple file
  

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

 reading from named pipe
simple file opened successfully
read successfully from named pipe
simple file closed
  

В RHEL это выводит ожидаемый результат:

 creating named pipe
creating thread
opening named pipe for read
inside new thread
opening a simple file
simple file opened successfully
simple file closed
  

И только тогда основной поток зависает до тех пор, пока что-то не будет передано в NamedPipe.

Мы работаем над обходным решением, которое не будет блокировать, но это связано с напряженным ожиданием, что не очень хорошо. Кто-нибудь может объяснить это поведение?

Комментарии:

1. Я могу только подтвердить ваши выводы. Я пробовал с g 9.3.0 и clang 8.0.1 в cygwin, и для меня это действует одинаково. Я попытался посмотреть, strace -f дал ли какие-либо подсказки, но я не видел ничего другого, кроме того, что он просто зависал на open() . :-/

Ответ №1:

В Cygwin open системный вызов блокирует таблицу файловых дескрипторов на все время системного вызова. Что означает, что все open системные вызовы по существу упорядочены.

См syscalls.cc/open() :

 extern "C" int
open (const char *unix_path, int flags, ...)
{
      . . .
      cygheap_fdnew fd;  // <-- here
  

И cygheap.h :

 class cygheap_fdnew : public cygheap_fdmanip
{
 public:
  cygheap_fdnew (int seed_fd = -1, bool lockit = true)
  {
    if (lockit)
      cygheap->fdtab.lock ();  // <-- here
    . . .
  

Я не вижу простого способа обойти это, но я предполагаю, что должна быть возможность разблокировать таблицу fd после создания дескриптора, по крайней мере, в случае fifo (см. fhandler_fifo ), поскольку fifo блокируется open . Вы можете обсудить это подробнее на cygwin-developers.

Комментарии:

1. Наш текущий обходной путь заключается в открытии с флагом O_NONBLOCK и отключении, пока результат чтения равен 0 прочитанным байтам