Re: Lazy, cached supplier: most performant mutex mechanism?
Ghadi Shayban via Concurrency-interest <[email protected]> Fri, 31 Jul 2020 14:56:10 -0400
| Newsgroups | gmane.comp.java.jsr.166-concurrency |
|---|---|
| Message-ID | <CAO3q7t-8vw=dd+H0kCpJMMJ65z4+cxnqm-+kePV7rJ0raCa-Mw@mail.gmail.com> |
Thanks everyone, I'll check these things out. Seems like the user-space dual of a condy instruction, except run max once. I was hoping to try an approach that wasn't based on synchronized, though synchronized saves you the allocation of a lock. Ron Pressler had another interesting suggestion to try: Read val; if it's null, CAS fn to null. If you win, allocate a lock and do an ordered set (with a VarHandle) to the lock field, lock it, invoke fn, do an ordered write (with a VarHandle) to val, and unlock, then do another ordered write to null out the lock. If you lose the CAS, spin with an ordered read on the lock field and the val. This will be a short spin, because all you're waiting for is the allocation of the lock. If you see a non-null val, you're done. If you see a non-null lock, lock on it, and then a normal read from val should be non-null. This requires a bit of care in terms of memory ordering, and will probably require testing on non-Intel platforms, as those have weaker memory ordering than Intel, and many concurrency bugs don't manifest on x86. But the code will still be small, and it's an important method, so a relatively elaborate mechanism there might be worth it. </suggestion> On Fri, Jul 31, 2020 at 2:33 PM Viktor Klang <[email protected]> wrote: > We spent quite some time on this, you can read all about it here: > https://docs.scala-lang.org/sips/improved-lazy-val-initialization.html > > On Fri, 31 Jul 2020 at 20:02, Benjamin Manes via Concurrency-interest < > [email protected]> wrote: > >> I believe double-checked locking, such as Martin's version in Guava >> Suppliers#memoize >> <https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Suppliers.java#L124-L137>, >> is the best approach on the JVM for instance-level memoization. >> >> On Fri, Jul 31, 2020 at 10:42 AM Ghadi Shayban via Concurrency-interest < >> [email protected]> wrote: >> >>> This seems like it would be a common stdlib ask, but what is the most >>> performant way to protect the code inside a supplier from being >>> concurrently realized more than once? Contention would be rare, and the >>> losing threads need to wait on the value being computed by the winning >>> thread. >>> >>> The most straightforward thing to do is a synchronized block, but this >>> currently pins a carrier thread in Project Loom. >>> >>> The Supplier needs to keep track of: >>> 1) the thunk, if unrealized >>> 2) a value, if realized >>> >>> Golang's sync.Once does this [1] (CAS, fallback to mutex. Doesn't >>> remember value) >>> Clojure's lazy sequences use synchronized [2] >>> >>> Is there a better way to approach this on the JVM? >>> >>> Thanks! >>> >>> [1] https://golang.org/src/sync/once.go >>> [2] >>> https://github.com/clojure/clojure/blob/30a36cbe0ef936e57ddba238b7fa6d58ee1cbdce/src/jvm/clojure/lang/LazySeq.java#L39-L46 >>> >>> >>> _______________________________________________ >>> Concurrency-interest mailing list >>> [email protected] >>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest >>> >> _______________________________________________ >> Concurrency-interest mailing list >> [email protected] >> http://cs.oswego.edu/mailman/listinfo/concurrency-interest >> > -- > Cheers, > √ > _______________________________________________ Concurrency-interest mailing list [email protected] http://cs.oswego.edu/mailman/listinfo/concurrency-interest