Segmentation fault while creating a number of threads entered by the user

254 Views Asked by At

I'm trying to solve the Producer-Consumer problem using pthreads and a buffer in the form of a vector. I want to be able to input the amount of threads I will have of Producers and Consumers. I get a segmentation fault as soon as I enter both values. I'm compiling the code using gcc and the -lpthread and I'm not getting a compile error. How do I fix this error?

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

#define MAX 1000//00            /* Numbers to produce */
#define SIZE 20                 /* Size of Buffer     */

typedef struct {
    int id;
} parm;

pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer[SIZE];
int c = 0;

/* 
    @Function: printState
    @In: integer i
    @Out: none

    @Description: Used to show the state of the buffer on a given state
*/
void printState(int i){
    int j;

    puts("Showing the state of the buffer: ");
    printf("[ ");
    for (j = 0; j < SIZE; j++){
        printf("%d ",buffer[j]);
    }
    printf("]\n");

}

/*
    @Function: producer
    @In: void *ptr
    @Out: none

    @Description: Call a producer on the process
*/

void* producer(void *ptr){
    int i;

    for (i = 1; i <= MAX; i++){
        printf("calling producer\n");// on position %d.\n",c+1);
        pthread_mutex_lock(&the_mutex); /* protect the buffer */

        if(c == SIZE){  /* If the buffer is full, wait */
            puts("The buffer is full. Waiting.");
            pthread_cond_wait(&condp, &the_mutex);
        }

        buffer[c] = 1;
        c++;

        printf("There are %d occupied positions on the buffer.\n", c);
        pthread_cond_signal(&condc); /* Wake up the consumer */
        pthread_mutex_unlock(&the_mutex); /* Release the buffer */

        //if(i == MAX/2){
        //  printState(i);
        //}

    }
    pthread_exit(0);
}

/*
    @Function: consumer
    @In: void *ptr
    @Out: none

    @Description: Call a consumer on the process
*/
void* consumer(void *ptr) {
    int i, j;

    for (i = 1; i <= MAX; i++){ 
        printf("calling consumer\n");// on position %d\n", c+1);    
        pthread_mutex_lock(&the_mutex); /* protect the buffer */
        if (c == 0){ /* If there is nothing in the buffer, wait */
            puts("Buffer is empty. Waiting.");
            pthread_cond_wait(&condc, &the_mutex);
        }
        buffer[c] = 0;
        c--;
        printf("There are %d occupied positions on the buffer.\n", c);

        pthread_cond_signal(&condp); /* wake up consumer */
        pthread_mutex_unlock(&the_mutex); /* release the buffer */

        //if(i == MAX){
        //  printState(i);
        //}

    }
    pthread_exit(0);
}

/*
    @Function: main
    @In: integer argc and character **argv
    @Out: none

    @Description: Main function of the algorithm
*/
int main(int argc, char **argv){
    pthread_t *pro_threads, *con_threads;
    pthread_attr_t pro_pthread_custom_attr, con_pthread_custom_attr;
    int i, M, N;
    parm *p_pro, *p_con;

    puts("Please, enter the number of producer threads:");
    scanf("%d",&N);

    puts("Please, enter the number of consumer threads:");
    scanf("%d",&M);

    for(i=0;i<SIZE;i++){
        buffer[i] = 0;
    } 

    // Allocate space for the threads

    pro_threads=(pthread_t *)malloc(N*sizeof(*pro_threads));
    pthread_attr_init(&pro_pthread_custom_attr);
    con_threads=(pthread_t *)malloc(M*sizeof(*con_threads));
    pthread_attr_init(&con_pthread_custom_attr);

    // Initialize the mutex and condition variables

    pthread_mutex_init(&the_mutex, NULL); /* Initialize the mutex */
    pthread_cond_init(&condc, NULL); /* Initialize the consumer condition variable */
    pthread_cond_init(&condp, NULL); /* Initialize the producer condition variable */

    // Create the threads

    for (i=0; i<N; i++){
        p_pro[i].id=i;
        pthread_create(&pro_threads[i], &pro_pthread_custom_attr, producer, (void *)(p_pro+i));
    }

    for (i=0; i<M; i++){
        p_con[i].id=i;
        pthread_create(&con_threads[i], &con_pthread_custom_attr, consumer, (void *)(p_con+i));
    }

    // Wait for the threads to finish.
    // Otherwise main might run to the end
    // and kill the entire process when it exits.

    for (i=0; i<N; i++){

        pthread_join(pro_threads[i], NULL);
    }

    for (i=0; i<M; i++){

        pthread_join(con_threads[i], NULL);
    }

    // Cleanup -- would happen automatically at the end of program

    pthread_mutex_destroy(&the_mutex); /* Free up the_mutex */
    pthread_cond_destroy(&condc); /* Free up the consumer condition variable */
    pthread_cond_destroy(&condp); /* Free up the producer condition variable */
    free(p_pro);
    free(p_con);
    return 0;
}
1

There are 1 best solutions below

3
On BEST ANSWER

Is this a university course or something?

The problem is reported by the compiler (gcc) if only you ask it to enable warnings. Whoever is "teaching" you c should have told you that.

meh.c: In function ‘printState’: meh.c:25:21: warning: unused parameter ‘i’ [-Wunused-parameter] void printState(int i){ ^ meh.c: In function ‘producer’: meh.c:47:22: warning: unused parameter ‘ptr’ [-Wunused-parameter] void* producer(void ptr){ ^ meh.c: In function ‘consumer’: meh.c:85:12: warning: unused variable ‘j’ [-Wunused-variable] int i, j; ^ meh.c:84:22: warning: unused parameter ‘ptr’ [-Wunused-parameter] void consumer(void *ptr) { ^ meh.c: In function ‘main’: meh.c:118:14: warning: unused parameter ‘argc’ [-Wunused-parameter] int main(int argc, char **argv){ ^ meh.c:118:27: warning: unused parameter ‘argv’ [-Wunused-parameter] int main(int argc, char **argv){ ^ meh.c:150:14: warning: ‘p_pro’ may be used uninitialized in this function [-Wmaybe-uninitialized] p_pro[i].id=i; ^ meh.c:155:14: warning: ‘p_con’ may be used uninitialized in this function [-Wmaybe-uninitialized] p_con[i].id=i;

However, the problem is trivially diagnosed even with standard methods like putting printfs here and there to narrow the crash site down.

As such, I'm confused as to what was the problem with figuring out what's wrong.

The code has some trivial bugs which makes it not work even with the segfault fixed. I'm omitting them as they deal with the general problem.

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

#define MAX 1000//00            /* Numbers to produce */
#define SIZE 20                 /* Size of Buffer     */

typedef struct {
    int id;
} parm;

pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer[SIZE];
int c = 0;

It is already 0. Terrible non-descriptive name for a global variable. //-----------------------------------------------------------------------------

/* @Function: printState @In: integer i @Out: none

    @Description: Used to show the state of the buffer on a given state
*/
void printState(int i){

Unused badly named argument.

    int j;

The idiom is to use 'i' as the loop index.

    puts("Showing the state of the buffer: ");
    printf("[ ");
    for (j = 0; j < SIZE; j++){
        printf("%d ",buffer[j]);
    }
    printf("]\n");

}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/*
    @Function: producer
    @In: void *ptr
    @Out: none

    @Description: Call a producer on the process
*/

void* producer(void *ptr){

Inconsitent placement of '*'. Placing it with the type name terrible.

    int i;

    for (i = 1; i <= MAX; i++){

'i' is not used within the loop, so the actual value does not matter. The idiom is to go from 0 to < MAX.

        printf("calling producer\n");// on position %d.\n",c+1);
        pthread_mutex_lock(&the_mutex); /* protect the buffer */

        if(c == SIZE){  /* If the buffer is full, wait */
            puts("The buffer is full. Waiting.");
            pthread_cond_wait(&condp, &the_mutex);
        }

        buffer[c] = 1;
        c++;

        printf("There are %d occupied positions on the buffer.\n", c);
        pthread_cond_signal(&condc); /* Wake up the consumer */
        pthread_mutex_unlock(&the_mutex); /* Release the buffer */

        //if(i == MAX/2){
        //  printState(i);
        //}

    }
    pthread_exit(0);
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
/*
    @Function: consumer
    @In: void *ptr
    @Out: none

    @Description: Call a consumer on the process
*/
void* consumer(void *ptr) {
    int i, j;

    for (i = 1; i <= MAX; i++){ 
        printf("calling consumer\n");// on position %d\n", c+1);    
        pthread_mutex_lock(&the_mutex); /* protect the buffer */
        if (c == 0){ /* If there is nothing in the buffer, wait */
            puts("Buffer is empty. Waiting.");
            pthread_cond_wait(&condc, &the_mutex);
        }
        buffer[c] = 0;
        c--;
        printf("There are %d occupied positions on the buffer.\n", c);

        pthread_cond_signal(&condp); /* wake up consumer */
        pthread_mutex_unlock(&the_mutex); /* release the buffer */

        //if(i == MAX){
        //  printState(i);
        //}

    }
    pthread_exit(0);
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/*
    @Function: main
    @In: integer argc and character **argv
    @Out: none

    @Description: Main function of the algorithm
*/
int main(int argc, char **argv){
    pthread_t *pro_threads, *con_threads;
    pthread_attr_t pro_pthread_custom_attr, con_pthread_custom_attr;
    int i, M, N;

Fully capitalised names are idiomatically used with macros. Terrible non-descriptive names.

    parm *p_pro, *p_con;

    puts("Please, enter the number of producer threads:");
    scanf("%d",&N);

    puts("Please, enter the number of consumer threads:");
    scanf("%d",&M);

I don't know who and why recommends that beginners use this. Use argv instead.

    for(i=0;i<SIZE;i++){
        buffer[i] = 0;
    } 

This buffer is already zeroed. Terrible spacing inconsistent with one employed earlier.

    // Allocate space for the threads

    pro_threads=(pthread_t *)malloc(N*sizeof(*pro_threads));

Casting malloc is actively harmful.

    pthread_attr_init(&pro_pthread_custom_attr);
    con_threads=(pthread_t *)malloc(M*sizeof(*con_threads));
    pthread_attr_init(&con_pthread_custom_attr);

    // Initialize the mutex and condition variables

    pthread_mutex_init(&the_mutex, NULL); /* Initialize the mutex */
    pthread_cond_init(&condc, NULL); /* Initialize the consumer condition variable */
    pthread_cond_init(&condp, NULL); /* Initialize the producer condition variable */

    // Create the threads

    for (i=0; i<N; i++){
        p_pro[i].id=i;

p_pro is not initialized.

        pthread_create(&pro_threads[i], &pro_pthread_custom_attr, producer, (void *)(p_pro+i));

Missing error checking. Inconsistent use of p_pro.

    }

    for (i=0; i<M; i++){
        p_con[i].id=i;
        pthread_create(&con_threads[i], &con_pthread_custom_attr, consumer, (void *)(p_con+i));
    }

    // Wait for the threads to finish.
    // Otherwise main might run to the end
    // and kill the entire process when it exits.

    for (i=0; i<N; i++){

        pthread_join(pro_threads[i], NULL);
    }

    for (i=0; i<M; i++){

        pthread_join(con_threads[i], NULL);
    }

    // Cleanup -- would happen automatically at the end of program

    pthread_mutex_destroy(&the_mutex); /* Free up the_mutex */
    pthread_cond_destroy(&condc); /* Free up the consumer condition variable */
    pthread_cond_destroy(&condp); /* Free up the producer condition variable */
    free(p_pro);
    free(p_con);
    return 0;
}