/* * Copyright (c) 2011-2013 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.test.core; import io.vertx.core.AbstractVerticle; import io.vertx.core.Context; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.WorkerExecutor; import org.junit.Test; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */ public class NamedWorkerPoolTest extends VertxTestBase { @Test public void testThread() { String poolName = TestUtils.randomAlphaString(10); WorkerExecutor worker = vertx.createSharedWorkerExecutor(poolName); AtomicBoolean onVertxThread = new AtomicBoolean(); AtomicBoolean onWorkerThread = new AtomicBoolean(); AtomicBoolean onEventLoopThread = new AtomicBoolean(); AtomicReference<String> threadName = new AtomicReference<>(); worker.executeBlocking(fut -> { onVertxThread.set(Context.isOnVertxThread()); onWorkerThread.set(Context.isOnWorkerThread()); onEventLoopThread.set(Context.isOnEventLoopThread()); threadName.set(Thread.currentThread().getName()); fut.complete(null); }, ar -> { testComplete(); }); // Use regular assertions because the thread name does not start with "vert.x-" // and it confuses the VertxTestBase asserts assertWaitUntil(() -> threadName.get() != null); assertTrue(onVertxThread.get()); assertTrue(onWorkerThread.get()); assertFalse(onEventLoopThread.get()); assertTrue(threadName.get().startsWith(poolName + "-")); } @Test public void testOrdered() { String poolName = "vert.x-" + TestUtils.randomAlphaString(10); WorkerExecutor worker = vertx.createSharedWorkerExecutor(poolName); int num = 1000; AtomicReference<Thread> t = new AtomicReference<>(); CountDownLatch submitted = new CountDownLatch(1); Context ctx = vertx.getOrCreateContext(); ctx.runOnContext(v -> { for (int i = 0;i < num;i++) { boolean first = i == 0; boolean last = i == num - 1; worker.executeBlocking(fut -> { if (first) { try { awaitLatch(submitted); } catch (InterruptedException e) { fail(e); return; } assertNull(t.get()); t.set(Thread.currentThread()); } else { assertEquals(t.get(), Thread.currentThread()); } assertTrue(Thread.currentThread().getName().startsWith(poolName + "-")); fut.complete(null); }, ar -> { if (last) { testComplete(); } }); } submitted.countDown(); }); await(); } @Test public void testUnordered() throws Exception { String poolName = "vert.x-" + TestUtils.randomAlphaString(10); int num = 5; waitFor(num); WorkerExecutor worker = vertx.createSharedWorkerExecutor(poolName); CountDownLatch latch1 = new CountDownLatch(num); CountDownLatch latch2 = new CountDownLatch(1); Context ctx = vertx.getOrCreateContext(); ctx.runOnContext(v -> { for (int i = 0; i < num; i++) { worker.executeBlocking(fut -> { latch1.countDown(); try { awaitLatch(latch2); } catch (InterruptedException e) { fail(e); return; } assertTrue(Thread.currentThread().getName().startsWith(poolName + "-")); fut.complete(null); }, false, ar -> { complete(); }); } }); awaitLatch(latch1); latch2.countDown(); await(); } @Test public void testPoolSize() throws Exception { String poolName = "vert.x-" + TestUtils.randomAlphaString(10); int poolSize = 5; waitFor(poolSize); WorkerExecutor worker = vertx.createSharedWorkerExecutor(poolName, poolSize); CountDownLatch latch1 = new CountDownLatch(poolSize * 100); Set<String> names = Collections.synchronizedSet(new HashSet<>()); for (int i = 0;i < poolSize * 100;i++) { worker.executeBlocking(fut -> { names.add(Thread.currentThread().getName()); latch1.countDown(); }, false, ar -> { }); } awaitLatch(latch1); assertEquals(5, names.size()); } @Test public void testCloseWorkerPool() throws Exception { String poolName = "vert.x-" + TestUtils.randomAlphaString(10); AtomicReference<Thread> thread = new AtomicReference<>(); WorkerExecutor worker1 = vertx.createSharedWorkerExecutor(poolName); WorkerExecutor worker2 = vertx.createSharedWorkerExecutor(poolName); worker1.executeBlocking(fut -> { thread.set(Thread.currentThread()); }, ar -> { }); assertWaitUntil(() -> thread.get() != null); worker1.close(); assertNotSame(thread.get().getState(), Thread.State.TERMINATED); worker2.close(); assertWaitUntil(() -> thread.get().getState() == Thread.State.TERMINATED); } @Test public void testDestroyWorkerPoolWhenVerticleUndeploys() throws Exception { String poolName = "vert.x-" + TestUtils.randomAlphaString(10); AtomicReference<Thread> thread = new AtomicReference<>(); CompletableFuture<String> deploymentIdRef = new CompletableFuture<>(); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { WorkerExecutor pool = vertx.createSharedWorkerExecutor(poolName); pool.executeBlocking(fut -> { thread.set(Thread.currentThread()); }, ar -> { }); } }, onSuccess(deploymentIdRef::complete)); assertWaitUntil(() -> thread.get() != null); String deploymentId = deploymentIdRef.get(20, TimeUnit.SECONDS); vertx.undeploy(deploymentId, onSuccess(v -> {})); assertWaitUntil(() -> thread.get().getState() == Thread.State.TERMINATED); } @Test public void testDeployUsingNamedPool() throws Exception { AtomicReference<Thread> thread = new AtomicReference<>(); String poolName = "vert.x-" + TestUtils.randomAlphaString(10); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { vertx.executeBlocking(fut -> { thread.set(Thread.currentThread()); assertTrue(Context.isOnVertxThread()); assertTrue(Context.isOnWorkerThread()); assertFalse(Context.isOnEventLoopThread()); assertTrue(Thread.currentThread().getName().startsWith(poolName + "-")); fut.complete(); }, onSuccess(v -> { vertx.undeploy(context.deploymentID()); })); } }, new DeploymentOptions().setWorkerPoolName(poolName), onSuccess(v -> {})); assertWaitUntil(() -> thread.get() != null && thread.get().getState() == Thread.State.TERMINATED); } @Test public void testDeployWorkerUsingNamedPool() throws Exception { AtomicReference<Thread> thread = new AtomicReference<>(); AtomicReference<String> deployment = new AtomicReference<>(); String poolName = "vert.x-" + TestUtils.randomAlphaString(10); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { thread.set(Thread.currentThread()); assertTrue(Context.isOnVertxThread()); assertTrue(Context.isOnWorkerThread()); assertFalse(Context.isOnEventLoopThread()); assertTrue(Thread.currentThread().getName().startsWith(poolName + "-")); context.runOnContext(v -> { vertx.undeploy(context.deploymentID()); }); } }, new DeploymentOptions().setWorker(true).setWorkerPoolName(poolName), onSuccess(deployment::set)); assertWaitUntil(() -> thread.get() != null && thread.get().getState() == Thread.State.TERMINATED); } @Test public void testCloseWorkerPoolsWhenVertxCloses() { Vertx vertx = Vertx.vertx(); WorkerExecutor exec = vertx.createSharedWorkerExecutor("vert.x-123"); vertx.close(v -> { try { vertx.executeBlocking(fut -> fail(), ar -> fail()); fail(); } catch (RejectedExecutionException ignore) { } try { exec.executeBlocking(fut -> fail(), ar -> fail()); fail(); } catch (RejectedExecutionException ignore) { } // Check we can still close exec.close(); testComplete(); }); await(); } }