You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用XLib过滤过期事件并为X服务器请求设置超时以避免无限阻塞?

如何使用XLib过滤过期事件并为X服务器请求设置超时以避免无限阻塞?

嘿,我完全懂你这个痛点——调用XLib接口请求X服务器后,万一遇到窗口关闭、资源销毁这类情况,就可能无限阻塞在等回复的环节,太闹心了!用X同步扩展的XSyncCreateAlarm()来实现超时是个非常合理的思路,我来给你拆解具体的实现步骤,顺便把过期事件的过滤逻辑也讲清楚。

首先得确保你的程序已经初始化了X同步扩展,不然相关函数根本用不了:

int event_base, error_base;
if (!XSyncQueryExtension(display, &event_base, &error_base)) {
    // 同步扩展不支持,得做降级处理或者报错退出
    fprintf(stderr, "X Sync Extension not supported!\n");
    return;
}

接下来是核心的超时设置和事件循环部分。我们先创建一个超时闹钟,然后在事件循环里同时监听目标回复事件和超时触发事件,还要过滤掉那些已经失效的过期事件:

void foo(Display* display, Window target_win, Atom target_prop, int timeout_ms) {
    // 第一步:发送你的X服务器请求,比如读取属性
    XGetWindowProperty(display, target_win, target_prop, 0, LONG_MAX, False, AnyPropertyType,
                       nullptr, nullptr, nullptr, nullptr, nullptr);

    // 第二步:创建超时闹钟
    XSyncAlarmAttributes alarm_attrs{};
    // 设置触发条件:相对于当前服务器时间,延迟timeout_ms毫秒
    alarm_attrs.trigger.counter = XSyncSystemTime;
    alarm_attrs.trigger.value_type = XSyncRelative;
    alarm_attrs.trigger.relative = timeout_ms;
    // 触发时发送AlarmNotify事件
    alarm_attrs.events = XSyncAlarmNotify;
    alarm_attrs.flags = XSyncAlarmTrigger | XSyncAlarmEvents;

    XSyncAlarm alarm = XSyncCreateAlarm(display, &alarm_attrs);
    if (alarm == None) {
        fprintf(stderr, "Failed to create sync alarm!\n");
        return;
    }

    bool got_valid_response = false;
    XEvent event{};

    while (true) {
        // 阻塞等待下一个事件
        XNextEvent(display, &event);

        // 先判断是不是超时触发的闹钟事件
        if (event.type == event_base + XSyncAlarmNotify) {
            XSyncAlarmNotifyEvent* alarm_event = reinterpret_cast<XSyncAlarmNotifyEvent*>(&event);
            if (alarm_event->alarm == alarm) {
                // 超时了,清理闹钟并退出循环
                XSyncDestroyAlarm(display, alarm);
                fprintf(stdout, "Request timed out!\n");
                break;
            }
        }
        // 再判断是不是我们要的目标回复事件(这里以PropertyNotify为例)
        else if (event.type == PropertyNotify) {
            PropertyNotifyEvent* prop_event = reinterpret_cast<PropertyNotifyEvent*>(&event);
            // 验证事件是否和我们的请求匹配:窗口、属性都对应,且窗口还存在
            bool window_exists = true;
            XWindowAttributes attrs{};
            if (XGetWindowAttributes(display, prop_event->window, &attrs) == 0) {
                window_exists = false;
            }

            if (prop_event->window == target_win && prop_event->atom == target_prop && window_exists) {
                // 处理有效的回复事件
                fprintf(stdout, "Got valid property response!\n");
                // 这里可以重新调用XGetWindowProperty获取实际数据
                // ...
                got_valid_response = true;
                XSyncDestroyAlarm(display, alarm);
                break;
            } else {
                // 事件已经过期(比如窗口已关、属性已被销毁),直接跳过
                fprintf(stdout, "Skipping expired event!\n");
                continue;
            }
        } else {
            // 其他无关事件,放回事件队列,别影响程序其他逻辑
            XPutBackEvent(display, &event);
        }
    }

    if (got_valid_response) {
        // 处理成功获取到的结果
        // ...
    }
}

最后给你提几个关键注意点:

  • 一定要清理闹钟:不管是拿到有效回复还是超时,都要调用XSyncDestroyAlarm释放资源,不然会留下内存泄漏或者无效的服务器资源。
  • 过滤过期事件的关键:在处理目标事件前,一定要验证关联的资源(比如窗口、属性)是否还存在,避免处理已经失效的事件。
  • 无关事件放回队列:如果遇到不是我们要的事件,用XPutBackEvent放回,保证程序其他部分的事件处理不受影响。
  • 错误处理:创建闹钟、检查窗口属性这些操作都可能失败,记得加上错误判断,避免程序崩溃。

备注:内容来源于stack exchange,提问作者Alexey104

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 09:53:14