使用Qpid Proton时thread_local引发线程重启崩溃的问题咨询
崩溃场景
- 启动继承自
thread和proton::messaging_handler的ProtonThread线程,在线程主方法作用域内执行proton::container::run(*this) - 该调用触发Proton内部UUID生成逻辑,会在当前线程中创建一个
thread_local对象 - 初次执行正常:
container::run()执行完毕后栈对象销毁,调用join确保线程干净退出 - 重启该线程时,执行
container相关逻辑发生崩溃
修复方法
- 不再在线程主函数中使用栈对象,将
proton::container改为ProtonThread类的成员变量 - 此时
proton::container会在构造ProtonThread的线程中创建,对应的thread_local对象归属构造线程,重启线程时不再崩溃
我的困惑
原以为即使使用栈对象,ProtonThread线程内的thread_local对象也能正常工作,无法理解重启线程时崩溃的原因,希望得到原因分析提示。
崩溃栈信息
Program terminated with signal SIGABRT, Aborted. #0 0x00007f13ede8b94c in __pthread_kill_implementation () from /lib64/libc.so.6 [Current thread is 1 (Thread 0x7f13e6ffb640 (LWP 792836))] Missing separate debuginfos, use: dnf debuginfo-install cyrus-sasl-gssapi-2.1.27-21.el9.x86_64 cyrus-sasl-lib-2.1.27-21.el9.x86_64 cyrus-sasl-plain-2.1.27-21.el9.x86_64 gdbm-libs-1.19-4.el9.x86_64 glibc-2.34-100.el9_4.4.x86_64 jsoncpp-1.9.5-1.el9.x86_64 keyutils-libs-1.6.3-1.el9.x86_64 krb5-libs-1.21.1-2.el9_4.x86_64 libcom_err-1.46.5-5.el9.x86_64 libgcc-11.4.1-3.el9.x86_64 libnsl2-2.0.0-1.el9.x86_64 libselinux-3.6-1.el9.x86_64 libstdc++-11.4.1-3.el9.x86_64 libtirpc-1.3.3-8.el9_4.x86_64 libxcrypt-4.4.18-3.el9.x86_64 pcre2-10.40-5.el9.x86_64 qpid-proton-c-0.37.0-2.el9.x86_64 qpid-proton-cpp-0.37.0-2.el9.x86_64 zlib-1.2.11-40.el9.x86_64 (gdb) where #0 0x00007f13ede8b94c in __pthread_kill_implementation () from /lib64/libc.so.6 #1 0x00007f13ede3e646 in raise () from /lib64/libc.so.6 #2 0x00007f13ede287f3 in abort () from /lib64/libc.so.6 #3 0x00007f13ede29130 in __libc_message.cold () from /lib64/libc.so.6 #4 0x00007f13ede959f7 in malloc_printerr () from /lib64/libc.so.6 #5 0x00007f13ede9671c in malloc_consolidate () from /lib64/libc.so.6 #6 0x00007f13ede982f8 in _int_malloc () from /lib64/libc.so.6 #7 0x00007f13ede99809 in malloc () from /lib64/libc.so.6 #8 0x00007f13ee73a75e in malloc (size=<optimized out>) at ../include/rtld-malloc.h:56 #9 allocate_dtv_entry (size=<optimized out>, alignment=8) at ../elf/dl-tls.c:730 #10 allocate_and_init (map=0x1f575e0) at ../elf/dl-tls.c:759 #11 tls_get_addr_tail (ti=0x7f13ed3fde58, dtv=0x7f13e00018f0, the_map=0x1f575e0) at ../elf/dl-tls.c:970 #12 0x00007f13ee73e76c in __tls_get_addr () at ../sysdeps/x86_64/tls_get_addr.S:55 #13 0x00007f13ed3dd187 in proton::uuid::random() () from /lib64/libqpid-proton-cpp.so.12 #14 0x00007f13ed3c6bfe in proton::container::container(proton::messaging_handler&) () from /lib64/libqpid-proton-cpp.so.12
原因分析提示
从崩溃栈来看,崩溃发生在__tls_get_addr调用过程中的内存分配阶段,指向线程本地存储(TLS)的复用或初始化异常,结合场景推测可能的原因:
线程ID复用导致TLS残留:Linux内核会复用已退出线程的ID。当重启的线程复用了之前的ID时,旧线程销毁时
thread_local对象的清理可能不彻底,新线程尝试初始化同名thread_local时,会遇到残留的TLS存储结构,触发内存分配错误。Proton库TLS对象生命周期问题:Proton内部的
thread_local对象(如UUID生成相关的engine)可能在线程退出时未被正确清理,或其依赖的资源已被提前释放。当新线程(复用ID)再次初始化该对象时,访问了已失效的内存区域,引发malloc断言。栈对象销毁与TLS绑定冲突:当
proton::container是栈对象时,其生命周期局限于线程主方法作用域。虽然thread_local对象理论上在线程退出时销毁,但Proton库可能在container销毁时提前释放了与TLS对象关联的资源,导致后续线程复用ID时,TLS初始化因依赖缺失而崩溃。
将container改为类成员后,container的生命周期与ProtonThread实例绑定,且在构造线程中创建,其关联的thread_local对象归属构造线程,避免了工作线程ID复用带来的TLS冲突。
内容的提问来源于stack exchange,提问作者cvomake

