Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a mutex lock needed around a pointer variable?

Is a mutex lock needed around a section of code that involves pointer indirection (where the pointer points to data that is part of a critical section)? An example code:

struct list {
    int i;
    struct list *next;
};

int modify_second_elem(struct list *head, int val);
void * func1(void *ptr);
void * func2(void *ptr);

int modify_second_elem(struct list *head, int val) {

    if(head == NULL)
        return 1;

    /* Check to see if second element exists.
       Here, I am using indirection to get the next pointer.
       Would I need synchronization here? */
    if(head->next == NULL)
        return -1;

    pthread_mutex_lock(&list_lock);
    (head->next)->i = val;
    pthread_mutex_unlock(&list_lock);

    return 0;

}

void * func1(void *ptr) {
    struct list *head;
    head = (struct list *) ptr;
    modify_second_elem(head, 4);
}

void * func2(void *ptr) {
    struct list *head;
    head = (struct list *) ptr;
    modify_second_elem(head, 6);
}

void main() {
    struct list *el1, *el2, *el3;
    pthread_t th1, th2;

    el1 = (struct list *) malloc(sizeof(list));
    el2 = (struct list *) malloc(sizeof(list));
    el3 = (struct list *) malloc(sizeof(list));

    el1->i = 1;
    el1->next = el2;
    el2->i = 2;
    el2->next = el3;
    el3->i = 3;
    el3->next = NULL;

    pthread_create(&th1, NULL, &func1, (void *) el1);
    pthread_create(&th2, NULL, &func2, (void *) el1);

    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    exit(EXIT_SUCCESS);
}
like image 390
torrential coding Avatar asked Dec 17 '22 15:12

torrential coding


1 Answers

There's not enough information given to give a really good answer. How is s "published" to other threads? How do other threads "subscribe" to s? In what data structure are struct s objects stored?

So, I'll give a generic answer:

Every kind of data that is shared between threads needs synchronization. This includes shared pointers.

About pointers:

You may have heard that on some commonplace CPUs loads/stores of correctly aligned pointers are atomic (this is not the case for all CPUs or for all kinds of pointers, e.g: far pointers on x86 are non-atomic). Stay away from using this if you don't have a thorough understanding of your CPU/VM memory model, there are many subtle things that can go wrong if you don't take locks (locks provide pretty strong guarantees).

Edit:

In your example, neither th1 nor th2 modifies the list, they only modify elements of the list. So, in this specific case, you don't need to lock the list, you just need to lock elements of the list (the pointer conceptually belongs to the linked list implementation).

In more typical cases, some threads would be traversing the list, while others would be modifying the list (adding and removing elements). This requires locking the list, or using some kind of lock-free algorithm.

There are several ways of doing this locking.

  • Have a global lock on the list, and use it also for elements of the list.
  • Use hierarchical locking, have a lock on the list, and a lock on every element. To read/modify an element, you would first take a lock on the list, find the element, take the element's lock, release the list's lock, process the element, and finally release the element's lock. This is useful if you need to do some complex processing on the element and don't want to prevent other threads from accessing the list. You have to take care to always take the locks in the same order, to avoid deadlocks.
like image 157
ninjalj Avatar answered Dec 24 '22 09:12

ninjalj