JDK-6815766 : LinkedBlockingQueue's iterator can return null if drainTo(c) executes concurrently
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 7
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2009-03-11
  • Updated: 2010-04-04
  • Resolved: 2009-08-14
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b70Fixed
Related Reports
Relates :  
Description
Martin Buchholz reports:

The following test program should never throw,
but it quickly throws NullPointerException.

import java.util.*;
import java.util.concurrent.*;

public class LBQRace {
    static void checkNotContainsNull(Iterable it) {
        for (Object x : it)
            if (x == null)
                throw new NullPointerException();
    }

    public static void main(String[] args) throws Throwable {
        final BlockingQueue q = new LinkedBlockingQueue();

        new Thread() { public void run() {
            for (;;) {
                q.offer(1);
            }}}.start();

        new Thread() { public void run() {
            for (;;) {
                List list = new ArrayList();
                q.drainTo(list);
                checkNotContainsNull(list);
            }}}.start();

        new Thread() { public void run() {
            for (;;) {
                checkNotContainsNull(q);
            }}}.start();
    }
}

Comments
EVALUATION changeset containing this fix: http://hg.openjdk.java.net/jdk7/tl/jdk/rev/49573ab3096a
07-08-2009

EVALUATION The drainTo(Collection c) methods unlinks the current chain of elements while under protection of the queue's locks but then copies across each item to the target collection without a lock: // Transfer the elements outside of locks int n = 0; for (Node<E> p = first; p != null; p = p.next) { c.add(p.item); p.item = null; ++n; } If an iterator() already has one of the Nodes as its current element then upon advancing to the next element it could encounter a null item which is subsequently returned from next(). As null is not a permitted element in the queue it should never be returned.
11-03-2009

SUGGESTED FIX Simple solution: move the element copy inside the locked section - as occurs with the drainTo(Collection c, int maxElements) method. More complex: in the loop that does the copy unlink each Node as the chain is traversed. This will prevent the iterator from continuing down the deleted chain. In Itr.next() check for a null item and treat it as finding a null next element.
11-03-2009