Skip to content

Commit 84b9b78

Browse files
committed
fix(dxinput): use global mutex to prevent X11 thread race condition
Previously, list.c, type.c, property.c, button_map.c, and keyboard.c each had their own local mutex, which failed to protect concurrent X11 access across different files. This caused heap corruption when multiple threads simultaneously called XOpenDisplay/XCloseDisplay, resulting in "malloc(): unaligned tcache chunk detected" errors. Log: - Add x11_mutex.h/c with global x11_global_mutex - Replace all local mutexes with x11_global_mutex - Add _unlocked internal versions of query_device_type and is_property_exist to avoid deadlock when called from within locked contexts - Ensure all top-level functions (called from Go) acquire the lock - Internal helper functions assume caller holds the lock Influence: This ensures thread-safe X11 access while avoiding deadlock through proper lock hierarchy. --- 修复(dxinput): 使用全局互斥锁防止 X11 线程竞争 之前 list.c、type.c、property.c、button_map.c 和 keyboard.c 各自 使用独立的局部互斥锁,无法保护跨文件的并发 X11 访问。这导致多个线程 同时调用 XOpenDisplay/XCloseDisplay 时发生堆损坏,触发 "malloc(): unaligned tcache chunk detected" 错误。 Log: - 新增 x11_mutex.h/c,定义全局 x11_global_mutex - 将所有局部互斥锁替换为 x11_global_mutex - 新增 query_device_type 和 is_property_exist 的 _unlocked 内部版本, 避免在已持锁的上下文中调用时发生死锁 - 确保所有顶层函数(被 Go 调用)获取锁 - 内部辅助函数假设调用者已持有锁 Influence: 通过正确的锁层次结构,确保线程安全的 X11 访问,同时避免死锁。
1 parent c1211d9 commit 84b9b78

File tree

9 files changed

+119
-80
lines changed

9 files changed

+119
-80
lines changed

dxinput/utils/button_map.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "button_map.h"
1414
#include "type.h"
15+
#include "x11_mutex.h"
1516

1617
static int get_button_number(Display* disp, const char* name);
1718
static const XDeviceInfo* find_device_by_name(const XDeviceInfo* devs,
@@ -20,8 +21,6 @@ static int get_device_button_number(const XDeviceInfo* dev);
2021
static unsigned char* do_get_button_map(Display* disp,
2122
unsigned long xid, int nbuttons);
2223

23-
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
24-
2524
unsigned char*
2625
get_button_map(unsigned long xid, const char* name, int* nbuttons)
2726
{
@@ -37,14 +36,14 @@ get_button_map(unsigned long xid, const char* name, int* nbuttons)
3736
return NULL;
3837
}
3938

40-
pthread_mutex_lock(&mutex);
39+
pthread_mutex_lock(&x11_global_mutex);
4140
setErrorHandler();
4241

4342
Display* disp = XOpenDisplay(NULL);
4443
if (!disp) {
4544
fprintf(stderr, "[get_button_map] open display failed for %lu %s\n",
4645
xid, name);
47-
pthread_mutex_unlock(&mutex);
46+
pthread_mutex_unlock(&x11_global_mutex);
4847
return NULL;
4948
}
5049

@@ -53,15 +52,15 @@ get_button_map(unsigned long xid, const char* name, int* nbuttons)
5352
XCloseDisplay(disp);
5453
fprintf(stderr, "[get_button_map] get button number failed for %lu %s\n",
5554
xid, name);
56-
pthread_mutex_unlock(&mutex);
55+
pthread_mutex_unlock(&x11_global_mutex);
5756
return NULL;
5857
}
5958

6059
*nbuttons = num_btn;
6160
unsigned char* map = do_get_button_map(disp, xid, num_btn);
6261
XCloseDisplay(disp);
6362

64-
pthread_mutex_unlock(&mutex);
63+
pthread_mutex_unlock(&x11_global_mutex);
6564

6665
return map;
6766
}
@@ -75,14 +74,14 @@ set_button_map(unsigned long xid, const char* name,
7574
return -1;
7675
}
7776

78-
pthread_mutex_lock(&mutex);
77+
pthread_mutex_lock(&x11_global_mutex);
7978
setErrorHandler();
8079

8180
Display* disp = XOpenDisplay(0);
8281
if (!disp) {
8382
fprintf(stderr, "[set_button_map] open display failed: %lu %s\n",
8483
xid, name);
85-
pthread_mutex_unlock(&mutex);
84+
pthread_mutex_unlock(&x11_global_mutex);
8685
return -1;
8786
}
8887

@@ -91,7 +90,7 @@ set_button_map(unsigned long xid, const char* name,
9190
XCloseDisplay(disp);
9291
fprintf(stderr, "[set_button_map] open device failed for %lu %s\n",
9392
xid, name);
94-
pthread_mutex_unlock(&mutex);
93+
pthread_mutex_unlock(&x11_global_mutex);
9594
return -1;
9695
}
9796

@@ -101,7 +100,7 @@ set_button_map(unsigned long xid, const char* name,
101100
XCloseDevice(disp, dev);
102101
XCloseDisplay(disp);
103102

104-
pthread_mutex_unlock(&mutex);
103+
pthread_mutex_unlock(&x11_global_mutex);
105104

106105
// TODO: if ret == MappingBusy, try again
107106
if (ret != MappingSuccess) {

dxinput/utils/keyboard.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,18 @@
1212
#include <X11/Xlib.h>
1313
#include <X11/XKBlib.h>
1414
#include "type.h"
15-
16-
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
15+
#include "x11_mutex.h"
1716

1817
int
1918
set_keyboard_repeat(int repeated, unsigned int delay, unsigned int interval)
2019
{
21-
pthread_mutex_lock(&mutex);
20+
pthread_mutex_lock(&x11_global_mutex);
2221
setErrorHandler();
2322

2423
Display *disp = XOpenDisplay(0);
2524
if (!disp) {
2625
fprintf(stderr, "Open display failed\n");
27-
pthread_mutex_unlock(&mutex);
26+
pthread_mutex_unlock(&x11_global_mutex);
2827
return -1;
2928
}
3029

@@ -49,7 +48,7 @@ set_keyboard_repeat(int repeated, unsigned int delay, unsigned int interval)
4948
XSync(disp, False);
5049
XCloseDisplay(disp);
5150

52-
pthread_mutex_unlock(&mutex);
51+
pthread_mutex_unlock(&x11_global_mutex);
5352

5453
return ret;
5554
}

dxinput/utils/list.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,27 @@
1313

1414
#include "list.h"
1515
#include "type.h"
16+
#include "x11_mutex.h"
1617

1718
static int append_device(DeviceInfo** devs, XIDeviceInfo* xinfo, int idx);
1819
static void free_device_info(DeviceInfo* dev);
19-
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2020

2121
DeviceInfo*
2222
list_device(int* num)
2323
{
24-
pthread_mutex_lock(&mutex);
24+
pthread_mutex_lock(&x11_global_mutex);
2525
setErrorHandler();
2626

2727
if (!num) {
2828
fprintf(stderr, "list_device failed, !num\n");
29-
pthread_mutex_unlock(&mutex);
29+
pthread_mutex_unlock(&x11_global_mutex);
3030
return NULL;
3131
}
3232

3333
Display* disp = XOpenDisplay(0);
3434
if (!disp) {
3535
fprintf(stderr, "Open display failed\n");
36-
pthread_mutex_unlock(&mutex);
36+
pthread_mutex_unlock(&x11_global_mutex);
3737
return NULL;
3838
}
3939

@@ -42,7 +42,7 @@ list_device(int* num)
4242
XCloseDisplay(disp);
4343
if (!xinfos) {
4444
fprintf(stderr, "List xinput device failed\n");
45-
pthread_mutex_unlock(&mutex);
45+
pthread_mutex_unlock(&x11_global_mutex);
4646
return NULL;
4747
}
4848

@@ -66,7 +66,7 @@ list_device(int* num)
6666
XIFreeDeviceInfo(xinfos);
6767
*num = j;
6868

69-
pthread_mutex_unlock(&mutex);
69+
pthread_mutex_unlock(&x11_global_mutex);
7070

7171
return devs;
7272
}
@@ -115,7 +115,7 @@ append_device(DeviceInfo** devs, XIDeviceInfo* xinfo, int idx)
115115
tmp[idx].name = name;
116116
tmp[idx].id = xinfo->deviceid;
117117
tmp[idx].enabled = xinfo->enabled;
118-
tmp[idx].ty = query_device_type(xinfo->deviceid);
118+
tmp[idx].ty = query_device_type_unlocked(xinfo->deviceid);
119119

120120
return 0;
121121
}

dxinput/utils/property.c

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010

1111
#include "property.h"
1212
#include "type.h"
13+
#include "x11_mutex.h"
1314

1415
#define MAX_BUF_LEN 1000
1516

16-
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
17-
1817
/**
1918
* The return data type if 'char' must be convert to 'int8_t*'
2019
* if 'int' must be convert to 'int32_t*'
@@ -33,21 +32,21 @@ get_prop(int id, const char* prop, int* nitems)
3332
return NULL;
3433
}
3534

36-
pthread_mutex_lock(&mutex);
35+
pthread_mutex_lock(&x11_global_mutex);
3736
setErrorHandler();
3837

3938
Display* disp = XOpenDisplay(0);
4039
if (!disp) {
4140
fprintf(stderr, "[get_prop] Open display failed for %d\n", id);
42-
pthread_mutex_unlock(&mutex);
41+
pthread_mutex_unlock(&x11_global_mutex);
4342
return NULL;
4443
}
4544

4645
Atom prop_id = XInternAtom(disp, prop, True);
4746
if (prop_id == None) {
4847
XCloseDisplay(disp);
4948
fprintf(stderr, "[get_prop] Intern atom %s failed\n", prop);
50-
pthread_mutex_unlock(&mutex);
49+
pthread_mutex_unlock(&x11_global_mutex);
5150
return NULL;
5251
}
5352

@@ -61,14 +60,14 @@ get_prop(int id, const char* prop, int* nitems)
6160
if (ret != Success) {
6261
XCloseDisplay(disp);
6362
fprintf(stderr, "[get_prop] Get %s data failed for %d\n", prop, id);
64-
pthread_mutex_unlock(&mutex);
63+
pthread_mutex_unlock(&x11_global_mutex);
6564
return NULL;
6665
}
6766

6867
*nitems = (int)num_items;
6968
XCloseDisplay(disp);
7069

71-
pthread_mutex_unlock(&mutex);
70+
pthread_mutex_unlock(&x11_global_mutex);
7271

7372
return data;
7473
}
@@ -83,25 +82,25 @@ set_prop_int(int id, const char* prop, unsigned char* data, int nitems, int bit)
8382
int
8483
set_prop_float(int id, const char* prop, unsigned char* data, int nitems)
8584
{
86-
pthread_mutex_lock(&mutex);
85+
pthread_mutex_lock(&x11_global_mutex);
8786
setErrorHandler();
8887

8988
Display* disp = XOpenDisplay(NULL);
9089
if (!disp) {
9190
fprintf(stderr, "[set_prop_float] open display failed\n");
92-
pthread_mutex_unlock(&mutex);
91+
pthread_mutex_unlock(&x11_global_mutex);
9392
return -1;
9493
}
9594

9695
Atom type = XInternAtom(disp, "FLOAT", False);
9796
XCloseDisplay(disp);
9897
if (type == None) {
9998
fprintf(stderr, "[set_prop_float] Intern 'FLOAT' atom failed\n");
100-
pthread_mutex_unlock(&mutex);
99+
pthread_mutex_unlock(&x11_global_mutex);
101100
return -1;
102101
}
103102

104-
pthread_mutex_unlock(&mutex);
103+
pthread_mutex_unlock(&x11_global_mutex);
105104

106105
// Format must be 32
107106
int ret = set_prop(id, prop, data, nitems, type, 32);
@@ -123,21 +122,21 @@ set_prop(int id, const char* prop, unsigned char* data, int nitems,
123122
return -1;
124123
}
125124

126-
pthread_mutex_lock(&mutex);
125+
pthread_mutex_lock(&x11_global_mutex);
127126
setErrorHandler();
128127

129128
Display* disp = XOpenDisplay(0);
130129
if (!disp) {
131130
fprintf(stderr, "[set_prop] Open display failed for %d\n", id);
132-
pthread_mutex_unlock(&mutex);
131+
pthread_mutex_unlock(&x11_global_mutex);
133132
return -1;
134133
}
135134

136135
Atom prop_id = XInternAtom(disp, prop, True);
137136
if (prop_id == None) {
138137
XCloseDisplay(disp);
139138
fprintf(stderr, "[set_prop] Intern atom %s failed\n", prop);
140-
pthread_mutex_unlock(&mutex);
139+
pthread_mutex_unlock(&x11_global_mutex);
141140
return -1;
142141
}
143142

@@ -146,7 +145,7 @@ set_prop(int id, const char* prop, unsigned char* data, int nitems,
146145
/* XFree(&prop_id); */
147146
XCloseDisplay(disp);
148147

149-
pthread_mutex_unlock(&mutex);
148+
pthread_mutex_unlock(&x11_global_mutex);
150149

151150
return 0;
152151
}

0 commit comments

Comments
 (0)