简体   繁体   中英

Deadlock concurrency issue in C eventcounter + sequencer

I'm trying to get this code to work, but for some reason it deadlocks within 30 seconds.

The deadlock seems to happen Putting or Getting, whether the buffer is full not.

Am I missing something obvious or not using something right? I'm new to C!

To compile: gcc -Wall -o test test.c -lpthread

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>



struct sequence {
    pthread_mutex_t mutex;
    int count;
};

struct event {
    pthread_mutex_t critical;
    pthread_mutex_t critical2;
    pthread_mutex_t signalM;
    pthread_cond_t signalC;
    int eventCount;
};

struct allVars {
    struct sequence putSeq;
    struct sequence getSeq;
    struct event inEvents;
    struct event outEvents;
    int bufferSize;
    char buffer[10][128];
};



/**
 * Issue tickets in sequence. Used in conjunction with an Event Counter
 */
int getTicket(struct sequence *seq) {
    // begin critical section
    if (pthread_mutex_lock(&seq->mutex) != 0) {
        printf("mutex_lock in getticket error\n");
    }

    // remember current count
    int oldCount = seq->count;

    // increment count
    seq->count++;

    // end critical section
    if (pthread_mutex_unlock(&seq->mutex) != 0) {
        printf("mutex_unlock in getticket error\n");
    }

    return oldCount;
}



/**
 * Advance the EventCount (ticket)
 */
void advance(struct event *event) {
    // begin critical section
    if (pthread_mutex_lock(&event->critical) != 0) {
        fprintf(stderr, "mutex_lock in advance error\n");
        exit(EXIT_FAILURE);
    }

    // increment the event counter
    event->eventCount++;

    // end critical section
    if (pthread_mutex_unlock(&event->critical) != 0) {
        fprintf(stderr, "mutex_unlock in advance error\n");
        exit(EXIT_FAILURE);
    }

    // signal await to continue
    if (pthread_cond_signal(&event->signalC) != 0) {
        fprintf(stderr, "cond_signal in advance error\n");
        exit(EXIT_FAILURE);
    }
}



/**
 * Wait for ticket and buffer availability
 */
void await(struct event *event, int ticket) {

    int eventCount;

    // begin critical section
    if (pthread_mutex_lock(&event->critical) != 0) {
        fprintf(stderr, "mutex_lock in advance error\n");
        exit(EXIT_FAILURE);
    }

    eventCount = event->eventCount;

    // end critical section
    if (pthread_mutex_unlock(&event->critical) != 0) {
        fprintf(stderr, "mutex_unlock in advance error\n");
        exit(EXIT_FAILURE);
    }


    // loop until the ticket machine shows your number
    while (ticket > eventCount) {
        // wait until a ticket is called
        pthread_cond_wait(&event->signalC, &event->signalM);

        // begin critical section
        if (pthread_mutex_lock(&event->critical) != 0) {
            fprintf(stderr, "mutex_lock in advance error\n");
            exit(EXIT_FAILURE);
        }

        eventCount = event->eventCount;

        // end critical section
        if (pthread_mutex_unlock(&event->critical) != 0) {
            fprintf(stderr, "mutex_unlock in advance error\n");
            exit(EXIT_FAILURE);
        }
    }
}



/**
 * Add to buffer
 */
void putBuffer(struct allVars *allVars, char data[]) {
    // get a ticket
    int ticket = getTicket(&allVars->putSeq);

    // get the current write position
    int in;

    // begin critical section
    if (pthread_mutex_lock(&allVars->inEvents.critical) != 0) {
        fprintf(stderr, "mutex_lock in put error\n");
        exit(EXIT_FAILURE);
    }

    in = allVars->inEvents.eventCount;

    // end critical section
    if (pthread_mutex_unlock(&allVars->inEvents.critical) != 0) {
        fprintf(stderr, "mutex_unlock in put error\n");
        exit(EXIT_FAILURE);
    }

    // wait for ticket to be called (sequential writing)
    await(&allVars->inEvents, ticket);

    // wait until theres a space free in the buffer
    await(&allVars->outEvents, in - allVars->bufferSize + 1);   // set to 2 to keep 1 index distance

    // begin critical section
    if (pthread_mutex_lock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_lock in put error\n");
        exit(EXIT_FAILURE);
    }

    // add data to buffer
    strcpy(allVars->buffer[ticket % allVars->bufferSize], data);

    // end critical section
    if (pthread_mutex_unlock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_unlock in put error\n");
        exit(EXIT_FAILURE);
    }

    // increment the ticket display
    advance(&allVars->inEvents);
}



/**
 * Get from buffer
 */
char *getBuffer(struct allVars *allVars) {
    // get a ticket
    int ticket = getTicket(&allVars->getSeq);

    // get the current read position
    int out;

    // begin critical section
    if (pthread_mutex_lock(&allVars->outEvents.critical) != 0) {
        fprintf(stderr, "mutex_lock in get error\n");
        exit(EXIT_FAILURE);
    }

    out = allVars->outEvents.eventCount;

    // end critical section
    if (pthread_mutex_unlock(&allVars->outEvents.critical) != 0) {
        fprintf(stderr, "mutex_unlock in get error\n");
        exit(EXIT_FAILURE);
    }

    // wait for ticket to be called (sequential reading)
    await(&allVars->outEvents, ticket);

    // wait until theres something in the buffer
    await(&allVars->inEvents, out + 1);

    char *str = malloc(128);

    // critical section
    if (pthread_mutex_lock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_lock in put error\n");
        exit(EXIT_FAILURE);
    }

    // get the buffer data
    strcpy(str, allVars->buffer[ticket % allVars->bufferSize]);

    // end critical section
    if (pthread_mutex_unlock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_unlock in put error\n");
        exit(EXIT_FAILURE);
    }

    // increment buffer availability
    advance(&allVars->outEvents);

    return str;
}



/** child thread (producer) */
void *childThread(void *allVars) {
    char str[10];
    int count = 0;

    while (true) {
        sprintf(str, "%d", count++);
        putBuffer(allVars, str);
    }

    pthread_exit(EXIT_SUCCESS);
}



int main(void) {
    // init structs
    struct sequence putSeq = {
        PTHREAD_MUTEX_INITIALIZER,
        0
    };
    struct sequence getSeq = {
        PTHREAD_MUTEX_INITIALIZER,
        0
    };
    struct event inEvents = {
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_COND_INITIALIZER,
        0
    };
    struct event outEvents = {
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_COND_INITIALIZER,
        0
    };
    struct allVars allVars = {
        putSeq,         // sequence
        getSeq,
        inEvents,       // events
        outEvents,
        10,             // buffersize
        {"", {""}}      // buffer[][]
    };
    pthread_mutex_lock(&allVars.inEvents.signalM);
    pthread_mutex_lock(&allVars.outEvents.signalM);


    // create child thread (producer)
    pthread_t thread;
    if (pthread_create(&thread, NULL, childThread, &allVars)) {
        fprintf(stderr, "failed to create child thread");
        exit(EXIT_FAILURE);
    }


    // (consumer)
    while (true) {
        char *out = getBuffer(&allVars);
        printf("buf: %s\n", out);
        free(out);
    }


    return (EXIT_SUCCESS);
}

I am not sure if that's the source of your problems, but it is one error that sure can cause locking problems:

You are using pthread_cond_wait and pthread_cond_signal wrong. You should always lock the condition's mutex before calling pthread_cond_wait or pthread_cond_signal .

pthread_cond_wait automatically releases the mutex for you after you called it and reacquires the mutex after a signal. Therefore, you have to release the lock after signaling a thread.

This pthread programming tutorial helped me a lot when I was learning pthread programming for a class at my university. I recommend taking a look at it, since it also covers some other aspects of pthread programming (eg mutex).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM