Several sources, including this issue comment:
state that a buffered channel of size 1 can be used as a trylock by select'ing on it with a default case.
I was thinking about it and it seemed to me that with such an implementation, it is unclear whether a failed acquisition of the trylock guarantees that the lock is held by someone else. More precisely, I can't tell if the following guarantee holds: a failed acquisition of the trylock cannot happen-before a successful acquisition, unless a release happens-between them. (To put it another way: can there be "spurious" failures of trylock acquisition?)
In particular:
1. The memory model documentation states ordering guarantees for *successful* channel receives and sends, but doesn't say anything about failed nonblocking receives and sends, so it seems to me that in principle it can't be promising anything about this behavior.
2. I can't tell whether the fast-path checks in the current implementation allow for this:
In particular, I don't understand why `chanrecv` does an atomic read of `qcount`, but `chansend` just does a normal read.
One case where this is relevant is implementing something like a write-back cache with at most one goroutine performing the writeback:
func update() {
mark_state_dirty()
if trylock.TryAcquire() {
go writeback_worker()
} // else: some writeback worker is guaranteed to be active
}
func writeback_worker() {
for {
perform_writeback()
trylock.Release()
if !state_is_dirty() {
return // nothing more to do
}
if !trylock.TryAcquire() {
return // a new writeback worker is guaranteed to be active
} // else: loop back around and do the writeback again
}
}
where, in the absence of this guarantee, an update can be "missed" and writeback can be delayed arbitrarily.
Thanks very much for your time.