Proposing new features definitely involves user scenarios. When the time comes to 2018, @deanveloper mentions a classic loading scenario: several very large files need to be loaded and I want a progress bar to show how close I am to completion.

He considered that this progress bar could be well implemented using TryLock, and the following is his sample code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func (b *ProgressBar) Add(n int) {
    atomic.AddInt64(&b.Progress, int64(n))

    if b.Progress >= b.Max {
        b.once.Do(b.updateClientsDone)
        return
    }

    if b.pctMx.TryLock() {
        defer b.pctMx.Unlock()
        b.updateClients()
    }
}

The basic logic of the above code is to keep updating the counter, followed by his scrolling loading progress bar by trying to get a lock.

Since the official thought that he would do better with channel+select-default, this user case is not enough to support the increase of TryLock’s functionality and is rejected again.

Bring it up again

After several extensive discussions in 2013 and 2018, the time has come again to 2021, and @TyeMcQueen cites some examples from a large number of h2 libraries, expressing some expectation that it would be better to have TryLock methods.

But it was also rejected, and Russ Cox objected for the following reasons.

golang try lock

Turnaround

In the first few failures, Russ Cox decided that the cases given were not convincing enough as a reason to add the TryLock family of methods.

The belief that more and more people needed to be added, coupled with the fact that Google’s bigwig Dmitry Vyukov later gave the following case.

golang

This means that libraries like gvisor, v.io/x/lib/nsync, trivago/tgo, etc. all use TryLock’s methods, and the implementation is basically the same as the simulation code.

golang

Eventually Russ Cox relented, saying, “Everyone agrees it’s unfortunate, but sometimes it’s necessary,” and felt reluctant to agree.

Russ Cox

The overall timeline history history is as follows.

  • 2013 @lukescott proposed “sync: mutex.TryLock”, which was rejected.
  • 2018 @deanveloper proposed “proposal: add sync.Mutex.TryLock”, which was rejected.
  • 2021 @TyeMcQueen proposes “sync: add Mutex.TryLock”, first rejected, then accepted.
  • 2022, due to previous Go1.17 feature freeze, scheduled for Go1.18 release (March).

New method sync.TryLock

In the upcoming Go1.18 release, the TryLock family of methods has been added to the sync standard library.

As follows.

Go1.18 release

  • Mutex.TryLock: Try to lock the mutual exclusion lock, return whether it is successful or not.
  • RWMutex.TryLock: Attempt to lock the read/write lock, and return if it succeeds.
  • RWMutex.TryRLock: Try to lock the read lock and return if it is successful.

Official special reminder: Although scenarios using TryLock do exist. However, they should be rare, and the use of TryLock can often be a sign of a deeper problem.

Summary

In Go1.18, the TryLock method that tries to get a lock has finally landed, and there are pros and cons to its existence. For example, it may become a common if-else judgment in the future, and it can avoid a lot of long holds caused by lock blocking.

But from the application design point of view, the use of the method, some of which is problematic, requires special attention and thought.

Not easy, after 9 years.