On 30/07/2015 8:41 PM, Peter Levart wrote:
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:
So r has been enqueued and one poll() removes it, so the second poll()
returns NULL, but r still claims to be enqueued. Sorry I'm not seeing
how that is possible.
David
if (r.isEnqueued()) {
Reference s = q.poll();
if (s == null) {
// somebody else already dequeued 'r'
assert !r.isEnqueued();
}
}
Regards, Peter
Regards, Peter