这可能是 libX11 中有关处理用于 xcb_wait_for_reply 的请求号的已知问题。
在引入 libxcb v1.5 代码以在内部各处使用 64 位序列号后的某个时间点,并添加了逻辑以在进入那些仍采用 32 位序列号的公共 API 时扩大序列号。
这是来自submitted libxcb bug report 的引用(实际电子邮件已删除):
我们有一个执行大量 XDrawString 和 XDrawLine 的应用程序。
几个小时后,应用程序因 XIOError 而退出。
XIOError 在文件 xcb_io.c 中的 libX11 中调用,函数
_X回复。它没有得到 xcb_wait_for_reply 的响应。
libxcb 1.5 很好,libxcb 1.8.1 不是。二等分 libxcb 指向
这个提交:
提交 ed37b087519ecb9e74412e4df8f8a217ab6d12a9 作者:Jamey
截止日期:2010 年 10 月 9 日星期六 17:13:45 -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 <jamey@xxxxxx.xxx>
在 1.8.1 之上恢复它会有所帮助。
向 libxcb 添加跟踪我发现最后一个请求号用于
xcb_wait_for_reply 是这些:4294900463 和 4294965487(两个调用
_XReply 函数的 while 循环),半秒后:63215
(然后调用 XIOError)。 widen_request 也是 63215,我会
预计 63215+2^32。因此,请求似乎不是
正确加宽。
上面的提交也将 poll_for_reply 中的比较从
XCB_SEQUENCE_COMPARE_32 到 XCB_SEQUENCE_COMPARE。也许是扩大
从未正常工作,但从未观察到,因为只有
比较了较低的 32 位。
重现问题
这是提交的错误报告中用于重现问题的原始代码 sn-p:
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;
详细的技术说明
请在下面找到包含detailed technical explanation by Jonas Petersen(也包含在上述错误报告中):
嗨,
这里有两个补丁。第一个修复了 32 位序列换行错误。
第二个补丁只对另一个相关语句添加了注释。
补丁包含一些细节。这是谁的全部故事
可能有兴趣:
Xlib (libx11) 将使应用程序崩溃,并显示“致命 IO 错误 11
(资源暂时不可用)”在 4 294 967 296 次请求后
服务器。这就是 Xlib 内部 32 位序列回绕的时候。
大多数应用程序可能很难达到这个数字,但如果他们
做到了,他们就有机会神秘死去。例如
我正在处理的应用程序总是在大约 20 小时后崩溃
我开始做一些压力测试。它做了一些密集的绘图
通过使用 gktmm2、像素图和 gc 绘制的 Xlib,每帧 40 帧
全高清分辨率第二(在 Ubuntu 上)。做了一些优化
将宽限期延长到大约 35 小时,但它仍然会崩溃。
接下来是令人沮丧的几周的挖掘和调试
意识到它不在我的应用程序中,也不在 gtkmm、gtk 或 glib 中
但这是自 2006-10-06 以来存在的 Xlib 中的这个小错误
显然。
花了一段时间才发现数字 0x100000000 (2^32) 有
一些相关性。 (很多)后来证明它可以用
仅限 Xlib,例如使用以下代码:
而(1){
XDrawPoint(display, drawable, gc, x, y);
XFlush(显示); }
可能需要一两个小时,但当它达到 42.94 亿
它会爆炸成“致命的 IO 错误 11”。
然后我了解到,即使 Xlib 使用内部 32 位
他们在这个过程中(巧妙地)扩大到 64 位的序列号
这样 32 位序列可以在没有任何中断的情况下换行
加宽 64 位序列。显然肯定有什么问题
那个。
在 _XReply() 中未收到致命 IO 错误
在应该有的地方回复,但原因在 _XSend() 中更早
在 Xlib 32 位序列号换行的那一刻。
问题是当它换成 0 时,'last_flushed' 的值
仍将位于上限(例如 0xffffffff)。有两个
地点在
_XSend() (xcb_io.c) 在这种状态下失败,因为它们依赖于这些值一直是连续的,第一个位置是:
requests = dpy->request - dpy->xcb->last_flushed;
I case of request = 0x0 and last_flushed = 0xffffffff 它将分配
0xffffffff00000001 到“请求”,然后到 XCB 作为数字(金额)
的请求。这是主要杀手。
第二个位置是这样的:
for(sequence = dpy->xcb->last_flushed + 1; 序列 request;
\
++序列)
I case of request = 0x0 (小于 last_flushed) 没有机会
永远进入循环,结果一些请求被忽略了。
解决方案是在这两个位置“解包”dpy->request 并
从而保留last_flushed相关的序列。
uint64_t unwrapped_request = ((uint64_t)(dpy->request xcb->last_flushed) request;
它会创建一个临时的 64 位请求编号,如果该编号设置了第 8 位,则
'request' 小于 'last_flushed'。然后在两个中使用
位置而不是 dpy->request。
我不确定使用该语句是否更有效
就地,而不是使用变量。
require_socket() 中还有一行让我开始担心:
dpy->xcb->last_flushed = dpy->request = sent;
这是一个 64 位、32 位、64 位的赋值。它将截断“发送”到
将其分配给“请求”时为 32 位,然后还分配
截断值到(64 位)“last_flushed”。但这似乎很重要。
我添加了一个注释,解释下一个可怜的灵魂调试
序列问题... :-)
Jonas Petersen (2): xcb_io: Fix Xlib 32-bit request number wrapping
xcb_io: 添加注释解释混合类型双重赋值
src/xcb_io.c | 14 +++++++++++--- 1 个文件已更改,11 次插入 (+),
3个删除(-)
--
1.7.10.4
祝你好运!