RFR: 8132306: java/lang/ref/ReferenceEnqueue.java fails with "RuntimeException: Error: poll() returned null; expected ref object"
Peter Levart
peter.levart at gmail.com
Thu Jul 30 10:41:35 UTC 2015
On 07/30/2015 12:24 PM, David Holmes wrote:
> On 30/07/2015 8:20 PM, Peter Levart wrote:
>>
>>
>> On 07/30/2015 11:12 AM, Daniel Fuchs wrote:
>>> Hi Peter,
>>>
>>> I'm glad you're looking at this too! We can't have too many eyes :-)
>>>
>>> On 30/07/15 10:38, Peter Levart wrote:
>>>> Suppose we have a Reference 'r' and it's associated ReferenceQueue
>>>> 'q'.
>>>> Currently it can happen that the following evaluates to true, which is
>>>> surprising:
>>>>
>>>> q.poll() == null && r.isEnqueued()
>>>>
>>>
>>> But on the other hand this can only happen if two different
>>> threads are polling the queue - in which case only one of them
>>> will get the reference. In such a situation, the symmetric condition
>>> would not be surprising (as the other thread would
>>> get q.poll() != null):
>>>
>>> r.isEnqueued() && q.poll() == null
>>>
>>> The original bug fixed by Kim is more surprising, because there's only
>>> one Thread doing the polling, and it does get:
>>>
>>> r.isEnqueud() && q.poll() == null
>>>
>>> although nobody else is polling the queue.
>>> This should no longer occur in this situation with Kim's fix.
>>>
>>> cheers,
>>>
>>> -- daniel
>>
>> Yes, these are two different issues. The one Kim has fixed is the race
>> of above expressions with Queue.enqueue() (when the queue is changing
>> from the associated instance to ENQUEUED). The one I'm pointing at and
>> Kim has already identified as potential issue is the race of the
>> following expression:
>>
>> r.isEnqueued() && q.poll() == null && r.isEnqueued() ==> true
>>
>> ....with Queue.[realy]poll() (when the queue is changing from ENQUEUED
>> to NULL).
>>
>> Which only happens if at least two threads are polling the queue, but is
>> equally surprising and prone to cause bugs, don't you think?
>
> Sorry I'm missing your point. The expression:
>
> r.isEnqueued() && q.poll() == null
>
> is exactly the race the current fix is addressing. Adding a second
> check of r.isEnqueued() which still returns true does not add anything
> that I can see. ??
>
> David
The expressions are similar, but the race is different. The one
addressed by Kim is the race of:
r.isEnqueued() && q.poll() == null ==> true
with q.enqueue(r)
There, the reversal of assignments to q.head and r.queue in
ReferenceQueue.enqueue() fixes the issue.
The one I'm pointing at is the race of:
r.isEnqueued() && q.poll() == null && r.isEnqueued() ==> true
with q.poll()
Here, the fix would be to also revert the assignments to q.head and
r.queue in ReferenceQueue.reallyPoll() this time.
The 1st race is the race with enqueue-ing and the 2nd race is the race
with de-queueing. Initially, my "surprising" expression was:
q.poll() == null && r.isEnqueued() ==> true
...but this is not representative, as it is also an expected outcome
when racing with enqueue-ing. So I added a pre-condition to express the
fact that it happens when racing with de-queueing only:
r.isEnqueued() && q.poll() == null && r.isEnqueued() ==> true
What is surprising in the above expression evaluating to true is the
fact that 'r' appears to be enqueued before and after the q.poll()
returns null. I can easily imagine code that would fail because it never
imagined above expression to evaluate to true. For example:
if (r.isEnqueued()) {
Reference s = q.poll();
if (s == null) {
// somebody else already dequeued 'r'
assert !r.isEnqueued();
}
}
Regards, Peter
>
>> Regards, Peter
>>
>>
More information about the hotspot-gc-dev
mailing list