How to account for a circular buffer size of 1 when using pthreads / cond_signal / cond_wait?

Go To StackoverFlow.com

0

I'm trying to implement a program that uses a thread to read data from a file and write to a buffer of an arbitrary size, while two other threads read info from this buffer. Everything works fine, except for when I specify a buffer size of 1. When I do that, everything locks up. I'm more or less adapting the classic "consumer/producer" example from here Here's my code:

Struct I'm using:

struct prodcons {
 char** buffer; pthread_mutex_t lock;
 pthread_cond_t notempty; pthread_cond_t notfull;
 int readpos, writepos, finished;
};

My "add to buffer" thread

static void buf_add(struct prodcons *b, char* data) {
 /* Add the data after locking the buffer */
 pthread_mutex_lock(&b-> lock);
 printf("Reader adding %s\n", data);

 int err;
 /*Wait until buffer is not full*/
 while ((b->writepos + 1) % numlines == b->readpos) {
      err = pthread_cond_wait(&b->notfull, &b->lock);
      if (err != 0) { fprintf(stderr, "cond wait");}
 }

 /* Needed to stop writes */
 if (data != NULL) {
      b->buffer[b->writepos] = strdup(data);
 } else {
      //fprintf(stderr, "End of file reached, adding NULL\n");
      b->buffer[b->writepos] = NULL;
 }

 /* Increments the writing position */
 (*b).writepos++;
 if ((*b).writepos >= numlines) {
      printf("Resetting write position\n");
      (*b).writepos = 0;
 }   

 /* Unlock */
 pthread_cond_signal(&b->notempty);
 pthread_mutex_unlock(&b->lock);
 }

Here's what output looks like:

 Reader adding 64.233.173.85

And then it just hangs. It's clear that it's never getting beyond that first while loop. It works with any other size, but not with 1. what would be the best way of implement a fix for this? If this helps, here is my "get from buffer" method.

 static void *read_from_buffer(struct prodcons *b) {
 pthread_mutex_lock(&b -> lock);

 /* We have to wait for the buffer to have something in it */
 while ((*b).writepos == (*b).readpos) {
      pthread_cond_wait(&b->notempty, &b->lock);
 }

 /* Set the thread delay */
 thread_delay.tv_sec = threaddelay / 100000;
 thread_delay.tv_nsec = 1000*threaddelay%100000;

 char *t = NULL;

 /* Read the data and advance the reader pointer */
 if ((*b).buffer[(*b).readpos] != NULL) {
      t = (char*)malloc(strlen ((*b).buffer[(*b).readpos] ) + 1);
      strcpy(t, (*b).buffer[(*b).readpos]);
      printf("Consumer %u reading from buffer: got %s\n", (unsigned int)pthread_self(), t);

      /*At this point, we should probably check is FQDN*/
      if (strcmp(t, "-1") == 0) {
           (*b).finished = 1;
      } else {
           nanosleep(&thread_delay, &thread_delay_rem);
           check_cache(t, &cache);
      }
 }

 /* We have to adjust the reading position */
 (*b).readpos++;
 if ( (*b).readpos >= numlines) {
      (*b).readpos = 0;
 }

 /*Need to signal and unlock */
 pthread_cond_signal (&b->notfull);
 pthread_mutex_unlock(&b->lock);
 return t;
 }

I'm sure there is a fairly simple fix to handle this edge case, but I can't seem to figure it out. Any suggestions would be much appreciated!

EDIT: I also initialize my buffer like so:

 static void init(struct prodcons *temp) {
 (*temp).buffer = (char**)malloc(numlines * sizeof(char*));
2012-04-05 01:35
by thomascirca


1

Not having stepped through your code, but you're writing a terminating NUL '\0' byte, and that would take up an entire 1-byte buffer. The writer waits forever for space in the buffer.

while ((b->writepos + 1) % numlines == b->readpos) { /* always satisfied */
2012-04-05 01:40
by Potatoswatter
Interesting. So in another method right before I call my "buf_add" method I do, "if (t[strlen(t) - 1] == '\n') { t[strlen(t) - 1] = '\0';}". Is it possible the NUL byte there is causing the problem? Otherwise there's no where else that I write it. I see that the writer waits forever, but I'm not sure how to break it - thomascirca 2012-04-05 01:49
Ads