Search in sources :

Example 16 with Publisher

use of org.reactivestreams.Publisher in project ratpack by ratpack.

the class FlattenPublisher method subscribe.

@Override
public void subscribe(Subscriber<? super T> subscriber) {
    subscriber.onSubscribe(new ManagedSubscription<T>(subscriber, disposer) {

        private Subscription outerSubscription;

        private Subscription innerSubscription;

        private final AtomicReference<State> state = new AtomicReference<>(State.INIT);

        volatile boolean pendingComplete;

        @Override
        protected void onRequest(long n) {
            if (state.compareAndSet(State.INIT, State.SUBSCRIBE)) {
                if (outerSubscription == null) {
                    subscribeUpstream();
                }
            } else if (innerSubscription != null) {
                innerSubscription.request(n);
            } else {
                nextPublisher();
            }
        }

        private void subscribeUpstream() {
            publisher.subscribe(new Subscriber<Publisher<T>>() {

                @Override
                public void onSubscribe(Subscription subscription) {
                    outerSubscription = subscription;
                    outerSubscription.request(1);
                }

                @Override
                public void onNext(Publisher<T> next) {
                    next.subscribe(new Subscriber<T>() {

                        @Override
                        public void onSubscribe(Subscription s) {
                            innerSubscription = s;
                            state.set(State.EMITTING);
                            innerSubscription.request(getDemand());
                        }

                        @Override
                        public void onNext(T t) {
                            emitNext(t);
                        }

                        @Override
                        public void onError(Throwable t) {
                            outerSubscription.cancel();
                            emitError(t);
                        }

                        @Override
                        public void onComplete() {
                            innerSubscription = null;
                            state.set(State.IDLE);
                            nextPublisher();
                        }
                    });
                }

                @Override
                public void onError(Throwable t) {
                    if (innerSubscription != null) {
                        innerSubscription.cancel();
                        innerSubscription = null;
                    }
                    emitError(t);
                }

                @Override
                public void onComplete() {
                    pendingComplete = true;
                }
            });
        }

        @Override
        protected void onCancel() {
            if (innerSubscription != null) {
                innerSubscription.cancel();
                innerSubscription = null;
            }
            if (outerSubscription != null) {
                outerSubscription.cancel();
                outerSubscription = null;
            }
        }

        private void nextPublisher() {
            if (state.compareAndSet(State.IDLE, State.PENDING)) {
                if (pendingComplete) {
                    emitComplete();
                } else if (hasDemand()) {
                    outerSubscription.request(1);
                } else {
                    state.set(State.IDLE);
                    if (hasDemand()) {
                        nextPublisher();
                    }
                }
            }
        }
    });
}
Also used : AtomicReference(java.util.concurrent.atomic.AtomicReference) Publisher(org.reactivestreams.Publisher) TransformablePublisher(ratpack.stream.TransformablePublisher) Subscriber(org.reactivestreams.Subscriber) Subscription(org.reactivestreams.Subscription)

Example 17 with Publisher

use of org.reactivestreams.Publisher in project reactive-streams-jvm by reactive-streams.

the class PublisherVerification method stochastic_spec103_mustSignalOnMethodsSequentially.

// Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#1.3
@Override
@Test
public void stochastic_spec103_mustSignalOnMethodsSequentially() throws Throwable {
    final int iterations = 100;
    final int elements = 10;
    stochasticTest(iterations, new Function<Integer, Void>() {

        @Override
        public Void apply(final Integer runNumber) throws Throwable {
            activePublisherTest(elements, true, new PublisherTestRun<T>() {

                @Override
                public void run(Publisher<T> pub) throws Throwable {
                    final Latch completionLatch = new Latch(env);
                    final AtomicInteger gotElements = new AtomicInteger(0);
                    pub.subscribe(new Subscriber<T>() {

                        private Subscription subs;

                        private ConcurrentAccessBarrier concurrentAccessBarrier = new ConcurrentAccessBarrier();

                        /**
               * Concept wise very similar to a {@link org.reactivestreams.tck.TestEnvironment.Latch}, serves to protect
               * a critical section from concurrent access, with the added benefit of Thread tracking and same-thread-access awareness.
               *
               * Since a <i>Synchronous</i> Publisher may choose to synchronously (using the same {@link Thread}) call
               * {@code onNext} directly from either {@code subscribe} or {@code request} a plain Latch is not enough
               * to verify concurrent access safety - one needs to track if the caller is not still using the calling thread
               * to enter subsequent critical sections ("nesting" them effectively).
               */
                        final class ConcurrentAccessBarrier {

                            private AtomicReference<Thread> currentlySignallingThread = new AtomicReference<Thread>(null);

                            private volatile String previousSignal = null;

                            public void enterSignal(String signalName) {
                                if ((!currentlySignallingThread.compareAndSet(null, Thread.currentThread())) && !isSynchronousSignal()) {
                                    env.flop(String.format("Illegal concurrent access detected (entering critical section)! " + "%s emited %s signal, before %s finished its %s signal.", Thread.currentThread(), signalName, currentlySignallingThread.get(), previousSignal));
                                }
                                this.previousSignal = signalName;
                            }

                            public void leaveSignal(String signalName) {
                                currentlySignallingThread.set(null);
                                this.previousSignal = signalName;
                            }

                            private boolean isSynchronousSignal() {
                                return (previousSignal != null) && Thread.currentThread().equals(currentlySignallingThread.get());
                            }
                        }

                        @Override
                        public void onSubscribe(Subscription s) {
                            final String signal = "onSubscribe()";
                            concurrentAccessBarrier.enterSignal(signal);
                            subs = s;
                            subs.request(1);
                            concurrentAccessBarrier.leaveSignal(signal);
                        }

                        @Override
                        public void onNext(T ignore) {
                            final String signal = String.format("onNext(%s)", ignore);
                            concurrentAccessBarrier.enterSignal(signal);
                            if (// requesting one more than we know are in the stream (some Publishers need this)
                            gotElements.incrementAndGet() <= elements)
                                subs.request(1);
                            concurrentAccessBarrier.leaveSignal(signal);
                        }

                        @Override
                        public void onError(Throwable t) {
                            final String signal = String.format("onError(%s)", t.getMessage());
                            concurrentAccessBarrier.enterSignal(signal);
                            // ignore value
                            concurrentAccessBarrier.leaveSignal(signal);
                        }

                        @Override
                        public void onComplete() {
                            final String signal = "onComplete()";
                            concurrentAccessBarrier.enterSignal(signal);
                            // entering for completeness
                            concurrentAccessBarrier.leaveSignal(signal);
                            completionLatch.close();
                        }
                    });
                    completionLatch.expectClose(elements * env.defaultTimeoutMillis(), String.format("Failed in iteration %d of %d. Expected completion signal after signalling %d elements (signalled %d), yet did not receive it", runNumber, iterations, elements, gotElements.get()));
                }
            });
            return null;
        }
    });
}
Also used : AtomicReference(java.util.concurrent.atomic.AtomicReference) Publisher(org.reactivestreams.Publisher) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Latch(org.reactivestreams.tck.TestEnvironment.Latch) Subscription(org.reactivestreams.Subscription) Override(java.lang.Override) Test(org.testng.annotations.Test) Override(java.lang.Override)

Example 18 with Publisher

use of org.reactivestreams.Publisher in project reactive-streams-jvm by reactive-streams.

the class PublisherVerificationTest method demandIgnoringAsynchronousPublisherVerification.

/**
   * Verification using a Publisher that publishes elements even with no demand available, from multiple threads (!).
   */
final PublisherVerification<Integer> demandIgnoringAsynchronousPublisherVerification(final ExecutorService signallersPool, final boolean swallowOnNextExceptions) {
    final AtomicInteger startedSignallingThreads = new AtomicInteger(0);
    final int maxSignallingThreads = 2;
    final AtomicBoolean concurrentAccessCaused = new AtomicBoolean(false);
    return new PublisherVerification<Integer>(newTestEnvironment()) {

        @Override
        public Publisher<Integer> createPublisher(long elements) {
            return new Publisher<Integer>() {

                @Override
                public void subscribe(final Subscriber<? super Integer> s) {
                    s.onSubscribe(new NoopSubscription() {

                        @Override
                        public void request(final long n) {
                            Runnable signalling = new Runnable() {

                                @Override
                                public void run() {
                                    for (long i = 0; i <= n; i++) {
                                        try {
                                            final long signal = i;
                                            signallersPool.execute(new Runnable() {

                                                @Override
                                                public void run() {
                                                    try {
                                                        s.onNext((int) signal);
                                                    } catch (Exception ex) {
                                                        if (!swallowOnNextExceptions) {
                                                            throw new RuntimeException("onNext threw an exception!", ex);
                                                        } else {
                                                        // yes, swallow the exception, we're not asserting and they'd just end up being logged (stdout),
                                                        // which we do not need in this specific PublisherVerificationTest
                                                        }
                                                    }
                                                }
                                            });
                                        } catch (Exception ex) {
                                            if (ex instanceof TestEnvironment.Latch.ExpectedOpenLatchException) {
                                                if (concurrentAccessCaused.compareAndSet(false, true)) {
                                                    throw new RuntimeException("Concurrent access detected", ex);
                                                } else {
                                                    // error signalled once already, stop more errors from propagating
                                                    return;
                                                }
                                            } else if (ex instanceof RejectedExecutionException) {
                                            // ignore - this may happen since one thread may have already gotten into a concurrent access
                                            // problem and initiated the pool's shutdown. It will then throw RejectedExecutionException.
                                            } else {
                                                if (concurrentAccessCaused.get()) {
                                                    return;
                                                } else {
                                                    throw new RuntimeException(ex);
                                                }
                                            }
                                        }
                                    }
                                }
                            };
                            // must be guarded like this in case a Subscriber triggers request() synchronously from it's onNext()
                            while (startedSignallingThreads.getAndAdd(1) < maxSignallingThreads) {
                                signallersPool.execute(signalling);
                            }
                        }
                    });
                }
            };
        }

        @Override
        public Publisher<Integer> createFailedPublisher() {
            return SKIP;
        }
    };
}
Also used : Publisher(org.reactivestreams.Publisher) TestException(org.reactivestreams.tck.support.TestException) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Subscriber(org.reactivestreams.Subscriber)

Example 19 with Publisher

use of org.reactivestreams.Publisher in project reactive-streams-jvm by reactive-streams.

the class PublisherVerificationTest method required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue_forSynchronousPublisher.

@Test
public void required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue_forSynchronousPublisher() throws Throwable {
    final AtomicInteger sent = new AtomicInteger();
    customPublisherVerification(new Publisher<Integer>() {

        @Override
        public void subscribe(final Subscriber<? super Integer> downstream) {
            downstream.onSubscribe(new Subscription() {

                boolean started;

                boolean cancelled;

                @Override
                public void request(long n) {
                    if (!started) {
                        started = true;
                        while (!cancelled) {
                            downstream.onNext(sent.getAndIncrement());
                        }
                    }
                }

                @Override
                public void cancel() {
                    cancelled = true;
                }
            });
        }
    }).required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue();
    // 11 due to the implementation of this particular TCK test (see impl)
    Assert.assertEquals(sent.get(), 11);
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Subscriber(org.reactivestreams.Subscriber) Publisher(org.reactivestreams.Publisher) Subscription(org.reactivestreams.Subscription) Test(org.testng.annotations.Test)

Example 20 with Publisher

use of org.reactivestreams.Publisher in project reactive-streams-jvm by reactive-streams.

the class PublisherVerificationTest method stochastic_spec103_mustSignalOnMethodsSequentially_shouldFailBy_concurrentlyAccessingOnNext.

@Test
public void stochastic_spec103_mustSignalOnMethodsSequentially_shouldFailBy_concurrentlyAccessingOnNext() throws Throwable {
    final AtomicInteger startedSignallingThreads = new AtomicInteger(0);
    // this is an arbitrary number, we just need "many threads" to try to force an concurrent access scenario
    final int maxSignallingThreads = 10;
    final ExecutorService signallersPool = Executors.newFixedThreadPool(maxSignallingThreads);
    final AtomicBoolean concurrentAccessCaused = new AtomicBoolean(false);
    // highly specialised threadpool driven publisher which aims to FORCE concurrent access,
    // so that we can confirm the test is able to catch this.
    final Publisher<Integer> concurrentAccessPublisher = new Publisher<Integer>() {

        @Override
        public void subscribe(final Subscriber<? super Integer> s) {
            s.onSubscribe(new NoopSubscription() {

                @Override
                public void request(final long n) {
                    Runnable signalling = new Runnable() {

                        @Override
                        public void run() {
                            for (long i = 0; i < n; i++) {
                                try {
                                    // shutdown cleanly in when the threadpool is shutting down
                                    if (Thread.interrupted()) {
                                        return;
                                    }
                                    s.onNext((int) i);
                                } catch (Exception ex) {
                                    // signal others to shut down
                                    signallersPool.shutdownNow();
                                    if (ex instanceof TestEnvironment.Latch.ExpectedOpenLatchException) {
                                        if (!concurrentAccessCaused.getAndSet(true)) {
                                            throw new RuntimeException("Concurrent access detected", ex);
                                        } else {
                                            // error signalled once already, stop more errors from propagating
                                            return;
                                        }
                                    } else {
                                        throw new RuntimeException(ex);
                                    }
                                }
                            }
                        }
                    };
                    // must be guarded like this in case a Subscriber triggers request() synchronously from it's onNext()
                    while (startedSignallingThreads.getAndAdd(1) < maxSignallingThreads && !signallersPool.isShutdown()) {
                        try {
                            signallersPool.execute(signalling);
                        } catch (RejectedExecutionException ex) {
                            // ignore, should be safe as it means the pool is shutting down -> which means we triggered the problem we wanted to
                            return;
                        }
                    }
                }
            });
        }
    };
    try {
        requireTestFailure(new ThrowingRunnable() {

            @Override
            public void run() throws Throwable {
                customPublisherVerification(concurrentAccessPublisher).stochastic_spec103_mustSignalOnMethodsSequentially();
            }
        }, "Illegal concurrent access detected");
    } finally {
        signallersPool.shutdownNow();
        signallersPool.awaitTermination(1, TimeUnit.SECONDS);
    }
}
Also used : Publisher(org.reactivestreams.Publisher) TestException(org.reactivestreams.tck.support.TestException) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Subscriber(org.reactivestreams.Subscriber) ExecutorService(java.util.concurrent.ExecutorService) Test(org.testng.annotations.Test)

Aggregations

Publisher (org.reactivestreams.Publisher)28 List (java.util.List)11 Mono (reactor.core.publisher.Mono)11 Test (org.junit.Test)9 Collections (java.util.Collections)8 Assert.assertSame (org.junit.Assert.assertSame)7 CharSequenceEncoder (org.springframework.core.codec.CharSequenceEncoder)7 HttpStatus (org.springframework.http.HttpStatus)7 EncoderHttpMessageWriter (org.springframework.http.codec.EncoderHttpMessageWriter)7 MockServerHttpRequest (org.springframework.mock.http.server.reactive.test.MockServerHttpRequest)7 Duration (java.time.Duration)6 CoreMatchers.instanceOf (org.hamcrest.CoreMatchers.instanceOf)6 CoreMatchers.startsWith (org.hamcrest.CoreMatchers.startsWith)6 Matchers.is (org.hamcrest.Matchers.is)6 Assert.assertEquals (org.junit.Assert.assertEquals)6 Assert.assertThat (org.junit.Assert.assertThat)6 Before (org.junit.Before)6 Subscriber (org.reactivestreams.Subscriber)6 AnnotationConfigApplicationContext (org.springframework.context.annotation.AnnotationConfigApplicationContext)6 Bean (org.springframework.context.annotation.Bean)6