Search in sources :

Example 1 with Cancellable

use of io.servicetalk.concurrent.Cancellable 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 2 with Cancellable

use of io.servicetalk.concurrent.Cancellable in project servicetalk by apple.

the class CompletableDoOnUtils method doOnSubscribeSupplier.

static Supplier<Subscriber> doOnSubscribeSupplier(Consumer<Cancellable> onSubscribe) {
    requireNonNull(onSubscribe);
    Subscriber subscriber = new Subscriber() {

        @Override
        public void onSubscribe(Cancellable cancellable) {
            onSubscribe.accept(cancellable);
        }

        @Override
        public void onComplete() {
        // NOOP
        }

        @Override
        public void onError(Throwable t) {
        // NOOP
        }
    };
    return () -> subscriber;
}
Also used : Subscriber(io.servicetalk.concurrent.CompletableSource.Subscriber) Cancellable(io.servicetalk.concurrent.Cancellable)

Example 3 with Cancellable

use of io.servicetalk.concurrent.Cancellable in project servicetalk by apple.

the class AbstractSubmitCompletable method handleSubscribe.

@Override
protected final void handleSubscribe(final CompletableSource.Subscriber subscriber) {
    DelayedCancellable cancellable = new DelayedCancellable();
    subscriber.onSubscribe(cancellable);
    final Cancellable eCancellable;
    try {
        eCancellable = runExecutor.execute(() -> {
            try {
                runnable().run();
            } catch (Throwable cause) {
                subscriber.onError(cause);
                return;
            }
            subscriber.onComplete();
        });
    } catch (Throwable cause) {
        // We assume that runExecutor.execute() either throws or executes the Runnable. Hence we do not have to
        // protect against duplicate terminal events.
        subscriber.onError(cause);
        return;
    }
    cancellable.delayedCancellable(eCancellable);
}
Also used : DelayedCancellable(io.servicetalk.concurrent.internal.DelayedCancellable) Cancellable(io.servicetalk.concurrent.Cancellable) DelayedCancellable(io.servicetalk.concurrent.internal.DelayedCancellable)

Example 4 with Cancellable

use of io.servicetalk.concurrent.Cancellable in project servicetalk by apple.

the class CallableSingleTest method cancelInterrupts.

@Test
void cancelInterrupts() throws Exception {
    final Single<Integer> source = Single.fromCallable(factory);
    final CountDownLatch latch = new CountDownLatch(1);
    when(factory.call()).then(invocation -> {
        try {
            // await till interrupted.
            latch.await();
        } catch (InterruptedException e) {
            latch.countDown();
            Thread.currentThread().interrupt();
            throw e;
        }
        return 1;
    });
    toSource(source).subscribe(new SingleSource.Subscriber<Integer>() {

        @Override
        public void onSubscribe(final Cancellable cancellable) {
            cancellable.cancel();
        }

        @Override
        public void onSuccess(@Nullable final Integer result) {
        // noop
        }

        @Override
        public void onError(final Throwable t) {
        // noop
        }
    });
    latch.await();
}
Also used : SingleSource(io.servicetalk.concurrent.SingleSource) Cancellable(io.servicetalk.concurrent.Cancellable) CountDownLatch(java.util.concurrent.CountDownLatch) Test(org.junit.jupiter.api.Test)

Example 5 with Cancellable

use of io.servicetalk.concurrent.Cancellable 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)

Aggregations

Cancellable (io.servicetalk.concurrent.Cancellable)68 Test (org.junit.jupiter.api.Test)46 CountDownLatch (java.util.concurrent.CountDownLatch)17 MatcherAssert.assertThat (org.hamcrest.MatcherAssert.assertThat)12 SingleSource (io.servicetalk.concurrent.SingleSource)11 Matchers.is (org.hamcrest.Matchers.is)10 CompletableSource (io.servicetalk.concurrent.CompletableSource)9 AtomicReference (java.util.concurrent.atomic.AtomicReference)9 Publisher.from (io.servicetalk.concurrent.api.Publisher.from)7 SourceAdapters.toSource (io.servicetalk.concurrent.api.SourceAdapters.toSource)7 DELIBERATE_EXCEPTION (io.servicetalk.concurrent.internal.DeliberateException.DELIBERATE_EXCEPTION)7 Subscriber (io.servicetalk.concurrent.CompletableSource.Subscriber)6 Executor (io.servicetalk.concurrent.api.Executor)6 Single (io.servicetalk.concurrent.api.Single)6 Single.succeeded (io.servicetalk.concurrent.api.Single.succeeded)6 TestCancellable (io.servicetalk.concurrent.api.TestCancellable)6 StreamingHttpResponse (io.servicetalk.http.api.StreamingHttpResponse)6 TimeUnit (java.util.concurrent.TimeUnit)6 PublisherSource (io.servicetalk.concurrent.PublisherSource)5 Completable (io.servicetalk.concurrent.api.Completable)5