pthread_join () зависает в стресс-тесте - PullRequest
1 голос
/ 20 марта 2012

Я запускаю программу тестирования 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 ]

5 голосов
/ 20 марта 2012

По крайней мере, одна «странность» отображается в вашем коде;Ваш обработчик очистки всегда разблокирует мьютекс , даже если вы не являетесь его владельцем.

Из руководства;

Вызов pthread_mutex_unlock () с помощьюмьютекс, который не содержит вызывающий поток, приведет к неопределенному поведению.

3 голосов
/ 20 марта 2012

Более серьезная проблема с вашим кодом и, вероятно, причина тупиков, заключается в использовании вами режима асинхронной отмены (я пропустил это раньше). Только 3 функции в POSIX безопасны для асинхронной отмены:

  • pthread_cancel ()
  • pthread_setcancelstate ()
  • pthread_setcanceltype ()

Источник: http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_05_04

Вы, конечно, не можете блокировать и разблокировать мьютексы, когда включен режим асинхронной отмены.

Для использования асинхронной отмены необходимо сделать однуиз следующих вещей:

  • Используйте его только с чисто вычислительным кодом, например, выполняйте сложную математику без каких-либо обращений к библиотеке, только арифметические операции или
  • Постоянно отключайте его ивозвращайтесь к каждому библиотечному вызову, который вы делаете.

Редактировать: Основываясь на комментариях, я думаю, что вы неправильно понимаете, что означает асинхронный тип отмены.Это не имеет ничего общего с тем, как работают обработчики очистки.Это просто вопрос того, в какой момент поток может перехватить запрос на отмену и начать действовать в соответствии с ним.

Когда цель находится в режиме отложенного отмены, вызов pthread_cancel для нее не обязательно сделает что-либо сразу, если толькоон уже заблокирован в функции (например, read или select), которая является точкой отмены.Вместо этого он просто установит флаг, и в следующий раз, когда будет вызвана функция, которая является точкой отмены, поток вместо этого заблокирует любые дальнейшие попытки отмены, запустит обработчики очистки отмены в обратном порядке, в котором они были выдвинуты, и выйдет со специальнымсостояние, указывающее, что поток был отменен.

Когда цель находится в режиме асинхронной отмены, вызов pthread_cancel на нем немедленно прервет поток (возможно, между любой парой соседних инструкций машинного кода).Если вы не понимаете, почему это потенциально опасно, подумайте об этом на секунду.Любая функция, имеющая внутреннее состояние (статические / глобальные переменные, файловые дескрипторы или другие ресурсы, выделяемые / освобождаемые и т. Д.) Может находиться в несогласованном состоянии в момент прерывания: переменная частично изменена, получена блокировка на полпути, получен ресурсно без записи о том, что он был получен или освобожден, но без записи о том, что он был освобожден и т. д.

В точке асинхронного прерывания дальнейшие запросы отмены блокируются, поэтому опасности вызова нетлюбую функцию, которая вам нравится из ваших обработчиков очистки.Когда обработчики очистки завершают работу, поток, конечно, перестает существовать.

Еще один потенциальный источник путаницы: обработчики очистки не работают параллельно с отменой потока.Когда отменяется действие, отмененный поток прекращает выполнение нормального потока кода и вместо этого запускает обработчики очистки, а затем завершает работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...