[concurrency-interest] Should I avoid compareAndSet with value-based classes?

Gil Tene gil at azul.com
Sat Jul 8 02:38:53 EDT 2017

Alex, your statements below are all arguments for why value-based classes (as specified) and value types (as they seem to be headed) should not exist as subclasses of Object. As noted before, I'd probably be on your side if that one.

But they do exist, and they (very clearly and explicitly) have no identity. We can't change that fact. We can avoid using them if we want. But we probably can't use them and argue that what we are using should be behaving in some directly-opposite-from-their-specification way.

For vale-based classes, the meaning of  == , identityHashCode, and synchronization are all clearly, explicitly and loudly undefined, promising unpredictable behavior. For value types it is not yet clear if the == operand will be similarly (explicitly) undefined, or if it will have a new meaning (act like == on an int), but things are pretty clear when it comes to identityHashCode and synchronization.

The reasoning for these value-based classes and value types goes far beyond compiler optimizations. And I'm not the one arguing for them.

My arguments here are not about trying to justify some compiler optimizations, or trying to justify the choices of making these things subclasses the Object. They are about pointing out the actual meaning of things as defined and the (very high risk) of coding against it based on a hope that some temporarily observed behavior is actually reliable, when everything that describes what it does says otherwise. My mentions of potential optimizations are simply attempts to demonstrate how strange things can happen to code that does these "may cause unpredictable behavior" things.

Sent from my iPad

On Jul 7, 2017, at 4:22 PM, Alex Otenko <oleksandr.otenko at gmail.com<mailto:oleksandr.otenko at gmail.com>> wrote:

A type with no identity certainly is a frankentype. I don’t see why the poor specification needs to be taken to its extreme interpretation. Conceptual clarity is more important than saving CPU cycles.

Having a type that is reified as something that does not have “a unique address” in memory (that we understand as the identity of Objects) is different from “no identity”.

Being unable to use “value type” instances for synchronization is perfectly reasonable - in the end, you aren’t meant to synchronize using random objects, and the whole purpose of supporting synchronization on every object is to ease construction of thread-safe code (through declaration of synchronized methods). If "the thing” is immutable, there is no need to support synchronization on those specific instances, and choosing such instances as the means of synchronizing something else requires justification, even if “the thing” subclasses from Object.

Restricting the meaning of identityHashCode is questionable, but in the end hinges on whether “the thing” is a subclass of an Object or not. If it is, then there are ways to support identityHashCode - even if there is no notion of “a unique address”.

Restricting the use of "==“ because of some compiler optimizations, is inventing obstacles. I don’t see why compiler problems of this sort should leak into the application programmer’s life, especially in the form of RandomExceptions and heisenbugs.


On 7 Jul 2017, at 18:32, Gil Tene <gil at azul.com<mailto:gil at azul.com>> wrote:

On Jul 7, 2017, at 10:04 AM, Kirk Pepperdine <kirk at kodewerk.com<mailto:kirk at kodewerk.com>> wrote:

3. The exception-throwing thing is IMO the healthy way to go IMO. I.e. throw some runtime exception when encountering an indentity-based operation on an instance of a value-based class. Hard to argue that "an exception should not be thrown here" given the clear warnings in the spec. My worry is mostly about coverage. Throwing exceptions in all cases where identity based operations are attempted would clearly be right, and healthy. But doing so only in some cases (and leaving some undefined behavior executing paths that don't throw exceptions) doesn't solve the problem [and may make things worse by encouraging false confidence]. Since no one *requires* us to throw an exception right now, JVM implementors will probably keep taking the lazy approach and not work hard on trying to achieve complete coverage. And since don't do it at all" is probably better/easier to verify than "do it in some places but not in others", we'll probably avoid it altogether for now. This *may* change when value-based optimizations materialize.

I think I’d have to (violently) disagree with this. There is a reason no one requires you to throw an exception when expressing equality and that is because this is a guaranteed property in the runtime. Without it you lose a fundamental property of the runtime.

Synchronous exceptions can be thrown for example because (https://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html#jls-11.1.2):

  *   evaluation of an expression violates the normal semantics of the Java programming language (§15.6<https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.6>), such as an integer divide by zero.

  *   an error occurs while loading, linking, or initializing part of the program (§12.2<https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.2>, §12.3<https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.3>, §12.4<https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4>); in this case, an instance of a subclass of LinkageError is thrown.

  *   an internal error or resource limitation prevents the Java Virtual Machine from implementing the semantics of the Java programming language; in this case, an instance of a subclass of VirtualMethodError is thrown. [GT: this is mis-spelled in the spec. Should be VirtualMachineError].

I'd hope to have it qualify under the first bullet, but it is not listed in the specific list of runtime exception causes in 15.6. However, evaluating an == expression where one of the operands is an instance of a value-based class (and has no identity) can probably qualify under "an internal error or resource limitation prevents the Java Virtual Machine from implementing the semantics of the Java programming language" (third bullet). Especially when the Java SE spec explicitly says "Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects".

Separately, asynchronous exceptions can be thrown anywhere in the code. E.g. An OOME can hit a == point in the code too.

I'd rather not make such exceptions a subclass of VirtualMachineError would be better tho...

— Kirk

Concurrency-interest mailing list
Concurrency-interest at cs.oswego.edu<mailto:Concurrency-interest at cs.oswego.edu>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20170708/b22f9b11/attachment.html>

More information about the Concurrency-interest mailing list