> From: "Chen Liang" <[email protected]> > To: "Da Vinci Machine Project" <[email protected]>, "Charles Oliver Nutter" > <[email protected]> > Sent: Wednesday, May 21, 2025 8:47:24 PM > Subject: Re: Years later, finally integrating ClassValue... but...
> Hi Charles, > Indeed, the docs for "computeValue" was wrong. The doc for "get" was right. > The > upcoming JDK 25 will provide a spec update that simplifies the mental model of > a ClassValue and correct these mistakes in the docs. > ClassValue always have been utilizing racy computation, that multiple threads > can compute a value, so there is minimal locking - the only locking would be > at > the time when a thread tries to associate a value. This association is > essentially a CAS, where only one thread's computeValue result is ever > installed. There is no synchronization lock on computeValue, and there is no > way for a synchronized computeValue to check whether a value is already > associated. The only threading guarantee provided by ClassValue is that the > associated value's identity/value is unique and its computation result is > published to all threads that saw the value via a get. > If you wish for synchronous initialization, I think you can store a container, > such as a memoized function (such as StableValue::supplier), in a ClassValue, > and then call the function (or a synchronized initializer) on all accessing > threads - this ensures the is exactly one memoized function object, and that > object is initialized exactly once. yes, a double check locking (or a stable value but it's in preview in 25) solve the issue. > Regards, > Chen Liang Rémi > From: mlvm-dev <[email protected]> on behalf of Charles Oliver Nutter > <[email protected]> > Sent: Wednesday, May 21, 2025 12:42 PM > To: Da Vinci Machine Project <[email protected]> > Subject: Years later, finally integrating ClassValue... but... > Due to issues for years trying to understand the lifecycle of class/value > pairs > stored in ClassValue, I am only now integrating the JDK-provided version of > ClassValue into JRuby's logic to store method tables for Java classes. And I > have run into a new inconsistency I want to clarify. > The docs for ClassValue state this for the "computeValue" method: > "This method will be invoked within the first thread that accesses the value > with the get method. > Normally, this method is invoked at most once per class, but it may be invoked > again if there has been a call to remove." > To me, that means computeValue will be invoked *exactly once* per class > (ignoring removals), regardless of how many threads attempt to compute it at > the same time. Otherwise, what's the point of saying it will be invoked by the > "first thread"? > But then the docs for "get" say something different: > "Returns the value for the given class. If no value has yet been computed, it > is > obtained by an invocation of the computeValue method. > The actual installation of the value on the class is performed atomically. At > that point, if several racing threads have computed values, one is chosen, and > returned to all the racing threads." > Ok hold up... so now it's possible for multiple threads to independently > computeValue? These two statements don't appear to mesh... I'm looking for the > behavior computeValue describes: basically computeIfAbsent. But in practice > (and from what I have read of the current implementation), multiple threads > might call computeValue for a given class. > In JRuby, where the computation of this value also sets up global namespace > tables, it results in warnings that the namespace entry has been initialized > multiple times. > Adding synchronized to my implementation of computeValue does not help; it > just > means two threads don't compute at the same time. They will block until the > first thread finishes its computeValue, and while that first thread is > initializing the ClassValue, they'll proceed to computeValue several more > times. > This was not a problem with my home-grown ClassValue implementation because I > double-check the cache before proceeding into synchronized code that makes the > computeValue call (computeIfAbsent behavior). > I can work around this by also overriding ClassValue.get to be synchronized, > but > the existing behavior does not seem right to me. It works this way on 1.8, 21, > and 24, so nothing has changed. Either the docs are wrong or the > implementation > is wrong. > Charles Oliver Nutter > Architect and Technologist > Headius Enterprises > [ https://www.headius.com/ | https://www.headius.com ] > [ mailto:[email protected] | [email protected] ] > _______________________________________________ > mlvm-dev mailing list > [email protected] > https://mail.openjdk.org/mailman/listinfo/mlvm-dev
_______________________________________________ mlvm-dev mailing list [email protected] https://mail.openjdk.org/mailman/listinfo/mlvm-dev
