Fuga
Fuga

Reputation: 55

Deadlock in producer-consumer using pthreads

I'm using pthreads to solve a producer-consumer problem. Basicaly the producer reads a file to a buffer and the consumers (at least one, but not limited) take entries from the buffer and operate them one by one. This is the producer:

//...Local stuff...
if(file){
    while(fgets(line, 256, file)){            
        pthread_mutex_lock(&buffer_mutex);            
        while(data->buffer->buffer_items == data->buffer->buffer_size){               
            pthread_cond_wait(&buffer_full_cv, &buffer_mutex);}
        data->buffer->buffer_items);
        reads++;
        add_to_head(data->buffer, line);
        pthread_cond_broadcast(&buffer_ready_cv);            
        pthread_mutex_unlock(&buffer_mutex);            
    }
    pthread_mutex_lock(&buffer_mutex);
    work = 0;
    pthread_mutex_unlock(&buffer_mutex);
    fclose(file);
}

And this is the consumer:

//...Local stuff...
while(1){
    pthread_mutex_lock(&buffer_mutex);        
    while(data->buffer->buffer_items == 0){
        if(work)
            pthread_cond_wait(&buffer_ready_cv, &buffer_mutex);            
        else if(!work && !data->buffer->buffer_items)
            pthread_exit(NULL);
    }
    remove_from_tail(data->buffer, string_to_check);
    data->buffer->buffer_items);
    pthread_cond_signal(&buffer_full_cv);
    pthread_mutex_unlock(&buffer_mutex);

    for(unsigned int i = 0; i < data->num_substrings; i++){    
        cur_occurrence = strstr(string_to_check, data->substrings[i]);
        while(cur_occurrence != NULL){
            pthread_mutex_lock(&buffer_mutex);
            data->occurrences[i]++;
            cur_occurrence++;
            cur_occurrence = strstr(cur_occurrence, data->substrings[i]);
            pthread_mutex_unlock(&buffer_mutex);
        }
    }
}

What seems to be happening is the file is completely read and there's still work to be done, but as the producer is not running anymore, the wait in the consumer never finishes.

PS.: I've also tried pthread_cond_signal instead of broadcast, but didn't work either.

Anyway, Is there something I'm missing here?

Upvotes: 1

Views: 404

Answers (1)

Florian Weimer
Florian Weimer

Reputation: 33719

What seems to be happening is the file is completely read and there's still work to be done, but as the producer is not running anymore, the wait in the consumer never finishes.

Technically, this is not a deadlock. This is a common challenge with producer/consumer thread configurations. There are various ways to deal with this.

  • You could use a special buffer value (separate from the empty buffer) which signals that the producer has finished. (If you have multiple consumers, this special value has to be left in the buffer.) Such in-band signaling, while sometimes convenient to implement, is typically not a good design, though.
  • If you have multiple producers, you probably should combine the buffer with a counter of the number of producers running, essentially adding a semaphore to the buffer. If the count of producers reaches zero, the consumers need to exit.
  • The thread which spawns the producers and consumers could use pthread_cancel after joining all consumers, so that pthread_cond_wait is aborted. This is tricky to get completely right, though, and cancellation is best avoided in general.

Note that if you have multiple consumers, each one needs to broadcast the signal after it observed that the end-of-data state has been reached, so that the other consumers have a chance to observe it, too,

Upvotes: 1

Related Questions