On Mon, 9 Jun 2025 16:53:07 GMT, kabutz <d...@openjdk.org> wrote:

>> We logged several bugs on the LinkedBlockingDeque. This aggregates them into 
>> a single bug report and PR.
>> 
>> 1. LinkedBlockingDeque does not immediately throw InterruptedException in 
>> put/take
>> 
>> The LinkedBlockingDeque does not behave consistently with other concurrency 
>> components. If we call putFirst(), putLast(), takeFirst(), or takeLast() 
>> with a thread that is interrupted, it does not immediately throw an 
>> InterruptedException, the way that ArrayBlockingQueue and 
>> LInkedBlockingQueue does, because instead of lockInterruptibly(), we call 
>> lock(). It will only throw an InterruptedException if the queue is full (on 
>> put) or empty (on take). Since interruptions are frequently used as a 
>> shutdown mechanism, this might prevent code from ever shutting down.
>> 
>> 2. LinkedBlockingDeque.clear() should preserve weakly-consistent iterators
>> 
>> LinkedBlockingDeque.clear() should preserve weakly-consistent iterators by 
>> linking f.prev and f.next back to f, allowing the iterators to continue from 
>> the first or last respectively. This would be consistent with how the other 
>> node-based weakly consistent queues LinkedBlockingQueue LinkedTransferQueue, 
>> ConcurrentLinkedQueue/Deque work.
>> 
>> The LBD already supports self-linking, since that is done by the 
>> unlinkFirst() and unlinkLast() methods, and the iterators and spliterator 
>> thus all support self-linking.
>> 
>> This can be fixed very easily by linking both f.prev and f.next back to f.
>> 
>> 3. LinkedBlockingDeque offer() creates nodes even if capacity has been 
>> reached
>> 
>> In the JavaDoc of LinkedBlockingDeque, it states: "Linked nodes are 
>> dynamically created upon each insertion unless this would bring the deque 
>> above capacity." However, in the current implementation, nodes are always 
>> created, even if the deque is full. This is because count is non-volatile, 
>> and we only check inside the linkFirst/Last() methods whether the queue is 
>> full. At this point we have already locked and have created the Node. 
>> Instead, the count could be volatile, and we could check before locking.
>> 
>> In the current version, calling offer() on a full LinkedBlockingDeque 
>> creates unnecessary objects and contention. Similarly for poll() and peek(), 
>> we could exit prior to locking by checking the count field.
>> 
>> 4. LinkedBlockingDeque allows us to overflow size with addAll()
>> 
>> In LinkedBlockingDeque.addAll() we first build up the chain of nodes and 
>> then add that chain in bulk to the existing nodes. We count the nodes in 
>> "int n" and then whilst hol...
>
> kabutz has updated the pull request incrementally with one additional commit 
> since the last revision:
> 
>   Whitespace

@kabutz Thanks for the updates, this is really looking good now. I added some 
suggestions for edits to avoid a couple of volatile reads, which I think makes 
sense for Aarch64-based systems (as we're already updating the code).

src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java line 
215:

> 213:         // assert lock.isHeldByCurrentThread();
> 214:         if (count >= capacity)
> 215:             return false;

Suggestion:

        int c;
        if ((c = count) >= capacity)
            return false;

src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java line 
225:

> 223:         ++count;
> 224:         notEmpty.signal();
> 225:         return true;

Suggestion:

        count = c + 1;
        notEmpty.signal();
        return true;

src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java line 
236:

> 234:         // assert lock.isHeldByCurrentThread();
> 235:         if (count >= capacity)
> 236:             return false;

Suggestion:

        int c;
        if ((c = count) >= capacity)
            return false;

src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java line 
246:

> 244:         ++count;
> 245:         notEmpty.signal();
> 246:         return true;

Suggestion:

        count = c + 1;
        notEmpty.signal();
        return true;

test/jdk/java/util/concurrent/tck/LinkedBlockingDequeTest.java line 1967:

> 1965: 
> 1966:     public void testWeaklyConsistentIterationWithIteratorRemove() {
> 1967:         final LinkedBlockingDeque<Item> q = new 
> LinkedBlockingDeque<>(15);

@kabutz Would 5 suffice?

-------------

PR Review: https://git.openjdk.org/jdk/pull/24925#pullrequestreview-2912857958
PR Review Comment: https://git.openjdk.org/jdk/pull/24925#discussion_r2137473157
PR Review Comment: https://git.openjdk.org/jdk/pull/24925#discussion_r2137473540
PR Review Comment: https://git.openjdk.org/jdk/pull/24925#discussion_r2137471349
PR Review Comment: https://git.openjdk.org/jdk/pull/24925#discussion_r2137472031
PR Review Comment: https://git.openjdk.org/jdk/pull/24925#discussion_r2137478626

Reply via email to