Tcl fileevent висит на 64-битной версии Tcl - PullRequest
1 голос
/ 02 июня 2019

Я использую 64-битную 8.4.3 Tcl Non-Threaded в Linux, и у меня странное поведение.

В моем приложении на C ++ есть таймер, который выполняет некоторые обработчики Xt, используя XtAppProcessEvent.Один из обработчиков вызывает Tcl_DoOneEvent.

У меня есть сценарий Tcl, который открывает пустой канал и присоединяет файловое событие к открытому каналу.

set jobId [open "| "]
fileevent $jobId readable

Это выполняется несколько раз.Через некоторое время, когда имя канала равно file35, инструмент зависает.Использование отладочной версии библиотеки Tcl.Он показывает, что readyMasks [0] всегда равен 0 после выполнения следующей части:

// tclUnixNotfy.c:772
numFound = select(tsdPtr->numFdBits,
    (SELECT_MASK *) &tsdPtr->readyMasks[0],
    (SELECT_MASK *) &tsdPtr->readyMasks[MASK_SIZE],
    (SELECT_MASK *) &tsdPtr->readyMasks[2*MASK_SIZE], timeoutPtr);

Маски проверки ввода: 72 (1001000).


Вот странная часть:

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

Инструмент работал нормально, когда он был 32-битным.Я не могу связать происходящее с 64-битной.

Я пробовал это на redhat 5, 6 и 7, и не было никакой разницы.

1 Ответ

1 голос
/ 10 июня 2019

Итак, я разобрался в проблеме.

Проблема в основном из-за смещения integer вместо long.Это в Tcl выполняется в tclUnixChan.c при расчете индекса и бит, который необходимо изменить в tsdPtr->checkMasks:

index = fd/(NBBY*sizeof(fd_mask)); 
bit = 1 << (fd%(NBBY*sizeof(fd_mask)));

Битовая строка должна быть 1L << вместо 1 <<.fd_mask сам по себе является long и определен в sys/types.h.Это было только частью проблемы.

sys/types.h также содержит определения макросов, которые следует использовать при установке или очистке битов в fd_masks (FD_CLR, FD_SET, FD_ISSET, FD_ZERO).

Эта проблема была частично исправлена ​​в версии 8.4.9 и полностью исправлена ​​в версии 8.4.20.

Исправление было сделано с помощью макросов, определенных в types.h, а не вручнуюманипулирование fd_mask битами.

Частичное исправление, введенное в версии 8.4.9, было в tclUnixNotfy.c:

  1. struct SelectMasks было создано с использованием типа fd_set, которыйопределяется в полях types.h
  2. ThreadSpecificData checkMasks и readyMasks, измененных из массивов fd_mask на значения SelectMasks
  3. index и bit, используемые в Tcl_CreateFileHandler, Tcl_DeleteFileHandler, Tcl_WaitForEvent, NotifierThreadProc для работы с масками были удалены и вместо них были использованы FD_CLR, FD_SET, FD_ISSET, FD_ZERO.
  4. Tcl_WaitForEventприсваивает readyMasks checkMasks вместо использования memcpy перед вызовом select
  5. Приведение readyMasks к SELECT_MASK * при вызове select теперь удаленоОператор nd & теперь используется непосредственно для элементов структуры SelectMasks типа fd_set.

Другая часть исправления, представленная в версии 8.4.20, была в tclUnixChan.c: *Переменные 1077 *

  1. index и bit, используемые в TclUnixWaitForFile для работы с масками, были удалены и вместо них использовались FD_CLR, FD_SET, FD_ISSET, FD_ZERO.
  2. fd_set тип использовался вместо создания fd_mask массивов в TclUnixWaitForFile.
  3. Приведение масок к SELECT_MASK * при вызове select теперь удалено, а оператор & теперь используется всоздал fd_set переменных напрямую.

Я исправил свою текущую версию 8.4.3, руководствуясь изменениями, сделанными в 8.4.9 и 8.4.20, потому что у меня нет гибкости в обновлениицелая Tcl версия.


Моя теория о том, почему открытие нового окна оболочки заставило инструмент работать:

Из-за повреждения памяти, вызванного перемещением в неправильный контейнерразмер и использование memcpy со странной длиной 3*MASK_SIZE, fd, на который я в конечном итоге указалТаким образом, управление indow связано с тем, почему при открытии новой оболочки было выбрано возвращение ненулевого значения из-за наличия данных о тех неправильных fd каналах, на которые я указываю.

Что привело меня к этой теории, так это вывод straceа также вывод lsof: вывод

strace при зависании повторял следующую часть: вывод

poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
select(36, [3 6], [], [], {0, 0})       = 0 (Timeout)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
select(36, [3 6], [], [], {0, 0})       = 0 (Timeout)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {0x4c3cd70, [CHLD], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x37eec0f790}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0

strace при открытии новой вкладки оболочки изменился на:Выход

poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 1 ([{fd=4, revents=POLLIN}])
recvfrom(4, "X\1\366\371\264\300\7=\3\0\22\0\10\377\0\0\26\1\26\1\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 0, NULL, NULL) = 256
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
recvfrom(6, "X\1\321/\264\300\7=\3\0\22\0\10\377\0\0\26\1\26\1\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 0, NULL, NULL) = 256
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
select(36, [3 6], [], [], {0, 0})       = 1 (in [3], left {0, 0})
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
read(35, "", 4096)                      = 0
close(35)                               = 0

lsof показывает следующее для каналов 4 и 6:

myexec 13626 aymansalah    4u  IPv4 1607796326       0t0        TCP rhe6x64:38787->nx-svr:7016 (ESTABLISHED)
myexec 13626 aymansalah    6u  IPv4 1607837231       0t0        TCP rhe6x64:38788->nx-svr:7016 (ESTABLISHED)

Каналы 4 и 6 - это каналы, соединяющие мою машину сСервер NX.


Ссылки:

...