Repository: netty/netty
File: buffer/src/test/java/io/netty/buffer/AlignedPooledByteBufAllocatorTest.java (Java)

Code before:
        // Get the smallSubpagePools[] array in arena.
        @SuppressWarnings("unchecked")
        PoolSubpage<byte[]>[] smallSubpagePools = (PoolSubpage<byte[]>[]) byteBuf.chunk.arena.smallSubpagePools;
        PoolSubpage<byte[]> head = null;
        for (PoolSubpage<byte[]> subpage : smallSubpagePools) {
            if (subpage.next != subpage) {
                // Find the head subpage which the byteBuf belongs to.
                head = subpage;
                break;
            }
        }
        assertNotNull(head);
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // Because the head subpage was already locked in the main thread, so this should hang and wait.
                byteBuf.release();
            }
        });
        t1.setDaemon(true);
        // Intentionally lock the head subpage in main thread.
        head.lock();
        try {
            t1.start();
            // Wait max 3 seconds.
            t1.join(1000 * 3);
            assertTrue(t1.isAlive(), "The t1 thread should still alive and wait for the head lock.");
        } finally {
            head.unlock();
        }
    }
    private static PooledByteBuf<?> pooledByteBuf(ByteBuf buffer) {
        // might need to unwrap if swapped (LE) and/or leak-aware-wrapped
        while (!(buffer instanceof PooledByteBuf)) {
            buffer = buffer.unwrap();
        }
        return (PooledByteBuf<?>) buffer;
    }
}


Review comment (suggestion):
Instead of having a mandatory 3-second wait here, we could make `PoolSubpage.lock` package-private and spin on a `while (!head.lock.hasQueuedThread(t1)) { ... }` condition, until some deadline.

Code after:
        final PooledByteBuf<?> byteBuf = pooledByteBuf(allocator.directBuffer(initialCapacity, 16));
        // Get the smallSubpagePools[] array in arena.
        @SuppressWarnings("unchecked")
        PoolSubpage<byte[]>[] smallSubpagePools = (PoolSubpage<byte[]>[]) byteBuf.chunk.arena.smallSubpagePools;
        PoolSubpage<byte[]> head = null;
        for (PoolSubpage<byte[]> subpage : smallSubpagePools) {
            if (subpage.next != subpage) {
                // Find the head subpage which the byteBuf belongs to.
                head = subpage;
                break;
            }
        }
        assertNotNull(head);
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // Because the head subpage was already locked in the main thread, so this should hang and wait.
                byteBuf.release();
            }
        });
        t1.setDaemon(true);
        // Intentionally lock the head subpage in main thread.
        head.lock();
        try {
            t1.start();
            long start = System.nanoTime();
            while (!head.lock.hasQueuedThread(t1)) {
                if ((System.nanoTime() - start) > TimeUnit.SECONDS.toNanos(3)) {
                    break;
                }
            }
            assertTrue(head.lock.hasQueuedThread(t1),
                    "The t1 thread should still be waiting for the head lock.");
        } finally {
            head.unlock();
        }
    }

    private static PooledByteBuf<?> pooledByteBuf(ByteBuf buffer) {
        // might need to unwrap if swapped (LE) and/or leak-aware-wrapped
        while (!(buffer instanceof PooledByteBuf)) {
            buffer = buffer.unwrap();
        }
        return (PooledByteBuf<?>) buffer;
    }
}