Example usage for java.util.concurrent CountDownLatch getCount

List of usage examples for java.util.concurrent CountDownLatch getCount

Introduction

In this page you can find the example usage for java.util.concurrent CountDownLatch getCount.

Prototype

public long getCount() 

Source Link

Document

Returns the current count.

Usage

From source file:io.druid.server.namespace.cache.NamespaceExtractionCacheManagerExecutorsTest.java

public void testDelete(final String ns) throws InterruptedException {
    final CountDownLatch latch = new CountDownLatch(5);
    final CountDownLatch latchMore = new CountDownLatch(10);

    final AtomicLong runs = new AtomicLong(0);
    long prior = 0;
    final URIExtractionNamespace namespace = new URIExtractionNamespace(ns, tmpFile.toURI(),
            new URIExtractionNamespace.ObjectMapperFlatDataParser(
                    URIExtractionNamespaceTest.registerTypes(new ObjectMapper())),
            new Period(1l), null);
    final String cacheId = UUID.randomUUID().toString();
    final CountDownLatch latchBeforeMore = new CountDownLatch(1);
    ListenableFuture<?> future = manager.schedule(namespace, factory, new Runnable() {
        @Override//from  www  . j  av a  2s. co m
        public void run() {
            try {
                if (!Thread.interrupted()) {
                    manager.getPostRunnable(namespace, factory, cacheId).run();
                } else {
                    Thread.currentThread().interrupt();
                }
                if (!Thread.interrupted()) {
                    runs.incrementAndGet();
                } else {
                    Thread.currentThread().interrupt();
                }
            } finally {
                latch.countDown();
                try {
                    if (latch.getCount() == 0) {
                        latchBeforeMore.await();
                    }
                } catch (InterruptedException e) {
                    log.debug("Interrupted");
                    Thread.currentThread().interrupt();
                } finally {
                    latchMore.countDown();
                }
            }
        }
    }, cacheId);
    latch.await();
    prior = runs.get();
    latchBeforeMore.countDown();
    Assert.assertFalse(future.isCancelled());
    Assert.assertFalse(future.isDone());
    Assert.assertTrue(fnCache.containsKey(ns));
    latchMore.await();
    Assert.assertTrue(runs.get() > prior);

    Assert.assertTrue(manager.implData.containsKey(ns));

    manager.delete("ns");
    Assert.assertFalse(manager.implData.containsKey(ns));
    Assert.assertFalse(fnCache.containsKey(ns));
    Assert.assertTrue(future.isCancelled());
    Assert.assertTrue(future.isDone());
    prior = runs.get();
    Thread.sleep(20);
    Assert.assertEquals(prior, runs.get());
}

From source file:org.apache.tinkerpop.gremlin.server.GremlinServerIntegrateTest.java

@Test
public void shouldRespectHighWaterMarkSettingAndSucceed() throws Exception {
    // the highwatermark should get exceeded on the server and thus pause the writes, but have no problem catching
    // itself up - this is a tricky tests to get passing on all environments so this assumption will deny the
    // test for most cases
    assumeThat("Set the 'assertNonDeterministic' property to true to execute this test",
            System.getProperty("assertNonDeterministic"), is("true"));

    final Cluster cluster = Cluster.open();
    final Client client = cluster.connect();

    try {/*  w  ww  . j a v  a  2  s .co  m*/
        final int resultCountToGenerate = 1000;
        final int batchSize = 3;
        final String fatty = IntStream.range(0, 175).mapToObj(String::valueOf).collect(Collectors.joining());
        final String fattyX = "['" + fatty + "'] * " + resultCountToGenerate;

        // don't allow the thread to proceed until all results are accounted for
        final CountDownLatch latch = new CountDownLatch(resultCountToGenerate);
        final AtomicBoolean expected = new AtomicBoolean(false);
        final AtomicBoolean faulty = new AtomicBoolean(false);
        final RequestMessage request = RequestMessage.build(Tokens.OPS_EVAL)
                .addArg(Tokens.ARGS_BATCH_SIZE, batchSize).addArg(Tokens.ARGS_GREMLIN, fattyX).create();

        client.submitAsync(request).thenAcceptAsync(r -> {
            r.stream().forEach(item -> {
                try {
                    final String aFattyResult = item.getString();
                    expected.set(aFattyResult.equals(fatty));
                } catch (Exception ex) {
                    ex.printStackTrace();
                    faulty.set(true);
                } finally {
                    latch.countDown();
                }
            });
        });

        assertTrue(latch.await(30000, TimeUnit.MILLISECONDS));
        assertEquals(0, latch.getCount());
        assertFalse(faulty.get());
        assertTrue(expected.get());

        assertTrue(recordingAppender.getMessages().stream()
                .anyMatch(m -> m.contains("Pausing response writing as writeBufferHighWaterMark exceeded on")));
    } catch (Exception ex) {
        fail("Shouldn't have tossed an exception");
    } finally {
        cluster.close();
    }
}

From source file:org.apache.awf.web.SystemTest.java

/**
 * TODO SLM This test does not make sense since stop use a callback and server store only one serverChannel
 * @Test//from w  ww.ja v a2s  . c o m
 */
public void multipleStartStopCombinations() throws InterruptedException {

    Configuration configuration = new Configuration();
    configuration.setHandlerPackage(SystemTest.class.getPackage().getName());

    final HttpServer server = new HttpServer(configuration);

    final int n = 10;
    final CountDownLatch latch = new CountDownLatch(n);
    for (int i = 0; i < n; i++) {
        IOLoop.INSTANCE.addCallback(new AsyncCallback() {
            public void onCallback() {
                server.listen(PORT + 1);
            }
        });
        IOLoop.INSTANCE.addCallback(new AsyncCallback() {
            public void onCallback() {
                server.stop();
                latch.countDown();
            }
        });
    }
    latch.await(50, TimeUnit.SECONDS);
    assertEquals(0, latch.getCount());
}

From source file:org.springframework.integration.jms.SubscribableJmsChannelTests.java

@Test
public void queueName() throws Exception {
    final CountDownLatch latch = new CountDownLatch(2);
    final List<Message<?>> receivedList1 = Collections.synchronizedList(new ArrayList<Message<?>>());
    MessageHandler handler1 = new MessageHandler() {

        public void handleMessage(Message<?> message) {
            receivedList1.add(message);/*from  w  w  w .  j  av a  2s .co m*/
            latch.countDown();
        }
    };
    final List<Message<?>> receivedList2 = Collections.synchronizedList(new ArrayList<Message<?>>());
    MessageHandler handler2 = new MessageHandler() {

        public void handleMessage(Message<?> message) {
            receivedList2.add(message);
            latch.countDown();
        }
    };
    JmsChannelFactoryBean factoryBean = new JmsChannelFactoryBean(true);
    factoryBean.setConnectionFactory(this.connectionFactory);
    factoryBean.setDestinationName("dynamicQueue");
    factoryBean.setPubSubDomain(false);
    factoryBean.afterPropertiesSet();

    SubscribableJmsChannel channel = (SubscribableJmsChannel) factoryBean.getObject();
    channel.afterPropertiesSet();
    channel.start();
    channel.subscribe(handler1);
    channel.subscribe(handler2);
    channel.send(new GenericMessage<String>("foo"));
    channel.send(new GenericMessage<String>("bar"));

    assertTrue("Countdown latch should have counted down to 0 but was " + latch.getCount(),
            latch.await(TIMEOUT, TimeUnit.MILLISECONDS));

    assertEquals(1, receivedList1.size());
    assertNotNull(receivedList1.get(0));
    assertEquals("foo", receivedList1.get(0).getPayload());
    assertEquals(1, receivedList2.size());
    assertNotNull(receivedList2.get(0));
    assertEquals("bar", receivedList2.get(0).getPayload());
    channel.stop();
}

From source file:org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainerIntegration2Tests.java

@Test
public void testExclusive() throws Exception {
    Log logger = spy(TestUtils.getPropertyValue(this.template.getConnectionFactory(), "logger", Log.class));
    doReturn(true).when(logger).isInfoEnabled();
    new DirectFieldAccessor(this.template.getConnectionFactory()).setPropertyValue("logger", logger);
    CountDownLatch latch1 = new CountDownLatch(1000);
    SimpleMessageListenerContainer container1 = new SimpleMessageListenerContainer(
            template.getConnectionFactory());
    container1.setMessageListener(new MessageListenerAdapter(new PojoListener(latch1)));
    container1.setQueueNames(queue.getName());
    GenericApplicationContext context = new GenericApplicationContext();
    context.getBeanFactory().registerSingleton("foo", queue);
    context.refresh();//from  w  ww .ja  v  a  2s . com
    container1.setApplicationContext(context);
    container1.setExclusive(true);
    container1.afterPropertiesSet();
    container1.start();
    int n = 0;
    while (n++ < 100 && container1.getActiveConsumerCount() < 1) {
        Thread.sleep(100);
    }
    assertTrue(n < 100);
    CountDownLatch latch2 = new CountDownLatch(1000);
    SimpleMessageListenerContainer container2 = new SimpleMessageListenerContainer(
            template.getConnectionFactory());
    container2.setMessageListener(new MessageListenerAdapter(new PojoListener(latch2)));
    container2.setQueueNames(queue.getName());
    container2.setApplicationContext(context);
    container2.setRecoveryInterval(1000);
    container2.setExclusive(true); // not really necessary, but likely people will make all consumers exclusive.
    ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
    container2.setApplicationEventPublisher(publisher);
    container2.afterPropertiesSet();
    Log containerLogger = spy(TestUtils.getPropertyValue(container2, "logger", Log.class));
    doReturn(true).when(containerLogger).isWarnEnabled();
    new DirectFieldAccessor(container2).setPropertyValue("logger", containerLogger);
    container2.start();
    for (int i = 0; i < 1000; i++) {
        template.convertAndSend(queue.getName(), i + "foo");
    }
    assertTrue(latch1.await(10, TimeUnit.SECONDS));
    assertEquals(1000, latch2.getCount());
    container1.stop();
    // container 2 should recover and process the next batch of messages
    for (int i = 0; i < 1000; i++) {
        template.convertAndSend(queue.getName(), i + "foo");
    }
    assertTrue(latch2.await(10, TimeUnit.SECONDS));
    container2.stop();
    ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
    verify(logger, atLeastOnce()).info(captor.capture());
    assertThat(captor.getAllValues(), contains(containsString("exclusive")));
    ArgumentCaptor<ListenerContainerConsumerFailedEvent> eventCaptor = ArgumentCaptor
            .forClass(ListenerContainerConsumerFailedEvent.class);
    verify(publisher).publishEvent(eventCaptor.capture());
    ListenerContainerConsumerFailedEvent event = eventCaptor.getValue();
    assertEquals("Consumer raised exception, attempting restart", event.getReason());
    assertFalse(event.isFatal());
    assertThat(event.getThrowable(), instanceOf(AmqpIOException.class));
    verify(containerLogger).warn(any());
}

From source file:org.commonjava.util.partyline.JoinableFileManagerTest.java

@Test
public void concurrentWriteAndRead_CleanupFileEntryOnLastClose() throws Exception {
    String src = "This is a test";

    File f = temp.newFile();/*from  w ww.  j  a v  a 2 s .co  m*/
    FileUtils.write(f, src);

    int count = 2;
    CountDownLatch writing = new CountDownLatch(count);
    CountDownLatch reading = new CountDownLatch(count);
    CountDownLatch end = new CountDownLatch(count);

    Logger logger = LoggerFactory.getLogger(getClass());
    ExecutorService executor = Executors.newCachedThreadPool();
    executor.execute(() -> {
        logger.info("Starting write: {}", f);
        try (OutputStream out = mgr.openOutputStream(f)) {
            logger.info("Signaling write starting: {}", f);
            writing.countDown();

            IOUtils.write(src, out);

            logger.info("Waiting for read to start...");
            reading.await(1, TimeUnit.SECONDS);
        } catch (IOException e) {
            e.printStackTrace();
            fail("Failed to write: " + f);
        } catch (InterruptedException e) {
            e.printStackTrace();
            fail("Write Interrupted!");
        } finally {
            end.countDown();
        }
    });

    executor.execute(() -> {
        logger.info("Signaling thread: {} has started", Thread.currentThread().getName());
        writing.countDown();
        try {
            logger.info("Waiting for other thread(s) to written...");
            writing.await(1, TimeUnit.SECONDS);

            assertThat("Threads did not written correctly!", writing.getCount(), equalTo(0L));

            logger.info("Opening: {}", f);

            try (InputStream in = mgr.openInputStream(f)) {
                logger.info("Signaling that reading has begun: {}", f);
                reading.countDown();
                assertThat(IOUtils.toString(in), equalTo(src));
            } catch (IOException e) {
                e.printStackTrace();
                fail("Cannot open: " + f);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            fail("Interrupted");
        } finally {
            logger.info("Signaling thread: {} has ended", Thread.currentThread().getName());
            end.countDown();
        }
    });

    logger.info("Waiting for end of threads");
    end.await(5, TimeUnit.SECONDS);

    assertThat("Threads did not end correctly!", end.getCount(), equalTo(0L));

    AtomicInteger counter = new AtomicInteger(0);
    mgr.getFileTree().forAll(entry -> true, entry -> counter.incrementAndGet());

    assertThat("FileEntry instance was not removed after closing!", counter.get(), equalTo(0));
}

From source file:org.apache.sshd.common.forward.PortForwardingLoadTest.java

@Test
public void testForwardingOnLoad() throws Exception {
    //        final String path = "/history/recent/troubles/";
    //        final String host = "www.bbc.co.uk";
    //        final String path = "";
    //        final String host = "www.bahn.de";
    final String path = "";
    final String host = TEST_LOCALHOST;
    final int nbThread = 2;
    final int nbDownloads = 2;
    final int nbLoops = 2;

    StringBuilder resp = new StringBuilder();
    resp.append("<html><body>\n");
    for (int i = 0; i < 1000; i++) {
        resp.append("0123456789\n");
    }// w w  w  . j av  a2 s.  com
    resp.append("</body></html>\n");
    final StringBuilder sb = new StringBuilder();
    sb.append("HTTP/1.1 200 OK").append('\n');
    sb.append("Content-Type: text/HTML").append('\n');
    sb.append("Content-Length: ").append(resp.length()).append('\n');
    sb.append('\n');
    sb.append(resp);
    NioSocketAcceptor acceptor = new NioSocketAcceptor();
    acceptor.setHandler(new IoHandlerAdapter() {
        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            session.write(IoBuffer.wrap(sb.toString().getBytes(StandardCharsets.UTF_8)));
        }
    });
    acceptor.setReuseAddress(true);
    acceptor.bind(new InetSocketAddress(0));
    final int port = acceptor.getLocalAddress().getPort();

    Session session = createSession();
    try {
        final int forwardedPort1 = session.setPortForwardingL(0, host, port);
        final int forwardedPort2 = Utils.getFreePort();
        session.setPortForwardingR(forwardedPort2, TEST_LOCALHOST, forwardedPort1);
        outputDebugMessage("URL: http://localhost %s", forwardedPort2);

        final CountDownLatch latch = new CountDownLatch(nbThread * nbDownloads * nbLoops);
        final Thread[] threads = new Thread[nbThread];
        final List<Throwable> errors = new CopyOnWriteArrayList<>();
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(getCurrentTestName() + "[" + i + "]") {
                @Override
                public void run() {
                    for (int j = 0; j < nbLoops; j++) {
                        final MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
                        final HttpClient client = new HttpClient(mgr);
                        client.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost(100);
                        client.getHttpConnectionManager().getParams().setMaxTotalConnections(1000);
                        for (int i = 0; i < nbDownloads; i++) {
                            try {
                                checkHtmlPage(client, new URL("http://localhost:" + forwardedPort2 + path));
                            } catch (Throwable e) {
                                errors.add(e);
                            } finally {
                                latch.countDown();
                                System.err.println("Remaining: " + latch.getCount());
                            }
                        }
                        mgr.shutdown();
                    }
                }
            };
        }
        for (Thread thread : threads) {
            thread.start();
        }
        latch.await();
        for (Throwable t : errors) {
            t.printStackTrace();
        }
        assertEquals(0, errors.size());
    } finally {
        session.disconnect();
    }
}

From source file:org.apache.awf.web.SystemTest.java

@Test
public void simpleConcurrentGetRequestTest() {

    int nThreads = 8;
    int nRequests = 2048;
    final CountDownLatch latch = new CountDownLatch(nRequests);
    ExecutorService executor = Executors.newFixedThreadPool(nThreads);

    for (int i = 1; i <= nRequests; i++) {
        executor.submit(new Runnable() {

            @Override//  w  ww  .  java 2  s  .  co m
            public void run() {
                try {
                    doSimpleGetRequest();
                    latch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });
    }
    try {
        latch.await(15 * 1000, TimeUnit.MILLISECONDS); // max wait time
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (latch.getCount() != 0) {
        assertTrue("Did not finish " + nRequests + " # of requests", false);
    }
}

From source file:org.apache.awf.web.SystemTest.java

@Test
public void timeoutTest() throws InterruptedException {

    long now = System.currentTimeMillis();
    final CountDownLatch latch = new CountDownLatch(5);
    final AsyncCallback cb = new AsyncCallback() {

        @Override//  w  w w . j  a  va  2 s . c o  m
        public void onCallback() {
            latch.countDown();
        }

    };

    Timeout t1 = new Timeout(now + 1000, cb);
    Timeout t2 = new Timeout(now + 1200, cb);
    Timeout t3 = new Timeout(now + 1400, cb);
    Timeout t4 = new Timeout(now + 1600, cb);
    Timeout t5 = new Timeout(now + 1800, cb);
    IOLoop.INSTANCE.addTimeout(t1);
    IOLoop.INSTANCE.addTimeout(t2);
    IOLoop.INSTANCE.addTimeout(t3);
    IOLoop.INSTANCE.addTimeout(t4);
    IOLoop.INSTANCE.addTimeout(t5);

    latch.await(5 * 1000, TimeUnit.MILLISECONDS);
    assertTrue(latch.getCount() == 0);
}

From source file:dendroscope.autumn.hybridnumber.ComputeHybridNumber.java

/**
 * recursively compute the hybrid number
 *
 * @param root1/*from w  w w.  ja v a2  s  . c  om*/
 * @param root2
 * @param isReduced       @return hybrid number
 * @param retry
 * @param topLevel
 * @param scoreAbove
 * @param additionalAbove
 */
private int computeHybridNumberRec(final Root root1, final Root root2, boolean isReduced,
        Integer previousHybrid, BitSet retry, final boolean topLevel, final int scoreAbove,
        final ValuesList additionalAbove) throws IOException, CanceledException {
    if (System.currentTimeMillis() > nextTime) {
        synchronized (progressListener) {
            nextTime += waitTime;
            waitTime *= 1.5;
            progressListener.incrementProgress();
        }
    } else
        progressListener.checkForCancel();

    // System.err.println("computeHybridNumberRec: tree1=" + Basic.toString(root1.getTaxa()) + " tree2=" + Basic.toString(root2.getTaxa()));
    // root1.reorderSubTree();
    //  root2.reorderSubTree();
    if (checking) {
        root1.checkTree();
        root2.checkTree();
    }

    BitSet taxa = root1.getTaxa();

    String key = root1.toStringTreeSparse() + root2.toStringTreeSparse();
    // System.err.println("Key: "+key);
    Integer value;
    synchronized (lookupTable) {
        value = (Integer) lookupTable.get(key);
        if (value != null)
            return value;
    }

    if (!root2.getTaxa().equals(taxa))
        throw new RuntimeException("Unequal taxon sets: X=" + Basic.toString(root1.getTaxa()) + " vs "
                + Basic.toString(root2.getTaxa()));
    if (!isReduced) {
        switch (SubtreeReduction.apply(root1, root2, null)) {
        case ISOMORPHIC:
            synchronized (lookupTable) {
                lookupTable.put(key, 0);
            }
            if (topLevel) {
                bestScore.lowerTo(0);
                progressListener.setSubtask("Best score: " + bestScore);
            }
            return 0; // two trees are isomorphic, no hybrid node needed
        case REDUCED: // a reduction was performed, cannot maintain lexicographical ordering in removal loop below
            previousHybrid = null;
            break;
        case IRREDUCIBLE:
            break;
        }

        Single<Integer> placeHolderTaxa = new Single<Integer>();
        final Pair<Root, Root> clusterTrees = ClusterReduction.apply(root1, root2, placeHolderTaxa);
        final boolean retryTop = false && (previousHybrid != null && placeHolderTaxa.get() < previousHybrid);
        // if the taxa involved in the cluster reduction come before the previously removed hybrid, do full retry
        // retryTop doesn't work
        final BitSet fRetry = retry;

        if (clusterTrees != null) // will perform cluster-reduction
        {
            final Value score1 = new Value(0);
            final Value score2 = new Value(1); // because the cluster could not be reduced using an subtree reduction, can assume that we will need one reticulation for this

            final boolean verbose = ProgramProperties.get("verbose-HL-parallel", false);
            if (verbose)
                System.err.println("Starting parallel loop");

            final CountDownLatch countDownLatch = new CountDownLatch(2);
            final Integer fPrevious = previousHybrid;

            // setup task:
            final Task task1 = new Task(); // first of two cluster-reduction tasks
            task1.setRunnable(new Runnable() {
                public void run() {
                    try {
                        if (verbose) {
                            System.err.println("Launching thread on cluster-reduction");
                            System.err
                                    .println("Active threads " + scheduledThreadPoolExecutor.getActiveCount());
                        }
                        final ValuesList additionalAbove1 = additionalAbove.copyWithAdditionalElement(score2);
                        if (scoreAbove + additionalAbove1.sum() < bestScore.get()) {
                            int h = computeHybridNumberRec(root1, root2, false, fPrevious, fRetry, false,
                                    scoreAbove, additionalAbove1);
                            score1.set(h);
                        } else {
                            score1.set(LARGE);
                        }
                        additionalAbove1.clear();
                    } catch (Exception ex) {
                        while (countDownLatch.getCount() > 0)
                            countDownLatch.countDown();
                    }
                    countDownLatch.countDown();
                }
            });

            final Task task2 = new Task(); // second of two cluster-reduction tasks
            task2.setRunnable(new Runnable() {
                public void run() {
                    try {
                        if (verbose) {
                            System.err.println("Launching thread on cluster-reduction");
                            System.err
                                    .println("Active threads " + scheduledThreadPoolExecutor.getActiveCount());
                        }
                        final ValuesList additionalAbove2 = additionalAbove.copyWithAdditionalElement(score1);
                        if (scoreAbove + additionalAbove2.sum() < bestScore.get()) {
                            int h = computeHybridNumberRec(clusterTrees.getFirst(), clusterTrees.getSecond(),
                                    true, fPrevious, fRetry, false, scoreAbove, additionalAbove2);
                            score2.set(h);
                        } else {
                            score2.set(LARGE);
                        }
                        additionalAbove2.clear();
                    } catch (Exception ex) {
                        while (countDownLatch.getCount() > 0)
                            countDownLatch.countDown();
                    }
                    countDownLatch.countDown();
                }
            });

            // start a task in this thread
            scheduledThreadPoolExecutor.execute(task1);
            task2.run();
            task1.run(); // try to run task1 in current thread if it hasn't yet started execution. If the task is already running or has completed, will simply return

            try {
                if (verbose)
                    System.err.println("waiting...");
                // wait until all tasks have completed
                countDownLatch.await();
                if (verbose)
                    System.err.println("done");
            } catch (InterruptedException e) {
                Basic.caught(e);
            }

            clusterTrees.getFirst().deleteSubTree();
            clusterTrees.getSecond().deleteSubTree();

            int total = scoreAbove + additionalAbove.sum() + score1.get() + score2.get();

            if (topLevel && (total < bestScore.get())) // score above will be zero, but put this here anyway to avoid confusion
            {
                bestScore.lowerTo(total);
                progressListener.setSubtask("Current best score: " + bestScore);
            }

            synchronized (lookupTable) {
                Integer old = (Integer) lookupTable.get(key);
                if (old == null || total < old)
                    lookupTable.put(key, total);
            }
            return score1.get() + score2.get();
        }
    }

    List<Root> leaves1 = root1.getAllLeaves();

    if (leaves1.size() <= 2) // try 2 rather than one...
    {
        return 0;
    }

    final boolean verbose = ProgramProperties.get("verbose-HL-parallel", false);
    if (verbose)
        System.err.println("Starting parallel loop");

    final CountDownLatch countDownLatch = new CountDownLatch(leaves1.size());

    final Value bestSubH = new Value(LARGE);

    // schedule all tasks to be performed
    final ConcurrentLinkedQueue<Task> queue = new ConcurrentLinkedQueue<Task>();

    for (Node leaf2remove : leaves1) {
        final BitSet taxa2remove = ((Root) leaf2remove).getTaxa();

        if (previousHybrid == null || previousHybrid < taxa2remove.nextSetBit(0)) {

            if (scoreAbove + additionalAbove.sum() + 1 >= bestScore.get())
                return LARGE; // other thread has found a better result, abort

            // setup task:
            final Task task = new Task();
            task.setRunnable(new Runnable() {
                public void run() {
                    try {
                        if (verbose) {
                            System.err.println("Launching thread on " + Basic.toString(taxa2remove));
                            System.err
                                    .println("Active threads " + scheduledThreadPoolExecutor.getActiveCount());
                        }
                        queue.remove(task);
                        if (scoreAbove + additionalAbove.sum() + 1 < bestScore.get()) {
                            Root tree1X = CopyWithTaxaRemoved.apply(root1, taxa2remove);
                            Root tree2X = CopyWithTaxaRemoved.apply(root2, taxa2remove);

                            Refine.apply(tree1X, tree2X);

                            int scoreBelow = computeHybridNumberRec(tree1X, tree2X, false,
                                    taxa2remove.nextSetBit(0), null, false, scoreAbove + 1, additionalAbove)
                                    + 1;

                            if (topLevel && scoreBelow < bestScore.get()) {
                                bestScore.lowerTo(scoreBelow);
                                progressListener.setSubtask("Current best score: " + bestScore);
                            }

                            synchronized (bestSubH) {
                                if (scoreBelow < bestSubH.get())
                                    bestSubH.set(scoreBelow);
                            }

                            tree1X.deleteSubTree();
                            tree2X.deleteSubTree();
                        }
                    } catch (Exception ex) {
                        while (countDownLatch.getCount() > 0)
                            countDownLatch.countDown();
                    }
                    countDownLatch.countDown();
                }
            });
            queue.add(task);
        } else // no task for this item, count down
        {
            countDownLatch.countDown();
            progressListener.checkForCancel();
        }
    }
    // grab one task for the current thread:
    Task taskForCurrentThread = queue.size() > 0 ? queue.poll() : null;
    // launch all others in the executor
    for (Task task : queue)
        scheduledThreadPoolExecutor.execute(task);

    // start a task in this thread
    if (taskForCurrentThread != null)
        taskForCurrentThread.run();

    // try to run other tasks from the queue. Note that any task that is already running will return immediately
    while (queue.size() > 0) {
        Task task = queue.poll();
        if (task != null)
            task.run();
    }
    try {
        if (verbose)
            System.err.println("waiting...");
        // wait until all tasks have completed
        countDownLatch.await();

        if (verbose)
            System.err.println("done");
    } catch (InterruptedException e) {
        Basic.caught(e);
        return LARGE;
    }
    // return the best value
    synchronized (lookupTable) {
        Integer old = (Integer) lookupTable.get(key);
        if (old == null || old > bestSubH.get())
            lookupTable.put(key, bestSubH.get());
    }
    return bestSubH.get();
}