Ошибка сегментации после pthread_cancel - PullRequest
0 голосов
/ 18 ноября 2018

В моей программе у меня есть два потока, один вычисляет некоторую рекурсивную функцию, а другой отображает «точки прогресса», когда первые потоки выполняют вычисления.Есть код:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#define test_errno(msg) do{if (errno) {perror(msg); exit(EXIT_FAILURE);}} while(0)

void *progressDotsThread(void* _arg){
    for(int i=0;;i++){
    printf(".");
    fflush(stdout);
    sleep(1);
    }
}
void *seriesCalculateThread(void* n){
    int result;

    if((long)n==1) return (void*)(1);
    else if((long)n==2) return (void*)(-5);
    else{
         int nmin1 = (long)seriesCalculateThread( (void*) ( (long)n -1   ));
         int nmin2 = (long)seriesCalculateThread( (void*) ( (long)n -2   ));
         result = nmin1*((long)n)*nmin2;
         return (void*)(result);
    }
}

int main(void) {
    long n=0;
    int result =0;
    pthread_t w1,w2;

    printf("Give n\n");
    scanf ("%d",&n);

    if(n<1){
        printf("Value must be higher than 0");
    }
    else{

        errno= pthread_create(&w2,NULL,seriesCalculateThread,(void *)n);
        test_errno("pthread_create");
        errno= pthread_create(&w1,NULL,progressDotsThread,NULL);
        test_errno("pthread_create");

        if(!pthread_join(w2,(void**)&result)){
            errno = pthread_cancel(w1); //<--- Where segmentation fault happens
            test_errno("pthread_cancel");
        }
        printf("%d\n", result);

    }

    return EXIT_SUCCESS;
}

I pthread_create оба, а затем I if(!pthread_join) тот, который выполняет вычисления, и когда вычисления заканчиваются, я отменяю тот, который отображает точки.

if(!pthread_join(w2,(void**)&result)){
                errno = pthread_cancel(w1); //<--- Where segmentation fault happens
                test_errno("pthread_cancel");
            }

Я делаю это, хотя я получаю ошибку сегментации после вызова pthread_cancel (w1), и я понятия не имею, почему это происходит.

Я попытался скомпилировать терминал с помощью gcc -g -Wall -pthread psthreadss.c -lpthread, и онне работал так же хорошо, как в затмении.Я использую Ubuntu 18.04, если это так.

1 Ответ

0 голосов
/ 18 ноября 2018

и затем я if(!pthread_join) тот, который выполняет вычисления, и когда вычисления заканчиваются, я отменяю тот, который отображает точки.

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

Логическая ошибка: вы предполагаете, что !pthread_join означает, что вычисления завершены. Это не означает . pthread_join возвращает ненулевой статус только , если вы вызвали его с неверными параметрами (идентификатор потока w2 неизвестен, не был запущен или уже был присоединен).

Вместо этого ваш код должен выглядеть примерно так:

int rc = pthread_join(w2, ...);
assert(rc == 0);
rc = pthread_cancel(w1);
assert (rc == 0);

Об ошибке повреждения памяти: в 64-разрядной системе (что, как я полагаю, вы используете), sizeof(int) == sizeof(result) == 4, но sizeof(void*) == 8.

Этот код:

 int result =0;
 ...
    pthread_join(w1, (void**)&result)

берет 4-байтовый адрес result и просит pthread_join сохранить там 8-байтовое значение. Предсказуемый результат - повреждение стека.

Я не понимаю, как именно это вызывает SIGSEGV, но если в вашей программе есть неопределенное поведение, все ставки отменяются.

Чтобы исправить это, измените тип result на intptr_t. Я ожидаю, что авария исчезнет после исправления.

Вам также следует попробовать использовать Address Sanitizer: gcc -fsanitize=address -g ... перед внесением каких-либо изменений. Существует большая вероятность, что он расскажет вам о переполнении стека.

...