8256517: (ref) Reference.clear during reference processing may lose notification

8240696: (ref) Reference.clear may extend the lifetime of the referent

Use private native helper to implement Reference.clear.

Reviewed-by: pliden, rkennke, mchung
This commit is contained in:
Kim Barrett 2020-11-25 03:34:50 +00:00
parent 3c230b8ac5
commit 66943fefa7
14 changed files with 295 additions and 31 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -34,6 +34,22 @@ class FinalReference<T> extends Reference<T> {
super(referent, q);
}
/* May only be called when the reference is inactive, so no longer weak. */
@Override
public T get() {
// Cannot call super.get() when active, as the GC could
// deactivate immediately after the test.
return getFromInactiveFinalReference();
}
/* May only be called when the reference is inactive, so no longer weak.
* Clearing while active would discard the finalization request.
*/
@Override
public void clear() {
clearInactiveFinalReference();
}
@Override
public boolean enqueue() {
throw new InternalError("should never reach here");

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -82,7 +82,7 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
}
try {
Object finalizee = this.getInactive();
Object finalizee = this.get();
assert finalizee != null;
if (!(finalizee instanceof java.lang.Enum)) {
jla.invokeFinalize(finalizee);

View file

@ -100,10 +100,10 @@ public abstract class Reference<T> {
* [active/unregistered] [1]
*
* Transitions:
* clear
* clear [2]
* [active/registered] -------> [inactive/registered]
* | |
* | | enqueue [2]
* | | enqueue
* | GC enqueue [2] |
* | -----------------|
* | |
@ -114,7 +114,7 @@ public abstract class Reference<T> {
* v | |
* [pending/enqueued] --- |
* | | poll/remove
* | poll/remove |
* | poll/remove | + clear [4]
* | |
* v ReferenceHandler v
* [pending/dequeued] ------> [inactive/dequeued]
@ -140,12 +140,14 @@ public abstract class Reference<T> {
* [1] Unregistered is not permitted for FinalReferences.
*
* [2] These transitions are not possible for FinalReferences, making
* [pending/enqueued] and [pending/dequeued] unreachable, and
* [inactive/registered] terminal.
* [pending/enqueued], [pending/dequeued], and [inactive/registered]
* unreachable.
*
* [3] The garbage collector may directly transition a Reference
* from [active/unregistered] to [inactive/unregistered],
* bypassing the pending-Reference list.
*
* [4] The queue handler for FinalReferences also clears the reference.
*/
private T referent; /* Treated specially by GC */
@ -342,22 +344,6 @@ public abstract class Reference<T> {
return this.referent;
}
/**
* Load referent with strong semantics. Treating the referent
* as strong referent is ok when the Reference is inactive,
* because then the referent is switched to strong semantics
* anyway.
*
* This is only used from Finalizer to bypass the intrinsic,
* which might return a null referent, even though it is not
* null, and would subsequently not finalize the referent/finalizee.
*/
T getInactive() {
assert this instanceof FinalReference;
assert next == this; // I.e. FinalReference is inactive
return this.referent;
}
/**
* Tests if the referent of this reference object is {@code obj}.
* Using a {@code null} {@code obj} returns {@code true} if the
@ -383,6 +369,41 @@ public abstract class Reference<T> {
* clears references it does so directly, without invoking this method.
*/
public void clear() {
clear0();
}
/* Implementation of clear(), also used by enqueue(). A simple
* assignment of the referent field won't do for some garbage
* collectors.
*/
private native void clear0();
/* -- Operations on inactive FinalReferences -- */
/* These functions are only used by FinalReference, and must only be
* called after the reference becomes inactive. While active, a
* FinalReference is considered weak but the referent is not normally
* accessed. Once a FinalReference becomes inactive it is considered a
* strong reference. These functions are used to bypass the
* corresponding weak implementations, directly accessing the referent
* field with strong semantics.
*/
/**
* Load referent with strong semantics.
*/
T getFromInactiveFinalReference() {
assert this instanceof FinalReference;
assert next != null; // I.e. FinalReference is inactive
return this.referent;
}
/**
* Clear referent with strong semantics.
*/
void clearInactiveFinalReference() {
assert this instanceof FinalReference;
assert next != null; // I.e. FinalReference is inactive
this.referent = null;
}
@ -413,7 +434,7 @@ public abstract class Reference<T> {
* it was not registered with a queue when it was created
*/
public boolean enqueue() {
this.referent = null;
clear0(); // Intentionally clear0() rather than clear()
return this.queue.enqueue(this);
}