use of io.servicetalk.concurrent.PublisherSource.Subscriber in project servicetalk by apple.
the class PublisherFlatMapMergeTest method concurrentSkipQueueDoesNotDeadlock.
@Test
void concurrentSkipQueueDoesNotDeadlock() throws Throwable {
assert executorService != null;
List<TestSubscriptionPublisherPair<Integer>> mappedPublishers = new ArrayList<>();
CountDownLatch onNextLatch = new CountDownLatch(1);
CountDownLatch onNextSecondLatch = new CountDownLatch(2);
CountDownLatch onNextThirdLatch = new CountDownLatch(3);
CountDownLatch onNextWaitLatch = new CountDownLatch(1);
CountDownLatch onCompleteLatch = new CountDownLatch(1);
AtomicReference<Throwable> errorRef = new AtomicReference<>();
TestSubscription upstreamSubscription = new TestSubscription();
publisher = new TestPublisher.Builder<Integer>().disableAutoOnSubscribe().build(subscriber1 -> {
subscriber1.onSubscribe(upstreamSubscription);
return subscriber1;
});
toSource(publisher.flatMapMerge(i -> {
TestSubscriptionPublisherPair<Integer> pair = new TestSubscriptionPublisherPair<>(i);
mappedPublishers.add(pair);
return pair.mappedPublisher;
}, 3)).subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(final Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}
@Override
public void onNext(@Nullable final Integer integer) {
onNextLatch.countDown();
onNextSecondLatch.countDown();
onNextThirdLatch.countDown();
try {
onNextWaitLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throwException(e);
}
}
@Override
public void onError(final Throwable t) {
errorRef.set(t);
onCompleteLatch.countDown();
}
@Override
public void onComplete() {
onCompleteLatch.countDown();
}
});
upstreamSubscription.awaitRequestN(2);
publisher.onNext(1, 2);
publisher.onComplete();
assertThat(mappedPublishers, hasSize(2));
TestSubscriptionPublisherPair<Integer> first = mappedPublishers.get(0);
TestSubscriptionPublisherPair<Integer> second = mappedPublishers.get(1);
Future<?> f = executorService.submit(() -> {
try {
first.doOnSubscribe(1);
first.mappedSubscription.awaitRequestN(1);
first.mappedPublisher.onNext(1);
} catch (Throwable cause) {
first.mappedPublisher.onError(cause);
return;
}
first.mappedPublisher.onComplete();
});
// Wait for the executorService thread to be in onNext, we want to force concurrent delivery.
onNextLatch.await();
// Deliver the first signal, and allow the executorService thread to exit onNext.
second.doOnSubscribe(2);
second.mappedSubscription.awaitRequestN(2);
second.mappedPublisher.onNext(2);
onNextWaitLatch.countDown();
// Wait for the onNext from this thread to be delivered in the executorService thread.
onNextSecondLatch.await();
// Wait for the executorService thread to deliver onComplete and release the lock in the operator.
f.get();
// The second mapped publisher previously had items queued, and there are no other thread holding the lock in
// the operator, it should be delivered.
second.mappedPublisher.onNext(3);
onNextThirdLatch.await();
// Deliver the last onComplete and verify normal termination.
second.mappedPublisher.onComplete();
onCompleteLatch.await();
Throwable cause = errorRef.get();
if (cause != null) {
throw cause;
}
}
use of io.servicetalk.concurrent.PublisherSource.Subscriber in project servicetalk by apple.
the class PublisherFlatMapMergeTest method cancelPropagatedBeforeErrorButOriginalErrorPreserved.
@Test
void cancelPropagatedBeforeErrorButOriginalErrorPreserved() {
CountDownLatch cancelledLatch = new CountDownLatch(1);
publisher = new TestPublisher.Builder<Integer>().disableAutoOnSubscribe().build(subscriber1 -> {
subscriber1.onSubscribe(new Subscription() {
@Override
public void request(final long n) {
}
@Override
public void cancel() {
try {
cancelledLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throwException(e);
}
subscriber1.onError(new IllegalStateException("shouldn't reach the Subscriber!"));
}
});
return subscriber1;
});
TestPublisher<Integer> mappedPublisher = new TestPublisher<>();
toSource(publisher.flatMapMerge(i -> mappedPublisher, 1)).subscribe(subscriber);
subscriber.awaitSubscription().request(1);
publisher.onNext(1);
assert executor != null;
executor.execute(() -> mappedPublisher.onError(DELIBERATE_EXCEPTION));
// Verify that cancel happens before terminal. This ensures that sources which allow for multiple sequential
// Subscribers can clear out there current subscriber in preparation for the next Subscriber and avoid duplicate
// subscribe related errors.
assertThat(subscriber.pollTerminal(TERMINAL_POLL_MS, MILLISECONDS), is(nullValue()));
cancelledLatch.countDown();
assertThat(subscriber.awaitOnError(), sameInstance(DELIBERATE_EXCEPTION));
}
use of io.servicetalk.concurrent.PublisherSource.Subscriber in project servicetalk by apple.
the class PublisherFlatMapMergeTest method errorFromMappedSubscriberIsSequencedWithOnNextSignals.
@Test
void errorFromMappedSubscriberIsSequencedWithOnNextSignals() throws InterruptedException {
List<TestSubscriptionPublisherPair<Integer>> mappedPublishers = new ArrayList<>();
TestSubscription upstreamSubscription = new TestSubscription();
publisher = new TestPublisher.Builder<Integer>().disableAutoOnSubscribe().build(subscriber1 -> {
subscriber1.onSubscribe(upstreamSubscription);
return subscriber1;
});
toSource(publisher.flatMapMerge(i -> {
TestSubscriptionPublisherPair<Integer> pair = new TestSubscriptionPublisherPair<>(i);
mappedPublishers.add(pair);
return pair.mappedPublisher;
}, 2)).subscribe(subscriber);
Subscription subscription = subscriber.awaitSubscription();
subscription.request(1);
verifyCumulativeDemand(upstreamSubscription, 2);
publisher.onNext(1, 2);
assertThat(mappedPublishers, hasSize(2));
TestSubscriptionPublisherPair<Integer> first = mappedPublishers.get(0);
TestSubscriptionPublisherPair<Integer> second = mappedPublishers.get(1);
first.doOnSubscribe(1);
first.mappedSubscription.awaitRequestN(1);
second.doOnSubscribe(2);
second.mappedSubscription.awaitRequestN(1);
// Exhaust outstanding requestN from downstream
first.mappedPublisher.onNext(10);
assertThat(subscriber.pollAllOnNext(), contains(10));
// These signals should be queued, and the error shouldn't jump the queue.
second.mappedPublisher.onNext(11);
second.mappedPublisher.onError(DELIBERATE_EXCEPTION);
assertThat(subscriber.pollAllOnNext(), is(empty()));
assertTrue(upstreamSubscription.isCancelled());
subscription.request(1);
assertThat(subscriber.pollAllOnNext(), contains(11));
assertThat(subscriber.awaitOnError(), sameInstance(DELIBERATE_EXCEPTION));
}
use of io.servicetalk.concurrent.PublisherSource.Subscriber in project servicetalk by apple.
the class PublisherFlatMapMergeTest method requestLongMaxMultipleTimes.
@Test
void requestLongMaxMultipleTimes() throws InterruptedException {
TestSubscription upstreamSubscription = new TestSubscription();
publisher = new TestPublisher.Builder<Integer>().disableAutoOnSubscribe().build(subscriber1 -> {
subscriber1.onSubscribe(upstreamSubscription);
return subscriber1;
});
toSource(publisher.flatMapMerge(Publisher::from, 2)).subscribe(subscriber);
Subscription subscription = subscriber.awaitSubscription();
subscription.request(Long.MAX_VALUE);
verifyCumulativeDemand(upstreamSubscription, 2);
publisher.onNext(1);
publisher.onNext(2);
subscription.request(Long.MAX_VALUE);
verifyCumulativeDemand(upstreamSubscription, 4);
publisher.onNext(3);
publisher.onNext(4);
verifyCumulativeDemand(upstreamSubscription, 6);
}
use of io.servicetalk.concurrent.PublisherSource.Subscriber in project servicetalk by apple.
the class PublisherFlatMapMergeTest method requestAndEmitConcurrency.
@Test
void requestAndEmitConcurrency() throws Exception {
int totalToRequest = 1000;
List<Integer> received = new ArrayList<>(totalToRequest);
CountDownLatch requestingStarting = new CountDownLatch(1);
TestSubscription upstreamSubscription = new TestSubscription();
publisher = new TestPublisher.Builder<Integer>().disableAutoOnSubscribe().build(subscriber1 -> {
subscriber1.onSubscribe(upstreamSubscription);
return subscriber1;
});
toSource(publisher.flatMapMerge(Publisher::from, 2).beforeOnNext(received::add)).subscribe(subscriber);
Subscription subscription = subscriber.awaitSubscription();
assert executorService != null;
Future<?> submitFuture = executorService.submit(() -> {
requestingStarting.countDown();
for (int i = 0; i < totalToRequest; i++) {
subscription.request(1);
}
});
// Just to make sure we have both threads running concurrently.
requestingStarting.await();
for (int i = 0; i < totalToRequest; i++) {
upstreamSubscription.awaitRequestN(i + 1);
publisher.onNext(i);
}
submitFuture.get();
assertThat("Unexpected items emitted.", received, hasSize(totalToRequest));
assertThat(received, containsInAnyOrder(IntStream.range(0, totalToRequest).boxed().toArray()));
}
Aggregations