我正在运行一个phread测试程序,直到它失败。这是代码的主要骨架:
int authSessionListMutexUnlock()
{
int rc = 0;
int rc2 = 0;
rc2 = pthread_mutex_trylock(&mutex);
ERR_IF( rc2 != EBUSY && rc2 != 0 );
rc2 = pthread_mutex_unlock(&mutex);
ERR_IF( rc2 != 0 );
cleanup:
return rc;
}
static void cleanup_handler(void *arg)
{
int rc = 0;
(void)arg;
rc = authSessionListMutexUnlock();
if (rc != 0)
AUTH_DEBUG5("authSessionListMutexUnlock() failed\n");
}
static void *destroy_expired_sessions(void *t)
{
int rc2 = 0;
(void)t;
pthread_cleanup_push(cleanup_handler, NULL);
rc2 = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (rc2 != 0)
AUTH_DEBUG5("pthread_setcancelstate(): rc2 == %d\n", rc2);
rc2 = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
if (rc2 != 0)
AUTH_DEBUG5("pthread_setcanceltype(): rc2 == %d\n", rc2);
while (1)
{
... // destroy expired session
sleep(min_timeout);
}
pthread_cleanup_pop(0);
}
int authDeinit( char *path )
{
...
rc2 = authSessionListDeInit();
ERR_IF( rc2 != 0 );
rc2 = pthread_cancel(destroy_thread);
ERR_IF( rc2 != 0 );
rc2 = pthread_join(destroy_thread, &status);
ERR_IF( rc2 != 0 || (int *)status != PTHREAD_CANCELED );
...
return 0
}
它与测试程序运行良好,但测试程序在第53743轮与pthread_join()挂起:
(gdb) bt
#0 0x40000410 in __kernel_vsyscall ()
#1 0x0094aa77 in pthread_join () from /lib/libpthread.so.0
#2 0x08085745 in authDeinit ()
at /users/qixu/src/moja/auth/src//app/libauth/authAPI.c:1562
#3 0x0807e747 in main ()
at /users/qixu/src/moja/auth/src//app/tests/test_session.c:45
看起来pthread_join()导致了死锁。但是看一下代码,我觉得pthread_join()没有导致死锁的原因。当pthread_join()有机会运行时,唯一的互斥操作就是线程本身。应该没有冲突吧?这里真的很困惑......
答案 0 :(得分:5)
代码中至少显示一个“奇怪”字样;你的清理处理程序将始终解锁互斥锁,即使你不是持有它的线程。
来自手册;
使用调用线程的互斥锁调用pthread_mutex_unlock() 不成立将导致未定义的行为。
答案 1 :(得分:3)
您的代码的一个更大的问题,可能是死锁的原因,是您使用异步取消模式(之前我错过了)。 POSIX中只有3个函数是async-cancel-safe:
来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_05_04
启用异步取消模式时,您无法锁定和解锁互斥锁。
要使异步取消可用,您必须执行以下操作之一:
编辑:根据评论,我认为您对异步取消类型的含义存在误解。它与清理处理程序的运行方式无关。这纯粹是线程能够捕获取消请求并开始对其采取行动的问题。
当目标处于延迟取消模式时,在其上调用pthread_cancel
不一定会立即执行任何操作,除非它已在某个函数中被阻止(例如read
或select
)取消点。相反,它只会设置一个标志,并且下次调用取消点的函数时,线程将阻止任何进一步的取消尝试,按照它们被推送的相反顺序运行取消清理处理程序,并以特殊方式退出表示该线程被取消的状态。
当目标处于异步取消模式时,在其上调用pthread_cancel
将立即中断线程(可能在任何一对相邻的机器代码指令之间)。如果你不明白为什么这有潜在危险,那就考虑一下。任何具有内部状态的函数(静态/全局变量,文件描述符或其他资源被分配/释放等)在中断点可能处于不一致状态:部分修改变量,获得中途锁定,获得资源但没有记录它已经获得,或被释放,但没有记录它已被释放,等等。
在异步中断时,进一步取消请求被阻止,因此不存在从清理处理程序调用您喜欢的任何函数的危险。当清理处理程序完成运行时,线程当然不再存在。
另一个潜在的混淆源:清理处理程序不会与被取消的线程并行运行。当取消被取消时,取消的线程停止运行正常的代码流,而是运行清理处理程序然后退出。