Search in sources :

Example 1 with Executor

use of io.servicetalk.concurrent.api.Executor in project servicetalk by apple.

the class AbstractPublisherOffloadingTest method testOffloading.

protected int testOffloading(BiFunction<Publisher<String>, Executor, Publisher<String>> offloadingFunction, TerminalOperation terminal) throws InterruptedException {
    Runnable appCode = () -> {
        try {
            // Insert a custom value into AsyncContext map
            AsyncContext.put(ASYNC_CONTEXT_CUSTOM_KEY, ASYNC_CONTEXT_VALUE);
            capture(CaptureSlot.APP);
            // Add thread recording test points
            final Publisher<String> original = testPublisher.liftSync((PublisherOperator<? super String, String>) subscriber -> {
                capture(CaptureSlot.OFFLOADED_SUBSCRIBE);
                return subscriber;
            }).beforeOnSubscribe(cancellable -> capture(CaptureSlot.ORIGINAL_ON_SUBSCRIBE)).beforeRequest((requested) -> capture(CaptureSlot.OFFLOADED_REQUEST)).beforeOnNext((item) -> capture(CaptureSlot.ORIGINAL_ON_NEXT)).beforeFinally(new TerminalSignalConsumer() {

                @Override
                public void onComplete() {
                    capture(CaptureSlot.ORIGINAL_ON_COMPLETE);
                }

                @Override
                public void onError(final Throwable throwable) {
                    capture(CaptureSlot.ORIGINAL_ON_ERROR);
                }

                @Override
                public void cancel() {
                    capture(CaptureSlot.OFFLOADED_CANCEL);
                }
            });
            // Perform offloading and add more thread recording test points
            Publisher<String> offloaded = offloadingFunction.apply(original, testExecutor.executor()).liftSync((PublisherOperator<? super String, String>) subscriber -> {
                capture(CaptureSlot.ORIGINAL_SUBSCRIBE);
                return subscriber;
            }).beforeOnSubscribe(cancellable -> capture(CaptureSlot.OFFLOADED_ON_SUBSCRIBE)).beforeRequest((requested) -> capture(CaptureSlot.ORIGINAL_REQUEST)).beforeOnNext((item) -> capture(CaptureSlot.OFFLOADED_ON_NEXT)).beforeFinally(new TerminalSignalConsumer() {

                @Override
                public void onComplete() {
                    capture(CaptureSlot.OFFLOADED_ON_COMPLETE);
                }

                @Override
                public void onError(final Throwable throwable) {
                    capture(CaptureSlot.OFFLOADED_ON_ERROR);
                }

                @Override
                public void cancel() {
                    capture(CaptureSlot.ORIGINAL_CANCEL);
                }
            });
            // subscribe
            toSource(offloaded).subscribe(testSubscriber);
            assertThat("Unexpected tasks " + testExecutor.executor().queuedTasksPending(), testExecutor.executor().queuedTasksPending(), lessThan(2));
            if (1 == testExecutor.executor().queuedTasksPending()) {
                // execute offloaded subscribe
                testExecutor.executor().executeNextTask();
            }
            PublisherSource.Subscription subscription = testSubscriber.awaitSubscription();
            assertThat("No Subscription", subscription, notNullValue());
            testPublisher.awaitSubscribed();
            assertThat("Source is not subscribed", testPublisher.isSubscribed());
            assertThat("Thread was interrupted", !Thread.currentThread().isInterrupted());
            // generate demand
            subscription.request(MAX_VALUE);
            assertThat("Unexpected tasks " + testExecutor.executor().queuedTasksPending(), testExecutor.executor().queuedTasksPending(), lessThan(2));
            if (1 == testExecutor.executor().queuedTasksPending()) {
                // execute offloaded request
                testExecutor.executor().executeNextTask();
            }
            testSubscription.awaitRequestN(1);
            // perform terminal
            switch(terminal) {
                case CANCEL:
                    subscription.cancel();
                    break;
                case COMPLETE:
                    testPublisher.onNext(ITEM_VALUE);
                    assertThat("Unexpected tasks " + testExecutor.executor().queuedTasksPending(), testExecutor.executor().queuedTasksPending(), lessThan(2));
                    if (1 == testExecutor.executor().queuedTasksPending()) {
                        // execute offloaded onNext
                        testExecutor.executor().executeNextTask();
                    }
                    String result = testSubscriber.takeOnNext();
                    assertThat("result is unexpected value", result, sameInstance(ITEM_VALUE));
                    testPublisher.onComplete();
                    break;
                case ERROR:
                    testPublisher.onError(DELIBERATE_EXCEPTION);
                    break;
                default:
                    throw new AssertionError("unexpected terminal mode");
            }
            assertThat("Unexpected tasks " + testExecutor.executor().queuedTasksPending(), testExecutor.executor().queuedTasksPending(), lessThan(2));
            if (1 == testExecutor.executor().queuedTasksPending()) {
                // execute offloaded terminal
                testExecutor.executor().executeNextTask();
            }
        } catch (Throwable all) {
            AbstractOffloadingTest.LOGGER.warn("Unexpected throwable", all);
            testSubscriber.onError(all);
        }
    };
    APP_EXECUTOR_EXT.executor().execute(appCode);
    // Ensure we reached the correct terminal condition
    switch(terminal) {
        case CANCEL:
            testSubscription.awaitCancelled();
            break;
        case ERROR:
            Throwable thrown = testSubscriber.awaitOnError();
            assertThat("unexpected exception " + thrown, thrown, sameInstance(DELIBERATE_EXCEPTION));
            break;
        case COMPLETE:
            testSubscriber.awaitOnComplete();
            break;
        default:
            throw new AssertionError("unexpected terminal mode");
    }
    // Ensure that Async Context Map was correctly set during signals
    ContextMap appMap = capturedContexts.captured(CaptureSlot.APP);
    assertThat(appMap, notNullValue());
    ContextMap subscribeMap = capturedContexts.captured(CaptureSlot.ORIGINAL_SUBSCRIBE);
    assertThat(subscribeMap, notNullValue());
    assertThat("Map was shared not copied", subscribeMap, not(sameInstance(appMap)));
    assertThat("Missing custom async context entry ", subscribeMap.get(ASYNC_CONTEXT_CUSTOM_KEY), sameInstance(ASYNC_CONTEXT_VALUE));
    EnumSet<CaptureSlot> checkSlots = EnumSet.complementOf(EnumSet.of(CaptureSlot.APP, CaptureSlot.ORIGINAL_SUBSCRIBE));
    checkSlots.stream().filter(slot -> null != capturedContexts.captured(slot)).forEach(slot -> {
        ContextMap map = capturedContexts.captured(slot);
        assertThat("Context map was not captured", map, is(notNullValue()));
        assertThat("Custom key missing from context map", map.containsKey(ASYNC_CONTEXT_CUSTOM_KEY));
        assertThat("Unexpected context map @ slot " + slot + " : " + map, map, sameInstance(subscribeMap));
    });
    assertThat("Pending offloading", testExecutor.executor().queuedTasksPending(), is(0));
    return testExecutor.executor().queuedTasksExecuted();
}
Also used : CoreMatchers.is(org.hamcrest.CoreMatchers.is) TestPublisherSubscriber(io.servicetalk.concurrent.test.internal.TestPublisherSubscriber) TestPublisher(io.servicetalk.concurrent.api.TestPublisher) Publisher(io.servicetalk.concurrent.api.Publisher) PublisherSource(io.servicetalk.concurrent.PublisherSource) BiFunction(java.util.function.BiFunction) MAX_VALUE(java.lang.Long.MAX_VALUE) CoreMatchers.not(org.hamcrest.CoreMatchers.not) TerminalSignalConsumer(io.servicetalk.concurrent.api.TerminalSignalConsumer) PublisherOperator(io.servicetalk.concurrent.api.PublisherOperator) SourceAdapters.toSource(io.servicetalk.concurrent.api.SourceAdapters.toSource) TestSubscription(io.servicetalk.concurrent.api.TestSubscription) CoreMatchers.notNullValue(org.hamcrest.CoreMatchers.notNullValue) AbstractOffloadingTest(io.servicetalk.concurrent.api.internal.AbstractOffloadingTest) ContextMap(io.servicetalk.context.api.ContextMap) AsyncContext(io.servicetalk.concurrent.api.AsyncContext) Matchers.lessThan(org.hamcrest.Matchers.lessThan) Executor(io.servicetalk.concurrent.api.Executor) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) EnumSet(java.util.EnumSet) CoreMatchers.sameInstance(org.hamcrest.CoreMatchers.sameInstance) TerminalSignalConsumer(io.servicetalk.concurrent.api.TerminalSignalConsumer) PublisherOperator(io.servicetalk.concurrent.api.PublisherOperator) TestPublisher(io.servicetalk.concurrent.api.TestPublisher) Publisher(io.servicetalk.concurrent.api.Publisher) TestSubscription(io.servicetalk.concurrent.api.TestSubscription) ContextMap(io.servicetalk.context.api.ContextMap)

Example 2 with Executor

use of io.servicetalk.concurrent.api.Executor in project servicetalk by apple.

the class AbstractSingleOffloadingTest method testOffloading.

protected int testOffloading(BiFunction<Single<String>, Executor, Single<String>> offloadingFunction, TerminalOperation terminal) throws InterruptedException {
    Runnable appCode = () -> {
        try {
            // Insert a custom value into AsyncContext map
            AsyncContext.put(ASYNC_CONTEXT_CUSTOM_KEY, ASYNC_CONTEXT_VALUE);
            capture(CaptureSlot.APP);
            // Add thread recording test points
            final Single<String> original = testSingle.liftSync((SingleOperator<String, String>) subscriber -> {
                capture(CaptureSlot.OFFLOADED_SUBSCRIBE);
                return subscriber;
            }).beforeOnSubscribe(cancellable -> capture(CaptureSlot.ORIGINAL_ON_SUBSCRIBE)).beforeFinally(new TerminalSignalConsumer() {

                @Override
                public void onComplete() {
                    capture(CaptureSlot.ORIGINAL_ON_COMPLETE);
                }

                @Override
                public void onError(final Throwable throwable) {
                    capture(CaptureSlot.ORIGINAL_ON_ERROR);
                }

                @Override
                public void cancel() {
                    capture(CaptureSlot.OFFLOADED_CANCEL);
                }
            });
            // Perform offloading and add more thread recording test points
            Single<String> offloaded = offloadingFunction.apply(original, testExecutor.executor()).liftSync((SingleOperator<String, String>) subscriber -> {
                capture(CaptureSlot.ORIGINAL_SUBSCRIBE);
                return subscriber;
            }).beforeOnSubscribe(cancellable -> capture(CaptureSlot.OFFLOADED_ON_SUBSCRIBE)).beforeFinally(new TerminalSignalConsumer() {

                @Override
                public void onComplete() {
                    capture(CaptureSlot.OFFLOADED_ON_COMPLETE);
                }

                @Override
                public void onError(final Throwable throwable) {
                    capture(CaptureSlot.OFFLOADED_ON_ERROR);
                }

                @Override
                public void cancel() {
                    capture(CaptureSlot.ORIGINAL_CANCEL);
                }
            });
            // subscribe and generate terminal
            toSource(offloaded).subscribe(testSubscriber);
            assertThat("Unexpected tasks " + testExecutor.executor().queuedTasksPending(), testExecutor.executor().queuedTasksPending(), lessThan(2));
            if (1 == testExecutor.executor().queuedTasksPending()) {
                // execute offloaded subscribe
                testExecutor.executor().executeNextTask();
            }
            Cancellable cancellable = testSubscriber.awaitSubscription();
            assertThat("No Cancellable", cancellable, notNullValue());
            testSingle.awaitSubscribed();
            assertThat("Source is not subscribed", testSingle.isSubscribed());
            assertThat("Thread was interrupted", !Thread.currentThread().isInterrupted());
            switch(terminal) {
                case CANCEL:
                    cancellable.cancel();
                    break;
                case COMPLETE:
                    testSingle.onSuccess(ITEM_VALUE);
                    break;
                case ERROR:
                    testSingle.onError(DELIBERATE_EXCEPTION);
                    break;
                default:
                    throw new AssertionError("unexpected terminal mode");
            }
            assertThat("Unexpected tasks " + testExecutor.executor().queuedTasksPending(), testExecutor.executor().queuedTasksPending(), lessThan(2));
            if (1 == testExecutor.executor().queuedTasksPending()) {
                // execute offloaded terminal
                testExecutor.executor().executeNextTask();
            }
        } catch (Throwable all) {
            AbstractOffloadingTest.LOGGER.warn("Unexpected throwable", all);
            testSubscriber.onError(all);
        }
    };
    APP_EXECUTOR_EXT.executor().execute(appCode);
    // Ensure we reached the correct terminal condition
    switch(terminal) {
        case CANCEL:
            testCancellable.awaitCancelled();
            break;
        case ERROR:
            Throwable thrown = testSubscriber.awaitOnError();
            assertThat("unexpected exception " + thrown, thrown, sameInstance(DELIBERATE_EXCEPTION));
            break;
        case COMPLETE:
            String result = testSubscriber.awaitOnSuccess();
            assertThat("Unexpected result", result, sameInstance(ITEM_VALUE));
            break;
        default:
            throw new AssertionError("unexpected terminal mode");
    }
    // Ensure that Async Context Map was correctly set during signals
    ContextMap appMap = capturedContexts.captured(CaptureSlot.APP);
    assertThat(appMap, notNullValue());
    ContextMap subscribeMap = capturedContexts.captured(CaptureSlot.ORIGINAL_SUBSCRIBE);
    assertThat(subscribeMap, notNullValue());
    assertThat("Map was shared not copied", subscribeMap, not(sameInstance(appMap)));
    assertThat("Missing custom async context entry ", subscribeMap.get(ASYNC_CONTEXT_CUSTOM_KEY), sameInstance(ASYNC_CONTEXT_VALUE));
    EnumSet<CaptureSlot> checkSlots = EnumSet.complementOf(EnumSet.of(CaptureSlot.APP, CaptureSlot.ORIGINAL_SUBSCRIBE));
    checkSlots.stream().filter(slot -> null != capturedContexts.captured(slot)).forEach(slot -> {
        ContextMap map = capturedContexts.captured(slot);
        assertThat("Context map was not captured", map, is(notNullValue()));
        assertThat("Custom key missing from context map", map.containsKey(ASYNC_CONTEXT_CUSTOM_KEY));
        assertThat("Unexpected context map @ slot " + slot + " : " + map, map, sameInstance(subscribeMap));
    });
    // Ensure that all offloading completed.
    assertThat("Offloading pending", testExecutor.executor().queuedTasksPending(), is(0));
    return testExecutor.executor().queuedTasksExecuted();
}
Also used : TerminalSignalConsumer(io.servicetalk.concurrent.api.TerminalSignalConsumer) CoreMatchers.is(org.hamcrest.CoreMatchers.is) TestSingle(io.servicetalk.concurrent.api.TestSingle) Single(io.servicetalk.concurrent.api.Single) BiFunction(java.util.function.BiFunction) CoreMatchers.not(org.hamcrest.CoreMatchers.not) TerminalSignalConsumer(io.servicetalk.concurrent.api.TerminalSignalConsumer) Cancellable(io.servicetalk.concurrent.Cancellable) TestCancellable(io.servicetalk.concurrent.api.TestCancellable) SourceAdapters.toSource(io.servicetalk.concurrent.api.SourceAdapters.toSource) CoreMatchers.notNullValue(org.hamcrest.CoreMatchers.notNullValue) AbstractOffloadingTest(io.servicetalk.concurrent.api.internal.AbstractOffloadingTest) SingleOperator(io.servicetalk.concurrent.api.SingleOperator) ContextMap(io.servicetalk.context.api.ContextMap) AsyncContext(io.servicetalk.concurrent.api.AsyncContext) Matchers.lessThan(org.hamcrest.Matchers.lessThan) Executor(io.servicetalk.concurrent.api.Executor) TestSingleSubscriber(io.servicetalk.concurrent.test.internal.TestSingleSubscriber) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) EnumSet(java.util.EnumSet) CoreMatchers.sameInstance(org.hamcrest.CoreMatchers.sameInstance) SingleOperator(io.servicetalk.concurrent.api.SingleOperator) TestSingle(io.servicetalk.concurrent.api.TestSingle) Single(io.servicetalk.concurrent.api.Single) Cancellable(io.servicetalk.concurrent.Cancellable) TestCancellable(io.servicetalk.concurrent.api.TestCancellable) ContextMap(io.servicetalk.context.api.ContextMap)

Example 3 with Executor

use of io.servicetalk.concurrent.api.Executor in project servicetalk by apple.

the class TimeoutPublisherTest method concurrentTimeoutInvocation.

@Test
void concurrentTimeoutInvocation() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(2);
    AtomicReference<Throwable> causeRef = new AtomicReference<>();
    // In order to simulate concurrent execution, we introduce an Executor that does not respect the delay for the
    // first timer schedule. Internally, we expect the TimeoutPublisher to reschedule the timer. For that we use
    // TestExecutor, which will allow us to advance the time and trigger the actual timeout, which will simulate
    // concurrent execution.
    toSource(publisher.timeout(10, MILLISECONDS, new Executor() {

        private final AtomicInteger timerCount = new AtomicInteger();

        @Override
        public Cancellable schedule(final Runnable task, final long delay, final TimeUnit unit) {
            int count = timerCount.incrementAndGet();
            if (count <= 2) {
                if (count == 1) {
                    try {
                        task.run();
                    } catch (Throwable cause) {
                        causeRef.compareAndSet(null, cause);
                        countDownToZero(latch);
                    }
                    latch.countDown();
                } else {
                    try {
                        try {
                            testExecutor.schedule(task, delay, unit);
                            testExecutor.advanceTimeBy(delay, unit);
                        } catch (Throwable cause) {
                            causeRef.compareAndSet(null, cause);
                            countDownToZero(latch);
                        }
                        latch.countDown();
                    } catch (Throwable cause) {
                        causeRef.compareAndSet(null, cause);
                        countDownToZero(latch);
                    }
                }
            }
            return IGNORE_CANCEL;
        }

        @Override
        public long currentTime(final TimeUnit unit) {
            return testExecutor.currentTime(unit);
        }

        @Override
        public Completable closeAsync() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Completable onClose() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Cancellable execute(final Runnable task) throws RejectedExecutionException {
            throw new UnsupportedOperationException();
        }
    })).subscribe(subscriber);
    latch.await();
    assertNull(causeRef.get());
    assertThat(subscriber.awaitOnError(), instanceOf(TimeoutException.class));
}
Also used : Completable(io.servicetalk.concurrent.api.Completable) Cancellable(io.servicetalk.concurrent.Cancellable) AtomicReference(java.util.concurrent.atomic.AtomicReference) CountDownLatch(java.util.concurrent.CountDownLatch) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) Executor(io.servicetalk.concurrent.api.Executor) DelegatingExecutor(io.servicetalk.concurrent.api.DelegatingExecutor) TestExecutor(io.servicetalk.concurrent.api.TestExecutor) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) TimeUnit(java.util.concurrent.TimeUnit) TimeoutException(java.util.concurrent.TimeoutException) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 4 with Executor

use of io.servicetalk.concurrent.api.Executor in project servicetalk by apple.

the class NettyPipelinedConnectionTest method multiThreadedWritesAllComplete.

@Test
void multiThreadedWritesAllComplete() throws Exception {
    // Avoid using EmbeddedChannel because it is not thread safe. This test writes/reads from multiple threads.
    @SuppressWarnings("unchecked") NettyConnection<Integer, Integer> connection = mock(NettyConnection.class);
    Executor connectionExecutor = Executors.newCachedThreadExecutor();
    try {
        doAnswer((Answer<Completable>) invocation -> {
            Publisher<Integer> writeStream = invocation.getArgument(0);
            return writeStream.ignoreElements().concat(connectionExecutor.submit(() -> {
            }));
        }).when(connection).write(any(), any(), any());
        doAnswer((Answer<Publisher<Integer>>) invocation -> connectionExecutor.submit(() -> {
        }).concat(Publisher.from(1))).when(connection).read();
        final int concurrentRequestCount = 300;
        NettyPipelinedConnection<Integer, Integer> pipelinedConnection = new NettyPipelinedConnection<>(connection, concurrentRequestCount);
        CyclicBarrier requestStartBarrier = new CyclicBarrier(concurrentRequestCount);
        List<Future<Collection<Integer>>> futures = new ArrayList<>(concurrentRequestCount);
        ExecutorService executor = new ThreadPoolExecutor(0, concurrentRequestCount, 1, SECONDS, new SynchronousQueue<>());
        try {
            for (int i = 0; i < concurrentRequestCount; ++i) {
                final int finalI = i;
                futures.add(executor.submit(() -> {
                    try {
                        requestStartBarrier.await();
                    } catch (Exception e) {
                        return Single.<Collection<Integer>>failed(new AssertionError("failure during request " + finalI, e)).toFuture().get();
                    }
                    return pipelinedConnection.write(Publisher.from(finalI)).toFuture().get();
                }));
            }
            for (Future<Collection<Integer>> future : futures) {
                assertThat(future.get(), hasSize(1));
            }
        } finally {
            executor.shutdown();
        }
    } finally {
        connectionExecutor.closeAsync().subscribe();
    }
}
Also used : UNSUPPORTED_PROTOCOL_CLOSE_HANDLER(io.servicetalk.transport.netty.internal.CloseHandler.UNSUPPORTED_PROTOCOL_CLOSE_HANDLER) BeforeEach(org.junit.jupiter.api.BeforeEach) Protocol(io.servicetalk.transport.api.ConnectionInfo.Protocol) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) ChannelInboundHandlerAdapter(io.netty.channel.ChannelInboundHandlerAdapter) MAX_VALUE(java.lang.Integer.MAX_VALUE) Future(java.util.concurrent.Future) HttpExecutionStrategies.defaultStrategy(io.servicetalk.http.api.HttpExecutionStrategies.defaultStrategy) Assertions.assertFalse(org.junit.jupiter.api.Assertions.assertFalse) Mockito.doAnswer(org.mockito.Mockito.doAnswer) Executor(io.servicetalk.concurrent.api.Executor) Executors.immediate(io.servicetalk.concurrent.api.Executors.immediate) TestPublisherSubscriber(io.servicetalk.concurrent.test.internal.TestPublisherSubscriber) CyclicBarrier(java.util.concurrent.CyclicBarrier) SynchronousQueue(java.util.concurrent.SynchronousQueue) PublisherSource(io.servicetalk.concurrent.PublisherSource) Collection(java.util.Collection) DefaultNettyConnection(io.servicetalk.transport.netty.internal.DefaultNettyConnection) TestSubscription(io.servicetalk.concurrent.api.TestSubscription) Test(org.junit.jupiter.api.Test) Matchers.instanceOf(org.hamcrest.Matchers.instanceOf) List(java.util.List) SubscriberUtils.deliverCompleteFromSource(io.servicetalk.concurrent.internal.SubscriberUtils.deliverCompleteFromSource) RetryableException(io.servicetalk.transport.api.RetryableException) WriteDemandEstimators(io.servicetalk.transport.netty.internal.WriteDemandEstimators) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) FlushStrategies.defaultFlushStrategy(io.servicetalk.transport.netty.internal.FlushStrategies.defaultFlushStrategy) Matchers.is(org.hamcrest.Matchers.is) CloseHandler(io.servicetalk.transport.netty.internal.CloseHandler) NoopConnectionObserver(io.servicetalk.transport.netty.internal.NoopTransportObserver.NoopConnectionObserver) Mockito.mock(org.mockito.Mockito.mock) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) FlushStrategy(io.servicetalk.transport.netty.internal.FlushStrategy) Assertions.assertNotNull(org.junit.jupiter.api.Assertions.assertNotNull) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) DEFAULT_ALLOCATOR(io.servicetalk.buffer.netty.BufferAllocators.DEFAULT_ALLOCATOR) TestPublisher(io.servicetalk.concurrent.api.TestPublisher) ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor) Publisher(io.servicetalk.concurrent.api.Publisher) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) Answer(org.mockito.stubbing.Answer) Matchers.hasSize(org.hamcrest.Matchers.hasSize) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) DELIBERATE_EXCEPTION(io.servicetalk.concurrent.internal.DeliberateException.DELIBERATE_EXCEPTION) ExecutorService(java.util.concurrent.ExecutorService) ClosedChannelException(java.nio.channels.ClosedChannelException) Single(io.servicetalk.concurrent.api.Single) Completable(io.servicetalk.concurrent.api.Completable) EmbeddedDuplexChannel(io.servicetalk.transport.netty.internal.EmbeddedDuplexChannel) Mockito.when(org.mockito.Mockito.when) Subscription(io.servicetalk.concurrent.PublisherSource.Subscription) SourceAdapters.toSource(io.servicetalk.concurrent.api.SourceAdapters.toSource) CompletableSource(io.servicetalk.concurrent.CompletableSource) Mockito.verify(org.mockito.Mockito.verify) Mockito.never(org.mockito.Mockito.never) WriteDemandEstimator(io.servicetalk.transport.netty.internal.WriteDemandEstimator) Completable.completed(io.servicetalk.concurrent.api.Completable.completed) Executors(io.servicetalk.concurrent.api.Executors) NettyConnection(io.servicetalk.transport.netty.internal.NettyConnection) SECONDS(java.util.concurrent.TimeUnit.SECONDS) Completable(io.servicetalk.concurrent.api.Completable) ArrayList(java.util.ArrayList) TestPublisher(io.servicetalk.concurrent.api.TestPublisher) Publisher(io.servicetalk.concurrent.api.Publisher) RetryableException(io.servicetalk.transport.api.RetryableException) ClosedChannelException(java.nio.channels.ClosedChannelException) CyclicBarrier(java.util.concurrent.CyclicBarrier) Executor(io.servicetalk.concurrent.api.Executor) ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor) ExecutorService(java.util.concurrent.ExecutorService) Future(java.util.concurrent.Future) Collection(java.util.Collection) ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor) Test(org.junit.jupiter.api.Test)

Example 5 with Executor

use of io.servicetalk.concurrent.api.Executor in project servicetalk by apple.

the class LingeringRoundRobinLoadBalancerTest method expiringAHostDoesntRaceWithConnectionAdding.

// Concurrency test, worth running >10K times to spot concurrency issues.
@Test
void expiringAHostDoesntRaceWithConnectionAdding() throws Exception {
    Executor executor = Executors.newFixedSizeExecutor(1);
    try {
        sendServiceDiscoveryEvents(upEvent("address-1"));
        assertConnectionCount(lb.usedAddresses(), connectionsCount("address-1", 0));
        AtomicReference<Exception> e = new AtomicReference<>();
        AtomicReference<TestLoadBalancedConnection> connection = new AtomicReference<>();
        Future<Object> f = executor.submit(() -> {
            try {
                connection.set(lb.selectConnection(alwaysNewConnectionFilter()).toFuture().get());
            } catch (Exception ex) {
                e.set(ex);
            }
            return null;
        }).toFuture();
        sendServiceDiscoveryEvents(downEvent("address-1"));
        f.get();
        Exception thrown = e.get();
        if (thrown != null) {
            // Connection was not added - SD event arrived before the attempt of adding the connection
            assertThat(thrown, instanceOf(ExecutionException.class));
            // Either the host was already CLOSED and removed from the usedHosts collection:
            assertThat(thrown.getCause(), either(instanceOf(NoAvailableHostException.class)).or(instanceOf(ConnectionRejectedException.class)));
            assertAddresses(lb.usedAddresses(), EMPTY_ARRAY);
            assertNull(connection.get());
        } else {
            // Connection was added first -> Let's validate the host was properly EXPIRED:
            assertConnectionCount(lb.usedAddresses(), connectionsCount("address-1", 1));
            // Confirm host is expired:
            ExecutionException ex = assertThrows(ExecutionException.class, () -> lb.selectConnection(alwaysNewConnectionFilter()).toFuture().get());
            assertThat(ex.getCause(), instanceOf(NoAvailableHostException.class));
            lb.closeAsyncGracefully().toFuture().get();
            verify(connection.get(), times(1)).closeAsyncGracefully();
        }
    } finally {
        executor.closeAsync().toFuture().get();
    }
}
Also used : Executor(io.servicetalk.concurrent.api.Executor) NoAvailableHostException(io.servicetalk.client.api.NoAvailableHostException) AtomicReference(java.util.concurrent.atomic.AtomicReference) ExecutionException(java.util.concurrent.ExecutionException) NoAvailableHostException(io.servicetalk.client.api.NoAvailableHostException) ExecutionException(java.util.concurrent.ExecutionException) ConnectionRejectedException(io.servicetalk.client.api.ConnectionRejectedException) Test(org.junit.jupiter.api.Test)

Aggregations

Executor (io.servicetalk.concurrent.api.Executor)15 MatcherAssert.assertThat (org.hamcrest.MatcherAssert.assertThat)8 Test (org.junit.jupiter.api.Test)8 Single (io.servicetalk.concurrent.api.Single)5 SourceAdapters.toSource (io.servicetalk.concurrent.api.SourceAdapters.toSource)5 Cancellable (io.servicetalk.concurrent.Cancellable)4 Completable (io.servicetalk.concurrent.api.Completable)4 Publisher (io.servicetalk.concurrent.api.Publisher)4 ServerContext (io.servicetalk.transport.api.ServerContext)4 DEFAULT_ALLOCATOR (io.servicetalk.buffer.netty.BufferAllocators.DEFAULT_ALLOCATOR)3 ConnectionRejectedException (io.servicetalk.client.api.ConnectionRejectedException)3 Executors (io.servicetalk.concurrent.api.Executors)3 Single.succeeded (io.servicetalk.concurrent.api.Single.succeeded)3 StreamingHttpClient (io.servicetalk.http.api.StreamingHttpClient)3 AddressUtils.localAddress (io.servicetalk.transport.netty.internal.AddressUtils.localAddress)3 ExecutionException (java.util.concurrent.ExecutionException)3 AtomicReference (java.util.concurrent.atomic.AtomicReference)3 Nullable (javax.annotation.Nullable)3 Matchers.is (org.hamcrest.Matchers.is)3 Buffer (io.servicetalk.buffer.api.Buffer)2