/** * Closes the Operation Queue and fails all Operations in it with the given exception. * * @param causingException The exception to fail with. If null, it will default to ObjectClosedException. */ private void closeQueue(Throwable causingException) { // Close the operation queue and extract any outstanding Operations from it. Collection<CompletableOperation> remainingOperations = this.operationQueue.close(); if (remainingOperations != null && remainingOperations.size() > 0) { // If any outstanding Operations were left in the queue, they need to be failed. // If no other cause was passed, assume we are closing the queue because we are shutting down. Throwable failException = causingException != null ? causingException : new CancellationException(); cancelIncompleteOperations(remainingOperations, failException); } // The commit queue will auto-close when we are done and it itself is empty. We just need to unblock it in case // it was idle and waiting on a pending take() operation. this.commitQueue.cancelPendingTake(); }
/** * Tests the ability to cancel a pending take() operation. */ @Test public void testCancelPendingTake() throws Exception { final int valueToQueue = 1234; @Cleanup BlockingDrainingQueue<Integer> queue = new BlockingDrainingQueue<>(); val takeResult = queue.take(MAX_READ_COUNT); Assert.assertFalse("take() returned a completed future.", takeResult.isDone()); queue.cancelPendingTake(); Assert.assertTrue("cancelPendingTake() did not cancel a pending take() future.", takeResult.isCancelled()); val takeResult2 = queue.take(MAX_READ_COUNT); queue.add(valueToQueue); Assert.assertEquals("take() did not work again after being cancelled.", valueToQueue, (int) takeResult2.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).poll()); }