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