Вероятно, это известная проблема в libX11, связанная с обработкой номеров запросов, используемых для xcb_wait_for_reply.
В какой-то момент после того, как libxcb v1.5 был введен код для внутреннего использования 64-битных порядковых номеров везде, и была добавлена логика для расширения порядковых номеров при входе в те общедоступные API, которые все еще принимают 32-битные порядковые номера.
Вот цитата из отправленного отчета об ошибке libxcb (фактические электронные письма удалены):
У нас есть приложение, которое делает много XDrawString и XDrawLine. Через несколько часов приложение закрывается с ошибкой XIOError.
XIOError вызывается в libX11 в файле xcb_io.c, функция _XReply. Он не получил ответа от xcb_wait_for_reply.
С libxcb 1.5 все в порядке, с libxcb 1.8.1 — нет. Разделение libxcb пополам указывает на этот коммит:
commit ed37b087519ecb9e74412e4df8f8a217ab6d12a9 Автор: Джейми Шарп Дата: суббота, 9 октября, 17:13:45 2010 -0700
xcb_in: Use 64-bit sequence numbers internally everywhere.
Widen sequence numbers on entry to those public APIs that still take
32-bit sequence numbers.
Signed-off-by: Jamey Sharp <[email protected]>
Откат поверх 1.8.1 помогает.
Добавляя трассировки в libxcb, я обнаружил, что последние номера запросов, используемые для xcb_wait_for_reply, таковы: 4294900463 и 4294965487 (два вызова в цикле while функции _XReply), через полсекунды: 63215 (затем вызывается XIOError). Расширение_запроса также равно 63215, я ожидал 63215+2^32. Поэтому кажется, что запрос неправильно расширен.
Коммит выше также изменил сравнения в poll_for_reply с XCB_SEQUENCE_COMPARE_32 на XCB_SEQUENCE_COMPARE. Возможно, расширение никогда не работало корректно, но оно никогда не наблюдалось, потому что сравнивались только младшие 32 бита.
Воспроизведение проблемы
Вот исходный фрагмент кода из отправленного отчета об ошибке, который использовался для воспроизведения проблемы:
for(;;) {
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
XFlush(dpy);
}
и, по-видимому, проблему можно воспроизвести с помощью еще более простого кода:
for(;;) {
XNoOp(dpy);
}
Согласно представленному отчету об ошибке libxcb, эти условия необходимы для воспроизведения (при условии, что код воспроизведения находится в xdraw.c):
- libxcb >= 1.8 (т.е. включает коммит ed37b08)
- скомпилировано с 32-битной версией: gcc -m32 -lX11 -o xdraw xdraw.c
- счетчик последовательности сбрасывается.
Предлагаемый патч
Предлагаемый патч, который можно применить поверх libxcb 1.8.1, выглядит следующим образом:
diff --git a/src/xcb_io.c b/src/xcb_io.c
index 300ef57..8616dce 100644
--- a/src/xcb_io.c
+++ b/src/xcb_io.c
@@ -454,7 +454,7 @@ void _XSend(Display *dpy, const char *data, long size)
static const xReq dummy_request;
static char const pad[3];
struct iovec vec[3];
- uint64_t requests;
+ unsigned long requests;
_XExtension *ext;
xcb_connection_t *c = dpy->xcb->connection;
if(dpy->flags & XlibDisplayIOError)
@@ -470,7 +470,7 @@ void _XSend(Display *dpy, const char *data, long size)
if(dpy->xcb->event_owner != XlibOwnsEventQueue || dpy->async_handlers)
{
uint64_t sequence;
- for(sequence = dpy->xcb->last_flushed + 1; sequence <= dpy->request; ++sequence)
+ for(sequence = dpy->xcb->last_flushed + 1; (unsigned long) sequence <= dpy->request; ++sequence)
append_pending_request(dpy, sequence);
}
requests = dpy->request - dpy->xcb->last_flushed;
Подробное техническое объяснение
Ниже вы найдете подробное техническое объяснение Джонаса Петерсена ( также включено в вышеупомянутый отчет об ошибке):
Hi,
Вот два патча. Первый исправляет ошибку переноса 32-битной последовательности. Второй патч лишь добавляет комментарий к другому соответствующему утверждению.
Патчи содержат некоторые детали. Вот вся история, кому может быть интересно:
Xlib (libx11) приведет к сбою приложения с «Фатальной ошибкой ввода-вывода 11 (ресурс временно недоступен)» после 4 294 967 296 запросов к серверу. Это происходит, когда внутренняя 32-битная последовательность Xlib завершается.
Большинство приложений, вероятно, вряд ли достигнут этого числа, но если они это сделают, у них есть шанс умереть загадочной смертью. Например, приложение, над которым я работаю, всегда аварийно завершало работу примерно через 20 часов, когда я начинал проводить стресс-тестирование. Он интенсивно рисует через Xlib, используя gktmm2, pixmaps и gc, рисуя со скоростью 40 кадров в секунду в разрешении Full HD (в Ubuntu). Некоторые оптимизации действительно увеличили отсрочку примерно до 35 часов, но все равно произошел сбой.
Затем последовали несколько разочаровывающих недель копания и отладки, чтобы понять, что это не в моем приложении, не в gtkmm, gtk или glib, а в том, что это небольшая ошибка в Xlib, которая, по-видимому, существует с 2006-10-06.
Потребовалось некоторое время, чтобы выяснить, что число 0x100000000 (2^32) имеет некоторое значение. (Намного) позже выяснилось, что это можно воспроизвести только с помощью Xlib, используя, например, этот код:
while(1) { XDrawPoint(display, drawable, gc, x, y); XFlush(отображение); }
Это может занять один или два часа, но когда он достигнет 4294 миллионов, произойдет «Фатальная ошибка ввода-вывода 11».
Затем я узнал, что, хотя Xlib использует внутренние 32-битные порядковые номера, они (разумно) расширяются до 64-битных в процессе, так что 32-битная последовательность может переноситься без каких-либо нарушений в расширенную 64-битную последовательность. Очевидно, с этим должно быть что-то не так.
Фатальная ошибка ввода-вывода выдается в _XReply(), когда она не получает ответа там, где он должен быть, но причина кроется в _XSend() раньше, в момент переноса 32-битного порядкового номера Xlib.
Проблема в том, что когда он переносится на 0, значение last_flushed все еще будет на верхней границе (например, 0xffffffff). В _XSend() (xcb_io.c) есть два места, которые терпят неудачу в этом состоянии, потому что они полагаются на то, что эти значения все время последовательны, первое место:
запросы = dpy->запрос - dpy->xcb->last_flushed;
В случае запроса = 0x0 и last_flushed = 0xffffffff он назначит 0xffffffff00000001 «запросам», а затем XCB как количество (количество) запросов. Это главный убийца.
Вторая локация такова:
for(последовательность = dpy->xcb->last_flush + 1; последовательность ‹= dpy->запрос; \++последовательность)
В случае запроса = 0x0 (меньше, чем last_flushed) нет возможности когда-либо войти в цикл, и в результате некоторые запросы игнорируются.
Решение состоит в том, чтобы «развернуть» dpy->request в этих двух местах и, таким образом, сохранить последовательность, связанную с last_flushed.
uint64_t unwrapped_request = ((uint64_t)(dpy->запрос ‹ \ dpy->xcb->last_flush) ‹‹ 32) + dpy->запрос;
Он создает временный 64-битный номер запроса, в котором бит 8 установлен, если «request» меньше, чем «last_flushed». Затем он используется в двух местах вместо dpy->request.
Я не уверен, что было бы более эффективно использовать этот оператор вместо использования переменной.
В require_socket() есть еще одна строка, которая поначалу меня беспокоила:
dpy->xcb->last_flushed = dpy->request = отправлено;
Это 64-битное, 32-битное, 64-битное назначение. Он усекает «отправлено» до 32-битного при присвоении его «запросу», а затем также присваивает усеченное значение (64-битному) «last_flushed». Но, похоже, заинтересован. Я добавил примечание, объясняющее, что для следующих проблем с последовательностью отладки бедняги... :-)
Йонас Петерсен (2): xcb_io: исправлен перенос номера 32-битного запроса Xlib xcb_io: добавлен комментарий, объясняющий двойное присвоение смешанного типа
источник/xcb_io.c | 14 +++++++++++ --- 1 файл изменен, 11 вставок(+), 3 удаления(-)
--
1.7.10.4
Удачи!
person
mzagar
schedule
26.05.2014