jeudi 20 juillet 2023

design pattern to avoid deadlock with mutex in golang

What would the appropriate design pattern to avoid deadlock when several functions use the same mutex ?

It is quite easy to forget what method uses the lock and so it happens that you call a function that do a mutex.Lock() inside a function that already locked the mutex --> deadlock.

For example in the following code, it is not explicit right away that the setToNextEvenNb() is a deadlock. You have to look in nested functions. And it would be even worse if the Lock was in function called 2 or 3 level behind the setToNextEvenNb().

package main

import (
    "fmt"
    "sync"
)

type data struct {
    value int
    mutex sync.RWMutex
}

func (d *data) isEven() bool {
    d.mutex.RLock()
    defer d.mutex.RUnlock()
    return d.value%2 == 0
}

func (d *data) setToNextEvenNb() {
    d.mutex.Lock()
    defer d.mutex.Unlock()
    if d.isEven() {
        d.value += 2
    }
    d.value += 1
}

func (d *data) set(value int) {
    d.mutex.Lock()
    defer d.mutex.Unlock()
    d.value = value
}

func main() {
    var d data
    d.set(10)
    fmt.Println("data", d.value)
    fmt.Println("is even ?", d.isEven())
    d.setToNextEvenNb()
    fmt.Println("data", d.value)
}

When you have a large code base, this can happen quite easily. I feel that there should be a design pattern to avoid this sort of scenario and I was looking for any advice on this ?

Aucun commentaire:

Enregistrer un commentaire