Hello, I’m fried fish.

The lock in the program is one of the most powerful tools used by many friends when writing distributed applications.

For those of you who use Go, most of you have experience in other languages, one of the things you might wonder about is that the lock in Go does not support reentrant.

So today, Fish Fry is taking you through the design considerations and why.

Reentrant lock

If you “lock” a normal mutex that is already locked, it will either fail or block until it is unlocked.

The lock scenario is as follows:

  • On the lock: If it is a reentrant mutex, the lock will succeed if the thread currently trying to lock is the one holding the lock.
  • On unlocking: A reentrant mutex generally records the number of times it is locked, and only the same number of unlock operations are performed to unlock it.

Simply put, a reentrant mutex is a type of mutex that can be locked multiple times by the same thread without causing a deadlock or blocking.

There may be some differences in implementation between languages, but the general idea is the same.

Please think about it, how is Go?

Go Support

We see the following example of the Go mutex:

var mu sync.Mutex

func main(a) {
	mu.Lock()
	mu.Lock()
}
Copy the code

Does this Go program block? No, the following error will be reported:

fatal error: all goroutines are asleep - deadlock!
Copy the code

Go clearly does not support reentrant mutex.

The official reply

Go design principles

The fundamental reason for using mutex in engineering is that to protect invariants, it can also be used to protect internal and external invariants.

For this reason, Go follows these principles in its mutex design. As follows:

  • In the callmutex.LockMethod to ensure that the invariance of these variables will not be destroyed in the subsequent process.
  • In the callmu.UnlockEnsure that:
    • Programs no longer need to rely on those invariants.
    • If your program breaks mutex locks while they are locked, you need to ensure that they have been recovered.

The reason for not supporting

Given Go’s own design principles, why not support reentrant?

In fact, Russ Cox replied in “Proof of Concept” in 2010 that recursive (or reentrance) mutual exclusion was a bad idea, and that the design was not a good one.

We can understand this in conjunction with official examples.

As follows:

func F(a) {
        mu.Lock()
        ... do some stuff ...
        G()
        ... do some more stuff ...
        mu.Unlock()
}

func G(a) {
        mu.Lock()
        ... do some stuff ...
        mu.Unlock()
}
Copy the code

In the above code, we add the Lock by calling the mu.Lock method in the F method. If reentrant locks are supported, then the G method is entered.

The fatal problem is that you don’t know if the F and G methods are doing anything to break the invariants when they are locked. After all, it’s perfectly possible to pick up a few coroutines and do something bad.

This is unacceptable for Go, and a reentrant design violates the previous design philosophy of “ensuring that the invariance of these variables is maintained and not destroyed in subsequent processes”.

For these reasons, the Go team chose not to support this feature.

conclusion

Go mutex does not support reentrant lock design, but also like the avenue to simple idea, may be more interference, as a direct simple to.

Do you have similar doubts in the process of work, welcome to leave a comment and exchange 🙂

If you have any questions, welcome feedback and communication in the comments section. The best relationship is mutual achievement. Your praise is the biggest motivation for the creation of fried fish, thank you for your support.

This article is constantly updated. You can read it on wechat by searching “Brain into fried fish”. GitHub github.com/eddycjy/blo… Already included, learning Go language can see Go learning map and route, welcome Star urged more.