pthread_cond_wait: ошибка случайной сегментации

Обновление 3

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

namespace thread {
    pthread_t terminated_thread_id, /* and others */;
    pthread_mutex_t terminate_thread = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t terminate_thread_signal = PTHREAD_COND_INITIALIZER;
    int total_thread_count = 0;
    int termination; // + sembufs

    inline void* Exit(void* value) {
    //  This must be unlocked after all join-related jobs are done
        semop(thread::termination, thread::termination_in_process, 2)
            pthread_mutex_lock(&thread::terminate_thread);
                thread::terminated_thread_id = pthread_self();
                pthread_cond_signal(&thread::terminate_thread_signal);
            pthread_mutex_unlock(&thread::terminate_thread);

        pthread_exit(value);
        return value;
    }
}
int main(int argc, const char** argv){
...
    pthread_mutex_lock(&thread::terminate_thread);
    if(0 != pthread_create(&thread::communication_handler_thread_id, NULL,    \
                           CommunicationHandler, NULL)){
        global::PrintDebug("pthread_create() failed", __FILE__, __LINE__);
    }
    /** 2 more pthread_create()-calls */       
    do{
        thread::terminated_thread_id = pthread_self();
        pthread_cond_wait(&thread::terminate_thread_signal,                   \
                          &thread::terminate_thread);
        if(!pthread_equal(thread::terminated_thread_id, pthread_self())){
            pthread_join(thread::terminated_thread_id, NULL);
    ...
            semop(thread::termination, thread::termination_done, 1)
        }
    }while(thread::total_thread_count > 0);

    pthread_mutex_unlock(&thread::terminate_thread);
    return 0;
}

Сигнал terminate_thread_signal испускается только в функции thread::Exit(). Эта функция также вызывается только в конце функции, которая используется для создания потока.

Вот что показывает отладчик для стека вызовов:

#0 (    0xb7fe2424 in __kernel_vsyscall() (??:??)
#1 0xb7fbdfcf   __pthread_cond_wait(cond=0x80539c0, mutex=0x8053998) (pthread_cond_wait.c:153)
#2 0x804a094    main(argc=1, argv=0xbffff9c4) (/home/papergay/SeekYourCar/0.2/Server/main.cpp:121)

Что я уже знаю, так это то, что если ошибка происходит, то ни один поток еще не вызвал thread::Exit(). Я также использую безымянное пространство имен с несколькими инициализациями (если это может быть актуально). Я использую Code::Blocks в качестве IDE и GCC в качестве компилятора.

3 ответа

Решение

Это довольно поздно, но я забыл опубликовать его для будущих ссылок. Вот как я это исправил:

Я обновил свой компилятор GCC с версии 4.5.X до версии 4.7.X, а также ядро ​​с 2.6.X до 3.2.X и исправил некоторые ошибки, касающиеся глобального создания экземпляров класса и статической переменной-члена, предоставив явный конструктор для того, чтобы разрешить глобальное объявление без инициализации. Но я думаю, что обновление компилятора GCC - это все, что было нужно.

Похоже, что реализация функции была неправильной. Или были какие-то ошибки в коде ядра?

pthread_cond_wait() разрешено пробуждаться не покладая рук, поэтому вам придется заново проверять само состояние после каждого пробуждения. Это может быть причиной вашей проблемы - если основной поток просыпается раньше thread::terminated_thread_id был установлен, он передаст неверный идентификатор потока pthread_join(),

Есть и другая проблема в вашем коде - нет гарантии, что сигнальный поток будет следующим, после того как мьютекс будет разблокирован, поэтому два потока могут вызвать thread::Exit() в быстрой последовательности, когда основной поток не работает до тех пор, пока второй выходной поток не разблокирует мьютекс. В этом случае вы никогда не будете звонить pthread_join() в первой теме.

Примерно так должно исправить эти проблемы:

namespace thread {
    int terminate_thread_set = 0;
    pthread_mutex_t terminate_thread = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t terminate_thread_set_cond = PTHREAD_COND_INITIALIZER;
    pthread_cond_t terminate_thread_unset_cond = PTHREAD_COND_INITIALIZER;

    /* ... */

    inline void Exit(void* value)
    {
        pthread_mutex_lock(&thread::terminate_thread);
        while (thread::terminate_thread_set)
            pthread_cond_wait(&thread::terminate_thread_unset_cond);
        thread::terminated_thread_id = pthread_self();
        thread::terminate_thread_set = 1;
        pthread_cond_signal(&thread::terminate_thread_set_cond);
        pthread_mutex_unlock(&thread::terminate_thread);

        pthread_exit(value);
    }
}

И в main:

pthread_mutex_lock(&thread::terminate_thread);

/* ... */

while(thread::total_thread_count > 0) {
    while (!thread::terminate_thread_set)
        pthread_cond_wait(&thread::terminate_thread_set_cond, &thread::terminate_thread);
    thread::terminate_thread_set = 0;
    pthread_join(thread::terminated_thread_id, NULL);
    pthread_cond_signal(&thread::terminate_thread_unset_cond);
...
}
pthread_mutex_unlock(&thread::terminate_thread);

Это не значит, что у вас нет других проблем, конечно.

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

Решением может быть использование буфера FIFO (например, std::queue или даже просто std:: vector) и передача идентификатора потока завершенных потоков в него в вашем Exit() функции, затем отправьте ваш сигнал, и пусть главный поток пройдет через буфер и присоединит к нему все потоки.

Если Exit() не вызывается в момент вашего segfault, это не должно быть причиной вашей проблемы, но вы все равно можете ее исправить.

Другие вопросы по тегам