Hi all.
I have a question about the mutexes provided by the sync library. It is correct to say that using the 3 lines of code below, anywhere in Go code, won't have any effect, other than to introduce a slight, non-blocking delay while they execute? In other words, the mutex is guaranteed to be the same before and after, and won’t hang your code, regardless of what else is going on, including in other GoRoutines.
// Note: x is a sync.Mutex.
if x.TryLock() {
// x was unlocked but is now locked by us.
x.Unlock()
}
If so, then would these functions be non-blocking, correct, and safe to use?
// Note: x will always be unlocked upon return.
func TryUnlock(x *sync.Mutex) (success bool) {
if x.TryLock() {
// x was unlocked but is now locked by us.
x.Unlock()
return true
}
// x was unlocked to start with.
return false
}
// Note: x will be the same (locked or unlocked) upon return.
func IsLocked(x *sync.Mutex) (locked bool) {
if x.TryLock() {
// x was unlocked but is now locked by us.
x.Unlock()
return false
}
// x was locked to start with.
return true
}
I do understand that what I'm trying to achieve here is ok to do in only rare cases. Indeed, I've only found a need for them and TryLock() on a few rare occasions.
Thanks,
John
John Souvestre New Orleans LA, USA 504-454-0899
Hi Jason.
I see what you are saying. Ok. One idea: What if everyone else using this mutex used only the TryLock and TryUnlock functions and proceeding only if they returned “success”. Then they would be honoring any pre-existing, or non-existing, lock.
This certainly isn’t what I originally had in mind, but it would be possible to make the changes.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/b0d6a801-7799-4119-b5fa-24e77db4863en%40googlegroups.com.
On Jan 2, 2026, at 11:15 PM, 'John Souvestre' via golang-nuts <golan...@googlegroups.com> wrote:
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/002101dc7c6f%24e7210ff0%24b5632fd0%24%40Souvestre.com.
Hi again. Ignore my 1st reply. I posted that without thinking about it enough.
Let me ask this: What do you say about my first question. Are my assertions about sync’s TryLock() correct? Is there any way that it can fail? This question says that you do not proceed arbitrarily after the TryLock(), but only if it succeeded. In that case, nobody can lock. And nobody else is going to try to Unlock(), since they don’t hold a lock.
It seems to me that this must be true, but if not them the rest of what I did certainly won’t work.
From: John Souvestre <Jo...@Souvestre.com>
Sent: 2026-01-02, Fri 23:15
To: 'Jason E. Aten' <j.e....@gmail.com>; 'golang-nuts' <golan...@googlegroups.com>
Subject: RE: [go-nuts] Re: TryUnlock() and IsLocked() functions
Hi Jason.
I see what you are saying. Ok. One idea: What if everyone else using this mutex used only the TryLock and TryUnlock functions and proceeding only if they returned “success”. Then they would be honoring any pre-existing, or non-existing, lock.
This certainly isn’t what I originally had in mind, but it would be possible to make the changes.
John
From: golan...@googlegroups.com <golan...@googlegroups.com> On Behalf Of Jason E. Aten
Sent: 2026-01-02, Fri 21:01
To: golang-nuts <golan...@googlegroups.com>
Subject: [go-nuts] Re: TryUnlock() and IsLocked() functions
The main difficulty is here is akin to a use-after-check race.
--
I hear you. I have developed work-arounds for the 2 situations I have in a program that I’m currently working on. But TryLock() caught my attention and it seemed that using it as a basis I could build the other two functions I needed, based on it.
What situations? One is trying to use defers when the code is flipping the lock on and off a few times. It gets messy fast. A single TryUnlock() eliminates that complexity.
Another situation is about trying to decide what can data can be displayed and what can’t, with the display routing running in its own GoRoutine, triggered by a timer tick. The data being changed is protected by a mutex which can sometimes be locked while a user edits it. I can’t have the display updates simply stop and wait. The TryLock() lets me know if I can lock, and display, or if I should skip over that data. Other solutions I used before v.18 add TryLock() worked, but added complexity.
I have an understanding of races. But it seems to me that TryLock() solves this for itself. And I’m building on top of it.
On Jan 3, 2026, at 12:12 AM, Jason E. Aten <j.e....@gmail.com> wrote:
Hi John,
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/4863c08c-414c-4519-827b-62bd89f3148bn%40googlegroups.com.
On Jan 3, 2026, at 12:12 AM, Jason E. Aten <j.e....@gmail.com> wrote:
Hi John,
--
Hi Jason.
Ok! Great. I felt that it should, but given the oddities about all the concurrency issues I wanted to make sure that I had it figured right. 😊
I do wonder why only TryLock() was added to the sync library. Seems like adding its compliment, TryUnlock(), would have been a natural thing to do at the same time. As I was describing to Robert a bit, I have a few cases where each would have been handy. My workarounds at the time just added complexity for all the code using mutexes. In contrast, the Try’s are simple to use. You just have to remember to proceed only if they succeed, not fail.
Thanks for your thoughts.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/4863c08c-414c-4519-827b-62bd89f3148bn%40googlegroups.com.
Hi again.
I’m sorry. I didn’t see the item-by-item notes when I first replied. Let me address those now.
> It can be simpler just to manually (Lock and Unlock) in multiple places if one defer doesn't handle it for the whole function.
Certainly – unless you are worried that code might abort for other reasons, before it reaches the Unlock. Having the deferred Unlock, which can run safely in any case, is nice.
> [2] is false because another goroutine could now hold the lock by the time ...
Granted. I should have asserted that a return would guarantee an Unlock, which anyone else could then act on.
> [4] is false because the TryLock failed, so the x was Locked when [4] was reached.
Ouch! That was a typo. I pasted the previous comment and didn't change enough of it. :)
> IsLocked() is problematic name... WasLocked() would seem more accurate.
Good point. Actually, I only use this to display the "current lock status" (part of some diagnostic info). Perhaps name it: WasLockedaFewNanoSecondsAgo? :)
> Are you seeing some kind of problem in your actual use that is prompting your queries?
No. Actually, I'm using versions of each in my code right now. The race detector hasn't complained and they seem to work. But I didn't set up a test case to try them at high rates.
From: golan...@googlegroups.com <golan...@googlegroups.com> On Behalf Of Jason E. Aten
Sent: 2026-01-03, Sat 00:11
To: golang-nuts <golan...@googlegroups.com>
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/4863c08c-414c-4519-827b-62bd89f3148bn%40googlegroups.com.
I do wonder why only TryLock() was added to the sync library. Seems like adding its compliment, TryUnlock(), would have been a natural thing to do at the same time.
Your TryUnlock is incorrect. A separate goroutine could come in and lock the mutex after your call to TryLock but before you return. All the function guarantees is that at some point while it was executing the mutex was unlocked. The return value does not tell you anything useful about who unlocked it either, because it could have been locked and unlocked by someone else in between your call to Unlock and the return.
Your IsLocked is similarly incorrect. The invariant promised by the comment is certainly not true: a separate routine could change the state.
You are correct that your TryLock/Unlock sequence executes quickly without blocking, but it does not tell you anything interesting.
From: 'John Souvestre' via golang-nuts <golan...@googlegroups.com>
Sent: Friday, January 2, 2026 9:42 PM
To: golan...@googlegroups.com
Subject: [go-nuts] TryUnlock() and IsLocked() functions
This message was sent by an external party.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/001a01dc7c5a%247c025220%247406f660%24%40Souvestre.com.
So you are saying that TryLock()’s return (succeeded or failed) might not be correct?
fmt.Println("No contention")
} else {
fmt.Println("Contention!")
x.Lock() // this blocks until the lock is obtained (of course it might already be free before this call)
} // either way, we now have the mutex locked
defer x.Unlock()
do_something()
return
More sensibly, you might have two sync.atomic counters, and increment one or the other depending on which branch is taken.
Hi Brian.
Not “some instant in the past” but the SAME instant that it is also locked. TryLock() is doing a “test-and-set” atomic operation. Else it would be useless.
From: 'Brian Candler' via golang-nuts <golan...@googlegroups.com>
Sent: 2026-01-05, Mon 14:50
To: golang-nuts <golan...@googlegroups.com>
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/d59acd47-8ffe-4f4a-b11d-b895a6da92bfn%40googlegroups.com.
P.S.
I would expect that a regular mutex Lock() does the same thing, except that when evaluating the test result it panics instead of returning “false”.
From: John Souvestre <Jo...@Souvestre.com>
Sent: 2026-01-05, Mon 18:45
To: 'Brian Candler' <bcand...@googlemail.com>; 'golang-nuts' <golan...@googlegroups.com>
Subject: RE: [go-nuts] TryUnlock() and IsLocked() functions
Hi Brian.
Not “some instant in the past” but the SAME instant that it is also locked. TryLock() is doing a “test-and-set” atomic operation. Else it would be useless.
John
From: 'Brian Candler' via golang-nuts <golan...@googlegroups.com>
Sent: 2026-01-05, Mon 14:50
To: golang-nuts <golan...@googlegroups.com>
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/d59acd47-8ffe-4f4a-b11d-b895a6da92bfn%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/000e01dc7ea5%2436c8dfb0%24a45a9f10%24%40Souvestre.com.
Hi Def.
I agree. TryLock() does this, I assume. An TryUnlock() does this too – because the only action it performs is after TryLock() succeeds, hence locking the Mutex.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/CADgY6e840WSn7ajEa%2B9bVCtFOOSwa1OCQSn8eYLHR_ZU10ukSw%40mail.gmail.com.
Yes, that's the underlying problem and that's why I used TryLock() to start with. It resolves the problem atomically. I would imagine that it determines if the mutex is locked, then locks it regardless, by doing something like a "swap".
"TryLock tries to lock m and reports whether it succeeded."
I take "succeed" to mean that TryLock() found the lock unset, and set it. The opposite, to "fail" means that TryLock() found the lock set, so didn't have to set it.
P.S.
- TryLock() is doing a “test-and-set” atomic operation.
I would expect that a regular mutex Lock() does the same thing, except that when evaluating the test result it panics instead of returning “false”.
What happens in this case is that Lock() will wait - indefinitely if necessary - until it can obtain the lock. Hence it always succeeds, eventually.
// Note: x will always be unlocked upon return.
func TryUnlock(x *sync.Mutex) (success bool) {
if x.TryLock() {
// x was unlocked but is now locked by us.
x.Unlock()
return true
}
// x was unlocked to start with.
return false
}
1. if the mutex is locked, TryLock() will fail. The function will return false, and the mutex could well still be locked (although it's also possible that some other goroutine unlocks it in the meantime)
2. if the mutex is not locked, TryLock() will succeed. The function will then unlock it, and the mutex could well be unlocked at function return (although it's also possible that some other goroutine locks it in the mean time).
Hence "x will always be unlocked upon return" is incorrect, as it's not changed by this function. And the true/false value tells you what the value was in the middle of execution of the function, but it could have changed by the time the function returns.