pthread_join()在压力测试中挂起

时间:2012-03-19 20:19:05

标签: c pthreads

我正在运行一个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()有机会运行时,唯一的互斥操作就是线程本身。应该没有冲突吧?这里真的很困惑......

2 个答案:

答案 0 :(得分:5)

代码中至少显示一个“奇怪”字样;你的清理处理程序将始终解锁互斥锁,即使你不是持有它的线程。

来自手册;

  

使用调用线程的互斥锁调用pthread_mutex_unlock()   不成立将导致未定义的行为。

答案 1 :(得分:3)

您的代码的一个更大的问题,可能是死锁的原因,是您使用异步取消模式(之前我错过了)。 POSIX中只有3个函数是async-cancel-safe:

  • pthread_cancel可以()
  • pthread_setcancelstate()
  • pthread_setcanceltype()

来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_05_04

启用异步取消模式时,您无法锁定和解锁互斥锁。

要使异步取消可用,您必须执行以下操作之一:

  • 仅将其用于纯计算的代码,例如:在没有任何库调用的情况下进行繁重的数学运算,只需算术运算,或
  • 不断切换关闭并重新打开每个库调用。

编辑:根据评论,我认为您对异步取消类型的含义存在误解。它与清理处理程序的运行方式无关。这纯粹是线程能够捕获取消请求并开始对其采取行动的问题。

当目标处于延迟取消模式时,在其上调用pthread_cancel不一定会立即执行任何操作,除非它已在某个函数中被阻止(例如readselect)取消点。相反,它只会设置一个标志,并且下次调用取消点的函数时,线程将阻止任何进一步的取消尝试,按照它们被推送的相反顺序运行取消清理处理程序,并以特殊方式退出表示该线程被取消的状态。

当目标处于异步取消模式时,在其上调用pthread_cancel将立即中断线程(可能在任何一对相邻的机器代码指令之间)。如果你不明白为什么这有潜在危险,那就考虑一下。任何具有内部状态的函数(静态/全局变量,文件描述符或其他资源被分配/释放等)在中断点可能处于不一致状态:部分修改变量,获得中途锁定,获得资源但没有记录它已经获得,或被释放,但没有记录它已被释放,等等。

在异步中断时,进一步取消请求被阻止,因此不存在从清理处理程序调用您喜欢的任何函数的危险。当清理处理程序完成运行时,线程当然不再存在。

另一个潜在的混淆源:清理处理程序不会与被取消的线程并行运行。当取消被取消时,取消的线程停止运行正常的代码流,而是运行清理处理程序然后退出。