为何这段dwm源代码片段未触发错误?
先还原问题场景:
在dwm源代码的main()函数调用的checkotherwm函数内,执行了以下代码:
xerrorxlib = XSetErrorHandler(xerrorstart);
其中xerrorxlib的定义是:
static int (*xerrorxlib)(Display *, XErrorEvent *);
由于这是首次调用XSetErrorHandler,函数返回NULL(因为此前没有自定义错误处理函数),所以xerrorxlib会被赋值为NULL。
之后紧接着执行:
XSetErrorHandler(xerror);
xerror函数的实现如下:
xerror(Display *dpy, XErrorEvent *ee) { if (ee->error_code == BadWindow || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) return 0; fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); return xerrorxlib(dpy, ee); /* may call exit */ }
疑问点:函数最后一行return xerrorxlib(dpy, ee);中,xerrorxlib是NULL,等价于调用NULL(dpy, ee),为什么不会触发错误?
核心原因:实际运行中几乎不会走到调用xerrorxlib的分支
xerror函数已覆盖所有可处理的非致命错误
前面的if条件枚举了dwm运行过程中所有“可忽略”或“可恢复”的错误:比如窗口不存在(BadWindow)、设置输入焦点失败(BadMatch)、绘图到无效窗口(BadDrawable)、快捷键/按钮抢占失败(BadAccess)等。遇到这些错误时,函数直接return 0,不会执行到最后一行。走到最后一行意味着遇到致命错误,崩溃是预期行为
只有当遇到if条件未覆盖的错误时,才会执行到return xerrorxlib(dpy, ee);——这种错误是dwm无法处理的致命错误,此时即使调用NULL指针导致程序崩溃,也是合理的:程序已处于无法恢复的状态,崩溃退出和Xlib默认错误处理函数的行为(调用exit()终止程序)本质上都是终止程序,只是表现形式不同。补充:XSetErrorHandler的返回值细节
虽然首次调用XSetErrorHandler返回NULL,但Xlib的默认错误处理逻辑是内置的——当没有自定义错误处理函数时,Xlib会直接打印错误信息并退出。dwm这里的写法相当于把“无法处理的致命错误”交还给默认逻辑,只是因为首次调用返回NULL,所以用“崩溃”替代了默认的exit,但实际效果都是终止程序,且这种场景极少出现。
内容的提问来源于stack exchange,提问作者Neat

