Почему Helgrind показывает сообщение об ошибке «нарушен порядок блокировки»? - PullRequest
1 голос
/ 25 мая 2020

См. Следующий код

    #include <stdio.h>
    #include <pthread.h>
    #include <assert.h>
    #include <stdlib.h>

    pthread_mutex_t g = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;

    void* worker(void* arg) 
    {
        pthread_mutex_lock(&g);

        if ((long long) arg == 0) {
        pthread_mutex_lock(&m1);
        pthread_mutex_lock(&m2);
        } else {
        pthread_mutex_lock(&m2);
        pthread_mutex_lock(&m1);
        }
        pthread_mutex_unlock(&m1);
        pthread_mutex_unlock(&m2);

        pthread_mutex_unlock(&g);
        return NULL;
    }

    int main(int argc, char *argv[]) {
        pthread_t p1, p2;
        pthread_create(&p1, NULL, worker, (void *) (long long) 0);
        pthread_create(&p2, NULL, worker, (void *) (long long) 1);
        pthread_join(p1, NULL);
        pthread_join(p2, NULL);
        return 0;
    }

Helgrind выдает следующую ошибку:

==10035== Helgrind, a thread error detector
==10035== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==10035== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10035== Command: ./Hw5
==10035== 
==10035== ---Thread-Announcement------------------------------------------
==10035== 
==10035== Thread #3 was created
==10035==    at 0x538987E: clone (clone.S:71)
==10035==    by 0x5050EC4: create_thread (createthread.c:100)
==10035==    by 0x5050EC4: pthread_create@@GLIBC_2.2.5 (pthread_create.c:797)
==10035==    by 0x4C36A27: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x1088BD: main (Hw5.c:28)
==10035== 
==10035== ----------------------------------------------------------------
==10035== 
==10035== Thread #3: lock order "0x309080 before 0x3090C0" violated
==10035== 
==10035== Observed (incorrect) order is: acquisition of lock at 0x3090C0
==10035==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x10882E: worker (Hw5.c:16)
==10035==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x50506DA: start_thread (pthread_create.c:463)
==10035==    by 0x538988E: clone (clone.S:95)
==10035== 
==10035==  followed by a later acquisition of lock at 0x309080
==10035==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x10883A: worker (Hw5.c:17)
==10035==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x50506DA: start_thread (pthread_create.c:463)
==10035==    by 0x538988E: clone (clone.S:95)
==10035== 
==10035== Required order was established by acquisition of lock at 0x309080
==10035==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x108814: worker (Hw5.c:13)
==10035==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x50506DA: start_thread (pthread_create.c:463)
==10035==    by 0x538988E: clone (clone.S:95)
==10035== 
==10035==  followed by a later acquisition of lock at 0x3090C0
==10035==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x108820: worker (Hw5.c:14)
==10035==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x50506DA: start_thread (pthread_create.c:463)
==10035==    by 0x538988E: clone (clone.S:95)
==10035== 
==10035==  Lock at 0x309080 was first observed
==10035==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x108814: worker (Hw5.c:13)
==10035==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x50506DA: start_thread (pthread_create.c:463)
==10035==    by 0x538988E: clone (clone.S:95)
==10035==  Address 0x309080 is 0 bytes inside data symbol "m1"
==10035== 
==10035==  Lock at 0x3090C0 was first observed
==10035==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x108820: worker (Hw5.c:14)
==10035==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==10035==    by 0x50506DA: start_thread (pthread_create.c:463)
==10035==    by 0x538988E: clone (clone.S:95)
==10035==  Address 0x3090c0 is 0 bytes inside data symbol "m2"
==10035== 
==10035== 
==10035== 
==10035== For counts of detected and suppressed errors, rerun with: -v
==10035== Use --history-level=approx or =none to gain increased speed, at
==10035== the cost of reduced accuracy of conflicting-access information
==10035== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 7)

Я думаю, что внешняя блокировка g не позволит двум потокам войти в критическую секцию в то же время. Только один поток может получить блокировку g в данный момент. Поэтому я считаю, что тупиковой ситуации быть не может. Я носился? Почему helgrind выдает эту ошибку? Пожалуйста, объясните.

1 Ответ

4 голосов
/ 25 мая 2020

Helgrind жалуется, что ваши потоки блокируют мьютексы m1 и m2 в разных относительных порядках, что также ясно из проверки кода. Helgrind ищет и отмечает такие различия в порядке получения, потому что, вообще говоря, они создают риск тупиковой ситуации.

Я думаю, что внешняя блокировка g не позволит двум потокам войти в критическую секцию на то же время. Только один поток может получить блокировку g в данный момент. Поэтому я считаю, что тупиковой ситуации быть не может. Я что носил?

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

Почему helgrind выдает эту ошибку?

Потому что helgrind - это heuristi c анализ поведения вашей программы во время одного запуска . Он не предполагает, что один запуск программы демонстрирует все возможные варианты поведения. С другой стороны, ваша оценка основана на анализе исходного кода.

Правило эвристики c, которое вы видите в действии, заключается в том, что никакие пары мьютексов не должны приобретаться в разных относительных порядках ни одним потоком. . Для вашей конкретной программы это приводит к ложному срабатыванию, но ваша программа кажется специально разработанной для его создания. Во-первых, нет необходимости в мьютексах m1 и m2, если мьютекс g всегда будет удерживаться при захвате любого из остальных. Однако, если бы любой другой поток мог получить m1 и m2, не удерживая g, то риск взаимоблокировки был бы реальным, независимо от порядка получения в указанном другом потоке.

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

...