@Test
  public void testFirstBlockedThenSucceed() throws Exception {
    final int size = 1024;
    final long capacity = 1024;
    final long maxBlockTimeInMs = 200;
    SimpleByteBufferPool pool = new SimpleByteBufferPool(capacity);
    CountDownLatch[] allocated = new CountDownLatch[2];
    CountDownLatch[] used = new CountDownLatch[2];
    BufferConsumer[] consumers = new BufferConsumer[2];
    Thread[] consumerThreads = new Thread[2];
    for (int i = 0; i < 2; i++) {
      allocated[i] = new CountDownLatch(1);
      used[i] = new CountDownLatch(1);
      consumers[i] = new BufferConsumer(size, maxBlockTimeInMs, pool, allocated[i], used[i]);
      consumerThreads[i] = new Thread(consumers[i]);
    }
    consumerThreads[0].start();
    if (!allocated[0].await(100, TimeUnit.MILLISECONDS)) {
      fail("SimpleByteBufferPool takes too long time to allocate a buffer to BufferConsumer[0].");
    }
    assertEquals(0, pool.availableMemory());
    consumerThreads[1].start();
    assertEquals("BufferConsumer-1 should not have got buffer from the buffer pool.", 1, allocated[1].getCount());
    used[0].countDown();
    consumerThreads[0].join();
    used[1].countDown();
    consumerThreads[1].join();
    for (int i = 0; i < 2; i++) {
      if (consumers[i].exception != null) {
        throw consumers[i].exception;
      }
    }
    assertEquals(capacity, pool.availableMemory());
  }
This scenario tests when a {@link SimpleByteBufferPool} tries to allocate a certain amount of memory in several
stages. First, the requested amount of memory is not availeble, so the allocation operation is blocking. Before
the operation is timed out, the requested amount of memory becomes available, and the allocation becomes successfully.
No exception should occur during this test.
ambry-utils/src/test/java/com/github/ambry/utils/SimpleByteBufferPoolTest.java