RFR (XS): 8014890 : Reference queues may return more entries than expected
Mandy Chung
mandy.chung at oracle.com
Wed Jun 19 21:40:24 UTC 2013
On 6/19/13 5:05 AM, David Holmes wrote:
> Hi Thomas,
>
> I think I'm going to need a lot more time to understand this issue
> completely. The synchronization being used seems complex and somewhat
> ill-defined, and also inadequate. I'm not convinced that adding code
> inside a synchronized block really fixes a race condition caused by
> unsynchronized access - but it may well narrow the window significantly.
It'll be good to get your time to look into it. In fact I suggested
this fix to Thomas while I do raise a question to myself whether we
should attempt to clean up the synchronization in this fix (this code
was implemented before the new Java memory model in 1.5).
Here is my understanding why I think the suggested fix is adequate.
r.queue will be set to ENQUEUED when enqueued and set to NULL when dequeued.
1) Read access to r.queue
a) no lock to read r.queue
in Reference.enqueue() method and ReferenceHandler thread
possibly race is that when calling RQ.enqueue(Reference) method
(i) it's already enqueued
(ii) it's already dequeued
RQ.enqueue(Reference) has to handle that (where the bug is)
b) Reference.isEnqueued holds the Reference object monitor
this.next is null initially and is set by the VM to non-null
pending for enqueue. I believe checking this.next != null
is an optimization.
Potential race is that the reference may be dequeued which
doesn't hold the Reference object lock. Is this a bug?
This race is acceptable in my opinion as I think the isEnqueued
method is useful to determine when a referent has been GC'ed
and then followed by dequeuing it from the queue. If there
are threads that doing dequeuing, the code calling isEnqueued
will prepare for the race when isEnqueued returning true,
the reference may have been dequeued shortly.
Precisely handling the race that isEnqueued returns true while
it may be dequeued doesn't prevent this situation.
2) Write to r.queue
a) acquire Reference object monitor and ReferenceQueue.lock
to set r.queue to ENQUEUED
Potential race in RQ.enqueue method
synchronized (r) {
if (r.queue == ENQUEUED) return false;
synchronized (lock) {
if (r.queue != this) return false;
...
Moving if (r.queue == ENQUEUED) line to after acquiring lock
makes it easier to understand and maintain but I don't see
a race in the existing code without holding lock.
b) acquire ReferenceQueue.lock to set r.queue to NULL
ReferenceQueue.poll and remove methods calling reallyPoll method
I don't know the history. The nested lock inversion problem
is probably the reason why it doesn't acquire the Reference object
monitor to dequeue.
This one is related to whether Reference.isEnqueued() should
return (see above).
Mandy
More information about the core-libs-dev
mailing list