It certainly seems that all the choices are bad.

The "obvious" choice is to simply say that WeakReference<Value> makes no sense, in that it sidesteps the hard semantics questions.  My fear is that this will cause new failures, where existing libraries that toss objects into WHMs to cache derived results, will start to experience new failures when the value types show up in the heap (after all, WeakReference::new takes Object.)  And we'll have to have something to tell those users, because they declared a WeakHashMap<User, UserData>, and someone created a value subtype of User -- which seems entirely valid.

It is possible we could do something special with WHM, since it is layered atop WR, but that still begs the question -- what?

On 1/19/2022 4:28 PM, Dan Heidinga wrote:
In the Dec 2021 update to State of Valhalla, part 2: The language
model, the section on "Identity-sensitive operations" states this
about weak references:

Weak references to value objects that contain no references to identity objects 
should never be
cleared; weak references to value objects that contain references to identity 
objects should be
cleared when those objects are no longer strongly reachable.
I'm concerned that this approach makes using weak references correctly
for values very difficult, especially in the face of scalarization.

Scalarization allows separating values into their individual
components and flowing them separately through the code.  This can -
with some caveats for decompilation and maybe other features - result
in very different lifetimes for each separate component, with some
object references going out of scope "earlier" than expected.  The
source code will of course show the *entire* value being carried along
even though only a single component may survive post-scalarization
which means developers will see very different behaviour between the
interpreter (which doesn't scalarize) and jitted code - making it very
easy to write tests that give them the wrong impression.

Reference::reachabilityFence was added in Java 9 to help users deal
with premature finalization and has gotten little attention from most
users. The proposed semantics for WeakReferences on value objects will
drag the reachabilityFence method into the light and expose users to a
problem they'd rather not have.  Just as we avoided exposing most
users to tearing of their (bucket 2) value objects, we shouldn't
expose them to having to reason about complicated reachability rules.

Which is to say, Reference objects and value objects need to be
incompatible to give users what they expect.  Anything else is loading
a gun and pointing it at their feet.

Here's an example of why the suggested rules for weak references using
the identity objects may bring reachability issues to the forefront
(where we don't want them):

public value class ResourcePair {
   static long nativePtr;
   static IdentityObject companion;

   ResourcePair(long ptr, IdentityObject o) {
     nativePtr = ptr;
     companion = o;
   }
}

class PtrRef extends WeakReference<ResourcePair> {
   long nativePtr;

   PtrRef(ResourcePair p, ReferenceQueue q) {
     super(p, q);
     nativePtr = p.nativePtr;
   }

   void free() { /* do something to nativePtr */ }
}

If the `companion` object of the ResourcePair value goes out of scope
before `nativePtr` is done being used - the issue of reachabilityFence
- the ReferenceQueue processor could call free() before the
`nativePtr` is done being used.  Scalarization will make this more
common and even very common if we're getting the benefits we expect
from it.

--Dan

Reply via email to