0

I'm trying to implement a concurrent queue in C. When running it, the line pthread_create(&p2, NULL, (void*)queue_enqueue, p2_args); gives this error: ../nptl/pthread_mutex_lock.c:81: __pthread_mutex_lock: Assertion mutex->__data.__owner == 0' failed. As far as I can tell I'm doing everything right, so what's causing this? And yes, I've read the manual. That didn't help at all, which is why I'm here.

This is my code:

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

#define NUM_THREADS 5

// QUEUE DEFINITIONS //
typedef struct __node_t {
  int             value; // value of node
  struct __node_t *next; // pointer to next node
} node_t;

typedef struct __queue_t {
  node_t          *head; // head of queue
  node_t          *tail; // tail of queue
  // locks for head and tail operations
  pthread_mutex_t head_lock, tail_lock; 
} queue_t;

void queue_init(queue_t *q) {
  node_t *tmp = malloc(sizeof(node_t)); // create node
  tmp -> next = NULL; // only node in array, doesn't point to anything
  q -> head = q -> tail = tmp; // tail and head are same because only 1 node
  pthread_mutex_init(&q -> head_lock, NULL); //  init head lock
  pthread_mutex_init(&q -> tail_lock, NULL); // init tail lock
  printf("init q\n");
}

void queue_enqueue(queue_t *q, int value) {
  node_t *tmp = malloc(sizeof(node_t));
  assert(tmp != NULL);
  tmp -> value = value;
  tmp -> next  = NULL; 

  pthread_mutex_lock(&q -> tail_lock);
  q -> tail -> next = tmp;
  q -> tail = tmp;
  pthread_mutex_unlock(&q -> tail_lock);
  printf("enq %d\n", value);
}

int queue_dequeue(queue_t *q, int *value) {
  pthread_mutex_lock(&q -> head_lock);
  node_t *tmp = q -> head;
  node_t *new_head = tmp -> next;

  if (!new_head) {
    pthread_mutex_unlock(&q -> head_lock);
    return -1; // queue was empty
  }
  *value = new_head -> value;
  q -> head = new_head;
  pthread_mutex_unlock(&q -> head_lock);
  free(tmp);
  return 0;
}


int template(int argc, char *argv[]) {
  queue_t queue;
  queue_init(&queue); 
  queue_enqueue(&queue, 3);
  queue_enqueue(&queue, 1);
  queue_enqueue(&queue, 2);
  int val;
  assert(queue_dequeue(&queue, &val) == 0);
  printf("val: %d\n", val);
  
  return 0;
}


typedef struct __enq_args {
  queue_t *q;
  int value;
} *enq_args;

typedef struct __deq_args {
  queue_t *q;
  int *value;
} deq_args;


int main(int argc, char *argv[]) {
  pthread_t p1, p2, p3, p4, p5;
  queue_t queue;

  enq_args p2_args = malloc(sizeof(enq_args));
  p2_args -> q = &queue;
  p2_args -> value = 1;


  pthread_create(&p1, NULL, (void*)queue_init, &queue);
  pthread_create(&p2, NULL, (void*)queue_enqueue, p2_args);
  
  pthread_join(p1, NULL);
  //pthread_join(p2, NULL);  
  return 0;
}```
Fathom820
  • 1
  • 3
  • `enq_args p2_args = malloc(sizeof(enq_args));` is wrong, you need to allocate space for the thing being pointed to. Change to `enq_args p2_args = malloc(sizeof(*p2_args));` – yano Apr 18 '22 at 00:49
  • That doesn't work. Same error. – Fathom820 Apr 18 '22 at 00:57
  • 1
    you also have a race condition on `q->tail_lock` at least, you can't be sure it will be initialized in `queue_init` before you try to lock it in `queue_enqueue` – yano Apr 18 '22 at 01:11
  • 1
    the signatures for your thread functions are wrong, they should be `void* thread_function(void* arg);` – yano Apr 18 '22 at 01:13
  • 1
    How do you know you're getting an error from [`pthread_create()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html)? It returns the error number, if any, and you're completely ignoring the return value from `pthread_create()`, so you have no idea whether anything is going wrong or not — or, at least, you have not demonstrated how you know things are going wrong. The thread functions do not set `errno`. – Jonathan Leffler Apr 18 '22 at 01:29
  • 1
    The `(void *)` casts in the calls to `pthread_create()` are bogus too. The thread functions will be called as if they are of the type `void *(*)(void *)`. Your thread functions do not have that signature, so you invoke undefined behaviour. Your `queue_enqueue()` function is particularly egregious — it takes two arguments but will be invoked with just one, leaving all bets off. The `(void *)` cast also converts function pointers to data pointers which is not allowed in strict standard C. Your thread functions should return `NULL` since they do not call `pthread_exit()`. – Jonathan Leffler Apr 18 '22 at 01:36
  • Note that you should not, in general, create function, variable, tag or macro names that start with an underscore. Part of [C11 §7.1.3 Reserved identifiers](https://port70.net/~nsz/c/c11/n1570.html#7.1.3) says: — _All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use._ — _All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces._ See also [What does double underscore (`__const`) mean in C?](https://stackoverflow.com/q/1449181) – Jonathan Leffler Apr 18 '22 at 02:18

0 Answers0