Hacker News new | past | comments | ask | show | jobs | submit login
Go: Story of TryLock Function (medium.com/a-journey-with-go)
16 points by ngaut on May 7, 2022 | hide | past | favorite | 3 comments



A try-lock operation can be useful when you need to hold multiple mutexes at once.

In general, if two pieces of code lock the same two mutexes, they have to do so in the same order. If thread 1 locks mutex A and then mutex B, while thread 2 locks mutex B and then mutex A, you can end up in a situation where thread 1 has A and is waiting to acquire B, while thread 2 has B and is waiting to acquire A; they’re both stuck waiting for each other, creating a deadlock. To solve this, you need to define a single consistent ordering, even if it’s arbitrary: all code is required to lock A first, then B.

But then what do you do if you have B already locked for some other reason, and now you need A?

In the general case: unlock B, lock A, then lock B again. Make sure to properly handle the case that someone took B while you left it unlocked, and modified whatever state it protects.

But with a try-lock operation, while holding B, you can first try-lock A. If that succeeds, you’re done: you’ve got both A and B safely locked despite locking them in the ‘wrong’ order. If the try-lock fails, you do have to fall back to unlocking and re-locking, so this isn’t some way to magically avoid the complexity of nested mutexes. Still, if the mutexes aren’t too heavily contended, the try-lock might well succeed the vast majority of the time, so it’s useful as an optimization.


That’s pretty cool. Thanks for the info!

TryLock seems like it would be good fit for multithreaded datastructures


In the go source there is the use of tryAcquireSema which is similar to TryLock in src/net/dnsclient.go:

    // tryUpdate tries to update conf with the named resolv.conf file.
    // The name variable only exists for testing. It is otherwise always
    // "/etc/resolv.conf".
    func (conf *resolverConfig) tryUpdate(name string) {
        conf.initOnce.Do(conf.init)

        // Ensure only one update at a time checks resolv.conf.
        if !conf.tryAcquireSema() {
                return
        }
        defer conf.releaseSema()
I think this has the same behaviour except it is implemented using channels. It is used because go wants to make sure changes to /etc/resolv.conf are eventually taken into account when making DNS lookups but they don't want to check the file on every DNS lookup.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: