コード例 #1
0
  /**
   * Basic test of the ability to add to a buffer with a fixed capacity queue and to drain the
   * elements from the queue including tests of the non-blocking aspects of the API.
   *
   * @throws TimeoutException
   * @throws ExecutionException
   * @throws InterruptedException
   */
  public void test_blockingBuffer()
      throws InterruptedException, ExecutionException, TimeoutException {

    final Integer e0 = new Integer(0);
    final Integer e1 = new Integer(1);
    final Integer e2 = new Integer(2);

    final int queueCapacity = 3;
    final BlockingQueue<Integer[]> queue = new ArrayBlockingQueue<Integer[]>(queueCapacity);
    final int chunkSize = 4;
    final long chunkTimeout = 1000;
    final TimeUnit chunkTimeoutUnit = TimeUnit.MILLISECONDS;
    /*
     * The test timeout is just a smidge longer than the chunk timeout.
     *
     * Note: use Long.MAX_VALUE iff debugging.
     */
    //      final long testTimeout = Long.MAX_VALUE;
    final long testTimeout = chunkTimeout + 20;
    final boolean ordered = false;

    final BlockingBuffer<Integer[]> buffer =
        new BlockingBuffer<Integer[]>(queue, chunkSize, chunkTimeout, chunkTimeoutUnit, ordered);

    // buffer is empty.
    assertTrue(buffer.isOpen());
    assertTrue(buffer.isEmpty());
    assertEquals("chunkCount", 0L, buffer.getChunksAddedCount());
    assertEquals("elementCount", 0L, buffer.getElementsAddedCount());

    final IAsynchronousIterator<Integer[]> itr = buffer.iterator();

    // nothing available from the iterator (non-blocking test).
    assertFalse(itr.hasNext(1, TimeUnit.NANOSECONDS));
    assertNull(itr.next(1, TimeUnit.NANOSECONDS));

    // add an element to the buffer - should not block.
    buffer.add(new Integer[] {e0});

    // should be one element and one chunk accepted by the buffer.
    assertTrue(buffer.isOpen());
    assertFalse(buffer.isEmpty());
    assertEquals("chunkCount", 1L, buffer.getChunksAddedCount());
    assertEquals("elementCount", 1L, buffer.getElementsAddedCount());

    // something should be available now (non-blocking).
    assertTrue(itr.hasNext(1, TimeUnit.NANOSECONDS));

    // something should be available now (blocking).
    assertTrue(itr.hasNext());

    // add another element to the buffer - should not block.
    buffer.add(new Integer[] {e1});

    // should be two elements and two chunks accepted into the buffer
    assertTrue(buffer.isOpen());
    assertFalse(buffer.isEmpty());
    assertEquals("chunkCount", 2L, buffer.getChunksAddedCount());
    assertEquals("elementCount", 2L, buffer.getElementsAddedCount());

    final ReentrantLock lock = new ReentrantLock();
    final Condition cond = lock.newCondition();
    final AtomicBoolean proceedFlag = new AtomicBoolean(false);

    // future of task writing a 3rd element on the buffer.
    final Future<?> producerFuture =
        service.submit(
            new Callable<Void>() {
              public Void call() throws Exception {

                lock.lockInterruptibly();
                try {
                  if (!proceedFlag.get()) {
                    cond.await();
                  }
                  /*
                   * add another element - should block until we take an
                   * element using the iterator.
                   */
                  buffer.add(new Integer[] {e2});

                  /*
                   * itr.hasNext() will block until the buffer is closed.
                   */
                  buffer.close();
                } finally {
                  lock.unlock();
                }
                // done.
                return null;
              }
            });

    // future of task draining the buffer.
    final Future<?> consumerFuture =
        service.submit(
            new Callable<Void>() {
              public Void call() throws Exception {

                try {
                  lock.lockInterruptibly();
                  try {

                    assertTrue(itr.hasNext());

                    // take the first chunk - two elements.
                    if (log.isInfoEnabled()) log.info("Awaiting first chunk");
                    assertSameArray(new Integer[] {e0, e1}, itr.next(50, TimeUnit.MILLISECONDS));
                    if (log.isInfoEnabled()) log.info("Have first chunk");

                    /*
                     * Verify that we obtained the first chunk before the
                     * buffer was closed. Otherwise next() blocked
                     * attempting to compile a full chunk until the producer
                     * timeout, at which point the producer closed the
                     * buffer and next() noticed the closed buffer and
                     * returned.
                     */
                    assertTrue(buffer.isOpen());
                    assertFalse("buffer was closed.", itr.isExhausted());

                    /*
                     * Verify that nothing is available from the iterator
                     * (non-blocking test).
                     */
                    assertFalse(itr.hasNext(1, TimeUnit.NANOSECONDS));
                    assertNull(itr.next(1, TimeUnit.NANOSECONDS));

                    // Signal the producer that it should continue.
                    proceedFlag.set(true);
                    cond.signal();

                  } finally {

                    lock.unlock();
                  }

                  // should block until we close the buffer.
                  assertTrue(itr.hasNext());

                  // last chunk
                  assertSameArray(new Integer[] {e2}, itr.next());

                  // should be immediately false.
                  assertFalse(itr.hasNext(1, TimeUnit.NANOSECONDS));
                  // should be immediately null.
                  assertNull(itr.next(1, TimeUnit.NANOSECONDS));

                  // The synchronous API should also report an exhausted
                  // itr.
                  assertFalse(itr.hasNext());
                  try {
                    itr.next();
                    fail("Expecting: " + NoSuchElementException.class);
                  } catch (NoSuchElementException ex) {
                    if (log.isInfoEnabled()) log.info("Ignoring expected exception: " + ex);
                  }

                  return null;

                } catch (Throwable t) {
                  log.error("Consumer failed or blocked: " + t, t);
                  throw new Exception(t);
                }
              }
            });

    // wait a little bit for the producer future.
    producerFuture.get(testTimeout, chunkTimeoutUnit);

    // wait a little bit for the consumer future.
    consumerFuture.get(testTimeout, chunkTimeoutUnit);
  }