Thanks to Robert Clapis via Twitter <https://twitter.com/rogpeppe/status/1202266693541748736> for this suggestion, using channels. As he said, it doesn't quite get there, because a reader can still acquire the lock even when a writer has requested it, but it's still potentially useful and reasonably simple:
https://play.golang.org/p/N33t7uyxeiM On Wed, 4 Dec 2019 at 18:34, Liam <networkimp...@gmail.com> wrote: > Thank you for the refs and pseudocode! > > Somehow I imagined this would be simpler... A proposal for Mutex.TryLock() > was turned aside years ago because it's trivial to implement with CAS. But > they didn't consider RWMutex.TryRLock(). > > https://github.com/golang/go/issues/6123 > > @ianlancetaylor, is this worth a new proposal? > > > On Tuesday, December 3, 2019 at 7:52:54 PM UTC-8, robert engels wrote: >> >> You actually only need a cas and a condition variable (technically the >> condition in Go requires a backing lock, but if you have wait/notify it >> isn’t needed). >> >> You can read this >> https://code.woboq.org/userspace/glibc/nptl/pthread_rwlock_common.c.html >> and/or https://6826.csail.mit.edu/2019/papers/HO17.pdf for the general >> idea. >> >> Essentially, you use some bits of the ‘atomic value’ to encode phases >> (waiting for write lock, holding write lock, reader bits) - and use the >> cond variable to block the writers (or readers for that matter) and loop >> and retry the cas. >> >> The following is simplified since it isn’t “fair”, but you can add >> another bit for “writer waiting” to accomplish this. Also, the ‘condition’ >> must be a ‘flag’ so that a signal() with no waiters is satisfied by the >> next wait() >> >> bits 0 - 30 number of readers >> bits 31 writer has lock >> >> WRITER = 1<<31 >> read() is the atomic read of v >> >> pseudo code >> >> try_lock() { >> for { >> v = read() >> if v has writer { >> return WOULD_BLOCK >> } >> if(cas(v,v + 1 reader)) { >> return OK >> } else { >> // loop and try again >> } >> } >> } >> >> runlock() { >> for{ >> v = read() >> if(cas(v,v -1 reader)) { >> cond.signal() // can be optimized to not always do this, i.e. >> only when readers is 0 >> return >> } else { >> // loop and try again >> } >> } >> } >> >> wlock() { >> for { >> v = read() >> if v !=0 { >> cond.wait() >> } >> else { >> if(cas(v,WRITER)) { // we are the writer >> return >> } else { >> // loop and try again >> } >> } >> } >> >> wunlock() { >> set(v,0); >> cond.signal() // for other waiting writers >> } >> >> Obviously if you need readers to block on writers (i.e. rlock() ) it is >> slightly more work, but you get the idea. >> >> >> On Dec 3, 2019, at 7:54 PM, Liam <networ...@gmail.com> wrote: >> >> Busy means would-block, yes. >> >> Burak thanks, but that doesn't work for read-lock. >> >> On Tuesday, December 3, 2019 at 5:39:48 PM UTC-8, Robert Engels wrote: >>> >>> It depends then, because technically the Go RW lock queues readers >>> behind a waiting writer so “busy” is somewhat undefined. If “busy” means >>> “would block” you can still do it - I’ll post the code tonight. >>> >>> > On Dec 3, 2019, at 6:49 PM, Robert Engels <ren...@ix.netcom.com> >>> wrote: >>> > >>> > I would use an atomic and a lock instead of two locks. >>> > >>> >>> On Dec 3, 2019, at 6:35 PM, burak serdar <bse...@computer.org> >>> wrote: >>> >>> >>> >>> On Tue, Dec 3, 2019 at 5:21 PM Liam Breck <networ...@gmail.com> >>> wrote: >>> >>> >>> >>> I have a problem that is trivially solved via >>> >>> >>> >>> door sync.RWMutex >>> >>> >>> >>> func Reader() T { >>> >>> if !door.TryRLock() { // missing in stdlib :-( >>> >>> return busy >>> >>> } >>> >>> defer door.RUnlock() >>> >>> ... >>> >>> } >>> >>> >>> >>> func Writer() { >>> >>> door.Lock() >>> >>> defer door.Unlock() >>> >>> ... >>> >>> } >>> >>> >>> >>> How does one achieve this in Go? >>> >> >>> >> Two locks and a bool? >>> >> >>> >> var door=sync.Mutex{} >>> >> var x=sync.Mutex{} >>> >> var b bool >>> >> >>> >> func trylock() bool { >>> >> x.Lock() >>> >> if b { >>> >> x.Unlock() >>> >> return false >>> >> } >>> >> b=true >>> >> door.Lock() >>> >> x.Unlock() >>> >> return true >>> >> } >>> >> >>> >> unlock: >>> >> >>> >> x.Lock() >>> >> b=false >>> >> door.Unlock() >>> >> x.Unlock() >>> >> >>> >> >>> >> >>> >> >>> >>> >>> >>> -- >>> >>> 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 golan...@googlegroups.com. >>> >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/CAKvHMgTO%3DxfFQ_u7aO9UE-1vHHEKmdhr47sro2mnp6DkEb6mPA%40mail.gmail.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 golan...@googlegroups.com. >>> >> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/CAMV2RqrDeBNhkeswg%2BhdCf1kSzMEJduota%3D6UrNq4z2PQRtzEQ%40mail.gmail.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 golan...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/022a5bb5-be27-4721-afe4-7e08f4531a3d%40googlegroups.com >> <https://groups.google.com/d/msgid/golang-nuts/022a5bb5-be27-4721-afe4-7e08f4531a3d%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> >> >> -- > 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+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/24f60a17-c572-433f-b1ff-10cdad98458f%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/24f60a17-c572-433f-b1ff-10cdad98458f%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- 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+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAJhgacjdqHoyZeXfPFp%3DSET4MgobijntFFm0bg%3Dv3hok6ySzdA%40mail.gmail.com.