0

I am currently working on a queue structure which is tread-safe. I lock before editing queue in push and pop. Unlock it when I finish doing that. Now I want to let thread wait when pop-empty-queue or push-full-queue(this queue is sized). So I check if queue is full at the beginning of push, and use same way to check if queue is empty the beginning of pop. If it is then I let it wait by pthread_cond_wait. To let it stop waiting, I check if queue is still empty at the end of push, and check if queue is still full at the end of pop. Is it's not empty or full, then I use pthread_cond_signal to wake up the thread. The struct that I defined is like

typedef struct node_t {
    void* content;
    struct node_t *next;
    struct node_t *prev;
} node_t;

typedef struct queue_t {
    node_t* head;
    node_t* tail;
    int size;
    int count;
    pthread_mutex_t mutex;
    pthread_cond_t full;    // To deal with the condition that the queue is full
    pthread_cond_t empty;   // To deal with the condition that the queue is empty
} queue_t;

And the way I use lock and wait & send code is like:

bool queue_push(queue_t *q, void *elem) {
    if (q == NULL) {
        return false;
    }

    while (q->count == q->size) {
        pthread_cond_wait(&q->full, &q->mutex);
    }

    if (q->head == NULL) {
        pthread_mutex_lock(&q->mutex);
        // I skip the steps for adding a node to queue
        if (q->count != 0) {
            pthread_cond_signal(&q->empty);
        }
        pthread_mutex_unlock(&q->mutex);
        return true;
    } else {
        pthread_mutex_lock(&q->mutex);
        // I skip the steps for adding a node to queue
        if (q->count != 0) {
            pthread_cond_signal(&q->empty);
        }
        pthread_mutex_unlock(&q->mutex);
        return true;
    }
}

And the code for pop is kind of similar.

bool queue_pop(queue_t *q, void **elem) {
    if (q->count == 0) {
        pthread_cond_wait(&q->empty, &q->mutex);
    }

    if (q->head == q->tail) {
        pthread_mutex_lock(&q->mutex);
        // I skip the steps for deleting a node to queue
        if (q->count != q->size) {
            pthread_cond_signal(&q->full);
        }
        pthread_mutex_unlock(&q->mutex);
        return true;
    } else {
        pthread_mutex_lock(&q->mutex);
        // I skip the steps for deleting a node to queue
        if (q->count != q->size) {
            pthread_cond_signal(&q->full);
        }
        pthread_mutex_unlock(&q->mutex);
        return true;
    }
}

Now when I use my test cases to test it, it never end. But I did unlock and send signal to let thread stop waiting. Can someone help me about this? Thanks!

MonstBlu
  • 81
  • 6
  • You need to lock the relevant mutex before calling `pthread_cond_wait()`. You said you're doing that, but your posted code isn't... It's locking the mutex *after* `pthread_cond_wait()` returns, which is wrong because pcw reacquires the mutex before it returns. – Shawn Nov 03 '22 at 07:11
  • Sorry, I haven’t put pthread_cond_wait() on the code above. So I guess I should use pthread_mutex_lock() instead of pthread_cond_wait(), right? since pthread_cond_wait() will cause dead lock. – MonstBlu Nov 03 '22 at 08:53
  • 1
    You need both mutexes and condition variables. – Shawn Nov 03 '22 at 18:44

1 Answers1

0

If you are using c++ just use semaphore or if you are using c you can use POSIX semaphore, if you want to implement it yourself check spinlock. In any case you can implement blocker and releaser with spinlock,you can create a middleware(chain responsibility pattern) and use it on all calls. Something like this.

void middleware (int (*func)(int, int)) {
    block();
    then();
    release();
} 

In case of c++ you can use templates to achieve one middleware for all methods.

magnagag
  • 94
  • 8
  • I think `semaphore` is kind of similar with `mutex`. Now that I've used `mutex`, I believe I don't need to re-use `semaphore`. My main question is how to keep the thread waiting while pop-empty-queue or push-full-queue. Do you have any good solution to this problem please? Should I add a thread lock to target this situation? (when pop-empty-queue or push-full-queue), and unlock when this situation is no longer true? – MonstBlu Nov 03 '22 at 06:51
  • 1
    A Mutex is different than a semaphore as it is a locking mechanism while a semaphore is a signalling mechanism. A binary semaphore can be used as a Mutex but a Mutex can never be used as a semaphore. [Check this](http://www.cs.kent.edu/~ruttan/sysprog/lectures/multi-thread/pthread_cond_init.html) ASYNC-SIGNAL SAFETY The condition functions are not async-signal safe, and should not be called from a signal handler. In particular, calling pthread_cond_signal or pthread_cond_broadcast from a signal handler may deadlock the calling thread. – magnagag Nov 03 '22 at 08:33