/* * Copyright 2014 Red Hat, Inc. * * 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.Future; import io.vertx.core.Vertx; import io.vertx.core.WorkerExecutor; import io.vertx.core.impl.ContextInternal; import io.vertx.core.impl.TaskQueue; import org.junit.Test; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /** * @author <a href="http://tfox.org">Tim Fox</a> */ public class ContextTest extends VertxTestBase { @Test public void testRunOnContext() throws Exception { vertx.runOnContext(v -> { Thread th = Thread.currentThread(); Context ctx = Vertx.currentContext(); ctx.runOnContext(v2 -> { assertEquals(th, Thread.currentThread()); // Execute it a few times to make sure it returns same context for (int i = 0; i < 10; i++) { Context c = Vertx.currentContext(); assertEquals(ctx, c); } // And simulate a third party thread - e.g. a 3rd party async library wishing to return a result on the // correct context new Thread() { public void run() { ctx.runOnContext(v3 -> { assertEquals(th, Thread.currentThread()); assertEquals(ctx, Vertx.currentContext()); testComplete(); }); } }.start(); }); }); await(); } @Test public void testNoContext() throws Exception { assertNull(Vertx.currentContext()); } class SomeObject { } @Test public void testPutGetRemoveData() throws Exception { SomeObject obj = new SomeObject(); vertx.runOnContext(v -> { Context ctx = Vertx.currentContext(); ctx.put("foo", obj); ctx.runOnContext(v2 -> { assertEquals(obj, ctx.get("foo")); assertTrue(ctx.remove("foo")); ctx.runOnContext(v3 -> { assertNull(ctx.get("foo")); testComplete(); }); }); }); await(); } @Test public void testGettingContextContextUnderContextAnotherInstanceShouldReturnDifferentContext() throws Exception { Vertx other = vertx(); Context context = vertx.getOrCreateContext(); context.runOnContext(v -> { Context otherContext = other.getOrCreateContext(); assertNotSame(otherContext, context); testComplete(); }); await(); } @Test public void testExecuteOrderedBlocking() throws Exception { Context context = vertx.getOrCreateContext(); context.executeBlocking(f -> { assertTrue(Context.isOnWorkerThread()); f.complete(1 + 2); }, r -> { assertTrue(Context.isOnEventLoopThread()); assertEquals(r.result(), 3); testComplete(); }); await(); } @Test public void testExecuteUnorderedBlocking() throws Exception { Context context = vertx.getOrCreateContext(); context.executeBlocking(f -> { assertTrue(Context.isOnWorkerThread()); f.complete(1 + 2); }, false, r -> { assertTrue(Context.isOnEventLoopThread()); assertEquals(r.result(), 3); testComplete(); }); await(); } @Test public void testEventLoopExecuteFromIo() throws Exception { ContextInternal eventLoopContext = (ContextInternal) vertx.getOrCreateContext(); // Check from other thread try { eventLoopContext.executeFromIO(this::fail); fail(); } catch (IllegalStateException expected) { } // Check from event loop thread eventLoopContext.nettyEventLoop().execute(() -> { // Should not be set yet assertNull(Vertx.currentContext()); Thread vertxThread = Thread.currentThread(); AtomicBoolean nested = new AtomicBoolean(true); eventLoopContext.executeFromIO(() -> { assertTrue(nested.get()); assertSame(eventLoopContext, Vertx.currentContext()); assertSame(vertxThread, Thread.currentThread()); }); nested.set(false); testComplete(); }); await(); } @Test public void testWorkerExecuteFromIo() throws Exception { AtomicReference<ContextInternal> workerContext = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { workerContext.set((ContextInternal) context); latch.countDown(); } }, new DeploymentOptions().setWorker(true)); awaitLatch(latch); workerContext.get().nettyEventLoop().execute(() -> { assertNull(Vertx.currentContext()); workerContext.get().executeFromIO(() -> { assertSame(workerContext.get(), Vertx.currentContext()); assertTrue(Context.isOnWorkerThread()); testComplete(); }); }); await(); } @Test public void testContextExceptionHandler() { RuntimeException failure = new RuntimeException(); Context context = vertx.getOrCreateContext(); context.exceptionHandler(err -> { assertSame(context, Vertx.currentContext()); assertSame(failure, err); testComplete(); }); context.runOnContext(v -> { throw failure; }); await(); } @Test public void testContextExceptionHandlerFailing() { RuntimeException failure = new RuntimeException(); Context context = vertx.getOrCreateContext(); AtomicInteger count = new AtomicInteger(); context.exceptionHandler(err -> { if (count.getAndIncrement() == 0) { throw new RuntimeException(); } else { assertSame(failure, err); testComplete(); } }); context.runOnContext(v -> { throw new RuntimeException(); }); context.runOnContext(v -> { throw failure; }); await(); } @Test public void testDefaultContextExceptionHandler() { RuntimeException failure = new RuntimeException(); Context context = vertx.getOrCreateContext(); vertx.exceptionHandler(err -> { assertSame(failure, err); testComplete(); }); context.runOnContext(v -> { throw failure; }); await(); } @Test public void testExceptionHandlerOnDeploymentAsyncResultHandlerFailure() { RuntimeException failure = new RuntimeException(); Context ctx = vertx.getOrCreateContext(); ctx.exceptionHandler(err -> { assertSame(failure, err); testComplete(); }); ctx.runOnContext(v -> { vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { } }, ar -> { throw failure; }); }); await(); } @Test public void testExceptionHandlerOnAsyncDeploymentAsyncResultHandlerFailure() { RuntimeException failure = new RuntimeException(); Context ctx = vertx.getOrCreateContext(); ctx.exceptionHandler(err -> { assertSame(failure, err); testComplete(); }); ctx.runOnContext(v -> { vertx.deployVerticle(new AbstractVerticle() { @Override public void start(Future<Void> startFuture) throws Exception { context.runOnContext(startFuture::complete); } }, ar -> { throw failure; }); }); await(); } @Test public void testVerticleUseDifferentExecuteBlockingOrderedExecutor() throws Exception { testVerticleUseDifferentOrderedExecutor(false); } @Test public void testWorkerVerticleUseDifferentExecuteBlockingOrderedExecutor() throws Exception { testVerticleUseDifferentOrderedExecutor(true); } private void testVerticleUseDifferentOrderedExecutor(boolean worker) throws Exception { waitFor(2); CountDownLatch latch1 = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(1); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { vertx.executeBlocking(fut -> { latch1.countDown(); try { awaitLatch(latch2); fut.complete(); } catch (InterruptedException e) { fut.fail(e); } }, ar -> { assertTrue(ar.succeeded()); complete(); }); } }, new DeploymentOptions().setWorker(worker)); awaitLatch(latch1); CountDownLatch latch3 = new CountDownLatch(1); vertx.deployVerticle(new AbstractVerticle() { @Override public void start() throws Exception { vertx.executeBlocking(fut -> { latch3.countDown(); fut.complete(); }, ar -> { assertTrue(ar.succeeded()); complete(); }); } }, new DeploymentOptions().setWorker(worker)); awaitLatch(latch3); latch2.countDown(); await(); } @Test public void testInternalExecuteBlockingWithQueue() { ContextInternal context = (ContextInternal) vertx.getOrCreateContext(); TaskQueue[] queues = new TaskQueue[] { new TaskQueue(), new TaskQueue()}; AtomicReference<Thread>[] current = new AtomicReference[queues.length]; waitFor(queues.length); for (int i = 0;i < queues.length;i++) { current[i] = new AtomicReference<>(); } CyclicBarrier barrier = new CyclicBarrier(queues.length); int numTasks = 10; for (int i = 0;i < numTasks;i++) { int ival = i; for (int j = 0;j < queues.length;j++) { int jval = j; context.executeBlocking(fut -> { if (ival == 0) { current[jval].set(Thread.currentThread()); } else { assertSame(Thread.currentThread(), current[jval].get()); } try { barrier.await(); } catch (Exception e) { fail(e); } if (ival == numTasks - 1) { complete(); } }, queues[j], ar -> {}); } } await(); } }