diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..62ab68a18e --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +Thanks for using RxJava but before you post an issue, please consider the following points: + + - [ ] Please include the library version number, including the minor and patch version, in the issue text. In addition, if you'd include the major version in the title (such as `2.x`) that would be great. + + - [ ] If you think you found a bug, please include a code sample that reproduces the problem. Dumping a stacktrace is usually not enough for us. + + - [ ] RxJava has more than 150 operators, we recommend searching the [javadoc](http://reactivex.io/RxJava/1.x/javadoc/rx/Observable.html) for keywords of what you try to accomplish. + + - [ ] If you have a question/issue about a library/technology built on top of RxJava (such as Retrofit, RxNetty, etc.), please consider asking a question on StackOverflow first (then maybe on their issue list). + + - [ ] Questions like "how do I X with RxJava" are generally better suited for StackOverflow (where it may already have an answer). + + - [ ] Please avoid cross-posting questions on StackOverflow, this issue list, the Gitter room or the mailing list. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..885315565f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thank you for contributing to RxJava. Before pressing the "Create Pull Request" button, please consider the following points: + + - [ ] Please give a description about what and why you are contributing, even if it's trivial. + + - [ ] Please include the issue list number(s) or other PR numbers in the description if you are contributing in response to those. + + - [ ] Please include a reasonable set of unit tests if you contribute new code or change an existing one. If you contribute an operator, (if applicable) please make sure you have tests for working with an `empty`, `just`, `range` of values as well as an `error` source, with and/or without backpressure and see if unsubscription/cancellation propagates correctly. diff --git a/.travis.yml b/.travis.yml index f07cf8d5e4..e9ae55c471 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: java jdk: -- oraclejdk7 -sudo: false +- oraclejdk8 +sudo: required # as per http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/ git: diff --git a/CHANGES.md b/CHANGES.md index 1741085701..31299c26f8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,231 @@ # RxJava Releases # +### Version 1.3.8 - March 31, 2018 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.8%7C)) + +RxJava 1.x is now officially **end-of-life (EOL)**. No further developments, bugfixes, enhancements, javadoc changes, maintenance will be provided by this project after version **1.3.8**. + +Users are encourage to [migrate to 2.x](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0). In accordance, the wiki will be updated in the coming months to describe 2.x features and properties. + +#### Bugfixes + +- [Pull 5935](https://github.com/ReactiveX/RxJava/pull/5935): Fix `take()` to route late errors to `RxJavaHooks`. + +### Version 1.3.7 - March 21, 2018 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.7%7C)) + +#### Bugfixes + +- [Pull 5917](https://github.com/ReactiveX/RxJava/pull/5917): Fix and deprecate evicting `groupBy` and add a new overload with the corrected signature. + +### Version 1.3.6 - February 15, 2018 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.6%7C)) + +#### Bugfixes + +- [Pull 5850](https://github.com/ReactiveX/RxJava/pull/5850): Fix a race condition that may make `OperatorMaterialize` emit the wrong signals. +- [Pull 5851](https://github.com/ReactiveX/RxJava/pull/5851): Fix a race condition in `OperatorMerge.InnerSubscriber#onError` causing incorrect terminal event. + +### Version 1.3.5 - January 27, 2018 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.5%7C)) + +#### Other + +- [Pull 5820](https://github.com/ReactiveX/RxJava/pull/5820): `RxJavaPlugins` lookup workaround for `System.getProperties()` access restrictions. + +### Version 1.3.4 - November 19, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.4%7C)) + +#### Bugfixes + +- [Pull 5696](https://github.com/ReactiveX/RxJava/pull/5696): Fix `Completable.concat` to use `replace` and don't dispose the old `Subscription` on switching to the next source. +- [Pull 5726](https://github.com/ReactiveX/RxJava/pull/5726): Fix broken backpressure through `unsubscribeOn()`. + +#### Documentation + +- [Pull 5719](https://github.com/ReactiveX/RxJava/pull/5719): Document the timed `take()` operator will signal the `onCompleted` event on the given `Scheduler` when it times out. + +### Version 1.3.3 - October 19, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.3%7C)) + +#### Bugfixes + +- [Pull 5660](https://github.com/ReactiveX/RxJava/pull/5660): Fix `timeout` (timed, selector) unsubscribe bug. + +### Version 1.3.2 - September 15, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.2%7C)) + +#### Bugfixes + +- [Pull 5602](https://github.com/ReactiveX/RxJava/pull/5602): Workaround for CHM.keySet bad type with Java 8 compiler + +### Version 1.3.1 - September 10, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.1%7C)) + +#### API changes +*Remark: submitted & merged before the feature freeze of June 1st.* + +- [Pull 5332](https://github.com/ReactiveX/RxJava/pull/5332): Add the `cast` operator to `Single`. + +#### Bugfixes + +- [Pull 5430](https://github.com/ReactiveX/RxJava/pull/5430): Fix premature cleanup in `AsyncOnSubscribe` when the last `Observable` is still running. +- [Pull 5437](https://github.com/ReactiveX/RxJava/pull/5437): `TestSubscriber::assertValuesAndClear` should reset `valueCount`. +- [Pull 5470](https://github.com/ReactiveX/RxJava/pull/5470): Fix eager call to `RxJavHooks.onError` in `SafeCompletableSuscriber`. + +### Version 1.3.0 - May 5, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.3.0%7C)) + +#### Summary + +Version 1.3.0 is the next minor release of the 1.x series of RxJava containing many formerly experimental API components promoted to standard. Most notably the `Completable` base reactive type is now standard as well. + +Note that the experimental `rx.Observable.fromEmitter()` has been removed in favor for the now also standard `Observable.create(Action1> emitter, Emitter.BackpressureMode backpressure)` + +The planned lifecycle of the 1.x line is as follows: + +Date | Remark +------------|------------------- + **June 1, 2017** | Feature freeze, only bugfixes from this on. + **September 1, 2017** | Release `1.4.0` finalizing the remaining API components. + **March 31, 2018** | End of development. + +The following components have been promoted to standard: + +**Classes, interfaces** + +- **classes**: `AssemblyStackTraceException`, `RxJavaCompletableExecutionHook`, `RxJavaHooks`, `UnicastSubject`, `BlockingSingle`, `Completable`, `AssertableSubscriber`, `AsyncCompletableSubscriber`, `SafeCompletableSubscriber` +- **interfaces**: `Cancellable`, `Emitter`, `SingleEmitter`, `CompletableEmitter`, `CompletableSubscriber`, `BackpressureOverflow.Strategy` + +**Operators** + +- **`Observable`**: `create`, `unsafeCreate`, `to`, `zip(Observable[], FuncN)`, `flatMapCompletable`, `flatMapSingle`, `groupby(Func1, Func1, Func1, Map>)`, `onTerminateDetach`, `rebatchRequests`, `subscribeOn(Scheduler, boolean)`, `sorted`, `withLatestFrom`, `test`, `toCompletable`, `concatDelayError`, `mergeDelayError`, `switchOnNextDelayError`, `using(Func0, Func1, Action1, boolean)`, `concatMapDelayError`, `delaySubscription(Observable)`, `distinctUntilChanged(Func2)`, `concatEager`, `concatMapEager`, `onBackpressureBuffer(long, Action0, BackpressureOverflow.Strategy)`, `switchMapDelayError`, `toSortedList(int)`, `toSortedList(Func2, int)` +- **`Completable`**: `fromEmitter`, `test` +- **`Single`**: `fromEmitter`, `merge`, `mergeDelayError`, `cache`, `to`, `doOnEach`, `doOnSuccess`, `test`, `onErrorResumeNext`, `toCompletable`, `doOnError`, `doOnSubscribe`, `delay`, `defer`, `doOnUnsubscribe`, `doAfterTerminate`, `flatMapCompletable`, `lift`, `toBlocking`, `using`, `delaySubscription(Observable)` +- **`TestSubscriber`**: `getCompletions`, `awaitValueCount`, `assertValuesAndClear` +- **`SyncOnSubscriber`**: `createSingleState`, `createStateful`, `createStateless` + +**Other** + +- `Schedulers.reset` +- `CompositeException(Throwable...)` constructor +- `Exceptions.throwOrReport` (4 overloads) +- `BlockingObservable.subscribe` (6 overloads) +- **`RxJavaSchedulersHook`**: `createComputationScheduler`, `createIoScheduler`, `createNewThreadScheduler` +- **internal**: `AssertableSubscriberObservable`, `FlatMapCompletable`, `FlatMapSingle`, `SchedulerWhen`, `BackpressureDrainManager`, `BlockingUtils`. +- **`RxJavaPlugins`**: `reset`, `getCompletableExecutionHook`, `registerCompletableExecutionHook` +- **`RxJavaErrorHandler`**: `handleOnNextValueRendering`, `render` + +In addition, the class `AsyncOnsubscribe` with its 7 factory methods and `Observable.create(AsyncOnSubscribe)` have been promoted to **beta**. + +#### Acknowledgements + +Thanks to all who contributed to the 1.x line in the past 6 months (in order they appear on the [commit](https://github.com/ReactiveX/RxJava/commits/1.x) page): + +@mtiidla, @dhendry, @mostroverkhov, @yshrsmz, @BraisGabin, @cesar1000, @Jawnnypoo, @chanx2, @abersnaze, @davidmoten, @ortex, @marwinxxii, @ekchang, @pyricau, @JakeWharton, @VictorAlbertos + + +### Version 1.2.10 - April 26, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.10%7C)) + +#### Bugfixes + +- [Pull 5225](https://github.com/ReactiveX/RxJava/pull/5225): Fix `Completable.onErrorResumeNext` unsubscribe not propagated. + +#### Other + +- [Pull 5250](https://github.com/ReactiveX/RxJava/pull/5250): Defer creation of the `TimeoutException` when using the `Single.timeout()` operator. +- [Pull 5258](https://github.com/ReactiveX/RxJava/pull/5258): Use IntelliJ IDE friendly assertion failure message. + +### Version 1.2.9 - March 24, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.9%7C)) + +#### API enhancements +- [Pull 5146](https://github.com/ReactiveX/RxJava/pull/5146): Add `Single.unsubscribeOn`. +- [Pull 5195](https://github.com/ReactiveX/RxJava/pull/5195): Enhance `UnicastSubject` with optional delay error behavior. + +#### Bugfixes + +- [Pull 5141](https://github.com/ReactiveX/RxJava/pull/5141): Fix timed `replay()` not terminating when all items timeout. +- [Pull 5181](https://github.com/ReactiveX/RxJava/pull/5181): `replay().refCount()` avoid leaking items between connections. + +### Version 1.2.7 - February 24, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.7%7C)) + +#### Deprecation of `create(OnSubscribe)` + +The method started out in RxJava 0.x as a simple and direct way for implementing custom operators because 0.x had a much simpler protocol requirements. Over the years, as the `Observable` protocol evolved and `create` became a powerful and complicated extension point of RxJava that required users to implement the `Observable` protocol, including cancellation and backpressure manually. + +Unfortunately, guides, blogs, StackOverflow answers and mere typical user behavior still leads to this `create` method and lots of confusion, unstoppable sequences and `MissingBackpressureException`. We tried remedying the situation by introducing `fromEmitter` with limited discoverability success. + +**Therefore, as of 1.2.7 the `create()` method is now deprecated** (but won't be removed due to binary compatibility requirements). In addition `fromEmitter` has been deprecate-renamed to `create(Action1, BackpressureMode)` and the experimental-marked `fromEmitter` itself will be removed in 1.3.0. + +Since the functionality of `create()` was useful for writing (custom) operators inside and outside of RxJava, the new `unsafeCreate(OnSubscribe)` method was introduced as the replacement. + +The new `create()` and `unsafeCreate()` methods will be fast-tracked to become standard in 1.3.0. + +#### API enhancements + +- [Pull 5086](https://github.com/ReactiveX/RxJava/pull/5086): Deprecate `create()`, add alternatives +- [Pull 5092](https://github.com/ReactiveX/RxJava/pull/5092): Add `Single.merge(Observable>)`, `Observable.flatMapSingle()` & `Observable.flatMapCompletable`. +- [Pull 5091](https://github.com/ReactiveX/RxJava/pull/5091): Add `subscribeOn(Scheduler, boolean)` avoid same-pool deadlock. + +#### API deprecations + +- [Pull 5086](https://github.com/ReactiveX/RxJava/pull/5086): + - Deprecate `Observable.create(OnSubscribe)`, + - Deprecate `fromEmitter` in favor of `Observable.create(Action1, BackpressureMode)`. + +#### Bugfixes + +- [Pull 5091](https://github.com/ReactiveX/RxJava/pull/5091): `create(Action1, BackpressureMode)`+`subscribeOn` avoid same-pool deadlock. +- [Pull 5123](https://github.com/ReactiveX/RxJava/pull/5123): `throttleFirst` detecting clock-drift backwards to open the gate + +#### Other + +- [Pull 5125](https://github.com/ReactiveX/RxJava/pull/5125): reduce stack depth with `switchIfEmpty` + +### Version 1.2.6 - February 3, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.6%7C)) + +#### Documentation + +- [Pull 5000](https://github.com/ReactiveX/RxJava/pull/5000): Add which are the other stardard methods of create +- [Pull 5007](https://github.com/ReactiveX/RxJava/pull/5007): update `sample(time)` diagram to indicate emission of last +- [Pull 5048](https://github.com/ReactiveX/RxJava/pull/5048): Improve the javadoc of `BehaviorSubject` + +#### Bugfixes + +- [Pull 5030](https://github.com/ReactiveX/RxJava/pull/5030): fix `groupBy` consuming the upstream in an unbounded manner + +### Version 1.2.5 - January 6, 2017 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.5%7C)) + +#### Documentation + +- [Pull 4963](https://github.com/ReactiveX/RxJava/pull/4963): Add missing marbles, fix image sizes + +#### Bugfixes + +- [Pull 4941](https://github.com/ReactiveX/RxJava/pull/4941): Fix `Completable.mergeDelayError` to check unsafe availability + +### Version 1.2.4 - December 15, 2016 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.4%7C)) + +#### Other + +- [Pull 4912](https://github.com/ReactiveX/RxJava/pull/4912): Fix `resolveAndroidApiVersion` when running under Robolectric +- [Pull 4908](https://github.com/ReactiveX/RxJava/pull/4908): Use `t` instead of value to allow for IDE naming. +- [Pull 4884](https://github.com/ReactiveX/RxJava/pull/4884): enable `TestScheduler` with nanosecond periodic scheduling + +### Version 1.2.3 - November 23, 2016 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.3%7C)) + +#### Documentation enhancements + +- [Pull 4846](https://github.com/ReactiveX/RxJava/pull/4846): Specify the system parameters configuring the schedulers in the `Schedulers` javadoc. + +#### API enhancements + +- [Pull 4777](https://github.com/ReactiveX/RxJava/pull/4777): add `AssertableSubscriber` to provide method chained version of `TestSubscriber` and support a `test()` method on the base reactive classes. +- [Pull 4851](https://github.com/ReactiveX/RxJava/pull/4851): add `Single.fromEmitter` +- [Pull 4852](https://github.com/ReactiveX/RxJava/pull/4852): `Single.takeUntil` `CancellationException` message enhancements + +#### Performance enhancements + +- [Pull 4846](https://github.com/ReactiveX/RxJava/pull/4846): remove ObjectPool, code style cleanups + +#### Bugfixes + +- [Pull 4826](https://github.com/ReactiveX/RxJava/pull/4826): `Schedule.when()` bug fix +- [Pull 4830](https://github.com/ReactiveX/RxJava/pull/4830): `Completable.doAfterTerminate` to run after `onError` as well. +- [Pull 4841](https://github.com/ReactiveX/RxJava/pull/4841): replace non-serializable value of `OnNextValue` with its `toString`. +- [Pull 4849](https://github.com/ReactiveX/RxJava/pull/4849): fix `Completable.concat` & `merge` hanging in async situations. + ### Version 1.2.2 - November 3, 2016 ([Maven](http://search.maven.org/#artifactdetails%7Cio.reactivex%7Crxjava%7C1.2.2%7C)) Note that the interface `Cancellable` has been moved to `rx.functions` affecting `CompletableEmitter` and the experimental `Observable.fromEmitter(Action1> emitter, AsyncEmitter.BackpressureMode backpressure)` has been removed. @@ -250,7 +476,7 @@ This release contains mostly internal cleanups, reinforced Observable-protocol a #### Bugfixes - - [Pull 4231](https://github.com/ReactiveX/RxJava/pull/4213): `Schedulers.io()` workers now wait until a blocking task finishes before becoming available again. + - [Pull 4231](https://github.com/ReactiveX/RxJava/pull/4231): `Schedulers.io()` workers now wait until a blocking task finishes before becoming available again. - [Pull 4244](https://github.com/ReactiveX/RxJava/pull/4244): Fix `all` multiple terminal events. - [Pull 4241](https://github.com/ReactiveX/RxJava/pull/4241): Fix reentrancy bug in `repeatWhen` and `retryWhen` when the resubscription happens. - [Pull 4225](https://github.com/ReactiveX/RxJava/pull/4225): `PublishSubject` now checks for unsubscribed child while dispatching events. diff --git a/README.md b/README.md index 3525242ea4..a0eabf5886 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ # RxJava: Reactive Extensions for the JVM +## End-of-Life notice + +As of March 31, 2018, The RxJava 1.x branch and version is end-of-life (EOL). No further development, bugfixes, documentation changes, PRs, releases or maintenance will be performed by the project on the 1.x line. + +Users are encouraged to migrate to [3.x](https://github.com/ReactiveX/RxJava) which is currently the only official RxJava version being managed. + +---------------------------------- + [![codecov.io](http://codecov.io/github/ReactiveX/RxJava/coverage.svg?branch=1.x)](http://codecov.io/github/ReactiveX/RxJava?branch=1.x) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.reactivex/rxjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.reactivex/rxjava) diff --git a/build.gradle b/build.gradle index 570ceb242f..8d0d2fa087 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ dependencies { testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' - testCompile 'com.google.guava:guava:19.0' + testCompile 'com.google.guava:guava:24.0-jre' testCompile 'com.pushtorefresh.java-private-constructor-checker:checker:1.2.0' perfCompile 'org.openjdk.jmh:jmh-core:1.11.3' diff --git a/src/main/java/rx/BackpressureOverflow.java b/src/main/java/rx/BackpressureOverflow.java index b47e40f825..6603a6d220 100644 --- a/src/main/java/rx/BackpressureOverflow.java +++ b/src/main/java/rx/BackpressureOverflow.java @@ -15,17 +15,19 @@ */ package rx; -import rx.annotations.Beta; import rx.exceptions.MissingBackpressureException; /** * Generic strategy and default implementations to deal with backpressure buffer overflows. * - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Beta public final class BackpressureOverflow { + private BackpressureOverflow() { + throw new IllegalStateException("No instances!"); + } + /** * Signal a MissingBackressureException due to lack of requests. */ @@ -65,10 +67,10 @@ public interface Strategy { /** * Drop oldest items from the buffer making room for newer ones. */ - static class DropOldest implements BackpressureOverflow.Strategy { + static final class DropOldest implements BackpressureOverflow.Strategy { static final DropOldest INSTANCE = new DropOldest(); - private DropOldest() {} + private DropOldest() { } @Override public boolean mayAttemptDrop() { @@ -80,10 +82,10 @@ public boolean mayAttemptDrop() { * Drop most recent items, but not {@code onError} nor unsubscribe from source * (as {code OperatorOnBackpressureDrop}). */ - static class DropLatest implements BackpressureOverflow.Strategy { + static final class DropLatest implements BackpressureOverflow.Strategy { static final DropLatest INSTANCE = new DropLatest(); - private DropLatest() {} + private DropLatest() { } @Override public boolean mayAttemptDrop() { @@ -94,11 +96,11 @@ public boolean mayAttemptDrop() { /** * {@code onError} a MissingBackpressureException and unsubscribe from source. */ - static class Error implements BackpressureOverflow.Strategy { + static final class Error implements BackpressureOverflow.Strategy { static final Error INSTANCE = new Error(); - private Error() {} + private Error() { } @Override public boolean mayAttemptDrop() throws MissingBackpressureException { diff --git a/src/main/java/rx/Completable.java b/src/main/java/rx/Completable.java index e9b78c569f..1ecc1e1993 100644 --- a/src/main/java/rx/Completable.java +++ b/src/main/java/rx/Completable.java @@ -20,7 +20,6 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; -import rx.annotations.*; import rx.exceptions.*; import rx.functions.*; import rx.internal.observers.AssertableSubscriberObservable; @@ -35,10 +34,9 @@ * Represents a deferred computation without any value but only indication for completion or exception. * * The class follows a similar event pattern as Reactive-Streams: onSubscribe (onError|onComplete)? - * - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * + * @since 1.3 */ -@Beta public class Completable { /** The actual subscription action. */ private final OnSubscribe onSubscribe; @@ -539,9 +537,8 @@ public void call(rx.CompletableSubscriber s) { * Completable's protocol are held. * @param producer the callback invoked for each incoming CompletableSubscriber * @return the new Completable instance - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public static Completable fromEmitter(Action1 producer) { return create(new CompletableFromEmitter(producer)); } @@ -1729,6 +1726,7 @@ public final Completable onErrorResumeNext(final Func1 R to(Func1 converter) { * @return the new Observable created */ public final Observable toObservable() { - return Observable.create(new Observable.OnSubscribe() { + return Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { unsafeSubscribe(s); @@ -2384,10 +2382,10 @@ public void call() { *
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
* + *

History: 1.2.3 - experimental * @return the new AssertableSubscriber instance - * @since 1.2.3 + * @since 1.3 */ - @Experimental public final AssertableSubscriber test() { AssertableSubscriberObservable ts = AssertableSubscriberObservable.create(Long.MAX_VALUE); subscribe(ts); diff --git a/src/main/java/rx/CompletableEmitter.java b/src/main/java/rx/CompletableEmitter.java index 09f51e0262..dc5f83efaa 100644 --- a/src/main/java/rx/CompletableEmitter.java +++ b/src/main/java/rx/CompletableEmitter.java @@ -15,7 +15,6 @@ */ package rx; -import rx.annotations.Experimental; import rx.functions.Cancellable; /** @@ -24,9 +23,8 @@ *

* All methods are thread-safe; calling onCompleted or onError twice or one after the other has * no effect. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Experimental public interface CompletableEmitter { /** diff --git a/src/main/java/rx/CompletableSubscriber.java b/src/main/java/rx/CompletableSubscriber.java index 2aef783769..054a97474e 100644 --- a/src/main/java/rx/CompletableSubscriber.java +++ b/src/main/java/rx/CompletableSubscriber.java @@ -15,12 +15,10 @@ */ package rx; -import rx.annotations.Experimental; - /** * Represents the subscription API callbacks when subscribing to a Completable instance. + * @since 1.3 */ -@Experimental public interface CompletableSubscriber { /** * Called once the deferred computation completes normally. diff --git a/src/main/java/rx/Emitter.java b/src/main/java/rx/Emitter.java index 5ed8697b71..3a81d7f01e 100644 --- a/src/main/java/rx/Emitter.java +++ b/src/main/java/rx/Emitter.java @@ -16,7 +16,6 @@ package rx; -import rx.annotations.Experimental; import rx.functions.Cancellable; /** @@ -29,9 +28,8 @@ * other methods are thread-safe. * * @param the value type to emit - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Experimental public interface Emitter extends Observer { /** diff --git a/src/main/java/rx/Observable.java b/src/main/java/rx/Observable.java index 0d25f8f3f2..99e84ec609 100644 --- a/src/main/java/rx/Observable.java +++ b/src/main/java/rx/Observable.java @@ -51,7 +51,7 @@ public class Observable { /** * Creates an Observable with a Function to execute when it is subscribed to. *

- * Note: Use {@link #create(OnSubscribe)} to create an Observable, instead of this constructor, + * Note: Use {@link #unsafeCreate(OnSubscribe)} to create an Observable, instead of this constructor, * unless you specifically have a need for inheritance. * * @param f @@ -62,10 +62,71 @@ protected Observable(OnSubscribe f) { } /** - * This method requires advanced knowledge about building operators and data sources; please consider - * other standard methods first; - * Returns an Observable that will execute the specified function when a {@link Subscriber} subscribes to - * it. + * Constructs an Observable in an unsafe manner, that is, unsubscription and backpressure handling + * is the responsibility of the OnSubscribe implementation. + * @param the value type emitted + * @param f the callback to execute for each individual Subscriber that subscribes to the + * returned Observable + * @return the new Observable instance + * @deprecated 1.2.7 - inherently unsafe, use the other create() methods for basic cases or + * see {@link #unsafeCreate(OnSubscribe)} for advanced cases (such as custom operators) + * @see #create(SyncOnSubscribe) + * @see #create(AsyncOnSubscribe) + * @see #create(Action1, rx.Emitter.BackpressureMode) + */ + @Deprecated + public static Observable create(OnSubscribe f) { + return new Observable(RxJavaHooks.onCreate(f)); + } + + /** + * Provides an API (via a cold Observable) that bridges the reactive world with the callback-style, + * generally non-backpressured world. + *

+ * Example: + *


+     * Observable.<Event>create(emitter -> {
+     *     Callback listener = new Callback() {
+     *         @Override
+     *         public void onEvent(Event e) {
+     *             emitter.onNext(e);
+     *             if (e.isLast()) {
+     *                 emitter.onCompleted();
+     *             }
+     *         }
+     *
+     *         @Override
+     *         public void onFailure(Exception e) {
+     *             emitter.onError(e);
+     *         }
+     *     };
+     *
+     *     AutoCloseable c = api.someMethod(listener);
+     *
+     *     emitter.setCancellation(c::close);
+     *
+     * }, BackpressureMode.BUFFER);
+     * 
+ *

+ * You should call the Emitter's onNext, onError and onCompleted methods in a serialized fashion. The + * rest of its methods are thread-safe. + *

History: 1.2.7 - experimental + * @param the element type + * @param emitter the emitter that is called when a Subscriber subscribes to the returned {@code Observable} + * @param backpressure the backpressure mode to apply if the downstream Subscriber doesn't request (fast) enough + * @return the new Observable instance + * @see Emitter + * @see Emitter.BackpressureMode + * @see rx.functions.Cancellable + * @since 1.3 + */ + public static Observable create(Action1> emitter, Emitter.BackpressureMode backpressure) { + return unsafeCreate(new OnSubscribeCreate(emitter, backpressure)); + } + + /** + * Returns an Observable that executes the given OnSubscribe action for each individual Subscriber + * that subscribes; unsubscription and backpressure must be implemented manually. *

* *

@@ -84,9 +145,9 @@ protected Observable(OnSubscribe f) { * document the fact that the consumer of the returned {@code Observable} has to apply one of * the {@code onBackpressureXXX} operators. *

Scheduler:
- *
{@code create} does not operate by default on a particular {@link Scheduler}.
+ *
{@code unsafeCreate} does not operate by default on a particular {@link Scheduler}.
* - * + *

History: 1.2.7 - experimental * @param * the type of the items that this Observable emits * @param f @@ -95,8 +156,9 @@ protected Observable(OnSubscribe f) { * @return an Observable that, when a {@link Subscriber} subscribes to it, will execute the specified * function * @see ReactiveX operators documentation: Create + * @since 1.3 */ - public static Observable create(OnSubscribe f) { + public static Observable unsafeCreate(OnSubscribe f) { return new Observable(RxJavaHooks.onCreate(f)); } @@ -111,7 +173,7 @@ public static Observable create(OnSubscribe f) { * {@link Observable#create(AsyncOnSubscribe) asynchronous overload}. * *

- * + * *

* See Rx Design Guidelines (PDF) for detailed * information. @@ -140,7 +202,7 @@ public static Observable create(OnSubscribe f) { * @since 1.2 */ public static Observable create(SyncOnSubscribe syncOnSubscribe) { - return create((OnSubscribe)syncOnSubscribe); + return unsafeCreate(syncOnSubscribe); } /** @@ -153,7 +215,7 @@ public static Observable create(SyncOnSubscribe syncOnSubscribe) * with the {@link Observable#create(SyncOnSubscribe) synchronous overload}. * *

- * + * *

* See Rx Design Guidelines (PDF) for detailed * information. @@ -179,11 +241,11 @@ public static Observable create(SyncOnSubscribe syncOnSubscribe) * @see AsyncOnSubscribe#createStateless(Action2) * @see AsyncOnSubscribe#createStateless(Action2, Action0) * @see ReactiveX operators documentation: Create - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 - beta */ - @Experimental + @Beta public static Observable create(AsyncOnSubscribe asyncOnSubscribe) { - return create((OnSubscribe)asyncOnSubscribe); + return unsafeCreate(asyncOnSubscribe); } /** @@ -234,7 +296,7 @@ public interface Operator extends Func1, Subscriber< * @see RxJava wiki: Implementing Your Own Operators */ public final Observable lift(final Operator operator) { - return create(new OnSubscribeLift(onSubscribe, operator)); + return unsafeCreate(new OnSubscribeLift(onSubscribe, operator)); } /** @@ -286,8 +348,8 @@ public interface Transformer extends Func1, Observable> { * @param the resulting object type * @param converter the function that receives the current Observable instance and returns a value * @return the value returned by the function + * @since 1.3 */ - @Experimental public final R to(Func1, R> converter) { return converter.call(this); } @@ -323,7 +385,7 @@ public Single toSingle() { * {@code ignoreAllElements()}) and calls onCompleted when this source observable calls * onCompleted. Error terminal events are propagated. *

- * *

@@ -338,10 +400,8 @@ public Single toSingle() { * calls onCompleted * @see ReactiveX documentation: * Completable - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical - * with the release number) + * @since 1.3 */ - @Beta public Completable toCompletable() { return Completable.fromObservable(this); } @@ -373,7 +433,7 @@ public Completable toCompletable() { * @see ReactiveX operators documentation: Amb */ public static Observable amb(Iterable> sources) { - return create(OnSubscribeAmb.amb(sources)); + return unsafeCreate(OnSubscribeAmb.amb(sources)); } /** @@ -399,7 +459,7 @@ public static Observable amb(Iterable> * @see ReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2) { - return create(OnSubscribeAmb.amb(o1, o2)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2)); } /** @@ -427,7 +487,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3) { - return create(OnSubscribeAmb.amb(o1, o2, o3)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3)); } /** @@ -457,7 +517,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { - return create(OnSubscribeAmb.amb(o1, o2, o3, o4)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3, o4)); } /** @@ -489,7 +549,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { - return create(OnSubscribeAmb.amb(o1, o2, o3, o4, o5)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3, o4, o5)); } /** @@ -523,7 +583,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { - return create(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6)); } /** @@ -559,7 +619,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { - return create(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6, o7)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6, o7)); } /** @@ -597,7 +657,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { - return create(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8)); } /** @@ -637,7 +697,7 @@ public static Observable amb(Observable o1, ObservableReactiveX operators documentation: Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { - return create(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8, o9)); + return unsafeCreate(OnSubscribeAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8, o9)); } /** @@ -1020,7 +1080,7 @@ public static Observable combineLates * @see ReactiveX operators documentation: CombineLatest */ public static Observable combineLatest(List> sources, FuncN combineFunction) { - return create(new OnSubscribeCombineLatest(sources, combineFunction)); + return unsafeCreate(new OnSubscribeCombineLatest(sources, combineFunction)); } /** @@ -1049,7 +1109,7 @@ public static Observable combineLatest(ListReactiveX operators documentation: CombineLatest */ public static Observable combineLatest(Iterable> sources, FuncN combineFunction) { - return create(new OnSubscribeCombineLatest(sources, combineFunction)); + return unsafeCreate(new OnSubscribeCombineLatest(sources, combineFunction)); } /** @@ -1080,7 +1140,7 @@ public static Observable combineLatest(IterableReactiveX operators documentation: CombineLatest */ public static Observable combineLatestDelayError(Iterable> sources, FuncN combineFunction) { - return create(new OnSubscribeCombineLatest(null, sources, combineFunction, RxRingBuffer.SIZE, true)); + return unsafeCreate(new OnSubscribeCombineLatest(null, sources, combineFunction, RxRingBuffer.SIZE, true)); } /** @@ -1430,10 +1490,9 @@ public static Observable concat(Observable t1, Observable the common element base type * @param sources the Observable sequence of Observables * @return the new Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ @SuppressWarnings({ "rawtypes", "unchecked" }) - @Beta public static Observable concatDelayError(Observable> sources) { return sources.concatMapDelayError((Func1)UtilityFunctions.identity()); } @@ -1455,9 +1514,8 @@ public static Observable concatDelayError(Observable the common element base type * @param sources the Iterable sequence of Observables * @return the new Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Iterable> sources) { return concatDelayError(from(sources)); } @@ -1482,9 +1540,8 @@ public static Observable concatDelayError(Iterable Observable concatDelayError(Observable t1, Observable t2) { return concatDelayError(just(t1, t2)); } @@ -1511,9 +1568,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t3 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2,Observable t3 ) { return concatDelayError(just(t1, t2, t3)); } @@ -1542,9 +1598,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t4 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2, Observable t3, Observable t4) { return concatDelayError(just(t1, t2, t3, t4)); } @@ -1575,9 +1630,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t5 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { return concatDelayError(just(t1, t2, t3, t4, t5)); } @@ -1610,9 +1664,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t6 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { return concatDelayError(just(t1, t2, t3, t4, t5, t6)); } @@ -1647,9 +1700,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t7 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { return concatDelayError(just(t1, t2, t3, t4, t5, t6, t7)); } @@ -1686,9 +1738,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t8 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { return concatDelayError(just(t1, t2, t3, t4, t5, t6, t7, t8)); } @@ -1727,9 +1778,8 @@ public static Observable concatDelayError(Observable t1, Obs * @param t9 * an Observable to be concatenated * @return an Observable with the concatenating behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable concatDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { return concatDelayError(just(t1, t2, t3, t4, t5, t6, t7, t8, t9)); } @@ -1762,7 +1812,7 @@ public static Observable concatDelayError(Observable t1, Obs * @see ReactiveX operators documentation: Defer */ public static Observable defer(Func0> observableFactory) { - return create(new OnSubscribeDefer(observableFactory)); + return unsafeCreate(new OnSubscribeDefer(observableFactory)); } /** @@ -1808,7 +1858,7 @@ public static Observable empty() { * @see ReactiveX operators documentation: Throw */ public static Observable error(Throwable exception) { - return create(new OnSubscribeThrow(exception)); + return unsafeCreate(new OnSubscribeThrow(exception)); } /** @@ -1838,7 +1888,7 @@ public static Observable error(Throwable exception) { */ @SuppressWarnings("cast") public static Observable from(Future future) { - return (Observable)create(OnSubscribeToObservableFuture.toObservableFuture(future)); + return (Observable)unsafeCreate(OnSubscribeToObservableFuture.toObservableFuture(future)); } /** @@ -1872,7 +1922,7 @@ public static Observable from(Future future) { */ @SuppressWarnings("cast") public static Observable from(Future future, long timeout, TimeUnit unit) { - return (Observable)create(OnSubscribeToObservableFuture.toObservableFuture(future, timeout, unit)); + return (Observable)unsafeCreate(OnSubscribeToObservableFuture.toObservableFuture(future, timeout, unit)); } /** @@ -1904,7 +1954,7 @@ public static Observable from(Future future, long timeout, T public static Observable from(Future future, Scheduler scheduler) { // TODO in a future revision the Scheduler will become important because we'll start polling instead of blocking on the Future @SuppressWarnings("cast") - Observable o = (Observable)create(OnSubscribeToObservableFuture.toObservableFuture(future)); + Observable o = (Observable)unsafeCreate(OnSubscribeToObservableFuture.toObservableFuture(future)); return o.subscribeOn(scheduler); } @@ -1929,7 +1979,7 @@ public static Observable from(Future future, Scheduler sched * @see ReactiveX operators documentation: From */ public static Observable from(Iterable iterable) { - return create(new OnSubscribeFromIterable(iterable)); + return unsafeCreate(new OnSubscribeFromIterable(iterable)); } /** @@ -1959,53 +2009,7 @@ public static Observable from(T[] array) { if (n == 1) { return just(array[0]); } - return create(new OnSubscribeFromArray(array)); - } - - /** - * Provides an API (via a cold Observable) that bridges the reactive world with the callback-style, - * generally non-backpressured world. - *

- * Example: - *


-     * Observable.<Event>fromEmitter(emitter -> {
-     *     Callback listener = new Callback() {
-     *         @Override
-     *         public void onEvent(Event e) {
-     *             emitter.onNext(e);
-     *             if (e.isLast()) {
-     *                 emitter.onCompleted();
-     *             }
-     *         }
-     *
-     *         @Override
-     *         public void onFailure(Exception e) {
-     *             emitter.onError(e);
-     *         }
-     *     };
-     *
-     *     AutoCloseable c = api.someMethod(listener);
-     *
-     *     emitter.setCancellation(c::close);
-     *
-     * }, BackpressureMode.BUFFER);
-     * 
- *

- * You should call the Emitter's onNext, onError and onCompleted methods in a serialized fashion. The - * rest of its methods are thread-safe. - * - * @param the element type - * @param emitter the emitter that is called when a Subscriber subscribes to the returned {@code Observable} - * @param backpressure the backpressure mode to apply if the downstream Subscriber doesn't request (fast) enough - * @return the new Observable instance - * @see Emitter - * @see Emitter.BackpressureMode - * @see rx.functions.Cancellable - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) - */ - @Experimental - public static Observable fromEmitter(Action1> emitter, Emitter.BackpressureMode backpressure) { - return create(new OnSubscribeFromEmitter(emitter, backpressure)); + return unsafeCreate(new OnSubscribeFromArray(array)); } /** @@ -2033,7 +2037,7 @@ public static Observable fromEmitter(Action1> emitter, Emitter * @since 1.2 */ public static Observable fromCallable(Callable func) { - return create(new OnSubscribeFromCallable(func)); + return unsafeCreate(new OnSubscribeFromCallable(func)); } /** @@ -2140,7 +2144,7 @@ public static Observable interval(long initialDelay, long period, TimeUnit * @since 1.0.12 */ public static Observable interval(long initialDelay, long period, TimeUnit unit, Scheduler scheduler) { - return create(new OnSubscribeTimerPeriodically(initialDelay, period, unit, scheduler)); + return unsafeCreate(new OnSubscribeTimerPeriodically(initialDelay, period, unit, scheduler)); } /** @@ -3002,9 +3006,8 @@ public static Observable mergeDelayError(ObservableReactiveX operators documentation: Merge - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable mergeDelayError(Observable> source, int maxConcurrent) { return source.lift(OperatorMerge.instance(true, maxConcurrent)); } @@ -3470,10 +3473,10 @@ public static Observable range(int start, int count) { if (start > Integer.MAX_VALUE - count + 1) { throw new IllegalArgumentException("start + count can not exceed Integer.MAX_VALUE"); } - if(count == 1) { + if (count == 1) { return Observable.just(start); } - return Observable.create(new OnSubscribeRange(start, start + (count - 1))); + return Observable.unsafeCreate(new OnSubscribeRange(start, start + (count - 1))); } /** @@ -3618,10 +3621,8 @@ public static Observable switchOnNext(ObservableReactiveX operators documentation: Switch - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable switchOnNextDelayError(Observable> sequenceOfSequences) { return sequenceOfSequences.lift(OperatorSwitch.instance(true)); } @@ -3733,7 +3734,7 @@ public static Observable timer(long delay, TimeUnit unit) { * @see ReactiveX operators documentation: Timer */ public static Observable timer(long delay, TimeUnit unit, Scheduler scheduler) { - return create(new OnSubscribeTimerOnce(delay, unit, scheduler)); + return unsafeCreate(new OnSubscribeTimerOnce(delay, unit, scheduler)); } /** @@ -3795,15 +3796,13 @@ public static Observable using( * a terminal event ({@code onComplete} or {@code onError}). * @return the Observable whose lifetime controls the lifetime of the dependent resource object * @see ReactiveX operators documentation: Using - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Observable using( final Func0 resourceFactory, final Func1> observableFactory, final Action1 disposeAction, boolean disposeEagerly) { - return create(new OnSubscribeUsing(resourceFactory, observableFactory, disposeAction, disposeEagerly)); + return unsafeCreate(new OnSubscribeUsing(resourceFactory, observableFactory, disposeAction, disposeEagerly)); } /** @@ -3900,8 +3899,8 @@ public static Observable zip(Iterable> ws, FuncN< * an item that will be emitted by the resulting Observable * @return an Observable that emits the zipped results * @see ReactiveX operators documentation: Zip + * @since 1.3 */ - @Experimental public static Observable zip(Observable[] ws, FuncN zipFunction) { return Observable.just(ws).lift(new OperatorZip(zipFunction)); } @@ -5070,7 +5069,7 @@ public final Observable collect(Func0 stateFactory, final Action2(this, stateFactory, collector)); + return unsafeCreate(new OnSubscribeCollect(this, stateFactory, collector)); } /** @@ -5103,7 +5102,7 @@ public final Observable concatMap(Func1 scalar = (ScalarSynchronousObservable) this; return scalar.scalarFlatMap(func); } - return create(new OnSubscribeConcatMap(this, func, 2, OnSubscribeConcatMap.IMMEDIATE)); + return unsafeCreate(new OnSubscribeConcatMap(this, func, 2, OnSubscribeConcatMap.IMMEDIATE)); } /** @@ -5126,15 +5125,14 @@ public final Observable concatMap(Func1 the result value type * @param func the function that maps the items of this Observable into the inner Observables. * @return the new Observable instance with the concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Observable concatMapDelayError(Func1> func) { if (this instanceof ScalarSynchronousObservable) { ScalarSynchronousObservable scalar = (ScalarSynchronousObservable) this; return scalar.scalarFlatMap(func); } - return create(new OnSubscribeConcatMap(this, func, 2, OnSubscribeConcatMap.END)); + return unsafeCreate(new OnSubscribeConcatMap(this, func, 2, OnSubscribeConcatMap.END)); } /** @@ -5390,6 +5388,8 @@ public final Observable defaultIfEmpty(final T defaultValue) { * Returns an Observable that emits the items emitted by the source Observable or the items of an alternate * Observable if the source Observable is empty. *

+ *

+ * *

*
Backpressure:
*
If the source {@code Observable} is empty, the alternate {@code Observable} is expected to honor backpressure. @@ -5413,7 +5413,7 @@ public final Observable switchIfEmpty(Observable alternate) { if (alternate == null) { throw new NullPointerException("alternate is null"); } - return lift(new OperatorSwitchIfEmpty(alternate)); + return unsafeCreate(new OnSubscribeSwitchIfEmpty(this, alternate)); } /** @@ -5578,7 +5578,7 @@ public final Observable delaySubscription(long delay, TimeUnit unit) { * @see ReactiveX operators documentation: Delay */ public final Observable delaySubscription(long delay, TimeUnit unit, Scheduler scheduler) { - return create(new OnSubscribeDelaySubscription(this, delay, unit, scheduler)); + return unsafeCreate(new OnSubscribeDelaySubscription(this, delay, unit, scheduler)); } /** @@ -5604,7 +5604,7 @@ public final Observable delaySubscription(long delay, TimeUnit unit, Schedule * @see ReactiveX operators documentation: Delay */ public final Observable delaySubscription(Func0> subscriptionDelay) { - return create(new OnSubscribeDelaySubscriptionWithSelector(this, subscriptionDelay)); + return unsafeCreate(new OnSubscribeDelaySubscriptionWithSelector(this, subscriptionDelay)); } /** @@ -5624,14 +5624,13 @@ public final Observable delaySubscription(Func0> * to this Observable. * @return an Observable that delays the subscription to this Observable * until the other Observable emits an element or completes normally. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Observable delaySubscription(Observable other) { if (other == null) { throw new NullPointerException(); } - return create(new OnSubscribeDelaySubscriptionOther(this, other)); + return unsafeCreate(new OnSubscribeDelaySubscriptionOther(this, other)); } /** @@ -5768,10 +5767,8 @@ public final Observable distinctUntilChanged(Func1ReactiveX operators documentation: Distinct - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical - * with the release number) + * @since 1.3 */ - @Beta public final Observable distinctUntilChanged(Func2 comparator) { return lift(new OperatorDistinctUntilChanged(comparator)); } @@ -5798,7 +5795,7 @@ public final Observable doOnCompleted(final Action0 onCompleted) { Action1 onError = Actions.empty(); Observer observer = new ActionObserver(onNext, onError, onCompleted); - return create(new OnSubscribeDoOnEach(this, observer)); + return unsafeCreate(new OnSubscribeDoOnEach(this, observer)); } /** @@ -5820,7 +5817,7 @@ public final Observable doOnCompleted(final Action0 onCompleted) { */ public final Observable doOnEach(final Action1> onNotification) { Observer observer = new ActionNotificationObserver(onNotification); - return create(new OnSubscribeDoOnEach(this, observer)); + return unsafeCreate(new OnSubscribeDoOnEach(this, observer)); } /** @@ -5847,7 +5844,7 @@ public final Observable doOnEach(final Action1> onNot * @see ReactiveX operators documentation: Do */ public final Observable doOnEach(Observer observer) { - return create(new OnSubscribeDoOnEach(this, observer)); + return unsafeCreate(new OnSubscribeDoOnEach(this, observer)); } /** @@ -5875,7 +5872,7 @@ public final Observable doOnError(final Action1 onError) { Action0 onCompleted = Actions.empty(); Observer observer = new ActionObserver(onNext, onError, onCompleted); - return create(new OnSubscribeDoOnEach(this, observer)); + return unsafeCreate(new OnSubscribeDoOnEach(this, observer)); } /** @@ -5900,7 +5897,7 @@ public final Observable doOnNext(final Action1 onNext) { Action0 onCompleted = Actions.empty(); Observer observer = new ActionObserver(onNext, onError, onCompleted); - return create(new OnSubscribeDoOnEach(this, observer)); + return unsafeCreate(new OnSubscribeDoOnEach(this, observer)); } /** @@ -5981,7 +5978,7 @@ public final Observable doOnTerminate(final Action0 onTerminate) { Observer observer = new ActionObserver(onNext, onError, onTerminate); - return create(new OnSubscribeDoOnEach(this, observer)); + return unsafeCreate(new OnSubscribeDoOnEach(this, observer)); } /** @@ -6030,9 +6027,8 @@ public final Observable doOnUnsubscribe(final Action0 unsubscribe) { * @param o1 the first source * @param o2 the second source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager(Observable o1, Observable o2) { return concatEager(Arrays.asList(o1, o2)); @@ -6056,9 +6052,8 @@ public static Observable concatEager(Observable o1, Observab * @param o2 the second source * @param o3 the third source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6086,9 +6081,8 @@ public static Observable concatEager( * @param o3 the third source * @param o4 the fourth source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6117,9 +6111,8 @@ public static Observable concatEager( * @param o4 the fourth source * @param o5 the fifth source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6150,9 +6143,8 @@ public static Observable concatEager( * @param o5 the fifth source * @param o6 the sixth source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6184,9 +6176,8 @@ public static Observable concatEager( * @param o6 the sixth source * @param o7 the seventh source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6220,9 +6211,8 @@ public static Observable concatEager( * @param o7 the seventh source * @param o8 the eighth source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6257,9 +6247,8 @@ public static Observable concatEager( * @param o8 the eighth source * @param o9 the ninth source * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings("unchecked") public static Observable concatEager( Observable o1, Observable o2, @@ -6287,9 +6276,8 @@ public static Observable concatEager( * @param the value type * @param sources a sequence of Observables that need to be eagerly concatenated * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings({ "unchecked", "rawtypes" }) public static Observable concatEager(Iterable> sources) { return Observable.from(sources).concatMapEager((Func1)UtilityFunctions.identity()); @@ -6312,9 +6300,8 @@ public static Observable concatEager(Iterable Observable concatEager(Iterable> sources, int capacityHint) { return Observable.from(sources).concatMapEager((Func1)UtilityFunctions.identity(), capacityHint); @@ -6336,9 +6323,8 @@ public static Observable concatEager(Iterable the value type * @param sources a sequence of Observables that need to be eagerly concatenated * @return the new Observable instance with the specified concatenation behavior - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta @SuppressWarnings({ "unchecked", "rawtypes" }) public static Observable concatEager(Observable> sources) { return sources.concatMapEager((Func1)UtilityFunctions.identity()); @@ -6361,9 +6347,8 @@ public static Observable concatEager(Observable Observable concatEager(Observable> sources, int capacityHint) { return sources.concatMapEager((Func1)UtilityFunctions.identity(), capacityHint); @@ -6387,9 +6372,8 @@ public static Observable concatEager(Observable Observable concatMapEager(Func1> mapper) { return concatMapEager(mapper, RxRingBuffer.SIZE); } @@ -6413,9 +6397,8 @@ public final Observable concatMapEager(Func1 Observable concatMapEager(Func1> mapper, int capacityHint) { if (capacityHint < 1) { throw new IllegalArgumentException("capacityHint > 0 required but it was " + capacityHint); @@ -6443,9 +6426,8 @@ public final Observable concatMapEager(Func1 Observable concatMapEager(Func1> mapper, int capacityHint, int maxConcurrent) { if (capacityHint < 1) { throw new IllegalArgumentException("capacityHint > 0 required but it was " + capacityHint); @@ -6557,7 +6539,7 @@ public final Observable exists(Func1 predicate) { * @see ReactiveX operators documentation: Filter */ public final Observable filter(Func1 predicate) { - return create(new OnSubscribeFilter(this, predicate)); + return unsafeCreate(new OnSubscribeFilter(this, predicate)); } /** @@ -6905,6 +6887,74 @@ public final Observable flatMap(final Func1(collectionSelector, resultSelector)), maxConcurrent); } + /** + * Maps all upstream values to Completables and runs them together until the upstream + * and all inner Completables complete normally. + *
+ *
Backpressure:
+ *
The operator consumes items from upstream in an unbounded manner and ignores downstream backpressure + * as it doesn't emit items but only terminal event.
+ *
Scheduler:
+ *
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param mapper the function that receives an upstream value and turns it into a Completable + * to be merged. + * @return the new Observable instance + * @see #flatMapCompletable(Func1, boolean, int) + * @since 1.3 + */ + public final Observable flatMapCompletable(Func1 mapper) { + return flatMapCompletable(mapper, false, Integer.MAX_VALUE); + } + + /** + * Maps all upstream values to Completables and runs them together, optionally delaying any errors, until the upstream + * and all inner Completables terminate. + *

+ *
Backpressure:
+ *
The operator consumes items from upstream in an unbounded manner and ignores downstream backpressure + * as it doesn't emit items but only terminal event.
+ *
Scheduler:
+ *
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param mapper the function that receives an upstream value and turns it into a Completable + * to be merged. + * @param delayErrors if true, errors from the upstream and from the inner Completables get delayed till + * the all of them terminate. + * @return the new Observable instance + * @since 1.3 + * @see #flatMapCompletable(Func1, boolean, int) + */ + public final Observable flatMapCompletable(Func1 mapper, boolean delayErrors) { + return flatMapCompletable(mapper, delayErrors, Integer.MAX_VALUE); + } + + /** + * Maps upstream values to Completables and runs up to the given number of them together at a time, + * optionally delaying any errors, until the upstream and all inner Completables terminate. + *

+ *
Backpressure:
+ *
The operator consumes at most maxConcurrent items from upstream and one-by-one after as the inner + * Completables terminate. The operator ignores downstream backpressure as it doesn't emit items but + * only the terminal event.
+ *
Scheduler:
+ *
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param mapper the function that receives an upstream value and turns it into a Completable + * to be merged. + * @param delayErrors if true, errors from the upstream and from the inner Completables get delayed till + * the all of them terminate. + * @param maxConcurrency the maximum number of inner Completables to run at a time + * @return the new Observable instance + * @since 1.3 + */ + public final Observable flatMapCompletable(Func1 mapper, boolean delayErrors, int maxConcurrency) { + return unsafeCreate(new OnSubscribeFlatMapCompletable(this, mapper, delayErrors, maxConcurrency)); + } + /** * Returns an Observable that merges each item emitted by the source Observable with the values in an * Iterable corresponding to that item that is generated by a selector. @@ -7038,6 +7088,74 @@ public final Observable flatMapIterable(Func1)flatMap(OperatorMapPair.convertSelector(collectionSelector), resultSelector, maxConcurrent); } + /** + * Maps all upstream values to Singles and runs them together until the upstream + * and all inner Singles complete normally. + *

+ *
Backpressure:
+ *
The operator consumes items from upstream in an unbounded manner and honors downstream backpressure.
+ *
Scheduler:
+ *
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param mapper the function that receives an upstream value and turns it into a Single + * to be merged. + * @return the new Observable instance + * @see #flatMapSingle(Func1, boolean, int) + * @since 1.3 + */ + public final Observable flatMapSingle(Func1> mapper) { + return flatMapSingle(mapper, false, Integer.MAX_VALUE); + } + + /** + * Maps all upstream values to Singles and runs them together, optionally delaying any errors, until the upstream + * and all inner Singles terminate. + *

+ *
Backpressure:
+ *
The operator consumes items from upstream in an unbounded manner and honors downstream backpressure.
+ *
Scheduler:
+ *
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param mapper the function that receives an upstream value and turns it into a Single + * to be merged. + * @param delayErrors if true, errors from the upstream and from the inner Singles get delayed till + * the all of them terminate. + * @return the new Observable instance + * @since 1.3 + * @see #flatMapSingle(Func1, boolean, int) + */ + public final Observable flatMapSingle(Func1> mapper, boolean delayErrors) { + return flatMapSingle(mapper, delayErrors, Integer.MAX_VALUE); + } + + /** + * Maps upstream values to Singles and runs up to the given number of them together at a time, + * optionally delaying any errors, until the upstream and all inner Singles terminate. + *

+ *
Backpressure:
+ *
The operator consumes at most maxConcurrent items from upstream and one-by-one after as the inner + * Singles terminate. The operator honors downstream backpressure.
+ *
Scheduler:
+ *
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param mapper the function that receives an upstream value and turns it into a Single + * to be merged. + * @param delayErrors if true, errors from the upstream and from the inner Singles get delayed till + * the all of them terminate. + * @param maxConcurrency the maximum number of inner Singles to run at a time + * @return the new Observable instance + * @since 1.3 + */ + public final Observable flatMapSingle(Func1> mapper, boolean delayErrors, int maxConcurrency) { + return unsafeCreate(new OnSubscribeFlatMapSingle(this, mapper, delayErrors, maxConcurrency)); + } + /** * Subscribes to the {@link Observable} and receives notifications for each element. *

@@ -7157,7 +7275,7 @@ public final void forEach(final Action1 onNext, final Action1ReactiveX operators documentation: GroupBy */ public final Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { - return lift(new OperatorGroupBy(keySelector, elementSelector)); + return lift(new OperatorGroupByEvicting(keySelector, elementSelector)); } /** @@ -7215,8 +7333,13 @@ public final Observable> groupBy(final Func1ReactiveX operators documentation: GroupBy + * @since 1.3 + * @deprecated since 1.3.7, use {@link #groupBy(Func1, Func1, int, boolean, Func1)} + * instead which uses much less memory. Please take note of the + * usage difference involving the evicting action which now expects + * the value from the map instead of the key. */ - @Experimental + @Deprecated public final Observable> groupBy(final Func1 keySelector, final Func1 elementSelector, final Func1, Map> evictingMapFactory) { if (evictingMapFactory == null) { @@ -7251,6 +7374,72 @@ public final Observable> groupBy(final Func1 + * {@code + * Func1, Map> mapFactory + * = action -> CacheBuilder.newBuilder() + * .maximumSize(1000) + * .expireAfterAccess(12, TimeUnit.HOURS) + * .removalListener(entry -> action.call(entry.getValue())) + * . build().asMap(); + * } + * + * + * @param + * the key type + * @param + * the element type + * @return an {@code Observable} that emits {@link GroupedObservable}s, each of which corresponds to a + * unique key value and each of which emits those items from the source Observable that share that + * key value + * @throws NullPointerException + * if {@code evictingMapFactory} is null + * @see ReactiveX operators documentation: GroupBy + * @since 1.3.7 + */ + @Experimental + public final Observable> groupBy(final Func1 keySelector, + final Func1 elementSelector, int bufferSize, boolean delayError, + final Func1, Map> evictingMapFactory) { + if (evictingMapFactory == null) { + throw new NullPointerException("evictingMapFactory cannot be null"); + } + return lift(new OperatorGroupByEvicting( + keySelector, elementSelector, bufferSize, delayError, evictingMapFactory)); + } + + /** + * Groups the items emitted by an {@code Observable} according to a specified criterion, and emits these + * grouped items as {@link GroupedObservable}s. The emitted {@code GroupedObservable} allows only a single + * {@link Subscriber} during its lifetime and if this {@code Subscriber} unsubscribes before the + * source terminates, the next emission by the source having the same key will trigger a new + * {@code GroupedObservable} emission. + *

+ * + *

+ * Note: A {@link GroupedObservable} will cache the items it is to emit until such time as it + * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those + * {@code GroupedObservable}s that do not concern you. Instead, you can signal to them that they may + * discard their buffers by applying an operator like {@link #ignoreElements} to them. + *

+ *
Scheduler:
+ *
{@code groupBy} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param keySelector + * a function that extracts the key for each item * @param * the key type * @return an {@code Observable} that emits {@link GroupedObservable}s, each of which corresponds to a @@ -7259,7 +7448,7 @@ public final Observable> groupBy(final Func1ReactiveX operators documentation: GroupBy */ public final Observable> groupBy(final Func1 keySelector) { - return lift(new OperatorGroupBy(keySelector)); + return lift(new OperatorGroupByEvicting(keySelector)); } /** @@ -7299,7 +7488,7 @@ public final Observable> groupBy(final Func1 Observable groupJoin(Observable right, Func1> leftDuration, Func1> rightDuration, Func2, ? extends R> resultSelector) { - return create(new OnSubscribeGroupJoin(this, right, leftDuration, rightDuration, resultSelector)); + return unsafeCreate(new OnSubscribeGroupJoin(this, right, leftDuration, rightDuration, resultSelector)); } /** @@ -7381,7 +7570,7 @@ public final Observable isEmpty() { public final Observable join(Observable right, Func1> leftDurationSelector, Func1> rightDurationSelector, Func2 resultSelector) { - return create(new OnSubscribeJoin(this, right, leftDurationSelector, rightDurationSelector, resultSelector)); + return unsafeCreate(new OnSubscribeJoin(this, right, leftDurationSelector, rightDurationSelector, resultSelector)); } /** @@ -7529,7 +7718,7 @@ public final Observable limit(int count) { * @see ReactiveX operators documentation: Map */ public final Observable map(Func1 func) { - return create(new OnSubscribeMap(this, func)); + return unsafeCreate(new OnSubscribeMap(this, func)); } private Observable mapNotification(Func1 onNext, Func1 onError, Func0 onCompleted) { @@ -7845,9 +8034,8 @@ public final Observable onBackpressureBuffer(long capacity, Action0 onOverflo * @param overflowStrategy how should the {@code Observable} react to buffer overflows. Null is not allowed. * @return the source {@code Observable} modified to buffer items up to the given capacity * @see ReactiveX operators documentation: backpressure operators - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Observable onBackpressureBuffer(long capacity, Action0 onOverflow, BackpressureOverflow.Strategy overflowStrategy) { return lift(new OperatorOnBackpressureBuffer(capacity, onOverflow, overflowStrategy)); } @@ -7871,7 +8059,6 @@ public final Observable onBackpressureBuffer(long capacity, Action0 onOverflo * @param onDrop the action to invoke for each item dropped. onDrop action should be fast and should never block. * @return the source Observable modified to drop {@code onNext} notifications on overflow * @see ReactiveX operators documentation: backpressure operators - * @Experimental The behavior of this can change at any time. * @since 1.1.0 */ public final Observable onBackpressureDrop(Action1 onDrop) { @@ -8102,11 +8289,10 @@ public final Observable onExceptionResumeNext(final Observable r *
* @return an Observable which out references to the upstream producer and downstream Subscriber if * the sequence is terminated or downstream unsubscribes - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable onTerminateDetach() { - return create(new OnSubscribeDetach(this)); + return unsafeCreate(new OnSubscribeDetach(this)); } /** @@ -8177,9 +8363,8 @@ public final Observable publish(Func1, ? extends Ob * * @param n the initial request amount, further request will happen after 75% of this value * @return the Observable that rebatches request amounts from downstream - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable rebatchRequests(int n) { if (n <= 0) { throw new IllegalArgumentException("n > 0 required but it was " + n); @@ -8223,7 +8408,7 @@ public final Observable reduce(Func2 accumulator) { * * It should use last() not takeLast(1) since it needs to emit an error if the sequence is empty. */ - return create(new OnSubscribeReduce(this, accumulator)); + return unsafeCreate(new OnSubscribeReduce(this, accumulator)); } /** @@ -8271,7 +8456,7 @@ public final Observable reduce(Func2 accumulator) { * @see Wikipedia: Fold (higher-order function) */ public final Observable reduce(R initialValue, Func2 accumulator) { - return create(new OnSubscribeReduceSeed(this, initialValue, accumulator)); + return unsafeCreate(new OnSubscribeReduceSeed(this, initialValue, accumulator)); } /** @@ -9098,7 +9283,7 @@ public final Observable retryWhen(final Func1 - * + * *
*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -9124,7 +9309,7 @@ public final Observable sample(long period, TimeUnit unit) { * Returns an Observable that emits the most recently emitted item (if any) emitted by the source Observable * within periodic time intervals, where the intervals are defined on a particular Scheduler. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -9484,7 +9669,7 @@ public final Observable skip(long time, TimeUnit unit) { * @see ReactiveX operators documentation: Skip */ public final Observable skip(long time, TimeUnit unit, Scheduler scheduler) { - return create(new OnSubscribeSkipTimed(this, time, unit, scheduler)); + return unsafeCreate(new OnSubscribeSkipTimed(this, time, unit, scheduler)); } /** @@ -10265,11 +10450,15 @@ static Subscription subscribe(Subscriber subscriber, Observable + * If there is a {@link #create(Action1, rx.Emitter.BackpressureMode)} type source up in the + * chain, it is recommended to use {@code subscribeOn(scheduler, false)} instead + * to avoid same-pool deadlock because requests pile up behind a eager/blocking emitter. + *

* *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Observable}'s backpressure - * behavior.
+ *
The operator doesn't interfere with backpressure amount which is determined by the source {@code Observable}'s backpressure + * behavior. However, the upstream is requested from the given scheduler thread.
*
Scheduler:
*
you specify which {@link Scheduler} this operator will use
*
@@ -10281,12 +10470,47 @@ static Subscription subscribe(Subscriber subscriber, ObservableReactiveX operators documentation: SubscribeOn * @see RxJava Threading Examples * @see #observeOn + * @see #subscribeOn(Scheduler, boolean) */ public final Observable subscribeOn(Scheduler scheduler) { + return subscribeOn(scheduler, !(this.onSubscribe instanceof OnSubscribeCreate)); + } + + /** + * Asynchronously subscribes Observers to this Observable on the specified {@link Scheduler} and + * optionally reroutes requests from other threads to the same {@link Scheduler} thread. + *

+ * If there is a {@link #create(Action1, rx.Emitter.BackpressureMode)} type source up in the + * chain, it is recommended to have {@code requestOn} false to avoid same-pool deadlock + * because requests pile up behind a eager/blocking emitter. + *

+ * + *

+ *
Backpressure:
+ *
The operator doesn't interfere with backpressure amount which is determined by the source {@code Observable}'s backpressure + * behavior. However, the upstream is requested from the given scheduler if requestOn is true.
+ *
Scheduler:
+ *
you specify which {@link Scheduler} this operator will use
+ *
+ *

History: 1.2.7 - experimental + * @param scheduler + * the {@link Scheduler} to perform subscription actions on + * @param requestOn if true, requests are rerouted to the given Scheduler as well (strong pipelining) + * if false, requests coming from any thread are simply forwarded to + * the upstream on the same thread (weak pipelining) + * @return the source Observable modified so that its subscriptions happen on the + * specified {@link Scheduler} + * @see ReactiveX operators documentation: SubscribeOn + * @see RxJava Threading Examples + * @see #observeOn + * @see #subscribeOn(Scheduler) + * @since 1.3 + */ + public final Observable subscribeOn(Scheduler scheduler, boolean requestOn) { if (this instanceof ScalarSynchronousObservable) { return ((ScalarSynchronousObservable)this).scalarScheduleOn(scheduler); } - return create(new OperatorSubscribeOn(this, scheduler)); + return unsafeCreate(new OperatorSubscribeOn(this, scheduler, requestOn)); } /** @@ -10345,10 +10569,8 @@ public final Observable switchMap(Func1ReactiveX operators documentation: FlatMap - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Observable switchMapDelayError(Func1> func) { return switchOnNextDelayError(map(func)); } @@ -10385,6 +10607,9 @@ public final Observable take(final int count) { * Returns an Observable that emits those items emitted by source Observable before a specified time runs * out. *

+ * If time runs out before the {@code Observable} completes normally, the {@code onComplete} event will be + * signaled on the default {@code computation} {@link Scheduler}. + *

* *

*
Backpressure:
@@ -10409,6 +10634,9 @@ public final Observable take(long time, TimeUnit unit) { * Returns an Observable that emits those items emitted by source Observable before a specified time (on a * specified Scheduler) runs out. *

+ * If time runs out before the {@code Observable} completes normally, the {@code onComplete} event will be + * signaled on the provided {@link Scheduler}. + *

* *

*
Backpressure:
@@ -10481,7 +10709,7 @@ public final Observable takeLast(final int count) { if (count == 0) { return ignoreElements(); } else if (count == 1) { - return create(new OnSubscribeTakeLastOne(this)); + return unsafeCreate(new OnSubscribeTakeLastOne(this)); } else { return lift(new OperatorTakeLast(count)); } @@ -11133,11 +11361,13 @@ public final Observable timeout(Func0> firstTi * if {@code timeoutSelector} is null * @see ReactiveX operators documentation: Timeout */ + @SuppressWarnings("unchecked") public final Observable timeout(Func0> firstTimeoutSelector, Func1> timeoutSelector, Observable other) { if (timeoutSelector == null) { throw new NullPointerException("timeoutSelector is null"); } - return lift(new OperatorTimeoutWithSelector(firstTimeoutSelector, timeoutSelector, other)); + return unsafeCreate(new OnSubscribeTimeoutSelectorWithFallback(this, + firstTimeoutSelector != null ? defer((Func0>)firstTimeoutSelector) : null, timeoutSelector, other)); } /** @@ -11292,7 +11522,7 @@ public final Observable timeout(long timeout, TimeUnit timeUnit, ObservableReactiveX operators documentation: Timeout */ public final Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { - return lift(new OperatorTimeout(timeout, timeUnit, other, scheduler)); + return unsafeCreate(new OnSubscribeTimeoutTimedWithFallback(this, timeout, timeUnit, scheduler, other)); } /** @@ -11439,7 +11669,7 @@ public final Observable> toList() { * @see ReactiveX operators documentation: To */ public final Observable> toMap(Func1 keySelector) { - return create(new OnSubscribeToMap(this, keySelector, UtilityFunctions.identity())); + return unsafeCreate(new OnSubscribeToMap(this, keySelector, UtilityFunctions.identity())); } /** @@ -11469,7 +11699,7 @@ public final Observable> toMap(Func1 keySe * @see ReactiveX operators documentation: To */ public final Observable> toMap(Func1 keySelector, Func1 valueSelector) { - return create(new OnSubscribeToMap(this, keySelector, valueSelector)); + return unsafeCreate(new OnSubscribeToMap(this, keySelector, valueSelector)); } /** @@ -11498,7 +11728,7 @@ public final Observable> toMap(Func1 ke * @see ReactiveX operators documentation: To */ public final Observable> toMap(Func1 keySelector, Func1 valueSelector, Func0> mapFactory) { - return create(new OnSubscribeToMap(this, keySelector, valueSelector, mapFactory)); + return unsafeCreate(new OnSubscribeToMap(this, keySelector, valueSelector, mapFactory)); } /** @@ -11521,7 +11751,7 @@ public final Observable> toMap(Func1 ke * @see ReactiveX operators documentation: To */ public final Observable>> toMultimap(Func1 keySelector) { - return create(new OnSubscribeToMultimap(this, keySelector, UtilityFunctions.identity())); + return unsafeCreate(new OnSubscribeToMultimap(this, keySelector, UtilityFunctions.identity())); } /** @@ -11549,7 +11779,7 @@ public final Observable>> toMultimap(Func1ReactiveX operators documentation: To */ public final Observable>> toMultimap(Func1 keySelector, Func1 valueSelector) { - return create(new OnSubscribeToMultimap(this, keySelector, valueSelector)); + return unsafeCreate(new OnSubscribeToMultimap(this, keySelector, valueSelector)); } /** @@ -11579,7 +11809,7 @@ public final Observable>> toMultimap(Func1ReactiveX operators documentation: To */ public final Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory) { - return create(new OnSubscribeToMultimap(this, keySelector, valueSelector, mapFactory)); + return unsafeCreate(new OnSubscribeToMultimap(this, keySelector, valueSelector, mapFactory)); } /** @@ -11611,7 +11841,7 @@ public final Observable>> toMultimap(Func1ReactiveX operators documentation: To */ public final Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory, Func1> collectionFactory) { - return create(new OnSubscribeToMultimap(this, keySelector, valueSelector, mapFactory, collectionFactory)); + return unsafeCreate(new OnSubscribeToMultimap(this, keySelector, valueSelector, mapFactory, collectionFactory)); } /** @@ -11685,9 +11915,8 @@ public final Observable> toSortedList(Func2ReactiveX operators documentation: To - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Observable> toSortedList(int initialCapacity) { return lift(new OperatorToObservableSortedList(initialCapacity)); } @@ -11713,9 +11942,8 @@ public final Observable> toSortedList(int initialCapacity) { * @return an Observable that emits a list that contains the items emitted by the source Observable in * sorted order * @see ReactiveX operators documentation: To - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Observable> toSortedList(Func2 sortFunction, int initialCapacity) { return lift(new OperatorToObservableSortedList(sortFunction, initialCapacity)); } @@ -11740,9 +11968,9 @@ public final Observable> toSortedList(Func2 sorted(){ + public final Observable sorted() { return toSortedList().flatMapIterable(UtilityFunctions.>identity()); } @@ -11765,8 +11993,8 @@ public final Observable sorted(){ * a function that compares two items emitted by the source Observable and returns an Integer * that indicates their sort order * @return an Observable that emits the items emitted by the source Observable in sorted order + * @since 1.3 */ - @Experimental public final Observable sorted(Func2 sortFunction) { return toSortedList(sortFunction).flatMapIterable(UtilityFunctions.>identity()); } @@ -11817,11 +12045,9 @@ public final Observable unsubscribeOn(Scheduler scheduler) { * @return an Observable that merges the specified Observable into this Observable by using the * {@code resultSelector} function only when the source Observable sequence (this instance) emits an * item - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 * @see ReactiveX operators documentation: CombineLatest */ - @Experimental public final Observable withLatestFrom(Observable other, Func2 resultSelector) { return lift(new OperatorWithLatestFrom(other, resultSelector)); } @@ -11850,12 +12076,10 @@ public final Observable withLatestFrom(Observable other, * @param o2 the second other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom(Observable o1, Observable o2, Func3 combiner) { - return create(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2 }, null, Functions.fromFunc(combiner))); + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2 }, null, Functions.fromFunc(combiner))); } /** @@ -11884,15 +12108,13 @@ public final Observable withLatestFrom(Observable o1, Observa * @param o3 the third other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom( Observable o1, Observable o2, Observable o3, Func4 combiner) { - return create(new OperatorWithLatestFromMany(this, + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2, o3 }, null, Functions.fromFunc(combiner))); } @@ -11924,15 +12146,13 @@ public final Observable withLatestFrom( * @param o4 the fourth other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom( Observable o1, Observable o2, Observable o3, Observable o4, Func5 combiner) { - return create(new OperatorWithLatestFromMany(this, + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2, o3, o4 }, null, Functions.fromFunc(combiner))); } /** @@ -11965,16 +12185,14 @@ public final Observable withLatestFrom( * @param o5 the fifth other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom( Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func6 combiner) { - return create(new OperatorWithLatestFromMany(this, + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2, o3, o4, o5 }, null, Functions.fromFunc(combiner))); } @@ -12010,16 +12228,14 @@ public final Observable withLatestFrom( * @param o6 the sixth other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom( Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func7 combiner) { - return create(new OperatorWithLatestFromMany(this, + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2, o3, o4, o5, o6 }, null, Functions.fromFunc(combiner))); } @@ -12057,17 +12273,15 @@ public final Observable withLatestFrom( * @param o7 the seventh other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom( Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func8 combiner) { - return create(new OperatorWithLatestFromMany(this, + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2, o3, o4, o5, o6, o7 }, null, Functions.fromFunc(combiner))); } @@ -12107,17 +12321,15 @@ public final Observable withLatestFrom( * @param o8 the eighth other Observable * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom( Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func9 combiner) { - return create(new OperatorWithLatestFromMany(this, + return unsafeCreate(new OperatorWithLatestFromMany(this, new Observable[] { o1, o2, o3, o4, o5, o6, o7, o8 }, null, Functions.fromFunc(combiner))); } @@ -12142,12 +12354,10 @@ public final Observable withLatestFrom( * @param others the array of other sources * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom(Observable[] others, FuncN combiner) { - return create(new OperatorWithLatestFromMany(this, others, null, combiner)); + return unsafeCreate(new OperatorWithLatestFromMany(this, others, null, combiner)); } /** @@ -12171,12 +12381,10 @@ public final Observable withLatestFrom(Observable[] others, FuncN c * @param others the iterable of other sources * @param combiner the function called with an array of values from each participating observable * @return the new Observable instance - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Observable withLatestFrom(Iterable> others, FuncN combiner) { - return create(new OperatorWithLatestFromMany(this, null, others, combiner)); + return unsafeCreate(new OperatorWithLatestFromMany(this, null, others, combiner)); } /** @@ -12655,10 +12863,10 @@ public final Observable zipWith(Observable other, Func2 *
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
+ *

History: 1.2.3 - experimental * @return the new AssertableSubscriber instance - * @since 1.2.3 + * @since 1.3 */ - @Experimental public final AssertableSubscriber test() { AssertableSubscriber ts = AssertableSubscriberObservable.create(Long.MAX_VALUE); subscribe(ts); @@ -12674,11 +12882,11 @@ public final AssertableSubscriber test() { *

Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
+ *

History: 1.2.3 - experimental * @return the new AssertableSubscriber instance * @param initialRequestAmount the amount to request from upstream upfront, non-negative (not verified) - * @since 1.2.3 + * @since 1.3 */ - @Experimental public final AssertableSubscriber test(long initialRequestAmount) { AssertableSubscriber ts = AssertableSubscriberObservable.create(initialRequestAmount); subscribe(ts); diff --git a/src/main/java/rx/Scheduler.java b/src/main/java/rx/Scheduler.java index 9dc789d463..89259b178b 100644 --- a/src/main/java/rx/Scheduler.java +++ b/src/main/java/rx/Scheduler.java @@ -17,10 +17,8 @@ import java.util.concurrent.TimeUnit; -import rx.annotations.Experimental; import rx.functions.*; -import rx.internal.schedulers.SchedulerWhen; -import rx.internal.subscriptions.SequentialSubscription; +import rx.internal.schedulers.*; import rx.schedulers.Schedulers; /** @@ -44,18 +42,6 @@ public abstract class Scheduler { * : Without virtual extension methods even additive changes are breaking and thus severely impede library * maintenance. */ - - /** - * The tolerance for a clock drift in nanoseconds where the periodic scheduler will rebase. - *

- * The associated system parameter, {@code rx.scheduler.drift-tolerance}, expects its value in minutes. - */ - static final long CLOCK_DRIFT_TOLERANCE_NANOS; - static { - CLOCK_DRIFT_TOLERANCE_NANOS = TimeUnit.MINUTES.toNanos( - Long.getLong("rx.scheduler.drift-tolerance", 15)); - } - /** * Retrieves or creates a new {@link Scheduler.Worker} that represents serial execution of actions. *

@@ -121,47 +107,8 @@ public abstract static class Worker implements Subscription { * @return a subscription to be able to prevent or cancel the execution of the action */ public Subscription schedulePeriodically(final Action0 action, long initialDelay, long period, TimeUnit unit) { - final long periodInNanos = unit.toNanos(period); - final long firstNowNanos = TimeUnit.MILLISECONDS.toNanos(now()); - final long firstStartInNanos = firstNowNanos + unit.toNanos(initialDelay); - - final SequentialSubscription first = new SequentialSubscription(); - final SequentialSubscription mas = new SequentialSubscription(first); - - final Action0 recursiveAction = new Action0() { - long count; - long lastNowNanos = firstNowNanos; - long startInNanos = firstStartInNanos; - @Override - public void call() { - action.call(); - - if (!mas.isUnsubscribed()) { - - long nextTick; - - long nowNanos = TimeUnit.MILLISECONDS.toNanos(now()); - // If the clock moved in a direction quite a bit, rebase the repetition period - if (nowNanos + CLOCK_DRIFT_TOLERANCE_NANOS < lastNowNanos - || nowNanos >= lastNowNanos + periodInNanos + CLOCK_DRIFT_TOLERANCE_NANOS) { - nextTick = nowNanos + periodInNanos; - /* - * Shift the start point back by the drift as if the whole thing - * started count periods ago. - */ - startInNanos = nextTick - (periodInNanos * (++count)); - } else { - nextTick = startInNanos + (++count * periodInNanos); - } - lastNowNanos = nowNanos; - - long delay = nextTick - nowNanos; - mas.replace(schedule(this, delay, TimeUnit.NANOSECONDS)); - } - } - }; - first.replace(schedule(recursiveAction, initialDelay, unit)); - return mas; + return SchedulePeriodicHelper.schedulePeriodically(this, action, + initialDelay, period, unit, null); } /** @@ -212,9 +159,9 @@ public long now() { * *

      * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
-     * 	// use merge max concurrent to limit the number of concurrent
-     * 	// callbacks two at a time
-     * 	return Completable.merge(Observable.merge(workers), 2);
+     *     // use merge max concurrent to limit the number of concurrent
+     *     // callbacks two at a time
+     *     return Completable.merge(Observable.merge(workers), 2);
      * });
      * 
*

@@ -230,9 +177,9 @@ public long now() { * *

      * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
-     * 	// use merge max concurrent to limit the number of concurrent
-     * 	// Observables two at a time
-     * 	return Completable.merge(Observable.merge(workers, 2));
+     *     // use merge max concurrent to limit the number of concurrent
+     *     // Observables two at a time
+     *     return Completable.merge(Observable.merge(workers, 2));
      * });
      * 
* @@ -243,10 +190,10 @@ public long now() { * *
      * Scheduler slowScheduler = Schedulers.computation().when(workers -> {
-     * 	// use concatenate to make each worker happen one at a time.
-     * 	return Completable.concat(workers.map(actions -> {
-     * 		// delay the starting of the next worker by 1 second.
-     * 		return Completable.merge(actions.delaySubscription(1, TimeUnit.SECONDS));
+     *     // use concatenate to make each worker happen one at a time.
+     *     return Completable.concat(workers.map(actions -> {
+     *         // delay the starting of the next worker by 1 second.
+     *         return Completable.merge(actions.delaySubscription(1, TimeUnit.SECONDS));
      *    }));
      * });
      * 
@@ -255,9 +202,9 @@ public long now() { * @param combine the function that takes a two-level nested Observable sequence of a Completable and returns * the Completable that will be subscribed to and should trigger the execution of the scheduled Actions. * @return the Scheduler with the customized execution behavior + * @since 1.3 */ @SuppressWarnings("unchecked") - @Experimental public S when(Func1>, Completable> combine) { return (S) new SchedulerWhen(combine, this); } diff --git a/src/main/java/rx/Single.java b/src/main/java/rx/Single.java index 7b85c4d3a5..0cf0d5f06f 100644 --- a/src/main/java/rx/Single.java +++ b/src/main/java/rx/Single.java @@ -145,8 +145,8 @@ public interface OnSubscribe extends Action1> { * the Operator that implements the Single-operating function to be applied to the source Single * @return a Single that is the result of applying the lifted Operator to the source Single * @see RxJava wiki: Implementing Your Own Operators + * @since 1.3 */ - @Beta public final Single lift(final Operator lift) { return create(new SingleLiftObservableOperator(this.onSubscribe, lift)); } @@ -202,7 +202,7 @@ public interface Transformer extends Func1, Single> { */ private static Observable asObservable(Single t) { // is this sufficient, or do I need to keep the outer Single and subscribe to it? - return Observable.create(new SingleToObservable(t.onSubscribe)); + return Observable.unsafeCreate(new SingleToObservable(t.onSubscribe)); } /* ********************************************************************************************************* @@ -210,6 +210,23 @@ private static Observable asObservable(Single t) { * ********************************************************************************************************* */ + /** + * Casts the success value of the current Single into the target type or signals a + * ClassCastException if not compatible. + *
+ *
Scheduler:
+ *
{@code cast} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the target type + * @param klass the type token to use for casting the success result from the current Single + * @return the new Single instance + * @since 1.3.1 - experimental + */ + @Experimental + public final Single cast(final Class klass) { + return map(new SingleOperatorCast(klass)); + } + /** * Returns an Observable that emits the items emitted by two Singles, one after the other. *

@@ -573,6 +590,46 @@ public static Single fromCallable(final Callable func) { return create(new SingleFromCallable(func)); } + /** + * Provides an API (in a cold Single) that bridges the Single-reactive world + * with the callback-based world. + *

The {@link SingleEmitter} allows registering a callback for + * cancellation/unsubscription of a resource. + *

+ * Example: + *


+     * Single.fromEmitter(emitter -> {
+     *     Callback listener = new Callback() {
+     *         @Override
+     *         public void onEvent(Event e) {
+     *             emitter.onSuccess(e.getData());
+     *         }
+     *
+     *         @Override
+     *         public void onFailure(Exception e) {
+     *             emitter.onError(e);
+     *         }
+     *     };
+     *
+     *     AutoCloseable c = api.someMethod(listener);
+     *
+     *     emitter.setCancellation(c::close);
+     *
+     * });
+     * 
+ *

All of the SingleEmitter's methods are thread-safe and ensure the + * Single's protocol are held. + *

History: 1.2.3 - experimental + * @param the success value type + * @param producer the callback invoked for each incoming SingleSubscriber + * @return the new Single instance + * @since 1.3 + */ + public static Single fromEmitter(Action1> producer) { + if (producer == null) { throw new NullPointerException("producer is null"); } + return create(new SingleFromEmitter(producer)); + } + /** * Returns a {@code Single} that emits a specified item. *

@@ -890,6 +947,97 @@ public static Observable merge(Single t1, Single + *

Backpressure:
+ *
The operator consumes items from the Observable in an unbounded manner and honors downstream backpressure.
+ *
Scheduler:
+ *
{@code merge} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param sources the Observable that emits Singles to be merged + * @return the new Observable instance + * @see #merge(Observable, int) + * @see #mergeDelayError(Observable) + * @see #mergeDelayError(Observable, int) + * @since 1.3 + */ + public static Observable merge(Observable> sources) { + return merge(sources, Integer.MAX_VALUE); + } + + /** + * Merges the Singles emitted by the Observable and runs up to the given number of them together at a time, + * until the Observable and all inner Singles terminate. + *

+ *
Backpressure:
+ *
The operator consumes at most maxConcurrent items from the Observable and one-by-one after as the inner + * Singles terminate. The operator ignores downstream backpressure as it doesn't emit items but + * only the terminal event.
+ *
Scheduler:
+ *
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param sources the Observable that emits Singles to be merged + * @param maxConcurrency the maximum number of inner Singles to run at a time + * @return the new Observable instance + * @since 1.3 + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static Observable merge(Observable> sources, int maxConcurrency) { + return sources.flatMapSingle((Func1)UtilityFunctions.identity(), false, maxConcurrency); + } + + /** + * Merges all Singles emitted by the Observable and runs them together, + * delaying errors from them and the Observable, until the source + * Observable and all inner Singles complete normally. + *

+ *
Backpressure:
+ *
The operator consumes items from the Observable in an unbounded manner and honors downstream backpressure.
+ *
Scheduler:
+ *
{@code merge} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param sources the Observable that emits Singles to be merged + * @return the new Observable instance + * @see #mergeDelayError(Observable, int) + * @see #merge(Observable) + * @see #merge(Observable, int) + * @since 1.3 + */ + public static Observable mergeDelayError(Observable> sources) { + return merge(sources, Integer.MAX_VALUE); + } + + /** + * Merges the Singles emitted by the Observable and runs up to the given number of them together at a time, + * delaying errors from them and the Observable, until the Observable and all inner Singles terminate. + *

+ *
Backpressure:
+ *
The operator consumes at most maxConcurrent items from the Observable and one-by-one after as the inner + * Singles terminate. The operator ignores downstream backpressure as it doesn't emit items but + * only the terminal event.
+ *
Scheduler:
+ *
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 1.2.7 - experimental + * @param the value type of the inner Singles and the resulting Observable + * @param sources the Observable that emits Singles to be merged + * @param maxConcurrency the maximum number of inner Singles to run at a time + * @return the new Observable instance + * @since 1.3 + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static Observable mergeDelayError(Observable> sources, int maxConcurrency) { + return sources.flatMapSingle((Func1)UtilityFunctions.identity(), true, maxConcurrency); + } + /** * Returns a Single that emits the results of a specified combiner function applied to two items emitted by * two other Singles. @@ -1321,8 +1469,8 @@ public static Single zip(Iterable> singles, FuncNReactiveX operators documentation: Replay + * @since 1.3 */ - @Experimental public final Single cache() { return toObservable().cacheWithInitialCapacity(1).toSingle(); } @@ -1406,9 +1554,8 @@ public final Observable flatMapObservable(Func1ReactiveX operators documentation: FlatMap - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Completable flatMapCompletable(final Func1 func) { return Completable.create(new CompletableFlatMapSingleToCompletable(this, func)); } @@ -1538,10 +1685,8 @@ public final Single onErrorReturn(Func1 resumeFunctio * @param resumeSingleInCaseOfError a Single that will take control if source Single encounters an error. * @return the original Single, with appropriately modified behavior. * @see ReactiveX operators documentation: Catch - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single onErrorResumeNext(Single resumeSingleInCaseOfError) { return new Single(SingleOperatorOnErrorResumeNext.withOther(this, resumeSingleInCaseOfError)); } @@ -1572,10 +1717,8 @@ public final Single onErrorResumeNext(Single resumeSingleInCaseO * @param resumeFunctionInCaseOfError a function that returns a Single that will take control if source Single encounters an error. * @return the original Single, with appropriately modified behavior. * @see ReactiveX operators documentation: Catch - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single onErrorResumeNext(final Func1> resumeFunctionInCaseOfError) { return new Single(SingleOperatorOnErrorResumeNext.withFunction(this, resumeFunctionInCaseOfError)); } @@ -1986,8 +2129,8 @@ public final Single takeUntil(final Single other) { * @param the resulting object type * @param converter the function that receives the current Single instance and returns a value * @return the value returned by the function + * @since 1.3 */ - @Experimental public final R to(Func1, R> converter) { return converter.call(this); } @@ -2019,10 +2162,8 @@ public final Observable toObservable() { * @return a {@link Completable} that calls {@code onCompleted} on it's subscriber when the source {@link Single} * calls {@code onSuccess}. * @see ReactiveX documentation: Completable - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical - * with the release number). + * @since 1.3 */ - @Beta public final Completable toCompletable() { return Completable.fromSingle(this); } @@ -2123,7 +2264,15 @@ public final Single timeout(long timeout, TimeUnit timeUnit, Single timeout(long timeout, TimeUnit timeUnit, Single other, Scheduler scheduler) { if (other == null) { - other = Single. error(new TimeoutException()); + // Use a defer instead of simply other = Single.error(new TimeoutException()) + // since instantiating an exception will cause the current stack trace to be inspected + // and we only want to incur that overhead when a timeout actually happens. + other = Single.defer(new Func0>() { + @Override + public Single call() { + return Single.error(new TimeoutException()); + } + }); } return create(new SingleTimeout(onSubscribe, timeout, timeUnit, scheduler, other.onSubscribe)); } @@ -2137,8 +2286,8 @@ public final Single timeout(long timeout, TimeUnit timeUnit, SingleReactiveX operators documentation: To + * @since 1.3 */ - @Beta public final BlockingSingle toBlocking() { return BlockingSingle.from(this); } @@ -2187,9 +2336,8 @@ public final Single zipWith(Single other, Func2ReactiveX operators documentation: Do - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single doOnError(final Action1 onError) { if (onError == null) { throw new IllegalArgumentException("onError is null"); @@ -2216,9 +2364,8 @@ public void call(final Throwable throwable) { * the action to invoke when the source {@link Single} calls {@code onSuccess} or {@code onError}. * @return the source {@link Single} with the side-effecting behavior applied * @see ReactiveX operators documentation: Do - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Single doOnEach(final Action1> onNotification) { if (onNotification == null) { throw new IllegalArgumentException("onNotification is null"); @@ -2250,9 +2397,8 @@ public void call(final Throwable throwable) { * the action to invoke when the source {@link Single} calls {@code onSuccess} * @return the source {@link Single} with the side-effecting behavior applied * @see ReactiveX operators documentation: Do - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final Single doOnSuccess(final Action1 onSuccess) { if (onSuccess == null) { throw new IllegalArgumentException("onSuccess is null"); @@ -2278,9 +2424,8 @@ public final Single doOnSuccess(final Action1 onSuccess) { * the action that gets called when an observer subscribes to this {@code Single} * @return the source {@code Single} modified so as to call this Action when appropriate * @see ReactiveX operators documentation: Do - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single doOnSubscribe(final Action0 subscribe) { return create(new SingleDoOnSubscribe(onSubscribe, subscribe)); } @@ -2303,9 +2448,8 @@ public final Single doOnSubscribe(final Action0 subscribe) { * the {@link Scheduler} to use for delaying * @return the source Single shifted in time by the specified delay * @see ReactiveX operators documentation: Delay - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single delay(long delay, TimeUnit unit, Scheduler scheduler) { return create(new SingleDelay(onSubscribe, delay, unit, scheduler)); } @@ -2326,9 +2470,8 @@ public final Single delay(long delay, TimeUnit unit, Scheduler scheduler) { * the {@link TimeUnit} in which {@code period} is defined * @return the source Single shifted in time by the specified delay * @see ReactiveX operators documentation: Delay - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single delay(long delay, TimeUnit unit) { return delay(delay, unit, Schedulers.computation()); } @@ -2356,9 +2499,8 @@ public final Single delay(long delay, TimeUnit unit) { * @return a {@link Single} whose {@link Observer}s' subscriptions trigger an invocation of the given * {@link Single} factory function. * @see ReactiveX operators documentation: Defer - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Single defer(final Callable> singleFactory) { return create(new OnSubscribe() { @Override @@ -2392,9 +2534,8 @@ public void call(SingleSubscriber singleSubscriber) { * the action that gets called when this {@link Single} is unsubscribed. * @return the source {@link Single} modified so as to call this Action when appropriate. * @see ReactiveX operators documentation: Do - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single doOnUnsubscribe(final Action0 action) { return create(new SingleDoOnUnsubscribe(onSubscribe, action)); } @@ -2414,9 +2555,8 @@ public final Single doOnUnsubscribe(final Action0 action) { * @return a {@link Single} that emits the same item or error as the source {@link Single}, then invokes the * {@link Action0} * @see ReactiveX operators documentation: Do - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single doAfterTerminate(Action0 action) { return create(new SingleDoAfterTerminate(this, action)); } @@ -2596,8 +2736,8 @@ public final Single retryWhen(final Func1, ? * the function that will dispose of the resource * @return the Single whose lifetime controls the lifetime of the dependent resource object * @see ReactiveX operators documentation: Using + * @since 1.3 */ - @Beta public static Single using( final Func0 resourceFactory, final Func1> singleFactory, @@ -2631,10 +2771,8 @@ public static Single using( * a terminal event ({@code onComplete} or {@code onError}). * @return the Single whose lifetime controls the lifetime of the dependent resource object * @see ReactiveX operators documentation: Using - * @Experimental The behavior of this can change at any time. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static Single using( final Func0 resourceFactory, final Func1> singleFactory, @@ -2667,9 +2805,8 @@ public static Single using( * to this Single. * @return a Single that delays the subscription to this Single * until the Observable emits an element or completes normally. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public final Single delaySubscription(Observable other) { if (other == null) { throw new NullPointerException(); @@ -2677,6 +2814,52 @@ public final Single delaySubscription(Observable other) { return create(new SingleOnSubscribeDelaySubscriptionOther(this, other)); } + /** + * Returns a Single which makes sure when a subscriber cancels the subscription, + * the dispose is called on the specified scheduler + * @param scheduler the target scheduler where to execute the cancellation + * @return the new Single instance + * @since 1.2.8 - experimental + */ + @Experimental + public final Single unsubscribeOn(final Scheduler scheduler) { + return create(new OnSubscribe() { + @Override + public void call(final SingleSubscriber t) { + final SingleSubscriber single = new SingleSubscriber() { + @Override + public void onSuccess(T value) { + t.onSuccess(value); + } + + @Override + public void onError(Throwable error) { + t.onError(error); + } + }; + + t.add(Subscriptions.create(new Action0() { + @Override + public void call() { + final Scheduler.Worker w = scheduler.createWorker(); + w.schedule(new Action0() { + @Override + public void call() { + try { + single.unsubscribe(); + } finally { + w.unsubscribe(); + } + } + }); + } + })); + + Single.this.subscribe(single); + } + }); + } + // ------------------------------------------------------------------------- // Fluent test support, super handy and reduces test preparation boilerplate // ------------------------------------------------------------------------- @@ -2689,10 +2872,10 @@ public final Single delaySubscription(Observable other) { *

Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
+ *

History: 1.2.3 - experimental * @return the new AssertableSubscriber instance - * @since 1.2.3 + * @since 1.3 */ - @Experimental public final AssertableSubscriber test() { AssertableSubscriberObservable ts = AssertableSubscriberObservable.create(Long.MAX_VALUE); subscribe(ts); diff --git a/src/main/java/rx/SingleEmitter.java b/src/main/java/rx/SingleEmitter.java new file mode 100644 index 0000000000..53d034a076 --- /dev/null +++ b/src/main/java/rx/SingleEmitter.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package rx; + +import rx.functions.Cancellable; + +/** + * Abstraction over a {@link SingleSubscriber} that gets either an onSuccess or onError + * signal and allows registering an cancellation/unsubscription callback. + *

+ * All methods are thread-safe; calling onSuccess or onError twice or one after the other has + * no effect. + *

History: 1.2.3 - experimental + * @param the success value type + * @since 1.3 + */ +public interface SingleEmitter { + + /** + * Notifies the SingleSubscriber that the {@link Single} has completed successfully with + * the given value. + *

+ * If the {@link Single} calls this method, it will not thereafter call + * {@link #onError}. + * + * @param t the success value + */ + void onSuccess(T t); + + /** + * Notifies the SingleSubscriber that the {@link Single} has experienced an error condition. + *

+ * If the {@link Single} calls this method, it will not thereafter call + * {@link #onSuccess}. + * + * @param t + * the exception encountered by the Observable + */ + void onError(Throwable t); + + /** + * Sets a Subscription on this emitter; any previous Subscription + * or Cancellation will be unsubscribed/cancelled. + * @param s the subscription, null is allowed + */ + void setSubscription(Subscription s); + + /** + * Sets a Cancellable on this emitter; any previous Subscription + * or Cancellation will be unsubscribed/cancelled. + * @param c the cancellable resource, null is allowed + */ + void setCancellation(Cancellable c); + +} diff --git a/src/main/java/rx/SingleSubscriber.java b/src/main/java/rx/SingleSubscriber.java index 53e9a70faa..0860dd331f 100644 --- a/src/main/java/rx/SingleSubscriber.java +++ b/src/main/java/rx/SingleSubscriber.java @@ -43,10 +43,10 @@ public abstract class SingleSubscriber implements Subscription { *

* The {@link Single} will not call this method if it calls {@link #onError}. * - * @param value + * @param t * the item emitted by the Single */ - public abstract void onSuccess(T value); + public abstract void onSuccess(T t); /** * Notifies the SingleSubscriber that the {@link Single} has experienced an error condition. diff --git a/src/main/java/rx/exceptions/AssemblyStackTraceException.java b/src/main/java/rx/exceptions/AssemblyStackTraceException.java index 8342adc637..c21cb94510 100644 --- a/src/main/java/rx/exceptions/AssemblyStackTraceException.java +++ b/src/main/java/rx/exceptions/AssemblyStackTraceException.java @@ -17,14 +17,13 @@ import java.util.*; -import rx.annotations.Experimental; import rx.plugins.RxJavaHooks; /** * A RuntimeException that is stackless but holds onto a textual * stacktrace from tracking the assembly location of operators. + * @since 1.3 */ -@Experimental public final class AssemblyStackTraceException extends RuntimeException { /** */ diff --git a/src/main/java/rx/exceptions/CompositeException.java b/src/main/java/rx/exceptions/CompositeException.java index 8c40d37bb7..855d720e49 100644 --- a/src/main/java/rx/exceptions/CompositeException.java +++ b/src/main/java/rx/exceptions/CompositeException.java @@ -18,8 +18,6 @@ import java.io.*; import java.util.*; -import rx.annotations.Beta; - /** * Represents an exception that is a composite of one or more other exceptions. A {@code CompositeException} * does not modify the structure of any exception it wraps, but at print-time it iterates through the list of @@ -85,8 +83,8 @@ public CompositeException(Collection errors) { /** * Constructs a CompositeException instance with the supplied initial Throwables. * @param errors the array of Throwables + * @since 1.3 */ - @Beta public CompositeException(Throwable... errors) { Set deDupedExceptions = new LinkedHashSet(); List localExceptions = new ArrayList(); @@ -141,7 +139,7 @@ public synchronized Throwable getCause() { // NOPMD List listOfCauses = getListOfCauses(e); // check if any of them have been seen before - for(Throwable child : listOfCauses) { + for (Throwable child : listOfCauses) { if (seenCauses.contains(child)) { // already seen this outer Throwable so skip e = new RuntimeException("Duplicate found in causal chain so cropping to prevent loop ..."); @@ -288,7 +286,7 @@ private List getListOfCauses(Throwable ex) { if (root == null || root == ex) { return list; } else { - while(true) { + while (true) { list.add(root); Throwable cause = root.getCause(); if (cause == null || cause == root) { @@ -311,7 +309,7 @@ private Throwable getRootCause(Throwable e) { if (root == null || root == e) { return e; } else { - while(true) { + while (true) { Throwable cause = root.getCause(); if (cause == null || cause == root) { return root; diff --git a/src/main/java/rx/exceptions/Exceptions.java b/src/main/java/rx/exceptions/Exceptions.java index 276e563e48..9e5f25393d 100644 --- a/src/main/java/rx/exceptions/Exceptions.java +++ b/src/main/java/rx/exceptions/Exceptions.java @@ -19,7 +19,6 @@ import rx.Observer; import rx.SingleSubscriber; -import rx.annotations.Beta; /** * Utility class with methods to wrap checked exceptions and @@ -182,9 +181,8 @@ public static void throwIfAny(List exceptions) { * @param t the exception * @param o the observer to report to * @param value the value that caused the exception - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static void throwOrReport(Throwable t, Observer o, Object value) { Exceptions.throwIfFatal(t); o.onError(OnErrorThrowable.addValueAsLastCause(t, value)); @@ -196,9 +194,8 @@ public static void throwOrReport(Throwable t, Observer o, Object value) { * @param t the exception * @param o the observer to report to * @param value the value that caused the exception - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static void throwOrReport(Throwable t, SingleSubscriber o, Object value) { Exceptions.throwIfFatal(t); o.onError(OnErrorThrowable.addValueAsLastCause(t, value)); @@ -208,9 +205,8 @@ public static void throwOrReport(Throwable t, SingleSubscriber o, Object valu * Forwards a fatal exception or reports it to the given Observer. * @param t the exception * @param o the observer to report to - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public static void throwOrReport(Throwable t, Observer o) { Exceptions.throwIfFatal(t); o.onError(t); @@ -221,9 +217,8 @@ public static void throwOrReport(Throwable t, Observer o) { * * @param throwable the exception. * @param subscriber the subscriber to report to. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number). + * @since 1.3 */ - @Beta public static void throwOrReport(Throwable throwable, SingleSubscriber subscriber) { Exceptions.throwIfFatal(throwable); subscriber.onError(throwable); diff --git a/src/main/java/rx/exceptions/OnErrorThrowable.java b/src/main/java/rx/exceptions/OnErrorThrowable.java index 77c255a874..b8c0c3555f 100644 --- a/src/main/java/rx/exceptions/OnErrorThrowable.java +++ b/src/main/java/rx/exceptions/OnErrorThrowable.java @@ -15,11 +15,10 @@ */ package rx.exceptions; -import java.util.HashSet; -import java.util.Set; +import java.io.*; +import java.util.*; -import rx.plugins.RxJavaErrorHandler; -import rx.plugins.RxJavaPlugins; +import rx.plugins.*; /** * Represents a {@code Throwable} that an {@code Observable} might notify its subscribers of, but that then can @@ -43,7 +42,17 @@ private OnErrorThrowable(Throwable exception) { private OnErrorThrowable(Throwable exception, Object value) { super(exception); hasValue = true; - this.value = value; + Object v; + if (value instanceof Serializable) { + v = value; + } else { + try { + v = String.valueOf(value); + } catch (Throwable ex) { + v = ex.getMessage(); + } + } + this.value = v; } /** @@ -150,7 +159,17 @@ private static Set> create() { */ public OnNextValue(Object value) { super("OnError while emitting onNext value: " + renderValue(value)); - this.value = value; + Object v; + if (value instanceof Serializable) { + v = value; + } else { + try { + v = String.valueOf(value); + } catch (Throwable ex) { + v = ex.getMessage(); + } + } + this.value = v; } /** @@ -177,7 +196,7 @@ public Object getValue() { * @return a string version of the object if primitive or managed through error plugin, * otherwise the class name of the object */ - static String renderValue(Object value){ + static String renderValue(Object value) { if (value == null) { return "null"; } diff --git a/src/main/java/rx/functions/Cancellable.java b/src/main/java/rx/functions/Cancellable.java index 1109522d86..7b92b71884 100644 --- a/src/main/java/rx/functions/Cancellable.java +++ b/src/main/java/rx/functions/Cancellable.java @@ -16,12 +16,10 @@ package rx.functions; -import rx.annotations.Experimental; - /** * A functional interface that has a single close method that can throw. + * @since 1.3 */ -@Experimental public interface Cancellable { /** diff --git a/src/main/java/rx/internal/observers/AssertableSubscriberObservable.java b/src/main/java/rx/internal/observers/AssertableSubscriberObservable.java index b9068b04ff..d9081b5a8d 100644 --- a/src/main/java/rx/internal/observers/AssertableSubscriberObservable.java +++ b/src/main/java/rx/internal/observers/AssertableSubscriberObservable.java @@ -20,7 +20,6 @@ import rx.Producer; import rx.Subscriber; -import rx.annotations.Experimental; import rx.functions.Action0; import rx.observers.TestSubscriber; import rx.observers.AssertableSubscriber; @@ -33,8 +32,8 @@ * * @param * the value type + * @since 1.3 */ -@Experimental public class AssertableSubscriberObservable extends Subscriber implements AssertableSubscriber { private final TestSubscriber ts; diff --git a/src/main/java/rx/internal/operators/BufferUntilSubscriber.java b/src/main/java/rx/internal/operators/BufferUntilSubscriber.java index 2a4401c52c..7855bdc5d9 100644 --- a/src/main/java/rx/internal/operators/BufferUntilSubscriber.java +++ b/src/main/java/rx/internal/operators/BufferUntilSubscriber.java @@ -102,7 +102,7 @@ public void call() { } } if (win) { - while(true) { + while (true) { Object o; while ((o = state.buffer.poll()) != null) { NotificationLite.accept(state.get(), o); diff --git a/src/main/java/rx/internal/operators/CompletableFromEmitter.java b/src/main/java/rx/internal/operators/CompletableFromEmitter.java index 1b7cea57c2..3be42f7aac 100644 --- a/src/main/java/rx/internal/operators/CompletableFromEmitter.java +++ b/src/main/java/rx/internal/operators/CompletableFromEmitter.java @@ -19,10 +19,8 @@ import rx.*; import rx.exceptions.Exceptions; -import rx.functions.Action1; -import rx.functions.Cancellable; -import rx.internal.operators.OnSubscribeFromEmitter.CancellableSubscription; -import rx.internal.subscriptions.SequentialSubscription; +import rx.functions.*; +import rx.internal.subscriptions.*; import rx.plugins.RxJavaHooks; /** diff --git a/src/main/java/rx/internal/operators/CompletableOnSubscribeConcat.java b/src/main/java/rx/internal/operators/CompletableOnSubscribeConcat.java index 1960a93207..10a7801beb 100644 --- a/src/main/java/rx/internal/operators/CompletableOnSubscribeConcat.java +++ b/src/main/java/rx/internal/operators/CompletableOnSubscribeConcat.java @@ -21,9 +21,9 @@ import rx.*; import rx.Completable.OnSubscribe; import rx.exceptions.MissingBackpressureException; +import rx.internal.subscriptions.SequentialSubscription; import rx.internal.util.unsafe.SpscArrayQueue; import rx.plugins.RxJavaHooks; -import rx.subscriptions.SerialSubscription; public final class CompletableOnSubscribeConcat implements OnSubscribe { final Observable sources; @@ -39,30 +39,29 @@ public CompletableOnSubscribeConcat(Observable sources, i public void call(CompletableSubscriber s) { CompletableConcatSubscriber parent = new CompletableConcatSubscriber(s, prefetch); s.onSubscribe(parent); - sources.subscribe(parent); + sources.unsafeSubscribe(parent); } static final class CompletableConcatSubscriber extends Subscriber { final CompletableSubscriber actual; - final SerialSubscription sr; + final SequentialSubscription sr; final SpscArrayQueue queue; - volatile boolean done; + final ConcatInnerSubscriber inner; final AtomicBoolean once; - final ConcatInnerSubscriber inner; + volatile boolean done; - final AtomicInteger wip; + volatile boolean active; public CompletableConcatSubscriber(CompletableSubscriber actual, int prefetch) { this.actual = actual; this.queue = new SpscArrayQueue(prefetch); - this.sr = new SerialSubscription(); + this.sr = new SequentialSubscription(); this.inner = new ConcatInnerSubscriber(); - this.wip = new AtomicInteger(); this.once = new AtomicBoolean(); add(sr); request(prefetch); @@ -74,9 +73,7 @@ public void onNext(Completable t) { onError(new MissingBackpressureException()); return; } - if (wip.getAndIncrement() == 0) { - next(); - } + drain(); } @Override @@ -94,9 +91,7 @@ public void onCompleted() { return; } done = true; - if (wip.getAndIncrement() == 0) { - next(); - } + drain(); } void innerError(Throwable e) { @@ -105,32 +100,45 @@ void innerError(Throwable e) { } void innerComplete() { - if (wip.decrementAndGet() != 0) { - next(); - } - if (!done) { - request(1); - } + active = false; + drain(); } - void next() { - boolean d = done; - Completable c = queue.poll(); - if (c == null) { - if (d) { - if (once.compareAndSet(false, true)) { - actual.onCompleted(); - } - return; - } - RxJavaHooks.onError(new IllegalStateException("Queue is empty?!")); + void drain() { + ConcatInnerSubscriber inner = this.inner; + if (inner.getAndIncrement() != 0) { return; } - c.unsafeSubscribe(inner); + do { + if (isUnsubscribed()) { + return; + } + if (!active) { + boolean d = done; + Completable c = queue.poll(); + boolean empty = c == null; + + if (d && empty) { + actual.onCompleted(); + return; + } + + if (!empty) { + active = true; + c.subscribe(inner); + + request(1); + } + } + } while (inner.decrementAndGet() != 0); } - final class ConcatInnerSubscriber implements CompletableSubscriber { + final class ConcatInnerSubscriber + extends AtomicInteger + implements CompletableSubscriber { + private static final long serialVersionUID = 7233503139645205620L; + @Override public void onSubscribe(Subscription d) { sr.set(d); diff --git a/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatArray.java b/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatArray.java index f291f0f649..cbac07ded5 100644 --- a/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatArray.java +++ b/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatArray.java @@ -20,7 +20,7 @@ import rx.*; import rx.Completable.OnSubscribe; -import rx.subscriptions.SerialSubscription; +import rx.internal.subscriptions.SequentialSubscription; public final class CompletableOnSubscribeConcatArray implements OnSubscribe { final Completable[] sources; @@ -45,17 +45,17 @@ static final class ConcatInnerSubscriber extends AtomicInteger implements Comple int index; - final SerialSubscription sd; + final SequentialSubscription sd; public ConcatInnerSubscriber(CompletableSubscriber actual, Completable[] sources) { this.actual = actual; this.sources = sources; - this.sd = new SerialSubscription(); + this.sd = new SequentialSubscription(); } @Override public void onSubscribe(Subscription d) { - sd.set(d); + sd.replace(d); } @Override diff --git a/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatIterable.java b/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatIterable.java index e5a3e95fc7..7506286906 100644 --- a/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatIterable.java +++ b/src/main/java/rx/internal/operators/CompletableOnSubscribeConcatIterable.java @@ -21,7 +21,8 @@ import rx.*; import rx.Completable.OnSubscribe; -import rx.subscriptions.*; +import rx.internal.subscriptions.SequentialSubscription; +import rx.subscriptions.Subscriptions; public final class CompletableOnSubscribeConcatIterable implements OnSubscribe { final Iterable sources; @@ -61,17 +62,17 @@ static final class ConcatInnerSubscriber extends AtomicInteger implements Comple final CompletableSubscriber actual; final Iterator sources; - final SerialSubscription sd; + final SequentialSubscription sd; public ConcatInnerSubscriber(CompletableSubscriber actual, Iterator sources) { this.actual = actual; this.sources = sources; - this.sd = new SerialSubscription(); + this.sd = new SequentialSubscription(); } @Override public void onSubscribe(Subscription d) { - sd.set(d); + sd.replace(d); } @Override diff --git a/src/main/java/rx/internal/operators/CompletableOnSubscribeMerge.java b/src/main/java/rx/internal/operators/CompletableOnSubscribeMerge.java index 19f5aa3857..b60f6c56ed 100644 --- a/src/main/java/rx/internal/operators/CompletableOnSubscribeMerge.java +++ b/src/main/java/rx/internal/operators/CompletableOnSubscribeMerge.java @@ -43,7 +43,7 @@ public CompletableOnSubscribeMerge(Observable source, int public void call(CompletableSubscriber s) { CompletableMergeSubscriber parent = new CompletableMergeSubscriber(s, maxConcurrency, delayErrors); s.onSubscribe(parent); - source.subscribe(parent); + source.unsafeSubscribe(parent); } static final class CompletableMergeSubscriber diff --git a/src/main/java/rx/internal/operators/CompletableOnSubscribeMergeDelayErrorIterable.java b/src/main/java/rx/internal/operators/CompletableOnSubscribeMergeDelayErrorIterable.java index 20477e64fd..0c8f5bafee 100644 --- a/src/main/java/rx/internal/operators/CompletableOnSubscribeMergeDelayErrorIterable.java +++ b/src/main/java/rx/internal/operators/CompletableOnSubscribeMergeDelayErrorIterable.java @@ -21,7 +21,8 @@ import rx.*; import rx.Completable.OnSubscribe; -import rx.internal.util.unsafe.MpscLinkedQueue; +import rx.internal.util.atomic.MpscLinkedAtomicQueue; +import rx.internal.util.unsafe.*; import rx.subscriptions.CompositeSubscription; public final class CompletableOnSubscribeMergeDelayErrorIterable implements OnSubscribe { @@ -53,7 +54,13 @@ public void call(final CompletableSubscriber s) { final AtomicInteger wip = new AtomicInteger(1); - final Queue queue = new MpscLinkedQueue(); + final Queue queue; + + if (UnsafeAccess.isUnsafeAvailable()) { + queue = new MpscLinkedQueue(); + } else { + queue = new MpscLinkedAtomicQueue(); + } for (;;) { if (set.isUnsubscribed()) { diff --git a/src/main/java/rx/internal/operators/EmptyObservableHolder.java b/src/main/java/rx/internal/operators/EmptyObservableHolder.java index e582407bee..e386539c66 100644 --- a/src/main/java/rx/internal/operators/EmptyObservableHolder.java +++ b/src/main/java/rx/internal/operators/EmptyObservableHolder.java @@ -28,7 +28,7 @@ public enum EmptyObservableHolder implements OnSubscribe { ; /** The singleton instance. */ - static final Observable EMPTY = Observable.create(INSTANCE); + static final Observable EMPTY = Observable.unsafeCreate(INSTANCE); /** diff --git a/src/main/java/rx/internal/operators/NeverObservableHolder.java b/src/main/java/rx/internal/operators/NeverObservableHolder.java index 6a80d22e8b..056c08b034 100644 --- a/src/main/java/rx/internal/operators/NeverObservableHolder.java +++ b/src/main/java/rx/internal/operators/NeverObservableHolder.java @@ -28,7 +28,7 @@ public enum NeverObservableHolder implements OnSubscribe { ; /** The singleton instance. */ - static final Observable NEVER = Observable.create(INSTANCE); + static final Observable NEVER = Observable.unsafeCreate(INSTANCE); /** * Returns a type-corrected singleton instance of the never Observable. diff --git a/src/main/java/rx/internal/operators/OnSubscribeAmb.java b/src/main/java/rx/internal/operators/OnSubscribeAmb.java index d64567b340..bd0b0fe2b5 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeAmb.java +++ b/src/main/java/rx/internal/operators/OnSubscribeAmb.java @@ -29,7 +29,7 @@ * Given multiple {@link Observable}s, propagates the one that first emits an item. * @param the value type */ -public final class OnSubscribeAmb implements OnSubscribe{ +public final class OnSubscribeAmb implements OnSubscribe { //give default access instead of private as a micro-optimization //for access from anonymous classes below final Iterable> sources; @@ -336,12 +336,12 @@ private boolean isSelected() { } @SuppressWarnings("serial") - static final class Selection extends AtomicReference> { + static final class Selection extends AtomicReference> { final Collection> ambSubscribers = new ConcurrentLinkedQueue>(); public void unsubscribeLosers() { AmbSubscriber winner = get(); - if(winner != null) { + if (winner != null) { unsubscribeOthers(winner); } } @@ -437,7 +437,7 @@ public void request(long n) { } static void unsubscribeAmbSubscribers(Collection> ambSubscribers) { - if(!ambSubscribers.isEmpty()) { + if (!ambSubscribers.isEmpty()) { for (AmbSubscriber other : ambSubscribers) { other.unsubscribe(); } diff --git a/src/main/java/rx/internal/operators/OnSubscribeAutoConnect.java b/src/main/java/rx/internal/operators/OnSubscribeAutoConnect.java index 87a81ae7e8..cf6e7e7473 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeAutoConnect.java +++ b/src/main/java/rx/internal/operators/OnSubscribeAutoConnect.java @@ -33,7 +33,7 @@ public final class OnSubscribeAutoConnect extends AtomicInteger implements OnSubscribe { // AtomicInteger aspect of `this` represents the number of clients - final ConnectableObservable source; + final ConnectableObservable source; final int numberOfSubscribers; final Action1 connection; diff --git a/src/main/java/rx/internal/operators/OnSubscribeFromEmitter.java b/src/main/java/rx/internal/operators/OnSubscribeCreate.java similarity index 90% rename from src/main/java/rx/internal/operators/OnSubscribeFromEmitter.java rename to src/main/java/rx/internal/operators/OnSubscribeCreate.java index 2cfec566d4..497b240ec0 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeFromEmitter.java +++ b/src/main/java/rx/internal/operators/OnSubscribeCreate.java @@ -20,22 +20,22 @@ import rx.*; import rx.Observable.OnSubscribe; -import rx.exceptions.*; -import rx.functions.Action1; -import rx.functions.Cancellable; +import rx.exceptions.MissingBackpressureException; +import rx.functions.*; +import rx.internal.subscriptions.CancellableSubscription; import rx.internal.util.RxRingBuffer; import rx.internal.util.atomic.SpscUnboundedAtomicArrayQueue; import rx.internal.util.unsafe.*; import rx.plugins.RxJavaHooks; import rx.subscriptions.SerialSubscription; -public final class OnSubscribeFromEmitter implements OnSubscribe { +public final class OnSubscribeCreate implements OnSubscribe { final Action1> Emitter; final Emitter.BackpressureMode backpressure; - public OnSubscribeFromEmitter(Action1> Emitter, Emitter.BackpressureMode backpressure) { + public OnSubscribeCreate(Action1> Emitter, Emitter.BackpressureMode backpressure) { this.Emitter = Emitter; this.backpressure = backpressure; } @@ -73,41 +73,6 @@ public void call(Subscriber t) { } - /** - * A Subscription that wraps an Cancellable instance. - */ - static final class CancellableSubscription - extends AtomicReference - implements Subscription { - - /** */ - private static final long serialVersionUID = 5718521705281392066L; - - public CancellableSubscription(Cancellable cancellable) { - super(cancellable); - } - - @Override - public boolean isUnsubscribed() { - return get() == null; - } - - @Override - public void unsubscribe() { - if (get() != null) { - Cancellable c = getAndSet(null); - if (c != null) { - try { - c.cancel(); - } catch (Exception ex) { - Exceptions.throwIfFatal(ex); - RxJavaHooks.onError(ex); - } - } - } - } - } - static abstract class BaseEmitter extends AtomicLong implements Emitter, Producer, Subscription { @@ -303,7 +268,7 @@ public void onError(Throwable e) { @Override void onOverflow() { - onError(new MissingBackpressureException("fromEmitter: could not emit value due to lack of requests")); + onError(new MissingBackpressureException("create: could not emit value due to lack of requests")); } } diff --git a/src/main/java/rx/internal/operators/OnSubscribeFlatMapCompletable.java b/src/main/java/rx/internal/operators/OnSubscribeFlatMapCompletable.java new file mode 100644 index 0000000000..8b22e740a0 --- /dev/null +++ b/src/main/java/rx/internal/operators/OnSubscribeFlatMapCompletable.java @@ -0,0 +1,215 @@ +/** + * Copyright 2017 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package rx.internal.operators; + +import java.util.concurrent.atomic.*; + +import rx.*; +import rx.exceptions.Exceptions; +import rx.functions.Func1; +import rx.internal.util.ExceptionsUtils; +import rx.plugins.RxJavaHooks; +import rx.subscriptions.CompositeSubscription; + +/** + * Maps upstream values to Completables and merges them, up to a given + * number of them concurrently, optionally delaying errors. + *

History: 1.2.7 - experimental + * @param the upstream value type + * @since 1.3 + */ +public final class OnSubscribeFlatMapCompletable implements Observable.OnSubscribe { + + final Observable source; + + final Func1 mapper; + + final boolean delayErrors; + + final int maxConcurrency; + + public OnSubscribeFlatMapCompletable(Observable source, Func1 mapper, + boolean delayErrors, int maxConcurrency) { + if (mapper == null) { + throw new NullPointerException("mapper is null"); + } + if (maxConcurrency <= 0) { + throw new IllegalArgumentException("maxConcurrency > 0 required but it was " + maxConcurrency); + } + this.source = source; + this.mapper = mapper; + this.delayErrors = delayErrors; + this.maxConcurrency = maxConcurrency; + } + + @Override + public void call(Subscriber child) { + FlatMapCompletableSubscriber parent = new FlatMapCompletableSubscriber(child, mapper, delayErrors, maxConcurrency); + child.add(parent); + child.add(parent.set); + source.unsafeSubscribe(parent); + } + + static final class FlatMapCompletableSubscriber extends Subscriber { + + final Subscriber actual; + + final Func1 mapper; + + final boolean delayErrors; + + final int maxConcurrency; + + final AtomicInteger wip; + + final CompositeSubscription set; + + final AtomicReference errors; + + FlatMapCompletableSubscriber(Subscriber actual, Func1 mapper, + boolean delayErrors, int maxConcurrency) { + this.actual = actual; + this.mapper = mapper; + this.delayErrors = delayErrors; + this.maxConcurrency = maxConcurrency; + this.wip = new AtomicInteger(1); + this.errors = new AtomicReference(); + this.set = new CompositeSubscription(); + this.request(maxConcurrency != Integer.MAX_VALUE ? maxConcurrency : Long.MAX_VALUE); + } + + @Override + public void onNext(T t) { + Completable c; + + try { + c = mapper.call(t); + if (c == null) { + throw new NullPointerException("The mapper returned a null Completable"); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + unsubscribe(); + onError(ex); + return; + } + + InnerSubscriber inner = new InnerSubscriber(); + set.add(inner); + wip.getAndIncrement(); + + c.unsafeSubscribe(inner); + } + + @Override + public void onError(Throwable e) { + if (delayErrors) { + ExceptionsUtils.addThrowable(errors, e); + onCompleted(); + } else { + set.unsubscribe(); + if (errors.compareAndSet(null, e)) { + actual.onError(ExceptionsUtils.terminate(errors)); + } else { + RxJavaHooks.onError(e); + } + } + } + + @Override + public void onCompleted() { + done(); + } + + boolean done() { + if (wip.decrementAndGet() == 0) { + Throwable ex = ExceptionsUtils.terminate(errors); + if (ex != null) { + actual.onError(ex); + } else { + actual.onCompleted(); + } + return true; + } + return false; + } + + public void innerError(InnerSubscriber inner, Throwable e) { + set.remove(inner); + if (delayErrors) { + ExceptionsUtils.addThrowable(errors, e); + if (!done() && maxConcurrency != Integer.MAX_VALUE) { + request(1); + } + } else { + set.unsubscribe(); + unsubscribe(); + if (errors.compareAndSet(null, e)) { + actual.onError(ExceptionsUtils.terminate(errors)); + } else { + RxJavaHooks.onError(e); + } + } + } + + public void innerComplete(InnerSubscriber inner) { + set.remove(inner); + if (!done() && maxConcurrency != Integer.MAX_VALUE) { + request(1); + } + } + + final class InnerSubscriber + extends AtomicReference + implements CompletableSubscriber, Subscription { + + private static final long serialVersionUID = -8588259593722659900L; + + @Override + public void unsubscribe() { + Subscription s = getAndSet(this); + if (s != null && s != this) { + s.unsubscribe(); + } + } + + @Override + public boolean isUnsubscribed() { + return get() == this; + } + + @Override + public void onCompleted() { + innerComplete(this); + } + + @Override + public void onError(Throwable e) { + innerError(this, e); + } + + @Override + public void onSubscribe(Subscription d) { + if (!compareAndSet(null, d)) { + d.unsubscribe(); + if (get() != this) { + RxJavaHooks.onError(new IllegalStateException("Subscription already set!")); + } + } + } + } + } +} diff --git a/src/main/java/rx/internal/operators/OnSubscribeFlatMapSingle.java b/src/main/java/rx/internal/operators/OnSubscribeFlatMapSingle.java new file mode 100644 index 0000000000..190d6acbc0 --- /dev/null +++ b/src/main/java/rx/internal/operators/OnSubscribeFlatMapSingle.java @@ -0,0 +1,334 @@ +/** + * Copyright 2017 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.Queue; +import java.util.concurrent.atomic.*; + +import rx.*; +import rx.exceptions.Exceptions; +import rx.functions.Func1; +import rx.internal.util.ExceptionsUtils; +import rx.internal.util.atomic.MpscLinkedAtomicQueue; +import rx.internal.util.unsafe.*; +import rx.plugins.RxJavaHooks; +import rx.subscriptions.CompositeSubscription; + +/** + * Maps upstream values to Singles and merges them, up to a given + * number of them concurrently, optionally delaying errors. + *

History: 1.2.7 - experimental + * @param the upstream value type + * @param the inner Singles and result value type + * @since 1.3 + */ +public final class OnSubscribeFlatMapSingle implements Observable.OnSubscribe { + + final Observable source; + + final Func1> mapper; + + final boolean delayErrors; + + final int maxConcurrency; + + public OnSubscribeFlatMapSingle(Observable source, Func1> mapper, + boolean delayErrors, int maxConcurrency) { + if (mapper == null) { + throw new NullPointerException("mapper is null"); + } + if (maxConcurrency <= 0) { + throw new IllegalArgumentException("maxConcurrency > 0 required but it was " + maxConcurrency); + } + this.source = source; + this.mapper = mapper; + this.delayErrors = delayErrors; + this.maxConcurrency = maxConcurrency; + } + + @Override + public void call(Subscriber child) { + FlatMapSingleSubscriber parent = new FlatMapSingleSubscriber(child, mapper, delayErrors, maxConcurrency); + child.add(parent.set); + child.add(parent.requested); + child.setProducer(parent.requested); + source.unsafeSubscribe(parent); + } + + static final class FlatMapSingleSubscriber extends Subscriber { + + final Subscriber actual; + + final Func1> mapper; + + final boolean delayErrors; + + final int maxConcurrency; + + final AtomicInteger wip; + + final AtomicInteger active; + + final CompositeSubscription set; + + final AtomicReference errors; + + final Queue queue; + + final Requested requested; + + volatile boolean done; + + volatile boolean cancelled; + + FlatMapSingleSubscriber(Subscriber actual, + Func1> mapper, + boolean delayErrors, int maxConcurrency) { + this.actual = actual; + this.mapper = mapper; + this.delayErrors = delayErrors; + this.maxConcurrency = maxConcurrency; + this.wip = new AtomicInteger(); + this.errors = new AtomicReference(); + this.requested = new Requested(); + this.set = new CompositeSubscription(); + this.active = new AtomicInteger(); + if (UnsafeAccess.isUnsafeAvailable()) { + queue = new MpscLinkedQueue(); + } else { + queue = new MpscLinkedAtomicQueue(); + } + this.request(maxConcurrency != Integer.MAX_VALUE ? maxConcurrency : Long.MAX_VALUE); + } + + @Override + public void onNext(T t) { + Single c; + + try { + c = mapper.call(t); + if (c == null) { + throw new NullPointerException("The mapper returned a null Single"); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + unsubscribe(); + onError(ex); + return; + } + + InnerSubscriber inner = new InnerSubscriber(); + set.add(inner); + active.incrementAndGet(); + + c.subscribe(inner); + } + + @Override + public void onError(Throwable e) { + if (delayErrors) { + ExceptionsUtils.addThrowable(errors, e); + } else { + set.unsubscribe(); + if (!errors.compareAndSet(null, e)) { + RxJavaHooks.onError(e); + return; + } + } + done = true; + drain(); + } + + @Override + public void onCompleted() { + done = true; + drain(); + } + + void innerSuccess(InnerSubscriber inner, R value) { + queue.offer(NotificationLite.next(value)); + set.remove(inner); + active.decrementAndGet(); + drain(); + } + + void innerError(InnerSubscriber inner, Throwable e) { + if (delayErrors) { + ExceptionsUtils.addThrowable(errors, e); + set.remove(inner); + if (!done && maxConcurrency != Integer.MAX_VALUE) { + request(1); + } + } else { + set.unsubscribe(); + unsubscribe(); + if (!errors.compareAndSet(null, e)) { + RxJavaHooks.onError(e); + return; + } + done = true; + } + active.decrementAndGet(); + drain(); + } + + void drain() { + if (wip.getAndIncrement() != 0) { + return; + } + + int missed = 1; + Subscriber a = actual; + Queue q = queue; + boolean delayError = this.delayErrors; + AtomicInteger act = active; + + for (;;) { + long r = requested.get(); + long e = 0L; + + while (e != r) { + if (cancelled) { + q.clear(); + return; + } + + boolean d = done; + + if (!delayError && d) { + Throwable ex = errors.get(); + if (ex != null) { + q.clear(); + a.onError(ExceptionsUtils.terminate(errors)); + return; + } + } + + Object o = q.poll(); + + boolean empty = o == null; + + if (d && act.get() == 0 && empty) { + Throwable ex = errors.get(); + if (ex != null) { + a.onError(ExceptionsUtils.terminate(errors)); + } else { + a.onCompleted(); + } + return; + } + + if (empty) { + break; + } + + a.onNext(NotificationLite.getValue(o)); + + e++; + } + + if (e == r) { + if (cancelled) { + q.clear(); + return; + } + + if (done) { + if (delayError) { + if (act.get() == 0 && q.isEmpty()) { + Throwable ex = errors.get(); + if (ex != null) { + a.onError(ExceptionsUtils.terminate(errors)); + } else { + a.onCompleted(); + } + return; + } + } else { + Throwable ex = errors.get(); + if (ex != null) { + q.clear(); + a.onError(ExceptionsUtils.terminate(errors)); + return; + } + else if (act.get() == 0 && q.isEmpty()) { + a.onCompleted(); + return; + } + } + } + } + + if (e != 0L) { + requested.produced(e); + if (!done && maxConcurrency != Integer.MAX_VALUE) { + request(e); + } + } + + missed = wip.addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + final class Requested extends AtomicLong implements Producer, Subscription { + + private static final long serialVersionUID = -887187595446742742L; + + @Override + public void request(long n) { + if (n > 0L) { + BackpressureUtils.getAndAddRequest(this, n); + drain(); + } + } + + void produced(long e) { + BackpressureUtils.produced(this, e); + } + + @Override + public void unsubscribe() { + cancelled = true; + FlatMapSingleSubscriber.this.unsubscribe(); + if (wip.getAndIncrement() == 0) { + queue.clear(); + } + } + + @Override + public boolean isUnsubscribed() { + return cancelled; + } + } + + final class InnerSubscriber extends SingleSubscriber { + + @Override + public void onSuccess(R t) { + innerSuccess(this, t); + } + + @Override + public void onError(Throwable error) { + innerError(this, error); + } + } + } +} diff --git a/src/main/java/rx/internal/operators/OnSubscribeFlattenIterable.java b/src/main/java/rx/internal/operators/OnSubscribeFlattenIterable.java index 608e6808b7..fe85e55369 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeFlattenIterable.java +++ b/src/main/java/rx/internal/operators/OnSubscribeFlattenIterable.java @@ -70,9 +70,9 @@ public static Observable createFrom(Observable source, Func1> mapper, int prefetch) { if (source instanceof ScalarSynchronousObservable) { T scalar = ((ScalarSynchronousObservable) source).get(); - return Observable.create(new OnSubscribeScalarFlattenIterable(scalar, mapper)); + return Observable.unsafeCreate(new OnSubscribeScalarFlattenIterable(scalar, mapper)); } - return Observable.create(new OnSubscribeFlattenIterable(source, mapper, prefetch)); + return Observable.unsafeCreate(new OnSubscribeFlattenIterable(source, mapper, prefetch)); } static final class FlattenIterableSubscriber extends Subscriber { diff --git a/src/main/java/rx/internal/operators/OnSubscribeGroupJoin.java b/src/main/java/rx/internal/operators/OnSubscribeGroupJoin.java index 4666751842..3c99b68262 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeGroupJoin.java +++ b/src/main/java/rx/internal/operators/OnSubscribeGroupJoin.java @@ -170,7 +170,7 @@ public void onNext(T1 args) { leftMap().put(id, subjSerial); } - Observable window = Observable.create(new WindowObservableFunc(subj, cancel)); + Observable window = Observable.unsafeCreate(new WindowObservableFunc(subj, cancel)); Observable duration = leftDuration.call(args); diff --git a/src/main/java/rx/internal/operators/OnSubscribeRedo.java b/src/main/java/rx/internal/operators/OnSubscribeRedo.java index 628c58f4ef..de4f5e4d15 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeRedo.java +++ b/src/main/java/rx/internal/operators/OnSubscribeRedo.java @@ -31,7 +31,7 @@ * limitations under the License. */ -import static rx.Observable.create; // NOPMD +import static rx.Observable.unsafeCreate; // NOPMD import java.util.concurrent.atomic.*; @@ -78,12 +78,12 @@ public Observable call(Observable> ts) { @Override public Notification call(Notification terminalNotification) { - if(count == 0) { + if (count == 0) { return terminalNotification; } num++; - if(num <= count) { + if (num <= count) { return Notification.createOnNext(num); } else { return terminalNotification; @@ -133,11 +133,11 @@ public static Observable retry(Observable source, final long count) { } public static Observable retry(Observable source, Func1>, ? extends Observable> notificationHandler) { - return create(new OnSubscribeRedo(source, notificationHandler, true, false, Schedulers.trampoline())); + return unsafeCreate(new OnSubscribeRedo(source, notificationHandler, true, false, Schedulers.trampoline())); } public static Observable retry(Observable source, Func1>, ? extends Observable> notificationHandler, Scheduler scheduler) { - return create(new OnSubscribeRedo(source, notificationHandler, true, false, scheduler)); + return unsafeCreate(new OnSubscribeRedo(source, notificationHandler, true, false, scheduler)); } public static Observable repeat(Observable source) { @@ -153,7 +153,7 @@ public static Observable repeat(Observable source, final long count) { } public static Observable repeat(Observable source, final long count, Scheduler scheduler) { - if(count == 0) { + if (count == 0) { return Observable.empty(); } if (count < 0) { @@ -163,15 +163,15 @@ public static Observable repeat(Observable source, final long count, S } public static Observable repeat(Observable source, Func1>, ? extends Observable> notificationHandler) { - return create(new OnSubscribeRedo(source, notificationHandler, false, true, Schedulers.trampoline())); + return unsafeCreate(new OnSubscribeRedo(source, notificationHandler, false, true, Schedulers.trampoline())); } public static Observable repeat(Observable source, Func1>, ? extends Observable> notificationHandler, Scheduler scheduler) { - return create(new OnSubscribeRedo(source, notificationHandler, false, true, scheduler)); + return unsafeCreate(new OnSubscribeRedo(source, notificationHandler, false, true, scheduler)); } public static Observable redo(Observable source, Func1>, ? extends Observable> notificationHandler, Scheduler scheduler) { - return create(new OnSubscribeRedo(source, notificationHandler, false, false, scheduler)); + return unsafeCreate(new OnSubscribeRedo(source, notificationHandler, false, false, scheduler)); } private OnSubscribeRedo(Observable source, Func1>, ? extends Observable> f, boolean stopOnComplete, boolean stopOnError, diff --git a/src/main/java/rx/internal/operators/OnSubscribeRefCount.java b/src/main/java/rx/internal/operators/OnSubscribeRefCount.java index c5898ac1b6..4a34663fbb 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeRefCount.java +++ b/src/main/java/rx/internal/operators/OnSubscribeRefCount.java @@ -129,7 +129,13 @@ void cleanup() { // and set the subscriptionCount to 0 lock.lock(); try { + if (baseSubscription == currentBase) { + // backdoor into the ConnectableObservable to cleanup and reset its state + if (source instanceof Subscription) { + ((Subscription)source).unsubscribe(); + } + baseSubscription.unsubscribe(); baseSubscription = new CompositeSubscription(); subscriptionCount.set(0); @@ -148,7 +154,13 @@ public void call() { lock.lock(); try { if (baseSubscription == current) { + if (subscriptionCount.decrementAndGet() == 0) { + // backdoor into the ConnectableObservable to cleanup and reset its state + if (source instanceof Subscription) { + ((Subscription)source).unsubscribe(); + } + baseSubscription.unsubscribe(); // need a new baseSubscription because once // unsubscribed stays that way diff --git a/src/main/java/rx/internal/operators/OperatorSwitchIfEmpty.java b/src/main/java/rx/internal/operators/OnSubscribeSwitchIfEmpty.java similarity index 70% rename from src/main/java/rx/internal/operators/OperatorSwitchIfEmpty.java rename to src/main/java/rx/internal/operators/OnSubscribeSwitchIfEmpty.java index f3ead2c604..db02dfbff9 100644 --- a/src/main/java/rx/internal/operators/OperatorSwitchIfEmpty.java +++ b/src/main/java/rx/internal/operators/OnSubscribeSwitchIfEmpty.java @@ -16,6 +16,8 @@ package rx.internal.operators; +import java.util.concurrent.atomic.AtomicInteger; + import rx.*; import rx.internal.producers.ProducerArbiter; import rx.subscriptions.SerialSubscription; @@ -26,22 +28,28 @@ * empty, the results of the given Observable will be emitted. * @param the value type */ -public final class OperatorSwitchIfEmpty implements Observable.Operator { - private final Observable alternate; +public final class OnSubscribeSwitchIfEmpty implements Observable.OnSubscribe { + + final Observable source; - public OperatorSwitchIfEmpty(Observable alternate) { + final Observable alternate; + + public OnSubscribeSwitchIfEmpty(Observable source, Observable alternate) { + this.source = source; this.alternate = alternate; } @Override - public Subscriber call(Subscriber child) { + public void call(Subscriber child) { final SerialSubscription serial = new SerialSubscription(); ProducerArbiter arbiter = new ProducerArbiter(); final ParentSubscriber parent = new ParentSubscriber(child, serial, arbiter, alternate); + serial.set(parent); child.add(serial); child.setProducer(arbiter); - return parent; + + parent.subscribe(source); } static final class ParentSubscriber extends Subscriber { @@ -52,11 +60,15 @@ static final class ParentSubscriber extends Subscriber { private final ProducerArbiter arbiter; private final Observable alternate; + final AtomicInteger wip; + volatile boolean active; + ParentSubscriber(Subscriber child, final SerialSubscription serial, ProducerArbiter arbiter, Observable alternate) { this.child = child; this.serial = serial; this.arbiter = arbiter; this.alternate = alternate; + this.wip = new AtomicInteger(); } @Override @@ -69,14 +81,33 @@ public void onCompleted() { if (!empty) { child.onCompleted(); } else if (!child.isUnsubscribed()) { - subscribeToAlternate(); + active = false; + subscribe(null); } } - private void subscribeToAlternate() { - AlternateSubscriber as = new AlternateSubscriber(child, arbiter); - serial.set(as); - alternate.unsafeSubscribe(as); + void subscribe(Observable source) { + if (wip.getAndIncrement() == 0) { + do { + if (child.isUnsubscribed()) { + break; + } + + if (!active) { + if (source == null) { + AlternateSubscriber as = new AlternateSubscriber(child, arbiter); + serial.set(as); + active = true; + alternate.unsafeSubscribe(as); + } else { + active = true; + source.unsafeSubscribe(this); + source = null; + } + } + + } while (wip.decrementAndGet() != 0); + } } @Override diff --git a/src/main/java/rx/internal/operators/OnSubscribeTimeoutSelectorWithFallback.java b/src/main/java/rx/internal/operators/OnSubscribeTimeoutSelectorWithFallback.java new file mode 100644 index 0000000000..75d75777c8 --- /dev/null +++ b/src/main/java/rx/internal/operators/OnSubscribeTimeoutSelectorWithFallback.java @@ -0,0 +1,247 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; + +import rx.*; +import rx.exceptions.Exceptions; +import rx.functions.Func1; +import rx.internal.operators.OnSubscribeTimeoutTimedWithFallback.FallbackSubscriber; +import rx.internal.producers.ProducerArbiter; +import rx.internal.subscriptions.SequentialSubscription; +import rx.plugins.RxJavaHooks; + +/** + * Switches to the fallback Observable if: the first upstream item doesn't arrive before + * the first timeout Observable signals an item or completes; or the Observable generated from + * the previous upstream item signals its item or completes before the upstream signals the next item + * of its own. + * + * @param the input and output value type + * @param the value type of the first timeout Observable + * @param the value type of the item-based timeout Observable + * + * @since 1.3.3 + */ +public final class OnSubscribeTimeoutSelectorWithFallback implements Observable.OnSubscribe { + + final Observable source; + + final Observable firstTimeoutIndicator; + + final Func1> itemTimeoutIndicator; + + final Observable fallback; + + public OnSubscribeTimeoutSelectorWithFallback(Observable source, + Observable firstTimeoutIndicator, + Func1> itemTimeoutIndicator, + Observable fallback) { + this.source = source; + this.firstTimeoutIndicator = firstTimeoutIndicator; + this.itemTimeoutIndicator = itemTimeoutIndicator; + this.fallback = fallback; + } + + @Override + public void call(Subscriber t) { + TimeoutMainSubscriber parent = new TimeoutMainSubscriber(t, itemTimeoutIndicator, fallback); + t.add(parent.upstream); + t.setProducer(parent.arbiter); + parent.startFirst(firstTimeoutIndicator); + source.subscribe(parent); + } + + static final class TimeoutMainSubscriber extends Subscriber { + + final Subscriber actual; + + final Func1> itemTimeoutIndicator; + + final Observable fallback; + + final ProducerArbiter arbiter; + + final AtomicLong index; + + final SequentialSubscription task; + + final SequentialSubscription upstream; + + long consumed; + + TimeoutMainSubscriber(Subscriber actual, + Func1> itemTimeoutIndicator, + Observable fallback) { + this.actual = actual; + this.itemTimeoutIndicator = itemTimeoutIndicator; + this.fallback = fallback; + this.arbiter = new ProducerArbiter(); + this.index = new AtomicLong(); + this.task = new SequentialSubscription(); + this.upstream = new SequentialSubscription(this); + this.add(task); + } + + + @Override + public void onNext(T t) { + long idx = index.get(); + if (idx == Long.MAX_VALUE || !index.compareAndSet(idx, idx + 1)) { + return; + } + + Subscription s = task.get(); + if (s != null) { + s.unsubscribe(); + } + + actual.onNext(t); + + consumed++; + + Observable timeoutObservable; + + try { + timeoutObservable = itemTimeoutIndicator.call(t); + if (timeoutObservable == null) { + throw new NullPointerException("The itemTimeoutIndicator returned a null Observable"); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + unsubscribe(); + index.getAndSet(Long.MAX_VALUE); + actual.onError(ex); + return; + } + + TimeoutConsumer tc = new TimeoutConsumer(idx + 1); + if (task.replace(tc)) { + timeoutObservable.subscribe(tc); + } + + } + + void startFirst(Observable firstTimeoutIndicator) { + if (firstTimeoutIndicator != null) { + TimeoutConsumer tc = new TimeoutConsumer(0L); + if (task.replace(tc)) { + firstTimeoutIndicator.subscribe(tc); + } + } + } + + @Override + public void onError(Throwable e) { + if (index.getAndSet(Long.MAX_VALUE) != Long.MAX_VALUE) { + task.unsubscribe(); + + actual.onError(e); + } else { + RxJavaHooks.onError(e); + } + } + + @Override + public void onCompleted() { + if (index.getAndSet(Long.MAX_VALUE) != Long.MAX_VALUE) { + task.unsubscribe(); + + actual.onCompleted(); + } + } + + @Override + public void setProducer(Producer p) { + arbiter.setProducer(p); + } + + void onTimeout(long idx) { + if (!index.compareAndSet(idx, Long.MAX_VALUE)) { + return; + } + + unsubscribe(); + + if (fallback == null) { + actual.onError(new TimeoutException()); + } else { + long c = consumed; + if (c != 0L) { + arbiter.produced(c); + } + + FallbackSubscriber fallbackSubscriber = new FallbackSubscriber(actual, arbiter); + + if (upstream.replace(fallbackSubscriber)) { + fallback.subscribe(fallbackSubscriber); + } + } + } + + void onTimeoutError(long idx, Throwable ex) { + if (index.compareAndSet(idx, Long.MAX_VALUE)) { + unsubscribe(); + + actual.onError(ex); + } else { + RxJavaHooks.onError(ex); + } + + } + + final class TimeoutConsumer extends Subscriber { + + final long idx; + + boolean done; + + TimeoutConsumer(long idx) { + this.idx = idx; + } + + @Override + public void onNext(Object t) { + if (!done) { + done = true; + unsubscribe(); + onTimeout(idx); + } + } + + @Override + public void onError(Throwable e) { + if (!done) { + done = true; + onTimeoutError(idx, e); + } else { + RxJavaHooks.onError(e); + } + } + + @Override + public void onCompleted() { + if (!done) { + done = true; + onTimeout(idx); + } + } + } + } +} diff --git a/src/main/java/rx/internal/operators/OnSubscribeTimeoutTimedWithFallback.java b/src/main/java/rx/internal/operators/OnSubscribeTimeoutTimedWithFallback.java new file mode 100644 index 0000000000..e70c57d667 --- /dev/null +++ b/src/main/java/rx/internal/operators/OnSubscribeTimeoutTimedWithFallback.java @@ -0,0 +1,227 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; + +import rx.*; +import rx.Scheduler.Worker; +import rx.functions.Action0; +import rx.internal.producers.ProducerArbiter; +import rx.internal.subscriptions.SequentialSubscription; +import rx.plugins.RxJavaHooks; + +/** + * Switches to consuming a fallback Observable if the main source doesn't signal an onNext event + * within the given time frame after subscription or the previous onNext event. + * + * @param the value type + * @since 1.3.3 + */ +public final class OnSubscribeTimeoutTimedWithFallback implements Observable.OnSubscribe { + + final Observable source; + + final long timeout; + + final TimeUnit unit; + + final Scheduler scheduler; + + final Observable fallback; + + public OnSubscribeTimeoutTimedWithFallback(Observable source, long timeout, + TimeUnit unit, Scheduler scheduler, + Observable fallback) { + this.source = source; + this.timeout = timeout; + this.unit = unit; + this.scheduler = scheduler; + this.fallback = fallback; + } + + @Override + public void call(Subscriber t) { + TimeoutMainSubscriber parent = new TimeoutMainSubscriber(t, timeout, unit, scheduler.createWorker(), fallback); + t.add(parent.upstream); + t.setProducer(parent.arbiter); + parent.startTimeout(0L); + source.subscribe(parent); + } + + static final class TimeoutMainSubscriber extends Subscriber { + + final Subscriber actual; + + final long timeout; + + final TimeUnit unit; + + final Worker worker; + + final Observable fallback; + + final ProducerArbiter arbiter; + + final AtomicLong index; + + final SequentialSubscription task; + + final SequentialSubscription upstream; + + long consumed; + + TimeoutMainSubscriber(Subscriber actual, long timeout, + TimeUnit unit, Worker worker, + Observable fallback) { + this.actual = actual; + this.timeout = timeout; + this.unit = unit; + this.worker = worker; + this.fallback = fallback; + this.arbiter = new ProducerArbiter(); + this.index = new AtomicLong(); + this.task = new SequentialSubscription(); + this.upstream = new SequentialSubscription(this); + this.add(worker); + this.add(task); + } + + + @Override + public void onNext(T t) { + long idx = index.get(); + if (idx == Long.MAX_VALUE || !index.compareAndSet(idx, idx + 1)) { + return; + } + + Subscription s = task.get(); + if (s != null) { + s.unsubscribe(); + } + + consumed++; + + actual.onNext(t); + + startTimeout(idx + 1); + } + + void startTimeout(long nextIdx) { + task.replace(worker.schedule(new TimeoutTask(nextIdx), timeout, unit)); + } + + @Override + public void onError(Throwable e) { + if (index.getAndSet(Long.MAX_VALUE) != Long.MAX_VALUE) { + task.unsubscribe(); + + actual.onError(e); + + worker.unsubscribe(); + } else { + RxJavaHooks.onError(e); + } + } + + @Override + public void onCompleted() { + if (index.getAndSet(Long.MAX_VALUE) != Long.MAX_VALUE) { + task.unsubscribe(); + + actual.onCompleted(); + + worker.unsubscribe(); + } + } + + @Override + public void setProducer(Producer p) { + arbiter.setProducer(p); + } + + void onTimeout(long idx) { + if (!index.compareAndSet(idx, Long.MAX_VALUE)) { + return; + } + + unsubscribe(); + + if (fallback == null) { + actual.onError(new TimeoutException()); + } else { + long c = consumed; + if (c != 0L) { + arbiter.produced(c); + } + + FallbackSubscriber fallbackSubscriber = new FallbackSubscriber(actual, arbiter); + + if (upstream.replace(fallbackSubscriber)) { + fallback.subscribe(fallbackSubscriber); + } + } + } + + final class TimeoutTask implements Action0 { + + final long idx; + + TimeoutTask(long idx) { + this.idx = idx; + } + + @Override + public void call() { + onTimeout(idx); + } + } + } + + static final class FallbackSubscriber extends Subscriber { + + final Subscriber actual; + + final ProducerArbiter arbiter; + + FallbackSubscriber(Subscriber actual, ProducerArbiter arbiter) { + this.actual = actual; + this.arbiter = arbiter; + } + + @Override + public void onNext(T t) { + actual.onNext(t); + } + + @Override + public void onError(Throwable e) { + actual.onError(e); + } + + @Override + public void onCompleted() { + actual.onCompleted(); + } + + @Override + public void setProducer(Producer p) { + arbiter.setProducer(p); + } + } +} diff --git a/src/main/java/rx/internal/operators/OnSubscribeToMultimap.java b/src/main/java/rx/internal/operators/OnSubscribeToMultimap.java index 6883e93d24..c412ea5af5 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeToMultimap.java +++ b/src/main/java/rx/internal/operators/OnSubscribeToMultimap.java @@ -155,7 +155,7 @@ public void onStart() { @Override public void onNext(T t) { - if (done){ + if (done) { return; } try { diff --git a/src/main/java/rx/internal/operators/OperatorGroupBy.java b/src/main/java/rx/internal/operators/OperatorGroupBy.java index abbe5cf3cf..5d6ec2f556 100644 --- a/src/main/java/rx/internal/operators/OperatorGroupBy.java +++ b/src/main/java/rx/internal/operators/OperatorGroupBy.java @@ -42,13 +42,16 @@ * the source and group value type * @param * the value type of the groups + * @deprecated + * since 1.3.7, use {@link OperatorGroupByEvicting} instead */ -public final class OperatorGroupBy implements Operator, T>{ +@Deprecated +public final class OperatorGroupBy implements Operator, T> { final Func1 keySelector; final Func1 valueSelector; final int bufferSize; final boolean delayError; - final Func1, Map> mapFactory; + final Func1, Map> mapFactory; //nullable @SuppressWarnings({ "unchecked", "rawtypes" }) public OperatorGroupBy(Func1 keySelector) { @@ -116,6 +119,10 @@ public static final class GroupBySubscriber final int bufferSize; final boolean delayError; final Map> groups; + + // double store the groups to workaround the bug in the + // signature of groupBy with evicting map factory + final Map> groupsCopy; final Queue> queue; final GroupByProducer producer; final Queue evictedKeys; @@ -134,7 +141,7 @@ public static final class GroupBySubscriber volatile boolean done; final AtomicInteger wip; - + public GroupBySubscriber(Subscriber> actual, Func1 keySelector, Func1 valueSelector, int bufferSize, boolean delayError, Func1, Map> mapFactory) { @@ -158,6 +165,7 @@ public GroupBySubscriber(Subscriber> actual, Fun this.evictedKeys = new ConcurrentLinkedQueue(); this.groups = createMap(mapFactory, new EvictionAction(evictedKeys)); } + this.groupsCopy = new ConcurrentHashMap>(); } static class EvictionAction implements Action1 { @@ -202,7 +210,7 @@ public void onNext(T t) { return; } - boolean notNew = true; + boolean newGroup = false; Object mapKey = key != null ? key : NULL_KEY; GroupedUnicast group = groups.get(mapKey); if (group == null) { @@ -211,12 +219,13 @@ public void onNext(T t) { if (!cancelled.get()) { group = GroupedUnicast.createWith(key, bufferSize, this, delayError); groups.put(mapKey, group); + if (evictedKeys != null) { + groupsCopy.put(mapKey, group); + } groupCount.getAndIncrement(); - notNew = false; - q.offer(group); - drain(); + newGroup = true; } else { return; } @@ -236,15 +245,18 @@ public void onNext(T t) { if (evictedKeys != null) { K evictedKey; while ((evictedKey = evictedKeys.poll()) != null) { - GroupedUnicast g = groups.get(evictedKey); + GroupedUnicast g = groupsCopy.remove(evictedKey); + // do a null check on g because cancel(K) could have cleared + // the map if (g != null) { g.onComplete(); } } } - if (notNew) { - s.request(1); + if (newGroup) { + q.offer(group); + drain(); } } @@ -271,6 +283,7 @@ public void onCompleted() { } groups.clear(); if (evictedKeys != null) { + groupsCopy.clear(); evictedKeys.clear(); } @@ -305,6 +318,9 @@ public void cancel(K key) { unsubscribe(); } } + if (evictedKeys != null) { + groupsCopy.remove(mapKey); + } } void drain() { @@ -365,6 +381,7 @@ void errorAll(Subscriber> a, Queue q, Throwab List> list = new ArrayList>(groups.values()); groups.clear(); if (evictedKeys != null) { + groupsCopy.clear(); evictedKeys.clear(); } diff --git a/src/main/java/rx/internal/operators/OperatorGroupByEvicting.java b/src/main/java/rx/internal/operators/OperatorGroupByEvicting.java new file mode 100644 index 0000000000..d02ecf5d39 --- /dev/null +++ b/src/main/java/rx/internal/operators/OperatorGroupByEvicting.java @@ -0,0 +1,605 @@ +/** + * Copyright 2018 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package rx.internal.operators; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +import rx.*; +import rx.Observable.*; +import rx.exceptions.Exceptions; +import rx.functions.*; +import rx.internal.producers.ProducerArbiter; +import rx.internal.util.*; +import rx.observables.GroupedObservable; +import rx.plugins.RxJavaHooks; +import rx.observers.Subscribers; +import rx.subscriptions.Subscriptions; + +/** + * Groups the items emitted by an Observable according to a specified criterion, and emits these + * grouped items as Observables, one Observable per group. + *

+ * + * + * @param + * the key type + * @param + * the source and group value type + * @param + * the value type of the groups + */ +public final class OperatorGroupByEvicting implements Operator, T>{ + + final Func1 keySelector; + final Func1 valueSelector; + final int bufferSize; + final boolean delayError; + final Func1, Map> mapFactory; //nullable + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public OperatorGroupByEvicting(Func1 keySelector) { + this(keySelector, (Func1)UtilityFunctions.identity(), RxRingBuffer.SIZE, false, null); + } + + public OperatorGroupByEvicting(Func1 keySelector, Func1 valueSelector) { + this(keySelector, valueSelector, RxRingBuffer.SIZE, false, null); + } + + public OperatorGroupByEvicting(Func1 keySelector, Func1 valueSelector, int bufferSize, boolean delayError, Func1, Map> mapFactory) { + this.keySelector = keySelector; + this.valueSelector = valueSelector; + this.bufferSize = bufferSize; + this.delayError = delayError; + this.mapFactory = mapFactory; + } + + @SuppressWarnings("unchecked") + @Override + public Subscriber call(Subscriber> child) { + Map> groups; + Queue> evictedGroups; + + if (mapFactory == null) { + evictedGroups = null; + groups = new ConcurrentHashMap>(); + } else { + evictedGroups = new ConcurrentLinkedQueue>(); + Action1 evictionAction = (Action1)(Action1) + new EvictionAction(evictedGroups); + try { + groups = (Map>)(Map) + mapFactory.call((Action1)(Action1) evictionAction); + } catch (Throwable ex) { + //Can reach here because mapFactory.call() may throw + Exceptions.throwOrReport(ex, child); + Subscriber parent2 = Subscribers.empty(); + parent2.unsubscribe(); + return parent2; + } + } + final GroupBySubscriber parent = new GroupBySubscriber( + child, keySelector, valueSelector, bufferSize, delayError, groups, evictedGroups); + + child.add(Subscriptions.create(new Action0() { + @Override + public void call() { + parent.cancel(); + } + })); + + child.setProducer(parent.producer); + + return parent; + } + + public static final class GroupByProducer implements Producer { + final GroupBySubscriber parent; + + public GroupByProducer(GroupBySubscriber parent) { + this.parent = parent; + } + @Override + public void request(long n) { + parent.requestMore(n); + } + } + + public static final class GroupBySubscriber + extends Subscriber { + final Subscriber> actual; + final Func1 keySelector; + final Func1 valueSelector; + final int bufferSize; + final boolean delayError; + final Map> groups; + final Queue> queue; + final GroupByProducer producer; + final Queue> evictedGroups; + + static final Object NULL_KEY = new Object(); + + final ProducerArbiter s; + + final AtomicBoolean cancelled; + + final AtomicLong requested; + + final AtomicInteger groupCount; + + Throwable error; + volatile boolean done; + + final AtomicInteger wip; + + public GroupBySubscriber(Subscriber> actual, Func1 keySelector, + Func1 valueSelector, int bufferSize, boolean delayError, Map> groups, + Queue> evictedGroups) { + this.actual = actual; + this.keySelector = keySelector; + this.valueSelector = valueSelector; + this.bufferSize = bufferSize; + this.delayError = delayError; + this.queue = new ConcurrentLinkedQueue>(); + this.s = new ProducerArbiter(); + this.s.request(bufferSize); + this.producer = new GroupByProducer(this); + this.cancelled = new AtomicBoolean(); + this.requested = new AtomicLong(); + this.groupCount = new AtomicInteger(1); + this.wip = new AtomicInteger(); + this.groups = groups; + this.evictedGroups = evictedGroups; + } + + @Override + public void setProducer(Producer s) { + this.s.setProducer(s); + } + + @Override + public void onNext(T t) { + if (done) { + return; + } + + final Queue> q = this.queue; + final Subscriber> a = this.actual; + + K key; + try { + key = keySelector.call(t); + } catch (Throwable ex) { + unsubscribe(); + errorAll(a, q, ex); + return; + } + + boolean newGroup = false; + @SuppressWarnings("unchecked") + K mapKey = key != null ? key : (K) NULL_KEY; + GroupedUnicast group = groups.get(mapKey); + if (group == null) { + // if the main has been cancelled, stop creating groups + // and skip this value + if (!cancelled.get()) { + group = GroupedUnicast.createWith(key, bufferSize, this, delayError); + groups.put(mapKey, group); + + groupCount.getAndIncrement(); + + newGroup = false; + q.offer(group); + drain(); + } else { + return; + } + } + + V v; + try { + v = valueSelector.call(t); + } catch (Throwable ex) { + unsubscribe(); + errorAll(a, q, ex); + return; + } + + group.onNext(v); + + if (evictedGroups != null) { + GroupedUnicast evictedGroup; + while ((evictedGroup = evictedGroups.poll()) != null) { + evictedGroup.onComplete(); + } + } + + if (newGroup) { + q.offer(group); + drain(); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaHooks.onError(t); + return; + } + error = t; + done = true; + groupCount.decrementAndGet(); + drain(); + } + + @Override + public void onCompleted() { + if (done) { + return; + } + + for (GroupedUnicast e : groups.values()) { + e.onComplete(); + } + groups.clear(); + if (evictedGroups != null) { + evictedGroups.clear(); + } + + done = true; + groupCount.decrementAndGet(); + drain(); + } + + public void requestMore(long n) { + if (n < 0) { + throw new IllegalArgumentException("n >= 0 required but it was " + n); + } + + BackpressureUtils.getAndAddRequest(requested, n); + drain(); + } + + public void cancel() { + // cancelling the main source means we don't want any more groups + // but running groups still require new values + if (cancelled.compareAndSet(false, true)) { + if (groupCount.decrementAndGet() == 0) { + unsubscribe(); + } + } + } + + public void cancel(K key) { + Object mapKey = key != null ? key : NULL_KEY; + if (groups.remove(mapKey) != null) { + if (groupCount.decrementAndGet() == 0) { + unsubscribe(); + } + } + } + + void drain() { + if (wip.getAndIncrement() != 0) { + return; + } + + int missed = 1; + + final Queue> q = this.queue; + final Subscriber> a = this.actual; + + for (;;) { + + if (checkTerminated(done, q.isEmpty(), a, q)) { + return; + } + + long r = requested.get(); + boolean unbounded = r == Long.MAX_VALUE; + long e = 0L; + + while (r != 0) { + boolean d = done; + + GroupedObservable t = q.poll(); + + boolean empty = t == null; + + if (checkTerminated(d, empty, a, q)) { + return; + } + + if (empty) { + break; + } + + a.onNext(t); + + r--; + e--; + } + + if (e != 0L) { + if (!unbounded) { + requested.addAndGet(e); + } + s.request(-e); + } + + missed = wip.addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + void errorAll(Subscriber> a, Queue q, Throwable ex) { + q.clear(); + List> list = new ArrayList>(groups.values()); + groups.clear(); + if (evictedGroups != null) { + evictedGroups.clear(); + } + + for (GroupedUnicast e : list) { + e.onError(ex); + } + + a.onError(ex); + } + + boolean checkTerminated(boolean d, boolean empty, + Subscriber> a, Queue q) { + if (d) { + Throwable err = error; + if (err != null) { + errorAll(a, q, err); + return true; + } else + if (empty) { + actual.onCompleted(); + return true; + } + } + return false; + } + } + + static class EvictionAction implements Action1> { + + final Queue> evictedGroups; + + EvictionAction(Queue> evictedGroups) { + this.evictedGroups = evictedGroups; + } + + @Override + public void call(GroupedUnicast group) { + evictedGroups.offer(group); + } + } + + static final class GroupedUnicast extends GroupedObservable { + + public static GroupedUnicast createWith(K key, int bufferSize, GroupBySubscriber parent, boolean delayError) { + State state = new State(bufferSize, parent, key, delayError); + return new GroupedUnicast(key, state); + } + + final State state; + + protected GroupedUnicast(K key, State state) { + super(key, state); + this.state = state; + } + + public void onNext(T t) { + state.onNext(t); + } + + public void onError(Throwable e) { + state.onError(e); + } + + public void onComplete() { + state.onComplete(); + } + } + + static final class State extends AtomicInteger implements Producer, Subscription, OnSubscribe { + /** */ + private static final long serialVersionUID = -3852313036005250360L; + + final K key; + final Queue queue; + final GroupBySubscriber parent; + final boolean delayError; + + final AtomicLong requested; + + volatile boolean done; + Throwable error; + + final AtomicBoolean cancelled; + + final AtomicReference> actual; + + final AtomicBoolean once; + + + public State(int bufferSize, GroupBySubscriber parent, K key, boolean delayError) { + this.queue = new ConcurrentLinkedQueue(); + this.parent = parent; + this.key = key; + this.delayError = delayError; + this.cancelled = new AtomicBoolean(); + this.actual = new AtomicReference>(); + this.once = new AtomicBoolean(); + this.requested = new AtomicLong(); + } + + @Override + public void request(long n) { + if (n < 0) { + throw new IllegalArgumentException("n >= required but it was " + n); + } + if (n != 0L) { + BackpressureUtils.getAndAddRequest(requested, n); + drain(); + } + } + + @Override + public boolean isUnsubscribed() { + return cancelled.get(); + } + + @Override + public void unsubscribe() { + if (cancelled.compareAndSet(false, true)) { + if (getAndIncrement() == 0) { + parent.cancel(key); + } + } + } + + @Override + public void call(Subscriber s) { + if (once.compareAndSet(false, true)) { + s.add(this); + s.setProducer(this); + actual.lazySet(s); + drain(); + } else { + s.onError(new IllegalStateException("Only one Subscriber allowed!")); + } + } + + public void onNext(T t) { + if (t == null) { + error = new NullPointerException(); + done = true; + } else { + queue.offer(NotificationLite.next(t)); + } + drain(); + } + + public void onError(Throwable e) { + error = e; + done = true; + drain(); + } + + public void onComplete() { + done = true; + drain(); + } + + void drain() { + if (getAndIncrement() != 0) { + return; + } + int missed = 1; + + final Queue q = queue; + final boolean delayError = this.delayError; + Subscriber a = actual.get(); + for (;;) { + if (a != null) { + if (checkTerminated(done, q.isEmpty(), a, delayError)) { + return; + } + + long r = requested.get(); + boolean unbounded = r == Long.MAX_VALUE; + long e = 0; + + while (r != 0L) { + boolean d = done; + Object v = q.poll(); + boolean empty = v == null; + + if (checkTerminated(d, empty, a, delayError)) { + return; + } + + if (empty) { + break; + } + + a.onNext(NotificationLite.getValue(v)); + + r--; + e--; + } + + if (e != 0L) { + if (!unbounded) { + requested.addAndGet(e); + } + parent.s.request(-e); + } + } + + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + if (a == null) { + a = actual.get(); + } + } + } + + boolean checkTerminated(boolean d, boolean empty, Subscriber a, boolean delayError) { + if (cancelled.get()) { + queue.clear(); + parent.cancel(key); + return true; + } + + if (d) { + if (delayError) { + if (empty) { + Throwable e = error; + if (e != null) { + a.onError(e); + } else { + a.onCompleted(); + } + return true; + } + } else { + Throwable e = error; + if (e != null) { + queue.clear(); + a.onError(e); + return true; + } else + if (empty) { + a.onCompleted(); + return true; + } + } + } + + return false; + } + } +} diff --git a/src/main/java/rx/internal/operators/OperatorMaterialize.java b/src/main/java/rx/internal/operators/OperatorMaterialize.java index c1d4a3b65e..ce9e1be604 100644 --- a/src/main/java/rx/internal/operators/OperatorMaterialize.java +++ b/src/main/java/rx/internal/operators/OperatorMaterialize.java @@ -134,6 +134,7 @@ private void drain() { missed = true; return; } + busy = true; } // drain loop final AtomicLong localRequested = this.requested; diff --git a/src/main/java/rx/internal/operators/OperatorMerge.java b/src/main/java/rx/internal/operators/OperatorMerge.java index fa30f46b10..a52eee07e9 100644 --- a/src/main/java/rx/internal/operators/OperatorMerge.java +++ b/src/main/java/rx/internal/operators/OperatorMerge.java @@ -847,8 +847,11 @@ public void onNext(T t) { } @Override public void onError(Throwable e) { - done = true; + // Need to queue the error first before setting done, so that after emitLoop() removes the subscriber, + // it is guaranteed to notice the error. Otherwise it would be possible that inner subscribers count was 0, + // and at the same time the error queue was empty. parent.getOrCreateErrorQueue().offer(e); + done = true; parent.emit(); } @Override diff --git a/src/main/java/rx/internal/operators/OperatorMulticast.java b/src/main/java/rx/internal/operators/OperatorMulticast.java index d07f60bf93..3f0777ca91 100644 --- a/src/main/java/rx/internal/operators/OperatorMulticast.java +++ b/src/main/java/rx/internal/operators/OperatorMulticast.java @@ -112,7 +112,7 @@ public void call() { guardedSubscription = gs.get(); // register any subscribers that are waiting with this new subject - for(final Subscriber s : waitingForConnect) { + for (final Subscriber s : waitingForConnect) { subject.unsafeSubscribe(new Subscriber(s) { @Override public void onNext(R t) { diff --git a/src/main/java/rx/internal/operators/OperatorOnBackpressureDrop.java b/src/main/java/rx/internal/operators/OperatorOnBackpressureDrop.java index 6c8770dd04..5ed221746d 100644 --- a/src/main/java/rx/internal/operators/OperatorOnBackpressureDrop.java +++ b/src/main/java/rx/internal/operators/OperatorOnBackpressureDrop.java @@ -99,7 +99,7 @@ public void onNext(T t) { requested.decrementAndGet(); } else { // item dropped - if(onDrop != null) { + if (onDrop != null) { try { onDrop.call(t); } catch (Throwable e) { diff --git a/src/main/java/rx/internal/operators/OperatorPublish.java b/src/main/java/rx/internal/operators/OperatorPublish.java index f7879a7ee1..c07d084abe 100644 --- a/src/main/java/rx/internal/operators/OperatorPublish.java +++ b/src/main/java/rx/internal/operators/OperatorPublish.java @@ -21,7 +21,8 @@ import rx.*; import rx.exceptions.*; import rx.functions.*; -import rx.internal.util.*; +import rx.internal.util.RxRingBuffer; +import rx.internal.util.atomic.SpscAtomicArrayQueue; import rx.internal.util.unsafe.*; import rx.observables.ConnectableObservable; import rx.subscriptions.Subscriptions; @@ -122,7 +123,7 @@ public static Observable create(final Observable source, public static Observable create(final Observable source, final Func1, ? extends Observable> selector, final boolean delayError) { - return create(new OnSubscribe() { + return unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber child) { final OnSubscribePublishMulticast op = new OnSubscribePublishMulticast(RxRingBuffer.SIZE, delayError); @@ -154,7 +155,7 @@ public void setProducer(Producer p) { child.add(op); child.add(subscriber); - selector.call(Observable.create(op)).unsafeSubscribe(subscriber); + selector.call(Observable.unsafeCreate(op)).unsafeSubscribe(subscriber); source.unsafeSubscribe(op.subscriber()); } @@ -244,7 +245,7 @@ static final class PublishSubscriber extends Subscriber implements Subscri public PublishSubscriber(AtomicReference> current) { this.queue = UnsafeAccess.isUnsafeAvailable() ? new SpscArrayQueue(RxRingBuffer.SIZE) - : new SynchronizedQueue(RxRingBuffer.SIZE); + : new SpscAtomicArrayQueue(RxRingBuffer.SIZE); this.producers = new AtomicReference(EMPTY); this.current = current; diff --git a/src/main/java/rx/internal/operators/OperatorReplay.java b/src/main/java/rx/internal/operators/OperatorReplay.java index 53511f9c80..6447b2c737 100644 --- a/src/main/java/rx/internal/operators/OperatorReplay.java +++ b/src/main/java/rx/internal/operators/OperatorReplay.java @@ -28,7 +28,7 @@ import rx.schedulers.Timestamped; import rx.subscriptions.Subscriptions; -public final class OperatorReplay extends ConnectableObservable { +public final class OperatorReplay extends ConnectableObservable implements Subscription { /** The source observable. */ final Observable source; /** Holds the current subscriber that is, will be or just was subscribed to the source observable. */ @@ -58,7 +58,7 @@ public Object call() { public static Observable multicastSelector( final Func0> connectableFactory, final Func1, ? extends Observable> selector) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber child) { ConnectableObservable co; @@ -254,6 +254,17 @@ private OperatorReplay(OnSubscribe onSubscribe, Observable sourc this.bufferFactory = bufferFactory; } + @Override + public void unsubscribe() { + current.lazySet(null); + } + + @Override + public boolean isUnsubscribed() { + ReplaySubscriber ps = current.get(); + return ps == null || ps.isUnsubscribed(); + } + @Override public void connect(Action1 connection) { boolean doConnect; @@ -1239,9 +1250,18 @@ Node getInitialHead() { Node prev = get(); Node next = prev.get(); - while (next != null && ((Timestamped)next.value).getTimestampMillis() <= timeLimit) { - prev = next; - next = next.get(); + while (next != null) { + Object o = next.value; + Object v = leaveTransform(o); + if (NotificationLite.isCompleted(v) || NotificationLite.isError(v)) { + break; + } + if (((Timestamped)o).getTimestampMillis() <= timeLimit) { + prev = next; + next = next.get(); + } else { + break; + } } return prev; diff --git a/src/main/java/rx/internal/operators/OperatorSubscribeOn.java b/src/main/java/rx/internal/operators/OperatorSubscribeOn.java index 9b90cbc26a..0be1b01829 100644 --- a/src/main/java/rx/internal/operators/OperatorSubscribeOn.java +++ b/src/main/java/rx/internal/operators/OperatorSubscribeOn.java @@ -31,68 +31,92 @@ public final class OperatorSubscribeOn implements OnSubscribe { final Scheduler scheduler; final Observable source; + final boolean requestOn; - public OperatorSubscribeOn(Observable source, Scheduler scheduler) { + public OperatorSubscribeOn(Observable source, Scheduler scheduler, boolean requestOn) { this.scheduler = scheduler; this.source = source; + this.requestOn = requestOn; } @Override public void call(final Subscriber subscriber) { final Worker inner = scheduler.createWorker(); + + SubscribeOnSubscriber parent = new SubscribeOnSubscriber(subscriber, requestOn, inner, source); + subscriber.add(parent); subscriber.add(inner); - inner.schedule(new Action0() { - @Override - public void call() { - final Thread t = Thread.currentThread(); + inner.schedule(parent); + } - Subscriber s = new Subscriber(subscriber) { - @Override - public void onNext(T t) { - subscriber.onNext(t); - } + static final class SubscribeOnSubscriber extends Subscriber implements Action0 { - @Override - public void onError(Throwable e) { - try { - subscriber.onError(e); - } finally { - inner.unsubscribe(); - } - } + final Subscriber actual; - @Override - public void onCompleted() { - try { - subscriber.onCompleted(); - } finally { - inner.unsubscribe(); - } - } + final boolean requestOn; + + final Worker worker; + + Observable source; + + Thread t; + + SubscribeOnSubscriber(Subscriber actual, boolean requestOn, Worker worker, Observable source) { + this.actual = actual; + this.requestOn = requestOn; + this.worker = worker; + this.source = source; + } - @Override - public void setProducer(final Producer p) { - subscriber.setProducer(new Producer() { + @Override + public void onNext(T t) { + actual.onNext(t); + } + + @Override + public void onError(Throwable e) { + try { + actual.onError(e); + } finally { + worker.unsubscribe(); + } + } + + @Override + public void onCompleted() { + try { + actual.onCompleted(); + } finally { + worker.unsubscribe(); + } + } + + @Override + public void call() { + Observable src = source; + source = null; + t = Thread.currentThread(); + src.unsafeSubscribe(this); + } + + @Override + public void setProducer(final Producer p) { + actual.setProducer(new Producer() { + @Override + public void request(final long n) { + if (t == Thread.currentThread() || !requestOn) { + p.request(n); + } else { + worker.schedule(new Action0() { @Override - public void request(final long n) { - if (t == Thread.currentThread()) { - p.request(n); - } else { - inner.schedule(new Action0() { - @Override - public void call() { - p.request(n); - } - }); - } + public void call() { + p.request(n); } }); } - }; - - source.unsafeSubscribe(s); - } - }); + } + }); + } } } \ No newline at end of file diff --git a/src/main/java/rx/internal/operators/OperatorSwitch.java b/src/main/java/rx/internal/operators/OperatorSwitch.java index 1f2806ab53..e50e7cafc9 100644 --- a/src/main/java/rx/internal/operators/OperatorSwitch.java +++ b/src/main/java/rx/internal/operators/OperatorSwitch.java @@ -113,7 +113,7 @@ public void call() { clearProducer(); } })); - child.setProducer(new Producer(){ + child.setProducer(new Producer() { @Override public void request(long n) { diff --git a/src/main/java/rx/internal/operators/OperatorTake.java b/src/main/java/rx/internal/operators/OperatorTake.java index 7835327ec4..d49f155d9d 100644 --- a/src/main/java/rx/internal/operators/OperatorTake.java +++ b/src/main/java/rx/internal/operators/OperatorTake.java @@ -19,6 +19,7 @@ import rx.*; import rx.Observable.Operator; +import rx.plugins.RxJavaHooks; /** * An {@code Observable} that emits the first {@code num} items emitted by the source {@code Observable}. @@ -66,6 +67,8 @@ public void onError(Throwable e) { } finally { unsubscribe(); } + } else { + RxJavaHooks.onError(e); } } @@ -97,7 +100,7 @@ public void setProducer(final Producer producer) { @Override public void request(long n) { - if (n >0 && !completed) { + if (n > 0 && !completed) { // because requests may happen concurrently use a CAS loop to // ensure we only request as much as needed, no more no less while (true) { diff --git a/src/main/java/rx/internal/operators/OperatorThrottleFirst.java b/src/main/java/rx/internal/operators/OperatorThrottleFirst.java index fbd3c64d7d..f9093efa39 100644 --- a/src/main/java/rx/internal/operators/OperatorThrottleFirst.java +++ b/src/main/java/rx/internal/operators/OperatorThrottleFirst.java @@ -48,7 +48,7 @@ public void onStart() { @Override public void onNext(T v) { long now = scheduler.now(); - if (lastOnNext == -1 || now - lastOnNext >= timeInMilliseconds) { + if (lastOnNext == -1 || now < lastOnNext || now - lastOnNext >= timeInMilliseconds) { lastOnNext = now; subscriber.onNext(v); } diff --git a/src/main/java/rx/internal/operators/OperatorTimeout.java b/src/main/java/rx/internal/operators/OperatorTimeout.java deleted file mode 100644 index 3c74663e80..0000000000 --- a/src/main/java/rx/internal/operators/OperatorTimeout.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.internal.operators; - -import java.util.concurrent.TimeUnit; - -import rx.*; -import rx.functions.Action0; - -/** - * Applies a timeout policy for each element in the observable sequence, using - * the specified scheduler to run timeout timers. If the next element isn't - * received within the specified timeout duration starting from its predecessor, - * the other observable sequence is used to produce future messages from that - * point on. - * @param the value type - */ -public final class OperatorTimeout extends OperatorTimeoutBase { - - public OperatorTimeout(final long timeout, final TimeUnit timeUnit, Observable other, Scheduler scheduler) { - super(new FirstTimeoutStub() { - - @Override - public Subscription call(final TimeoutSubscriber timeoutSubscriber, final Long seqId, Scheduler.Worker inner) { - return inner.schedule(new Action0() { - @Override - public void call() { - timeoutSubscriber.onTimeout(seqId); - } - }, timeout, timeUnit); - } - }, new TimeoutStub() { - - @Override - public Subscription call(final TimeoutSubscriber timeoutSubscriber, final Long seqId, T value, Scheduler.Worker inner) { - return inner.schedule(new Action0() { - @Override - public void call() { - timeoutSubscriber.onTimeout(seqId); - } - }, timeout, timeUnit); - } - }, other, scheduler); - } -} diff --git a/src/main/java/rx/internal/operators/OperatorTimeoutBase.java b/src/main/java/rx/internal/operators/OperatorTimeoutBase.java deleted file mode 100644 index 435a60e503..0000000000 --- a/src/main/java/rx/internal/operators/OperatorTimeoutBase.java +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.internal.operators; - -import java.util.concurrent.TimeoutException; - -import rx.*; -import rx.Observable.Operator; -import rx.functions.*; -import rx.internal.producers.ProducerArbiter; -import rx.observers.SerializedSubscriber; -import rx.subscriptions.SerialSubscription; - -class OperatorTimeoutBase implements Operator { - final FirstTimeoutStub firstTimeoutStub; - final TimeoutStub timeoutStub; - final Observable other; - final Scheduler scheduler; - - /** - * Set up the timeout action on the first value. - * - * @param - */ - /* package-private */interface FirstTimeoutStub extends - Func3, Long, Scheduler.Worker, Subscription> { - } - - /** - * Set up the timeout action based on every value - * - * @param - */ - /* package-private */interface TimeoutStub extends - Func4, Long, T, Scheduler.Worker, Subscription> { - } - - /* package-private */OperatorTimeoutBase(FirstTimeoutStub firstTimeoutStub, TimeoutStub timeoutStub, Observable other, Scheduler scheduler) { - this.firstTimeoutStub = firstTimeoutStub; - this.timeoutStub = timeoutStub; - this.other = other; - this.scheduler = scheduler; - } - - @Override - public Subscriber call(Subscriber subscriber) { - Scheduler.Worker inner = scheduler.createWorker(); - subscriber.add(inner); - // Use SynchronizedSubscriber for safe memory access - // as the subscriber will be accessed in the current thread or the - // scheduler or other Observables. - final SerializedSubscriber synchronizedSubscriber = new SerializedSubscriber(subscriber); - - final SerialSubscription serial = new SerialSubscription(); - synchronizedSubscriber.add(serial); - - TimeoutSubscriber timeoutSubscriber = new TimeoutSubscriber(synchronizedSubscriber, timeoutStub, serial, other, inner); - - synchronizedSubscriber.add(timeoutSubscriber); - synchronizedSubscriber.setProducer(timeoutSubscriber.arbiter); - - serial.set(firstTimeoutStub.call(timeoutSubscriber, 0L, inner)); - - return timeoutSubscriber; - } - - /* package-private */static final class TimeoutSubscriber extends - Subscriber { - - final SerialSubscription serial; - - final SerializedSubscriber serializedSubscriber; - - final TimeoutStub timeoutStub; - - final Observable other; - - final Scheduler.Worker inner; - - final ProducerArbiter arbiter; - - /** Guarded by this. */ - boolean terminated; - /** Guarded by this. */ - long actual; - - TimeoutSubscriber( - SerializedSubscriber serializedSubscriber, - TimeoutStub timeoutStub, SerialSubscription serial, - Observable other, - Scheduler.Worker inner) { - this.serializedSubscriber = serializedSubscriber; - this.timeoutStub = timeoutStub; - this.serial = serial; - this.other = other; - this.inner = inner; - this.arbiter = new ProducerArbiter(); - } - - @Override - public void setProducer(Producer p) { - arbiter.setProducer(p); - } - - @Override - public void onNext(T value) { - boolean onNextWins = false; - long a; - synchronized (this) { - if (!terminated) { - a = ++actual; - onNextWins = true; - } else { - a = actual; - } - } - if (onNextWins) { - serializedSubscriber.onNext(value); - serial.set(timeoutStub.call(this, a, value, inner)); - } - } - - @Override - public void onError(Throwable error) { - boolean onErrorWins = false; - synchronized (this) { - if (!terminated) { - terminated = true; - onErrorWins = true; - } - } - if (onErrorWins) { - serial.unsubscribe(); - serializedSubscriber.onError(error); - } - } - - @Override - public void onCompleted() { - boolean onCompletedWins = false; - synchronized (this) { - if (!terminated) { - terminated = true; - onCompletedWins = true; - } - } - if (onCompletedWins) { - serial.unsubscribe(); - serializedSubscriber.onCompleted(); - } - } - - public void onTimeout(long seqId) { - long expected = seqId; - boolean timeoutWins = false; - synchronized (this) { - if (expected == actual && !terminated) { - terminated = true; - timeoutWins = true; - } - } - if (timeoutWins) { - if (other == null) { - serializedSubscriber.onError(new TimeoutException()); - } else { - Subscriber second = new Subscriber() { - @Override - public void onNext(T t) { - serializedSubscriber.onNext(t); - } - - @Override - public void onError(Throwable e) { - serializedSubscriber.onError(e); - } - - @Override - public void onCompleted() { - serializedSubscriber.onCompleted(); - } - - @Override - public void setProducer(Producer p) { - arbiter.setProducer(p); - } - }; - other.unsafeSubscribe(second); - serial.set(second); - } - } - } - } -} \ No newline at end of file diff --git a/src/main/java/rx/internal/operators/OperatorTimeoutWithSelector.java b/src/main/java/rx/internal/operators/OperatorTimeoutWithSelector.java deleted file mode 100644 index 16034dba6c..0000000000 --- a/src/main/java/rx/internal/operators/OperatorTimeoutWithSelector.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.internal.operators; - -import rx.*; -import rx.exceptions.Exceptions; -import rx.functions.*; -import rx.schedulers.Schedulers; -import rx.subscriptions.Subscriptions; - -/** - * Returns an Observable that mirrors the source Observable. If either the first - * item emitted by the source Observable or any subsequent item don't arrive - * within time windows defined by provided Observables, switch to the - * other Observable if provided, or emit a TimeoutException . - * @param the value type of the main Observable - * @param the value type of the first timeout Observable - * @param the value type of the subsequent timeout Observable - */ -public class OperatorTimeoutWithSelector extends - OperatorTimeoutBase { - - public OperatorTimeoutWithSelector( - final Func0> firstTimeoutSelector, - final Func1> timeoutSelector, - Observable other) { - super(new FirstTimeoutStub() { - - @Override - public Subscription call( - final TimeoutSubscriber timeoutSubscriber, - final Long seqId, Scheduler.Worker inner) { - if (firstTimeoutSelector != null) { - Observable o; - try { - o = firstTimeoutSelector.call(); - } catch (Throwable t) { - Exceptions.throwOrReport(t, timeoutSubscriber); - return Subscriptions.unsubscribed(); - } - return o.unsafeSubscribe(new Subscriber() { - - @Override - public void onCompleted() { - timeoutSubscriber.onTimeout(seqId); - } - - @Override - public void onError(Throwable e) { - timeoutSubscriber.onError(e); - } - - @Override - public void onNext(U t) { - timeoutSubscriber.onTimeout(seqId); - } - - }); - } else { - return Subscriptions.unsubscribed(); - } - } - }, new TimeoutStub() { - - @Override - public Subscription call( - final TimeoutSubscriber timeoutSubscriber, - final Long seqId, T value, Scheduler.Worker inner) { - Observable o; - try { - o = timeoutSelector.call(value); - } catch (Throwable t) { - Exceptions.throwOrReport(t, timeoutSubscriber); - return Subscriptions.unsubscribed(); - } - return o.unsafeSubscribe(new Subscriber() { - - @Override - public void onCompleted() { - timeoutSubscriber.onTimeout(seqId); - } - - @Override - public void onError(Throwable e) { - timeoutSubscriber.onError(e); - } - - @Override - public void onNext(V t) { - timeoutSubscriber.onTimeout(seqId); - } - - }); - } - }, other, Schedulers.immediate()); - } - -} diff --git a/src/main/java/rx/internal/operators/OperatorUnsubscribeOn.java b/src/main/java/rx/internal/operators/OperatorUnsubscribeOn.java index 2f72d62d6b..a581009884 100644 --- a/src/main/java/rx/internal/operators/OperatorUnsubscribeOn.java +++ b/src/main/java/rx/internal/operators/OperatorUnsubscribeOn.java @@ -52,6 +52,10 @@ public void onNext(T t) { subscriber.onNext(t); } + @Override + public void setProducer(Producer p) { + subscriber.setProducer(p); + } }; subscriber.add(Subscriptions.create(new Action0() { diff --git a/src/main/java/rx/internal/operators/SingleFromEmitter.java b/src/main/java/rx/internal/operators/SingleFromEmitter.java new file mode 100644 index 0000000000..b87c90f054 --- /dev/null +++ b/src/main/java/rx/internal/operators/SingleFromEmitter.java @@ -0,0 +1,118 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.concurrent.atomic.AtomicBoolean; + +import rx.*; +import rx.Single.OnSubscribe; +import rx.exceptions.Exceptions; +import rx.functions.*; +import rx.internal.subscriptions.*; +import rx.plugins.RxJavaHooks; + +/** + * Calls an action with a SingleEmitter instance for each individual subscribers that + * generates a terminal signal (eventually). + * + * @param the success value type + */ +public final class SingleFromEmitter implements OnSubscribe { + + final Action1> producer; + + public SingleFromEmitter(Action1> producer) { + this.producer = producer; + } + + @Override + public void call(SingleSubscriber t) { + SingleEmitterImpl parent = new SingleEmitterImpl(t); + t.add(parent); + + try { + producer.call(parent); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + parent.onError(ex); + } + } + + static final class SingleEmitterImpl + extends AtomicBoolean + implements SingleEmitter, Subscription { + private static final long serialVersionUID = 8082834163465882809L; + + final SingleSubscriber actual; + + final SequentialSubscription resource; + + SingleEmitterImpl(SingleSubscriber actual) { + this.actual = actual; + this.resource = new SequentialSubscription(); + } + + @Override + public void unsubscribe() { + if (compareAndSet(false, true)) { + resource.unsubscribe(); + } + } + + @Override + public boolean isUnsubscribed() { + return get(); + } + + @Override + public void onSuccess(T t) { + if (compareAndSet(false, true)) { + try { + actual.onSuccess(t); + } finally { + resource.unsubscribe(); + } + } + } + + @Override + public void onError(Throwable t) { + if (t == null) { + t = new NullPointerException(); + } + if (compareAndSet(false, true)) { + try { + actual.onError(t); + } finally { + resource.unsubscribe(); + } + } else { + RxJavaHooks.onError(t); + } + } + + @Override + public void setSubscription(Subscription s) { + resource.update(s); + } + + @Override + public void setCancellation(Cancellable c) { + setSubscription(new CancellableSubscription(c)); + } + } +} diff --git a/src/main/java/rx/internal/operators/SingleOperatorCast.java b/src/main/java/rx/internal/operators/SingleOperatorCast.java new file mode 100644 index 0000000000..b77381b161 --- /dev/null +++ b/src/main/java/rx/internal/operators/SingleOperatorCast.java @@ -0,0 +1,37 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package rx.internal.operators; + +import rx.functions.Func1; + +/** + * Converts the element of a Single to the specified type. + * @param the input value type + * @param the output value type + */ +public class SingleOperatorCast implements Func1 { + + final Class castClass; + + public SingleOperatorCast(Class castClass) { + this.castClass = castClass; + } + + @Override + public R call(T t) { + return castClass.cast(t); + } +} diff --git a/src/main/java/rx/internal/operators/SingleTakeUntilCompletable.java b/src/main/java/rx/internal/operators/SingleTakeUntilCompletable.java index 28110ade31..17111084e7 100644 --- a/src/main/java/rx/internal/operators/SingleTakeUntilCompletable.java +++ b/src/main/java/rx/internal/operators/SingleTakeUntilCompletable.java @@ -86,7 +86,7 @@ public void onError(Throwable error) { @Override public void onCompleted() { - onError(new CancellationException("Stream was canceled before emitting a terminal event.")); + onError(new CancellationException("Single::takeUntil(Completable) - Stream was canceled before emitting a terminal event.")); } } } diff --git a/src/main/java/rx/internal/operators/SingleTakeUntilObservable.java b/src/main/java/rx/internal/operators/SingleTakeUntilObservable.java index 81764449c4..604fda6f1f 100644 --- a/src/main/java/rx/internal/operators/SingleTakeUntilObservable.java +++ b/src/main/java/rx/internal/operators/SingleTakeUntilObservable.java @@ -96,7 +96,7 @@ public void onError(Throwable error) { @Override public void onCompleted() { - onError(new CancellationException("Stream was canceled before emitting a terminal event.")); + onError(new CancellationException("Single::takeUntil(Observable) - Stream was canceled before emitting a terminal event.")); } } } diff --git a/src/main/java/rx/internal/operators/SingleTakeUntilSingle.java b/src/main/java/rx/internal/operators/SingleTakeUntilSingle.java index 05dfc00efc..24c0439817 100644 --- a/src/main/java/rx/internal/operators/SingleTakeUntilSingle.java +++ b/src/main/java/rx/internal/operators/SingleTakeUntilSingle.java @@ -86,7 +86,7 @@ public void onError(Throwable error) { final class OtherSubscriber extends SingleSubscriber { @Override public void onSuccess(U value) { - onError(new CancellationException("Stream was canceled before emitting a terminal event.")); + onError(new CancellationException("Single::takeUntil(Single) - Stream was canceled before emitting a terminal event.")); } @Override diff --git a/src/main/java/rx/internal/schedulers/CachedThreadScheduler.java b/src/main/java/rx/internal/schedulers/CachedThreadScheduler.java index 940fe685f2..e5728540d5 100644 --- a/src/main/java/rx/internal/schedulers/CachedThreadScheduler.java +++ b/src/main/java/rx/internal/schedulers/CachedThreadScheduler.java @@ -24,7 +24,7 @@ import rx.subscriptions.*; public final class CachedThreadScheduler extends Scheduler implements SchedulerLifecycle { - private static final long KEEP_ALIVE_TIME = 60; + private static final long KEEP_ALIVE_TIME; private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.SECONDS; static final ThreadWorker SHUTDOWN_THREADWORKER; @@ -41,6 +41,8 @@ public final class CachedThreadScheduler extends Scheduler implements SchedulerL NONE = new CachedWorkerPool(null, 0, null); NONE.shutdown(); + + KEEP_ALIVE_TIME = Integer.getInteger("rx.io-scheduler.keepalive", 60); } static final class CachedWorkerPool { diff --git a/src/main/java/rx/internal/schedulers/NewThreadWorker.java b/src/main/java/rx/internal/schedulers/NewThreadWorker.java index dd63eddf9a..23f8af90e2 100644 --- a/src/main/java/rx/internal/schedulers/NewThreadWorker.java +++ b/src/main/java/rx/internal/schedulers/NewThreadWorker.java @@ -16,7 +16,7 @@ package rx.internal.schedulers; import java.lang.reflect.*; -import java.util.Iterator; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; @@ -110,10 +110,12 @@ public static void deregisterExecutor(ScheduledExecutorService service) { } /** Purges each registered executor and eagerly evicts shutdown executors. */ - @SuppressAnimalSniffer // CHM.keySet returns KeySetView in Java 8+; false positive here static void purgeExecutors() { try { - Iterator it = EXECUTORS.keySet().iterator(); + // This prevents map.keySet to compile to a Java 8+ KeySetView return type + // and cause NoSuchMethodError on Java 6-7 runtimes. + Map map = EXECUTORS; + Iterator it = map.keySet().iterator(); while (it.hasNext()) { ScheduledThreadPoolExecutor exec = it.next(); if (!exec.isShutdown()) { diff --git a/src/main/java/rx/internal/schedulers/SchedulePeriodicHelper.java b/src/main/java/rx/internal/schedulers/SchedulePeriodicHelper.java new file mode 100644 index 0000000000..e11e072c0a --- /dev/null +++ b/src/main/java/rx/internal/schedulers/SchedulePeriodicHelper.java @@ -0,0 +1,102 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.schedulers; + +import java.util.concurrent.TimeUnit; + +import rx.Scheduler.Worker; +import rx.Subscription; +import rx.functions.Action0; +import rx.internal.subscriptions.SequentialSubscription; + +/** + * Utility method for scheduling tasks periodically (at a fixed rate) by using Worker.schedule(Action0, long, TimeUnit). + */ +public final class SchedulePeriodicHelper { + + /** Utility class. */ + private SchedulePeriodicHelper() { + throw new IllegalStateException("No instances!"); + } + + /** + * The tolerance for a clock drift in nanoseconds where the periodic scheduler will rebase. + *

+ * The associated system parameter, {@code rx.scheduler.drift-tolerance}, expects its value in minutes. + */ + public static final long CLOCK_DRIFT_TOLERANCE_NANOS; + static { + CLOCK_DRIFT_TOLERANCE_NANOS = TimeUnit.MINUTES.toNanos( + Long.getLong("rx.scheduler.drift-tolerance", 15)); + } + + /** + * Return the current time in nanoseconds. + */ + public interface NowNanoSupplier { + long nowNanos(); + } + + public static Subscription schedulePeriodically( + final Worker worker, + final Action0 action, + long initialDelay, long period, TimeUnit unit, + final NowNanoSupplier nowNanoSupplier) { + final long periodInNanos = unit.toNanos(period); + final long firstNowNanos = nowNanoSupplier != null ? nowNanoSupplier.nowNanos() : TimeUnit.MILLISECONDS.toNanos(worker.now()); + final long firstStartInNanos = firstNowNanos + unit.toNanos(initialDelay); + + final SequentialSubscription first = new SequentialSubscription(); + final SequentialSubscription mas = new SequentialSubscription(first); + + final Action0 recursiveAction = new Action0() { + long count; + long lastNowNanos = firstNowNanos; + long startInNanos = firstStartInNanos; + @Override + public void call() { + action.call(); + + if (!mas.isUnsubscribed()) { + + long nextTick; + + long nowNanos = nowNanoSupplier != null ? nowNanoSupplier.nowNanos() : TimeUnit.MILLISECONDS.toNanos(worker.now()); + // If the clock moved in a direction quite a bit, rebase the repetition period + if (nowNanos + CLOCK_DRIFT_TOLERANCE_NANOS < lastNowNanos + || nowNanos >= lastNowNanos + periodInNanos + CLOCK_DRIFT_TOLERANCE_NANOS) { + nextTick = nowNanos + periodInNanos; + /* + * Shift the start point back by the drift as if the whole thing + * started count periods ago. + */ + startInNanos = nextTick - (periodInNanos * (++count)); + } else { + nextTick = startInNanos + (++count * periodInNanos); + } + lastNowNanos = nowNanos; + + long delay = nextTick - nowNanos; + mas.replace(worker.schedule(this, delay, TimeUnit.NANOSECONDS)); + } + } + }; + first.replace(worker.schedule(recursiveAction, initialDelay, unit)); + return mas; + } + +} diff --git a/src/main/java/rx/internal/schedulers/SchedulerWhen.java b/src/main/java/rx/internal/schedulers/SchedulerWhen.java index 1310a96e08..f35528bb12 100644 --- a/src/main/java/rx/internal/schedulers/SchedulerWhen.java +++ b/src/main/java/rx/internal/schedulers/SchedulerWhen.java @@ -26,7 +26,6 @@ import rx.Observer; import rx.Scheduler; import rx.Subscription; -import rx.annotations.Experimental; import rx.functions.Action0; import rx.functions.Func1; import rx.internal.operators.BufferUntilSubscriber; @@ -63,9 +62,9 @@ * *

  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
- * 	// use merge max concurrent to limit the number of concurrent
- * 	// callbacks two at a time
- * 	return Completable.merge(Observable.merge(workers), 2);
+ *     // use merge max concurrent to limit the number of concurrent
+ *     // callbacks two at a time
+ *     return Completable.merge(Observable.merge(workers), 2);
  * });
  * 
*

@@ -81,9 +80,9 @@ * *

  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
- * 	// use merge max concurrent to limit the number of concurrent
- * 	// Observables two at a time
- * 	return Completable.merge(Observable.merge(workers, 2));
+ *     // use merge max concurrent to limit the number of concurrent
+ *     // Observables two at a time
+ *     return Completable.merge(Observable.merge(workers, 2));
  * });
  * 
* @@ -94,15 +93,15 @@ * *
  * Scheduler slowScheduler = Schedulers.computation().when(workers -> {
- * 	// use concatenate to make each worker happen one at a time.
- * 	return Completable.concat(workers.map(actions -> {
- * 		// delay the starting of the next worker by 1 second.
- * 		return Completable.merge(actions.delaySubscription(1, TimeUnit.SECONDS));
- * 	}));
+ *     // use concatenate to make each worker happen one at a time.
+ *     return Completable.concat(workers.map(actions -> {
+ *         // delay the starting of the next worker by 1 second.
+ *         return Completable.merge(actions.delaySubscription(1, TimeUnit.SECONDS));
+ *     }));
  * });
  * 
+ * @since 1.3 */ -@Experimental public class SchedulerWhen extends Scheduler implements Subscription { private final Scheduler actualScheduler; private final Observer> workerObserver; diff --git a/src/main/java/rx/internal/subscriptions/CancellableSubscription.java b/src/main/java/rx/internal/subscriptions/CancellableSubscription.java new file mode 100644 index 0000000000..dcfd1bd075 --- /dev/null +++ b/src/main/java/rx/internal/subscriptions/CancellableSubscription.java @@ -0,0 +1,59 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.subscriptions; + +import java.util.concurrent.atomic.AtomicReference; + +import rx.Subscription; +import rx.exceptions.Exceptions; +import rx.functions.Cancellable; +import rx.plugins.RxJavaHooks; + +/** + * A Subscription that wraps an Cancellable instance. + */ +public final class CancellableSubscription +extends AtomicReference +implements Subscription { + + /** */ + private static final long serialVersionUID = 5718521705281392066L; + + public CancellableSubscription(Cancellable cancellable) { + super(cancellable); + } + + @Override + public boolean isUnsubscribed() { + return get() == null; + } + + @Override + public void unsubscribe() { + if (get() != null) { + Cancellable c = getAndSet(null); + if (c != null) { + try { + c.cancel(); + } catch (Exception ex) { + Exceptions.throwIfFatal(ex); + RxJavaHooks.onError(ex); + } + } + } + } +} diff --git a/src/main/java/rx/internal/util/BackpressureDrainManager.java b/src/main/java/rx/internal/util/BackpressureDrainManager.java index 9fde1e62f7..da31c78178 100644 --- a/src/main/java/rx/internal/util/BackpressureDrainManager.java +++ b/src/main/java/rx/internal/util/BackpressureDrainManager.java @@ -18,16 +18,14 @@ import java.util.concurrent.atomic.AtomicLong; import rx.Producer; -import rx.annotations.Experimental; /** * Manages the producer-backpressure-consumer interplay by * matching up available elements with requested elements and/or * terminal events. - * - * @since 1.1.0 + *

History: 1.1.0 - experimental + * @since 1.3 */ -@Experimental public final class BackpressureDrainManager extends AtomicLong implements Producer { /** */ private static final long serialVersionUID = 2826241102729529449L; diff --git a/src/main/java/rx/internal/util/BlockingUtils.java b/src/main/java/rx/internal/util/BlockingUtils.java index 17b0143e52..cb010af57e 100644 --- a/src/main/java/rx/internal/util/BlockingUtils.java +++ b/src/main/java/rx/internal/util/BlockingUtils.java @@ -17,7 +17,6 @@ package rx.internal.util; import rx.Subscription; -import rx.annotations.Experimental; import java.util.concurrent.CountDownLatch; @@ -25,8 +24,8 @@ * Utility functions relating to blocking types. *

* Not intended to be part of the public API. + * @since 1.3 */ -@Experimental public final class BlockingUtils { private BlockingUtils() { } @@ -37,7 +36,6 @@ private BlockingUtils() { } * @param latch a CountDownLatch * @param subscription the Subscription to wait on. */ - @Experimental public static void awaitForComplete(CountDownLatch latch, Subscription subscription) { if (latch.getCount() == 0) { // Synchronous observable completes before awaiting for it. diff --git a/src/main/java/rx/internal/util/IndexedRingBuffer.java b/src/main/java/rx/internal/util/IndexedRingBuffer.java index 3699fa1d8e..30ba900b7e 100644 --- a/src/main/java/rx/internal/util/IndexedRingBuffer.java +++ b/src/main/java/rx/internal/util/IndexedRingBuffer.java @@ -52,15 +52,6 @@ public final class IndexedRingBuffer implements Subscription { /* package for unit testing */final AtomicInteger index = new AtomicInteger(); /* package for unit testing */final AtomicInteger removedIndex = new AtomicInteger(); - private static final ObjectPool> POOL = new ObjectPool>() { - - @Override - protected IndexedRingBuffer createObject() { - return new IndexedRingBuffer(); - } - - }; - /* package for unit testing */static final int SIZE; // default size of ring buffer @@ -255,9 +246,8 @@ protected IndexedRingBuffer createObject() { SIZE = defaultSize; } - @SuppressWarnings("unchecked") public static IndexedRingBuffer getInstance() { - return (IndexedRingBuffer) POOL.borrowObject(); + return new IndexedRingBuffer(); } /** @@ -283,7 +273,6 @@ public void releaseToPool() { index.set(0); removedIndex.set(0); - POOL.returnObject(this); } @Override diff --git a/src/main/java/rx/internal/util/ObjectPool.java b/src/main/java/rx/internal/util/ObjectPool.java deleted file mode 100644 index 032e1338c3..0000000000 --- a/src/main/java/rx/internal/util/ObjectPool.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Modified from http://www.javacodegeeks.com/2013/08/simple-and-lightweight-pool-implementation.html - */ -package rx.internal.util; - -import java.util.Queue; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; - -import rx.internal.schedulers.*; -import rx.internal.util.unsafe.*; -import rx.plugins.RxJavaHooks; - -public abstract class ObjectPool implements SchedulerLifecycle { - Queue pool; - final int minSize; - final int maxSize; - private final long validationInterval; - - private final AtomicReference> periodicTask; - - public ObjectPool() { - this(0, 0, 67); // NOPMD - } - - /** - * Creates the pool. - * - * @param min - * minimum number of objects residing in the pool - * @param max - * maximum number of objects residing in the pool - * @param validationInterval - * time in seconds for periodical checking of minIdle / maxIdle conditions in a separate thread. - * When the number of objects is less than minIdle, missing instances will be created. - * When the number of objects is greater than maxIdle, too many instances will be removed. - */ - private ObjectPool(final int min, final int max, final long validationInterval) { - this.minSize = min; - this.maxSize = max; - this.validationInterval = validationInterval; - this.periodicTask = new AtomicReference>(); - // initialize pool - initialize(min); - - start(); - } - - /** - * Gets the next free object from the pool. If the pool doesn't contain any objects, - * a new object will be created and given to the caller of this method back. - * - * @return T borrowed object - */ - public T borrowObject() { - T object; - if ((object = pool.poll()) == null) { - object = createObject(); - } - - return object; - } - - /** - * Returns object back to the pool. - * - * @param object - * object to be returned - */ - public void returnObject(T object) { - if (object == null) { - return; - } - - this.pool.offer(object); - } - - /** - * Shutdown this pool. - */ - @Override - public void shutdown() { - Future f = periodicTask.getAndSet(null); - if (f != null) { - f.cancel(false); - } - } - - @Override - public void start() { - for (;;) { - if (periodicTask.get() != null) { - return; - } - ScheduledExecutorService w = GenericScheduledExecutorService.getInstance(); - - Future f; - try { - f = w.scheduleAtFixedRate(new Runnable() { - - @Override - public void run() { - int size = pool.size(); - if (size < minSize) { - int sizeToBeAdded = maxSize - size; - for (int i = 0; i < sizeToBeAdded; i++) { - pool.add(createObject()); - } - } else if (size > maxSize) { - int sizeToBeRemoved = size - maxSize; - for (int i = 0; i < sizeToBeRemoved; i++) { - // pool.pollLast(); - pool.poll(); - } - } - } - - }, validationInterval, validationInterval, TimeUnit.SECONDS); - } catch (RejectedExecutionException ex) { - RxJavaHooks.onError(ex); - break; - } - if (!periodicTask.compareAndSet(null, f)) { - f.cancel(false); - } else { - break; - } - } - } - - /** - * Creates a new object. - * - * @return T new object - */ - protected abstract T createObject(); - - private void initialize(final int min) { - if (UnsafeAccess.isUnsafeAvailable()) { - pool = new MpmcArrayQueue(Math.max(maxSize, 1024)); - } else { - pool = new ConcurrentLinkedQueue(); - } - - for (int i = 0; i < min; i++) { - pool.add(createObject()); - } - } -} \ No newline at end of file diff --git a/src/main/java/rx/internal/util/OpenHashSet.java b/src/main/java/rx/internal/util/OpenHashSet.java index c5045ace70..238411656f 100644 --- a/src/main/java/rx/internal/util/OpenHashSet.java +++ b/src/main/java/rx/internal/util/OpenHashSet.java @@ -173,7 +173,7 @@ void rehash() { for (int j = size; j-- != 0; ) { - while (a[--i] == null); // NOPMD + while (a[--i] == null) { } // NOPMD int pos = mix(a[i].hashCode()) & m; if (b[pos] != null) { for (;;) { diff --git a/src/main/java/rx/internal/util/PlatformDependent.java b/src/main/java/rx/internal/util/PlatformDependent.java index 1a48e8067b..2b6d5c6e65 100644 --- a/src/main/java/rx/internal/util/PlatformDependent.java +++ b/src/main/java/rx/internal/util/PlatformDependent.java @@ -15,9 +15,6 @@ */ package rx.internal.util; -import java.security.AccessController; -import java.security.PrivilegedAction; - /** * Allow platform dependent logic such as checks for Android. * @@ -67,7 +64,7 @@ public static int getAndroidApiVersion() { private static int resolveAndroidApiVersion() { try { return (Integer) Class - .forName("android.os.Build$VERSION", true, getSystemClassLoader()) + .forName("android.os.Build$VERSION") .getField("SDK_INT") .get(null); } catch (Exception e) { // NOPMD @@ -76,20 +73,4 @@ private static int resolveAndroidApiVersion() { return ANDROID_API_VERSION_IS_NOT_ANDROID; } } - - /** - * Return the system {@link ClassLoader}. - */ - static ClassLoader getSystemClassLoader() { - if (System.getSecurityManager() == null) { - return ClassLoader.getSystemClassLoader(); - } else { - return AccessController.doPrivileged(new PrivilegedAction() { - @Override - public ClassLoader run() { - return ClassLoader.getSystemClassLoader(); - } - }); - } - } } diff --git a/src/main/java/rx/internal/util/RxRingBuffer.java b/src/main/java/rx/internal/util/RxRingBuffer.java index dfbb6cbd6f..366fffd4c2 100644 --- a/src/main/java/rx/internal/util/RxRingBuffer.java +++ b/src/main/java/rx/internal/util/RxRingBuffer.java @@ -21,6 +21,7 @@ import rx.Subscription; import rx.exceptions.MissingBackpressureException; import rx.internal.operators.NotificationLite; +import rx.internal.util.atomic.SpscAtomicArrayQueue; import rx.internal.util.unsafe.SpmcArrayQueue; import rx.internal.util.unsafe.SpscArrayQueue; import rx.internal.util.unsafe.UnsafeAccess; @@ -128,7 +129,6 @@ public class RxRingBuffer implements Subscription { private Queue queue; private final int size; - private final ObjectPool> pool; /** * We store the terminal state separately so it doesn't count against the size. @@ -261,29 +261,9 @@ public class RxRingBuffer implements Subscription { } public static final int SIZE; - /* Public so Schedulers can manage the lifecycle of the inner worker. */ - public static final ObjectPool> SPSC_POOL = new ObjectPool>() { - - @Override - protected SpscArrayQueue createObject() { - return new SpscArrayQueue(SIZE); - } - - }; - - /* Public so Schedulers can manage the lifecycle of the inner worker. */ - public static final ObjectPool> SPMC_POOL = new ObjectPool>() { - - @Override - protected SpmcArrayQueue createObject() { - return new SpmcArrayQueue(SIZE); - } - - }; - public static RxRingBuffer getSpscInstance() { if (UnsafeAccess.isUnsafeAvailable()) { - return new RxRingBuffer(SPSC_POOL, SIZE); + return new RxRingBuffer(false, SIZE); } else { return new RxRingBuffer(); } @@ -291,7 +271,7 @@ public static RxRingBuffer getSpscInstance() { public static RxRingBuffer getSpmcInstance() { if (UnsafeAccess.isUnsafeAvailable()) { - return new RxRingBuffer(SPMC_POOL, SIZE); + return new RxRingBuffer(true, SIZE); } else { return new RxRingBuffer(); } @@ -299,24 +279,16 @@ public static RxRingBuffer getSpmcInstance() { private RxRingBuffer(Queue queue, int size) { this.queue = queue; - this.pool = null; this.size = size; } - private RxRingBuffer(ObjectPool> pool, int size) { - this.pool = pool; - this.queue = pool.borrowObject(); + private RxRingBuffer(boolean spmc, int size) { + this.queue = spmc ? new SpmcArrayQueue(size) : new SpscArrayQueue(size); this.size = size; } public synchronized void release() { // NOPMD - Queue q = queue; - ObjectPool> p = pool; - if (p != null && q != null) { - q.clear(); - queue = null; - p.returnObject(q); - } + // 1.2.3: no longer pooling } @Override @@ -325,7 +297,7 @@ public void unsubscribe() { } /* package accessible for unit tests */RxRingBuffer() { - this(new SynchronizedQueue(SIZE), SIZE); + this(new SpscAtomicArrayQueue(SIZE), SIZE); } /** diff --git a/src/main/java/rx/internal/util/ScalarSynchronousObservable.java b/src/main/java/rx/internal/util/ScalarSynchronousObservable.java index d14269953c..141030266e 100644 --- a/src/main/java/rx/internal/util/ScalarSynchronousObservable.java +++ b/src/main/java/rx/internal/util/ScalarSynchronousObservable.java @@ -122,7 +122,7 @@ public void call() { }; } - return create(new ScalarAsyncOnSubscribe(t, onSchedule)); + return unsafeCreate(new ScalarAsyncOnSubscribe(t, onSchedule)); } /** The OnSubscribe callback for the Observable constructor. */ @@ -225,7 +225,7 @@ public String toString() { * @return the new observable */ public Observable scalarFlatMap(final Func1> func) { - return create(new OnSubscribe() { + return unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber child) { Observable o = func.call(t); diff --git a/src/main/java/rx/internal/util/SynchronizedQueue.java b/src/main/java/rx/internal/util/SynchronizedQueue.java deleted file mode 100644 index 27ae5de7f5..0000000000 --- a/src/main/java/rx/internal/util/SynchronizedQueue.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package rx.internal.util; - -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Queue; - -/** - * Intended for use when the `sun.misc.Unsafe` implementations can't be used. - * - * @param - */ -public class SynchronizedQueue implements Queue, Cloneable { - - private final Queue list = new LinkedList(); - private final int size; - - public SynchronizedQueue() { - this.size = -1; - } - - public SynchronizedQueue(int size) { - this.size = size; - } - - @Override - public synchronized boolean isEmpty() { // NOPMD - return list.isEmpty(); - } - - @Override - public synchronized boolean contains(Object o) { // NOPMD - return list.contains(o); - } - - @Override - public synchronized Iterator iterator() { // NOPMD - return list.iterator(); - } - - @Override - public synchronized int size() { // NOPMD - return list.size(); - } - - @Override - public synchronized boolean add(T e) { // NOPMD - return list.add(e); - } - - @Override - public synchronized boolean remove(Object o) { // NOPMD - return list.remove(o); - } - - @Override - public synchronized boolean containsAll(Collection c) { // NOPMD - return list.containsAll(c); - } - - @Override - public synchronized boolean addAll(Collection c) { // NOPMD - return list.addAll(c); - } - - @Override - public synchronized boolean removeAll(Collection c) { // NOPMD - return list.removeAll(c); - } - - @Override - public synchronized boolean retainAll(Collection c) { // NOPMD - return list.retainAll(c); - } - - @Override - public synchronized void clear() { // NOPMD - list.clear(); - } - - @Override - public synchronized String toString() { // NOPMD - return list.toString(); - } - - @Override - public int hashCode() { - return list.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - SynchronizedQueue other = (SynchronizedQueue) obj; - return list.equals(other.list); - } - - @Override - public synchronized T peek() { // NOPMD - return list.peek(); - } - - @Override - public synchronized T element() { // NOPMD - return list.element(); - } - - @Override - public synchronized T poll() { // NOPMD - return list.poll(); - } - - @Override - public synchronized T remove() { // NOPMD - return list.remove(); - } - - @Override - public synchronized boolean offer(T e) { // NOPMD - return !(size > -1 && list.size() + 1 > size) && list.offer(e); - } - - @Override - public synchronized Object clone() { // NOPMD - SynchronizedQueue q = new SynchronizedQueue(size); - q.addAll(list); - return q; - } - - @Override - public synchronized Object[] toArray() { // NOPMD - return list.toArray(); - } - - @Override - public synchronized R[] toArray(R[] a) { // NOPMD - return list.toArray(a); - } - -} diff --git a/src/main/java/rx/internal/util/atomic/AtomicReferenceArrayQueue.java b/src/main/java/rx/internal/util/atomic/AtomicReferenceArrayQueue.java index 8a167f0c72..24691ca832 100644 --- a/src/main/java/rx/internal/util/atomic/AtomicReferenceArrayQueue.java +++ b/src/main/java/rx/internal/util/atomic/AtomicReferenceArrayQueue.java @@ -36,7 +36,7 @@ public Iterator iterator() { @Override public void clear() { // we have to test isEmpty because of the weaker poll() guarantee - while (poll() != null || !isEmpty()) ; // NOPMD + while (poll() != null || !isEmpty()) { } // NOPMD } protected final int calcElementOffset(long index, int mask) { return (int)index & mask; diff --git a/src/main/java/rx/internal/util/atomic/BaseLinkedAtomicQueue.java b/src/main/java/rx/internal/util/atomic/BaseLinkedAtomicQueue.java index 81c12fc645..64e5bd9c49 100644 --- a/src/main/java/rx/internal/util/atomic/BaseLinkedAtomicQueue.java +++ b/src/main/java/rx/internal/util/atomic/BaseLinkedAtomicQueue.java @@ -70,7 +70,7 @@ public final int size() { // must chase the nodes all the way to the producer node, but there's no need to chase a moving target. while (chaserNode != producerNode && size < Integer.MAX_VALUE) { LinkedQueueNode next; - while((next = chaserNode.lvNext()) == null); // NOPMD + while ((next = chaserNode.lvNext()) == null) { } // NOPMD chaserNode = next; size++; } diff --git a/src/main/java/rx/internal/util/atomic/MpscLinkedAtomicQueue.java b/src/main/java/rx/internal/util/atomic/MpscLinkedAtomicQueue.java index 6831cbe923..54c4bbd1aa 100644 --- a/src/main/java/rx/internal/util/atomic/MpscLinkedAtomicQueue.java +++ b/src/main/java/rx/internal/util/atomic/MpscLinkedAtomicQueue.java @@ -94,7 +94,7 @@ public E poll() { } else if (currConsumerNode != lvProducerNode()) { // spin, we are no longer wait free - while((nextNode = currConsumerNode.lvNext()) == null); // NOPMD + while ((nextNode = currConsumerNode.lvNext()) == null) { } // NOPMD // got the next node... // we have to null out the value because we are going to hang on to the node @@ -114,7 +114,7 @@ public E peek() { } else if (currConsumerNode != lvProducerNode()) { // spin, we are no longer wait free - while((nextNode = currConsumerNode.lvNext()) == null); // NOPMD + while ((nextNode = currConsumerNode.lvNext()) == null) { } // NOPMD // got the next node... return nextNode.lpValue(); } diff --git a/src/main/java/rx/internal/util/atomic/SpscAtomicArrayQueue.java b/src/main/java/rx/internal/util/atomic/SpscAtomicArrayQueue.java index 3cd0b0e81f..5bf922346c 100644 --- a/src/main/java/rx/internal/util/atomic/SpscAtomicArrayQueue.java +++ b/src/main/java/rx/internal/util/atomic/SpscAtomicArrayQueue.java @@ -57,10 +57,10 @@ public boolean offer(E e) { final int offset = calcElementOffset(index, mask); if (index >= producerLookAhead) { int step = lookAheadStep; - if (null == lvElement(buffer, calcElementOffset(index + step, mask))) {// LoadLoad + if (null == lvElement(buffer, calcElementOffset(index + step, mask))) { // LoadLoad producerLookAhead = index + step; } - else if (null != lvElement(buffer, offset)){ + else if (null != lvElement(buffer, offset)) { return false; } } diff --git a/src/main/java/rx/internal/util/atomic/SpscExactAtomicArrayQueue.java b/src/main/java/rx/internal/util/atomic/SpscExactAtomicArrayQueue.java index 9e9affe472..b3f71e264c 100644 --- a/src/main/java/rx/internal/util/atomic/SpscExactAtomicArrayQueue.java +++ b/src/main/java/rx/internal/util/atomic/SpscExactAtomicArrayQueue.java @@ -82,7 +82,7 @@ public T peek() { } @Override public void clear() { - while (poll() != null || !isEmpty()); // NOPMD + while (poll() != null || !isEmpty()) { } // NOPMD } @Override public boolean isEmpty() { diff --git a/src/main/java/rx/internal/util/atomic/SpscLinkedArrayQueue.java b/src/main/java/rx/internal/util/atomic/SpscLinkedArrayQueue.java index c2b4f43eac..a796e1c373 100644 --- a/src/main/java/rx/internal/util/atomic/SpscLinkedArrayQueue.java +++ b/src/main/java/rx/internal/util/atomic/SpscLinkedArrayQueue.java @@ -78,7 +78,7 @@ public boolean offer(final T e) { final int lookAheadStep = producerLookAheadStep; // go around the buffer or resize if full (unless we hit max capacity) int lookAheadElementOffset = calcWrappedOffset(index + lookAheadStep, mask); - if (null == lvElement(buffer, lookAheadElementOffset)) {// LoadLoad + if (null == lvElement(buffer, lookAheadElementOffset)) { // LoadLoad producerLookAhead = index + lookAheadStep - 1; // joy, there's plenty of room return writeToQueue(buffer, e, index, offset); } else if (null == lvElement(buffer, calcWrappedOffset(index + 1, mask))) { // buffer is not full @@ -178,7 +178,7 @@ public T peek() { @Override public void clear() { - while (poll() != null || !isEmpty()); // NOPMD + while (poll() != null || !isEmpty()) { } // NOPMD } @SuppressWarnings("unchecked") diff --git a/src/main/java/rx/internal/util/atomic/SpscUnboundedAtomicArrayQueue.java b/src/main/java/rx/internal/util/atomic/SpscUnboundedAtomicArrayQueue.java index 9468173869..87bf5afc9a 100644 --- a/src/main/java/rx/internal/util/atomic/SpscUnboundedAtomicArrayQueue.java +++ b/src/main/java/rx/internal/util/atomic/SpscUnboundedAtomicArrayQueue.java @@ -78,7 +78,7 @@ public boolean offer(final T e) { final int lookAheadStep = producerLookAheadStep; // go around the buffer or resize if full (unless we hit max capacity) int lookAheadElementOffset = calcWrappedOffset(index + lookAheadStep, mask); - if (null == lvElement(buffer, lookAheadElementOffset)) {// LoadLoad + if (null == lvElement(buffer, lookAheadElementOffset)) { // LoadLoad producerLookAhead = index + lookAheadStep - 1; // joy, there's plenty of room return writeToQueue(buffer, e, index, offset); } else if (null != lvElement(buffer, calcWrappedOffset(index + 1, mask))) { // buffer is not full @@ -178,7 +178,7 @@ public T peek() { @Override public void clear() { - while (poll() != null || !isEmpty()); // NOPMD + while (poll() != null || !isEmpty()) { } // NOPMD } @SuppressWarnings("unchecked") diff --git a/src/main/java/rx/internal/util/unsafe/BaseLinkedQueue.java b/src/main/java/rx/internal/util/unsafe/BaseLinkedQueue.java index f2d80eb6c5..cdc5ed93d3 100644 --- a/src/main/java/rx/internal/util/unsafe/BaseLinkedQueue.java +++ b/src/main/java/rx/internal/util/unsafe/BaseLinkedQueue.java @@ -106,7 +106,7 @@ public final int size() { // must chase the nodes all the way to the producer node, but there's no need to chase a moving target. while (chaserNode != producerNode && size < Integer.MAX_VALUE) { LinkedQueueNode next; - while((next = chaserNode.lvNext()) == null); // NOPMD + while ((next = chaserNode.lvNext()) == null) { } // NOPMD chaserNode = next; size++; } diff --git a/src/main/java/rx/internal/util/unsafe/ConcurrentCircularArrayQueue.java b/src/main/java/rx/internal/util/unsafe/ConcurrentCircularArrayQueue.java index 7a11f99d85..08456f0b0e 100644 --- a/src/main/java/rx/internal/util/unsafe/ConcurrentCircularArrayQueue.java +++ b/src/main/java/rx/internal/util/unsafe/ConcurrentCircularArrayQueue.java @@ -182,6 +182,6 @@ public Iterator iterator() { @Override public void clear() { // we have to test isEmpty because of the weaker poll() guarantee - while (poll() != null || !isEmpty()) ; // NOPMD + while (poll() != null || !isEmpty()) { } // NOPMD } } diff --git a/src/main/java/rx/internal/util/unsafe/MpscLinkedQueue.java b/src/main/java/rx/internal/util/unsafe/MpscLinkedQueue.java index e6b4c350e9..b69a5a0daa 100644 --- a/src/main/java/rx/internal/util/unsafe/MpscLinkedQueue.java +++ b/src/main/java/rx/internal/util/unsafe/MpscLinkedQueue.java @@ -49,7 +49,7 @@ protected LinkedQueueNode xchgProducerNode(LinkedQueueNode newVal) { Object oldVal; do { oldVal = producerNode; - } while(!UNSAFE.compareAndSwapObject(this, P_NODE_OFFSET, oldVal, newVal)); + } while (!UNSAFE.compareAndSwapObject(this, P_NODE_OFFSET, oldVal, newVal)); return (LinkedQueueNode) oldVal; } @@ -110,7 +110,7 @@ public E poll() { } else if (currConsumerNode != lvProducerNode()) { // spin, we are no longer wait free - while((nextNode = currConsumerNode.lvNext()) == null); // NOPMD + while ((nextNode = currConsumerNode.lvNext()) == null) { } // NOPMD // got the next node... // we have to null out the value because we are going to hang on to the node @@ -130,7 +130,7 @@ public E peek() { } else if (currConsumerNode != lvProducerNode()) { // spin, we are no longer wait free - while((nextNode = currConsumerNode.lvNext()) == null); // NOPMD + while ((nextNode = currConsumerNode.lvNext()) == null) { } // NOPMD // got the next node... return nextNode.lpValue(); } diff --git a/src/main/java/rx/internal/util/unsafe/README.md b/src/main/java/rx/internal/util/unsafe/README.md index 1bff119cd7..c847ea77e5 100644 --- a/src/main/java/rx/internal/util/unsafe/README.md +++ b/src/main/java/rx/internal/util/unsafe/README.md @@ -2,7 +2,7 @@ This package contains code that relies on sun.misc.Unsafe. Before using it you M Much of the code in this package comes from or is inspired by the JCTools project: https://github.com/JCTools/JCTools -JCTools has now published artifacts (https://github.com/JCTools/JCTools/issues/17) so RxJava could add JCTools as a "shadow" dependency (https://github.com/ReactiveX/RxJava/issues/1735). +JCTools has now published artifacts (https://github.com/JCTools/JCTools/issues/17) so RxJava could add JCTools as a "shadow" dependency (https://github.com/ReactiveX/RxJava/issues/1735). RxJava has a "zero dependency" policy for the core library, so if we do add it as a dependency, it won't be an externally visible dependency that results in a separate jar. The license for the JCTools code is https://github.com/JCTools/JCTools/blob/master/LICENSE diff --git a/src/main/java/rx/internal/util/unsafe/SpmcArrayQueue.java b/src/main/java/rx/internal/util/unsafe/SpmcArrayQueue.java index 325bfd6eae..043511cac8 100644 --- a/src/main/java/rx/internal/util/unsafe/SpmcArrayQueue.java +++ b/src/main/java/rx/internal/util/unsafe/SpmcArrayQueue.java @@ -130,12 +130,12 @@ public boolean offer(final E e) { if (null != lvElement(lb, offset)) { long size = currProducerIndex - lvConsumerIndex(); - if(size > lMask) { + if (size > lMask) { return false; } else { // spin wait for slot to clear, buggers wait freedom - while(null != lvElement(lb, offset)); // NOPMD + while (null != lvElement(lb, offset)) { } // NOPMD } } spElement(lb, offset, e); diff --git a/src/main/java/rx/internal/util/unsafe/SpscArrayQueue.java b/src/main/java/rx/internal/util/unsafe/SpscArrayQueue.java index 15c9a9d06f..f5bab6f124 100644 --- a/src/main/java/rx/internal/util/unsafe/SpscArrayQueue.java +++ b/src/main/java/rx/internal/util/unsafe/SpscArrayQueue.java @@ -25,7 +25,7 @@ abstract class SpscArrayQueueColdField extends ConcurrentCircularArrayQueue extends SpscArrayQueueColdField { @@ -112,7 +112,7 @@ public boolean offer(final E e) { final E[] lElementBuffer = buffer; final long index = producerIndex; final long offset = calcElementOffset(index); - if (null != lvElement(lElementBuffer, offset)){ + if (null != lvElement(lElementBuffer, offset)) { return false; } soElement(lElementBuffer, offset, e); // StoreStore diff --git a/src/main/java/rx/internal/util/unsafe/SpscUnboundedArrayQueue.java b/src/main/java/rx/internal/util/unsafe/SpscUnboundedArrayQueue.java index eab0fec910..aa7baed670 100644 --- a/src/main/java/rx/internal/util/unsafe/SpscUnboundedArrayQueue.java +++ b/src/main/java/rx/internal/util/unsafe/SpscUnboundedArrayQueue.java @@ -50,7 +50,7 @@ abstract class SpscUnboundedArrayQueueConsumerField extends SpscUnboundedArra @SuppressAnimalSniffer public class SpscUnboundedArrayQueue extends SpscUnboundedArrayQueueConsumerField - implements QueueProgressIndicators{ + implements QueueProgressIndicators { static final int MAX_LOOK_AHEAD_STEP = Integer.getInteger("jctools.spsc.max.lookahead.step", 4096); private final static long P_INDEX_OFFSET; private final static long C_INDEX_OFFSET; @@ -126,7 +126,7 @@ public final boolean offer(final E e) { final int lookAheadStep = producerLookAheadStep; // go around the buffer or resize if full (unless we hit max capacity) long lookAheadElementOffset = calcWrappedOffset(index + lookAheadStep, mask); - if (null == lvElement(buffer, lookAheadElementOffset)) {// LoadLoad + if (null == lvElement(buffer, lookAheadElementOffset)) { // LoadLoad producerLookAhead = index + lookAheadStep - 1; // joy, there's plenty of room return writeToQueue(buffer, e, index, offset); } else if (null != lvElement(buffer, calcWrappedOffset(index + 1, mask))) { // buffer is not full @@ -159,11 +159,11 @@ private void resize(final E[] oldBuffer, final long currIndex, final long offset } private void soNext(E[] curr, E[] next) { - soElement(curr, calcDirectOffset(curr.length -1), next); + soElement(curr, calcDirectOffset(curr.length - 1), next); } @SuppressWarnings("unchecked") private E[] lvNext(E[] curr) { - return (E[]) lvElement(curr, calcDirectOffset(curr.length -1)); + return (E[]) lvElement(curr, calcDirectOffset(curr.length - 1)); } /** * {@inheritDoc} diff --git a/src/main/java/rx/observables/AsyncOnSubscribe.java b/src/main/java/rx/observables/AsyncOnSubscribe.java index 35eb618736..7a1eef7112 100644 --- a/src/main/java/rx/observables/AsyncOnSubscribe.java +++ b/src/main/java/rx/observables/AsyncOnSubscribe.java @@ -23,7 +23,7 @@ import rx.Observable; import rx.Observable.OnSubscribe; import rx.Observer; -import rx.annotations.Experimental; +import rx.annotations.Beta; import rx.functions.*; import rx.internal.operators.BufferUntilSubscriber; import rx.observers.SerializedObserver; @@ -33,7 +33,7 @@ /** * A utility class to create {@code OnSubscribe} functions that respond correctly to back * pressure requests from subscribers. This is an improvement over - * {@link rx.Observable#create(OnSubscribe) Observable.create(OnSubscribe)} which does not provide + * {@link rx.Observable#unsafeCreate(OnSubscribe) Observable.create(OnSubscribe)} which does not provide * any means of managing back pressure requests out-of-the-box. This variant of an OnSubscribe * function allows for the asynchronous processing of requests. * @@ -43,8 +43,9 @@ * {@link #onUnsubscribe(Object) onUnsubscribe(S)}. * @param * the type of {@code Subscribers} that will be compatible with {@code this}. + * @since 1.3 - beta */ -@Experimental +@Beta public abstract class AsyncOnSubscribe implements OnSubscribe { /** @@ -110,7 +111,6 @@ protected void onUnsubscribe(S state) { * {@link #next(Object, long, Observer) next(S, long, Observer)}) * @return an AsyncOnSubscribe that emits data in a protocol compatible with back-pressure. */ - @Experimental public static AsyncOnSubscribe createSingleState(Func0 generator, final Action3>> next) { Func3>, S> nextFunc = @@ -141,7 +141,6 @@ public S call(S state, Long requested, Observer> subscri * @return an AsyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. */ - @Experimental public static AsyncOnSubscribe createSingleState(Func0 generator, final Action3>> next, final Action1 onUnsubscribe) { @@ -171,7 +170,6 @@ public S call(S state, Long requested, Observer> subscri * @return an AsyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. */ - @Experimental public static AsyncOnSubscribe createStateful(Func0 generator, Func3>, ? extends S> next, Action1 onUnsubscribe) { @@ -192,7 +190,6 @@ public static AsyncOnSubscribe createStateful(Func0 ge * @return an AsyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. */ - @Experimental public static AsyncOnSubscribe createStateful(Func0 generator, Func3>, ? extends S> next) { return new AsyncOnSubscribeImpl(generator, next); @@ -212,7 +209,6 @@ public static AsyncOnSubscribe createStateful(Func0 ge * @return an AsyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. */ - @Experimental public static AsyncOnSubscribe createStateless(final Action2>> next) { Func3>, Void> nextFunc = new Func3>, Void>() { @@ -240,7 +236,6 @@ public Void call(Void state, Long requested, Observer> s * @return an AsyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. */ - @Experimental public static AsyncOnSubscribe createStateless(final Action2>> next, final Action0 onUnsubscribe) { Func3>, Void> nextFunc = @@ -538,7 +533,10 @@ boolean tryEmit(long n) { expectedDelivery = n; nextIteration(n); - if (hasTerminated || isUnsubscribed()) { + //hasTerminated will be true when onCompleted was already emitted from the request callback + //even if the the observer has not seen onCompleted from the requested observable, + //so we should not clean up while there are active subscriptions + if (hasTerminated && !subscriptions.hasSubscriptions() || isUnsubscribed()) { cleanup(); return true; } diff --git a/src/main/java/rx/observables/BlockingObservable.java b/src/main/java/rx/observables/BlockingObservable.java index 59cb245415..ebde4486b0 100644 --- a/src/main/java/rx/observables/BlockingObservable.java +++ b/src/main/java/rx/observables/BlockingObservable.java @@ -22,7 +22,6 @@ import rx.*; import rx.Observable; import rx.Observer; -import rx.annotations.Beta; import rx.exceptions.*; import rx.functions.*; import rx.internal.operators.*; @@ -469,9 +468,8 @@ public void onNext(final T item) { /** * Runs the source observable to a terminal event, ignoring any values and rethrowing any exception. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public void subscribe() { final CountDownLatch cdl = new CountDownLatch(1); final Throwable[] error = { null }; @@ -503,9 +501,8 @@ public void onCompleted() { /** * Subscribes to the source and calls back the Observer methods on the current thread. * @param observer the observer to call event methods on - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public void subscribe(Observer observer) { final BlockingQueue queue = new LinkedBlockingQueue(); @@ -548,10 +545,9 @@ public void onCompleted() { *

* The unsubscription and backpressure is composed through. * @param subscriber the subscriber to forward events and calls to in the current thread - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ @SuppressWarnings("unchecked") - @Beta public void subscribe(Subscriber subscriber) { final BlockingQueue queue = new LinkedBlockingQueue(); final Producer[] theProducer = { null }; @@ -631,9 +627,8 @@ public void call() { * * @param onNext the callback action for each source value * @see #forEach(Action1) - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public void subscribe(final Action1 onNext) { subscribe(onNext, new Action1() { @Override @@ -647,9 +642,8 @@ public void call(Throwable t) { * Subscribes to the source and calls the given actions on the current thread. * @param onNext the callback action for each source value * @param onError the callback action for an error event - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Beta public void subscribe(final Action1 onNext, final Action1 onError) { subscribe(onNext, onError, Actions.empty()); } @@ -659,9 +653,8 @@ public void subscribe(final Action1 onNext, final Action1 onNext, final Action1 onError, final Action0 onCompleted) { subscribe(new Observer() { @Override diff --git a/src/main/java/rx/observables/ConnectableObservable.java b/src/main/java/rx/observables/ConnectableObservable.java index 09ac04257d..f1fffceb5f 100644 --- a/src/main/java/rx/observables/ConnectableObservable.java +++ b/src/main/java/rx/observables/ConnectableObservable.java @@ -76,7 +76,7 @@ public void call(Subscription t1) { * @see ReactiveX documentation: RefCount */ public Observable refCount() { - return create(new OnSubscribeRefCount(this)); + return unsafeCreate(new OnSubscribeRefCount(this)); } /** @@ -125,6 +125,6 @@ public Observable autoConnect(int numberOfSubscribers, Action1(this, numberOfSubscribers, connection)); + return unsafeCreate(new OnSubscribeAutoConnect(this, numberOfSubscribers, connection)); } } diff --git a/src/main/java/rx/observables/SyncOnSubscribe.java b/src/main/java/rx/observables/SyncOnSubscribe.java index 6e4b887da4..9d4b5245f4 100644 --- a/src/main/java/rx/observables/SyncOnSubscribe.java +++ b/src/main/java/rx/observables/SyncOnSubscribe.java @@ -20,7 +20,6 @@ import rx.*; import rx.Observable.OnSubscribe; -import rx.annotations.Beta; import rx.exceptions.Exceptions; import rx.functions.*; import rx.internal.operators.BackpressureUtils; @@ -29,7 +28,7 @@ /** * A utility class to create {@code OnSubscribe} functions that responds correctly to back * pressure requests from subscribers. This is an improvement over - * {@link rx.Observable#create(OnSubscribe) Observable.create(OnSubscribe)} which does not provide + * {@link rx.Observable#unsafeCreate(OnSubscribe) Observable.create(OnSubscribe)} which does not provide * any means of managing back pressure requests out-of-the-box. * * @param @@ -120,8 +119,8 @@ protected void onUnsubscribe(S state) { * produces data to the downstream subscriber (see {@link #next(Object, Observer) * next(S, Subscriber)}) * @return a SyncOnSubscribe that emits data in a protocol compatible with back-pressure. + * @since 1.3 */ - @Beta public static SyncOnSubscribe createSingleState(Func0 generator, final Action2> next) { Func2, S> nextFunc = new Func2, S>() { @@ -151,8 +150,8 @@ public S call(S state, Observer subscriber) { * clean up behavior (see {@link #onUnsubscribe(Object) onUnsubscribe(S)}) * @return a SyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. + * @since 1.3 */ - @Beta public static SyncOnSubscribe createSingleState(Func0 generator, final Action2> next, final Action1 onUnsubscribe) { @@ -181,8 +180,8 @@ public S call(S state, Observer subscriber) { * clean up behavior (see {@link #onUnsubscribe(Object) onUnsubscribe(S)}) * @return a SyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. + * @since 1.3 */ - @Beta public static SyncOnSubscribe createStateful(Func0 generator, Func2, ? extends S> next, Action1 onUnsubscribe) { @@ -202,8 +201,8 @@ public static SyncOnSubscribe createStateful(Func0 gen * next(S, Subscriber)}) * @return a SyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. + * @since 1.3 */ - @Beta public static SyncOnSubscribe createStateful(Func0 generator, Func2, ? extends S> next) { return new SyncOnSubscribeImpl(generator, next); @@ -222,8 +221,8 @@ public static SyncOnSubscribe createStateful(Func0 gen * next(S, Subscriber)}) * @return a SyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. + * @since 1.3 */ - @Beta public static SyncOnSubscribe createStateless(final Action1> next) { Func2, Void> nextFunc = new Func2, Void>() { @Override @@ -250,8 +249,8 @@ public Void call(Void state, Observer subscriber) { * clean up behavior (see {@link #onUnsubscribe(Object) onUnsubscribe(S)}) * @return a SyncOnSubscribe that emits data downstream in a protocol compatible with * back-pressure. + * @since 1.3 */ - @Beta public static SyncOnSubscribe createStateless(final Action1> next, final Action0 onUnsubscribe) { Func2, Void> nextFunc = new Func2, Void>() { @@ -261,7 +260,7 @@ public Void call(Void state, Observer subscriber) { return null; } }; - Action1 wrappedOnUnsubscribe = new Action1(){ + Action1 wrappedOnUnsubscribe = new Action1() { @Override public void call(Void t) { onUnsubscribe.call(); @@ -350,7 +349,7 @@ public boolean isUnsubscribed() { @Override public void unsubscribe() { - while(true) { + while (true) { long requestCount = get(); if (compareAndSet(0L, -1L)) { doUnsubscribe(); diff --git a/src/main/java/rx/observers/AssertableSubscriber.java b/src/main/java/rx/observers/AssertableSubscriber.java index 756d3049d0..21180c7eb2 100644 --- a/src/main/java/rx/observers/AssertableSubscriber.java +++ b/src/main/java/rx/observers/AssertableSubscriber.java @@ -19,7 +19,6 @@ import java.util.concurrent.TimeUnit; import rx.*; -import rx.annotations.Experimental; import rx.functions.Action0; /** @@ -30,10 +29,10 @@ *

* This interface extends {@link Observer} and allows injecting onXXX signals into * the testing process. + *

History: 1.2.3 - experimental * @param the value type consumed by this Observer - * @since 1.2.3 + * @since 1.3 */ -@Experimental public interface AssertableSubscriber extends Observer, Subscription { /** diff --git a/src/main/java/rx/observers/AsyncCompletableSubscriber.java b/src/main/java/rx/observers/AsyncCompletableSubscriber.java index 28c346da76..81933b80e6 100644 --- a/src/main/java/rx/observers/AsyncCompletableSubscriber.java +++ b/src/main/java/rx/observers/AsyncCompletableSubscriber.java @@ -19,7 +19,6 @@ import rx.CompletableSubscriber; import rx.Subscription; -import rx.annotations.Experimental; import rx.plugins.RxJavaHooks; /** @@ -54,9 +53,8 @@ * } * } * - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Experimental public abstract class AsyncCompletableSubscriber implements CompletableSubscriber, Subscription { /** * Indicates the unsubscribed state. diff --git a/src/main/java/rx/observers/SafeCompletableSubscriber.java b/src/main/java/rx/observers/SafeCompletableSubscriber.java index bc7b35e858..7430fa371a 100644 --- a/src/main/java/rx/observers/SafeCompletableSubscriber.java +++ b/src/main/java/rx/observers/SafeCompletableSubscriber.java @@ -17,7 +17,6 @@ import rx.CompletableSubscriber; import rx.Subscription; -import rx.annotations.Experimental; import rx.exceptions.*; import rx.plugins.RxJavaHooks; @@ -25,9 +24,8 @@ * Wraps another CompletableSubscriber and handles exceptions thrown * from onError and onCompleted. * - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Experimental public final class SafeCompletableSubscriber implements CompletableSubscriber, Subscription { final CompletableSubscriber actual; @@ -56,8 +54,8 @@ public void onCompleted() { @Override public void onError(Throwable e) { - RxJavaHooks.onError(e); if (done) { + RxJavaHooks.onError(e); return; } done = true; diff --git a/src/main/java/rx/observers/SafeSubscriber.java b/src/main/java/rx/observers/SafeSubscriber.java index 717946c032..16027b0d52 100644 --- a/src/main/java/rx/observers/SafeSubscriber.java +++ b/src/main/java/rx/observers/SafeSubscriber.java @@ -124,14 +124,14 @@ public void onError(Throwable e) { * The {@code Observable} will not call this method again after it calls either {@link #onCompleted} or * {@link #onError}. * - * @param args + * @param t * the item emitted by the Observable */ @Override - public void onNext(T args) { + public void onNext(T t) { try { if (!done) { - actual.onNext(args); + actual.onNext(t); } } catch (Throwable e) { // we handle here instead of another method so we don't add stacks to the frame diff --git a/src/main/java/rx/observers/TestSubscriber.java b/src/main/java/rx/observers/TestSubscriber.java index cfa1f74ef9..d35602f572 100644 --- a/src/main/java/rx/observers/TestSubscriber.java +++ b/src/main/java/rx/observers/TestSubscriber.java @@ -20,7 +20,6 @@ import rx.*; import rx.Observer; -import rx.annotations.Experimental; import rx.exceptions.CompositeException; /** @@ -225,9 +224,8 @@ public List> getOnCompletedEvents() { /** * Returns the number of times onCompleted was called on this TestSubscriber. * @return the number of times onCompleted was called on this TestSubscriber. - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final int getCompletions() { return completions; } @@ -337,11 +335,11 @@ private void assertItem(T expected, int i) { if (expected == null) { // check for null equality if (actual != null) { - assertionError("Value at index: " + i + " expected to be [null] but was: [" + actual + "]\n"); + assertionError("Value at index: " + i + " expected: [null] but was: [" + actual + "]\n"); } } else if (!expected.equals(actual)) { assertionError("Value at index: " + i - + " expected to be [" + expected + "] (" + expected.getClass().getSimpleName() + + " expected: [" + expected + "] (" + expected.getClass().getSimpleName() + ") but was: [" + actual + "] (" + (actual != null ? actual.getClass().getSimpleName() : "null") + ")\n"); } @@ -356,9 +354,8 @@ private void assertItem(T expected, int i) { * @param unit the time unit of waiting * @return true if the expected number of onNext events happened * @throws RuntimeException if the sleep is interrupted - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final boolean awaitValueCount(int expected, long timeout, TimeUnit unit) { while (timeout != 0 && valueCount < expected) { try { @@ -696,9 +693,8 @@ final void assertionError(String message) { * * @param expectedFirstValue the expected first value * @param expectedRestValues the optional rest values - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public final void assertValuesAndClear(T expectedFirstValue, T... expectedRestValues) { int n = 1 + expectedRestValues.length; assertValueCount(n); @@ -707,5 +703,6 @@ public final void assertValuesAndClear(T expectedFirstValue, T... expectedRestVa assertItem(expectedRestValues[i], i + 1); } values.clear(); + valueCount = 0; } } diff --git a/src/main/java/rx/plugins/RxJavaCompletableExecutionHook.java b/src/main/java/rx/plugins/RxJavaCompletableExecutionHook.java index c8303ed779..d759d0d021 100644 --- a/src/main/java/rx/plugins/RxJavaCompletableExecutionHook.java +++ b/src/main/java/rx/plugins/RxJavaCompletableExecutionHook.java @@ -16,7 +16,6 @@ package rx.plugins; import rx.*; -import rx.annotations.Experimental; import rx.functions.Func1; /** @@ -35,9 +34,8 @@ * should be fast. If anything time-consuming is to be done it should be spawned asynchronously onto separate * worker threads. * - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Experimental public abstract class RxJavaCompletableExecutionHook { // NOPMD /** * Invoked during the construction by {@link Completable#create(Completable.OnSubscribe)} diff --git a/src/main/java/rx/plugins/RxJavaErrorHandler.java b/src/main/java/rx/plugins/RxJavaErrorHandler.java index 9324cb9062..4c0dfba9d0 100644 --- a/src/main/java/rx/plugins/RxJavaErrorHandler.java +++ b/src/main/java/rx/plugins/RxJavaErrorHandler.java @@ -16,7 +16,6 @@ package rx.plugins; import rx.*; -import rx.annotations.Beta; import rx.exceptions.Exceptions; /** @@ -64,10 +63,8 @@ public void handleError(Throwable e) { * {@code OnErrorThrowable.OnNextValue} * @return a short {@link String} representation of the item if one is known for its type, or null for * default - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the - * release number) + * @since 1.3 */ - @Beta public final String handleOnNextValueRendering(Object item) { try { @@ -95,10 +92,8 @@ public final String handleOnNextValueRendering(Object item) { * @return a short {@link String} representation of the item if one is known for its type, or null for * default * @throws InterruptedException if the rendering thread is interrupted - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the - * release number) + * @since 1.3 */ - @Beta protected String render (Object item) throws InterruptedException { //do nothing by default return null; diff --git a/src/main/java/rx/plugins/RxJavaHooks.java b/src/main/java/rx/plugins/RxJavaHooks.java index 0cac048984..106b9e627f 100644 --- a/src/main/java/rx/plugins/RxJavaHooks.java +++ b/src/main/java/rx/plugins/RxJavaHooks.java @@ -19,7 +19,6 @@ import java.util.concurrent.ScheduledExecutorService; import rx.*; -import rx.annotations.Experimental; import rx.functions.*; import rx.internal.operators.*; @@ -29,8 +28,8 @@ *

* The class features a lockdown state, see {@link #lockdown()} and {@link #isLockdown()}, to * prevent further changes to the hooks. + * @since 1.3 */ -@Experimental public final class RxJavaHooks { /** * Prevents changing the hook callbacks when set to true. diff --git a/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java b/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java index f1b897fa21..47ab6e4095 100644 --- a/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java +++ b/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java @@ -41,7 +41,7 @@ */ public abstract class RxJavaObservableExecutionHook { // NOPMD /** - * Invoked during the construction by {@link Observable#create(OnSubscribe)} + * Invoked during the construction by {@link Observable#unsafeCreate(OnSubscribe)} *

* This can be used to decorate or replace the onSubscribe function or just perform extra * logging, metrics and other such things and pass through the function. diff --git a/src/main/java/rx/plugins/RxJavaPlugins.java b/src/main/java/rx/plugins/RxJavaPlugins.java index 91ba9fa1fa..cac5b167c6 100644 --- a/src/main/java/rx/plugins/RxJavaPlugins.java +++ b/src/main/java/rx/plugins/RxJavaPlugins.java @@ -17,7 +17,6 @@ import java.util.*; import java.util.concurrent.atomic.AtomicReference; -import rx.annotations.Experimental; /** * Registry for plugin implementations that allows global override and handles the retrieval of correct @@ -84,9 +83,9 @@ public static RxJavaPlugins getInstance() { * during application runtime and also bad code could invoke it in * the middle of an application life-cycle and really break applications * if not used cautiously. For more detailed discussions: - * * @see Make RxJavaPlugins.reset() public + * @see Make RxJavaPlugins.reset() public + * @since 1.3 */ - @Experimental public void reset() { INSTANCE.errorHandler.set(null); INSTANCE.observableExecutionHook.set(null); @@ -106,7 +105,7 @@ public void reset() { public RxJavaErrorHandler getErrorHandler() { if (errorHandler.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class, System.getProperties()); + Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class, getSystemPropertiesSafe()); if (impl == null) { // nothing set via properties so initialize with default errorHandler.compareAndSet(null, DEFAULT_ERROR_HANDLER); @@ -148,7 +147,7 @@ public void registerErrorHandler(RxJavaErrorHandler impl) { public RxJavaObservableExecutionHook getObservableExecutionHook() { if (observableExecutionHook.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class, System.getProperties()); + Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class, getSystemPropertiesSafe()); if (impl == null) { // nothing set via properties so initialize with default observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance()); @@ -190,7 +189,7 @@ public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl) public RxJavaSingleExecutionHook getSingleExecutionHook() { if (singleExecutionHook.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaSingleExecutionHook.class, System.getProperties()); + Object impl = getPluginImplementationViaProperty(RxJavaSingleExecutionHook.class, getSystemPropertiesSafe()); if (impl == null) { // nothing set via properties so initialize with default singleExecutionHook.compareAndSet(null, RxJavaSingleExecutionHookDefault.getInstance()); @@ -228,13 +227,12 @@ public void registerSingleExecutionHook(RxJavaSingleExecutionHook impl) { * full class name to load. * * @return {@link RxJavaCompletableExecutionHook} implementation to use - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public RxJavaCompletableExecutionHook getCompletableExecutionHook() { if (completableExecutionHook.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaCompletableExecutionHook.class, System.getProperties()); + Object impl = getPluginImplementationViaProperty(RxJavaCompletableExecutionHook.class, getSystemPropertiesSafe()); if (impl == null) { // nothing set via properties so initialize with default completableExecutionHook.compareAndSet(null, new RxJavaCompletableExecutionHook() { }); @@ -256,15 +254,27 @@ public RxJavaCompletableExecutionHook getCompletableExecutionHook() { * @throws IllegalStateException * if called more than once or after the default was initialized (if usage occurs before trying * to register) - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ - @Experimental public void registerCompletableExecutionHook(RxJavaCompletableExecutionHook impl) { if (!completableExecutionHook.compareAndSet(null, impl)) { throw new IllegalStateException("Another strategy was already registered: " + singleExecutionHook.get()); } } + /** + * A security manager may prevent accessing the System properties entirely, + * therefore, the SecurityException is turned into an empty properties. + * @return the Properties to use for looking up settings + */ + /* test */ static Properties getSystemPropertiesSafe() { + try { + return System.getProperties(); + } catch (SecurityException ex) { + return new Properties(); + } + } + /* test */ static Object getPluginImplementationViaProperty(Class pluginClass, Properties propsIn) { // Make a defensive clone because traversal may fail with ConcurrentModificationException // if the properties get changed by something outside RxJava. @@ -287,25 +297,32 @@ public void registerCompletableExecutionHook(RxJavaCompletableExecutionHook impl String classSuffix = ".class"; String implSuffix = ".impl"; - for (Map.Entry e : props.entrySet()) { - String key = e.getKey().toString(); - if (key.startsWith(pluginPrefix) && key.endsWith(classSuffix)) { - String value = e.getValue().toString(); + try { + for (Map.Entry e : props.entrySet()) { + String key = e.getKey().toString(); + if (key.startsWith(pluginPrefix) && key.endsWith(classSuffix)) { + String value = e.getValue().toString(); + + if (classSimpleName.equals(value)) { + String index = key.substring(0, key.length() - classSuffix.length()).substring(pluginPrefix.length()); - if (classSimpleName.equals(value)) { - String index = key.substring(0, key.length() - classSuffix.length()).substring(pluginPrefix.length()); + String implKey = pluginPrefix + index + implSuffix; - String implKey = pluginPrefix + index + implSuffix; + implementingClass = props.getProperty(implKey); - implementingClass = props.getProperty(implKey); + if (implementingClass == null) { + throw new IllegalStateException("Implementing class declaration for " + classSimpleName + " missing: " + implKey); + } - if (implementingClass == null) { - throw new IllegalStateException("Implementing class declaration for " + classSimpleName + " missing: " + implKey); + break; } - - break; } } + } catch (SecurityException ex) { + // https://github.com/ReactiveX/RxJava/issues/5819 + // We don't seem to have access to all properties. + // At least print the exception to the console. + ex.printStackTrace(); } } @@ -342,7 +359,7 @@ public void registerCompletableExecutionHook(RxJavaCompletableExecutionHook impl public RxJavaSchedulersHook getSchedulersHook() { if (schedulersHook.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class, System.getProperties()); + Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class, getSystemPropertiesSafe()); if (impl == null) { // nothing set via properties so initialize with default schedulersHook.compareAndSet(null, RxJavaSchedulersHook.getDefaultInstance()); diff --git a/src/main/java/rx/plugins/RxJavaSchedulersHook.java b/src/main/java/rx/plugins/RxJavaSchedulersHook.java index cc1675f322..e65ab1f3bc 100644 --- a/src/main/java/rx/plugins/RxJavaSchedulersHook.java +++ b/src/main/java/rx/plugins/RxJavaSchedulersHook.java @@ -18,7 +18,6 @@ import java.util.concurrent.ThreadFactory; import rx.Scheduler; -import rx.annotations.Experimental; import rx.functions.Action0; import rx.internal.schedulers.CachedThreadScheduler; import rx.internal.schedulers.EventLoopsScheduler; @@ -47,8 +46,8 @@ public class RxJavaSchedulersHook { /** * Create an instance of the default {@link Scheduler} used for {@link Schedulers#computation()}. * @return the created Scheduler instance + * @since 1.3 */ - @Experimental public static Scheduler createComputationScheduler() { return createComputationScheduler(new RxThreadFactory("RxComputationScheduler-")); } @@ -58,8 +57,8 @@ public static Scheduler createComputationScheduler() { * except using {@code threadFactory} for thread creation. * @param threadFactory the factory to use for each worker thread * @return the created Scheduler instance + * @since 1.3 */ - @Experimental public static Scheduler createComputationScheduler(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory == null"); @@ -70,8 +69,8 @@ public static Scheduler createComputationScheduler(ThreadFactory threadFactory) /** * Create an instance of the default {@link Scheduler} used for {@link Schedulers#io()}. * @return the created Scheduler instance + * @since 1.3 */ - @Experimental public static Scheduler createIoScheduler() { return createIoScheduler(new RxThreadFactory("RxIoScheduler-")); } @@ -81,8 +80,8 @@ public static Scheduler createIoScheduler() { * except using {@code threadFactory} for thread creation. * @param threadFactory the factory to use for each worker thread * @return the created Scheduler instance + * @since 1.3 */ - @Experimental public static Scheduler createIoScheduler(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory == null"); @@ -93,8 +92,8 @@ public static Scheduler createIoScheduler(ThreadFactory threadFactory) { /** * Create an instance of the default {@link Scheduler} used for {@link Schedulers#newThread()}. * @return the created Scheduler instance + * @since 1.3 */ - @Experimental public static Scheduler createNewThreadScheduler() { return createNewThreadScheduler(new RxThreadFactory("RxNewThreadScheduler-")); } @@ -104,8 +103,8 @@ public static Scheduler createNewThreadScheduler() { * except using {@code threadFactory} for thread creation. * @param threadFactory the factory to use for each worker thread * @return the created Scheduler instance + * @since 1.3 */ - @Experimental public static Scheduler createNewThreadScheduler(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory == null"); diff --git a/src/main/java/rx/schedulers/Schedulers.java b/src/main/java/rx/schedulers/Schedulers.java index 502a59bcf7..1327c6ba93 100644 --- a/src/main/java/rx/schedulers/Schedulers.java +++ b/src/main/java/rx/schedulers/Schedulers.java @@ -15,19 +15,38 @@ */ package rx.schedulers; -import rx.Scheduler; -import rx.annotations.Experimental; -import rx.internal.schedulers.ExecutorScheduler; -import rx.internal.schedulers.GenericScheduledExecutorService; -import rx.internal.schedulers.SchedulerLifecycle; -import rx.internal.util.RxRingBuffer; -import rx.plugins.*; - import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; +import rx.Scheduler; +import rx.internal.schedulers.*; +import rx.plugins.*; + /** * Static factory methods for creating Schedulers. + *

+ * System configuration properties: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property nameDescriptionDefault
{@code rx.io-scheduler.keepalive}time (in seconds) to keep an unused backing + * thread-pool60
{@code rx.scheduler.max-computation-threads}number of threads the + * computation scheduler uses (between 1 and number of available processors) + * number of available processors.
{@code rx.scheduler.jdk6.purge-frequency-millis}time (in milliseconds) between calling + * purge on any active backing thread-pool on a Java 6 runtime1000
{@code rx.scheduler.jdk6.purge-force} boolean forcing the call to purge on any active + * backing thread-poolfalse
*/ public final class Schedulers { @@ -165,8 +184,8 @@ public static Scheduler from(Executor executor) { * Resets the current {@link Schedulers} instance. * This will re-init the cached schedulers on the next usage, * which can be useful in testing. + * @since 1.3 */ - @Experimental public static void reset() { Schedulers s = INSTANCE.getAndSet(null); if (s != null) { @@ -185,10 +204,6 @@ public static void start() { synchronized (s) { GenericScheduledExecutorService.INSTANCE.start(); - - RxRingBuffer.SPSC_POOL.start(); - - RxRingBuffer.SPMC_POOL.start(); } } /** @@ -201,10 +216,6 @@ public static void shutdown() { synchronized (s) { GenericScheduledExecutorService.INSTANCE.shutdown(); - - RxRingBuffer.SPSC_POOL.shutdown(); - - RxRingBuffer.SPMC_POOL.shutdown(); } } diff --git a/src/main/java/rx/schedulers/TestScheduler.java b/src/main/java/rx/schedulers/TestScheduler.java index f540569b19..0106cdbc67 100644 --- a/src/main/java/rx/schedulers/TestScheduler.java +++ b/src/main/java/rx/schedulers/TestScheduler.java @@ -23,6 +23,8 @@ import rx.Scheduler; import rx.Subscription; import rx.functions.Action0; +import rx.internal.schedulers.SchedulePeriodicHelper; +import rx.internal.schedulers.SchedulePeriodicHelper.NowNanoSupplier; import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; @@ -130,7 +132,7 @@ public Worker createWorker() { return new InnerTestScheduler(); } - final class InnerTestScheduler extends Worker { + final class InnerTestScheduler extends Worker implements NowNanoSupplier { private final BooleanSubscription s = new BooleanSubscription(); @@ -172,11 +174,22 @@ public void call() { }); } + @Override + public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit) { + return SchedulePeriodicHelper.schedulePeriodically(this, + action, initialDelay, period, unit, this); + } + @Override public long now() { return TestScheduler.this.now(); } + @Override + public long nowNanos() { + return TestScheduler.this.time; + } + } } diff --git a/src/main/java/rx/singles/BlockingSingle.java b/src/main/java/rx/singles/BlockingSingle.java index 470c012cdb..a113f272ba 100644 --- a/src/main/java/rx/singles/BlockingSingle.java +++ b/src/main/java/rx/singles/BlockingSingle.java @@ -20,7 +20,6 @@ import java.util.concurrent.atomic.AtomicReference; import rx.*; -import rx.annotations.*; import rx.exceptions.Exceptions; import rx.internal.operators.BlockingOperatorToFuture; import rx.internal.util.BlockingUtils; @@ -33,9 +32,8 @@ * or {@link Single#toBlocking()}. * * @param the value type of the sequence - * @since (if this graduates from Experimental/Beta to supported, replace this parenthetical with the release number) + * @since 1.3 */ -@Beta public final class BlockingSingle { private final Single single; diff --git a/src/main/java/rx/subjects/BehaviorSubject.java b/src/main/java/rx/subjects/BehaviorSubject.java index 551f7f6c23..8a45cf1df9 100644 --- a/src/main/java/rx/subjects/BehaviorSubject.java +++ b/src/main/java/rx/subjects/BehaviorSubject.java @@ -35,14 +35,14 @@ *

*

 {@code
 
-  // observer will receive all events.
+  // observer will receive all 4 events (including "default").
   BehaviorSubject subject = BehaviorSubject.create("default");
   subject.subscribe(observer);
   subject.onNext("one");
   subject.onNext("two");
   subject.onNext("three");
 
-  // observer will receive the "one", "two" and "three" events, but not "zero"
+  // observer will receive the "one", "two" and "three" events, but not "default" and "zero"
   BehaviorSubject subject = BehaviorSubject.create("default");
   subject.onNext("zero");
   subject.onNext("one");
diff --git a/src/main/java/rx/subjects/UnicastSubject.java b/src/main/java/rx/subjects/UnicastSubject.java
index c1c69f9e36..e7addc5cd8 100644
--- a/src/main/java/rx/subjects/UnicastSubject.java
+++ b/src/main/java/rx/subjects/UnicastSubject.java
@@ -19,7 +19,6 @@
 import java.util.concurrent.atomic.*;
 
 import rx.*;
-import rx.annotations.Experimental;
 import rx.exceptions.*;
 import rx.functions.Action0;
 import rx.internal.operators.*;
@@ -32,10 +31,11 @@
  * amount. In this case, the buffered values are no longer retained. If the Subscriber
  * requests a limited amount, queueing is involved and only those values are retained which
  * weren't requested by the Subscriber at that time.
- *
+ * 

+ * * @param the input and output value type + * @since 1.3 */ -@Experimental public final class UnicastSubject extends Subject { final State state; @@ -49,6 +49,7 @@ public final class UnicastSubject extends Subject { public static UnicastSubject create() { return create(16); } + /** * Constructs an empty UnicastSubject instance with a capacity hint. *

The capacity hint determines the internal queue's island size: the larger @@ -59,7 +60,19 @@ public static UnicastSubject create() { * @return the created BufferUntilSubscriber instance */ public static UnicastSubject create(int capacityHint) { - State state = new State(capacityHint, null); + State state = new State(capacityHint, false, null); + return new UnicastSubject(state); + } + + /** + * Constructs an empty UnicastSubject instance with the default capacity hint of 16 elements. + * + * @param delayError deliver pending next events before error. + * @param the input and output value type + * @return the created UnicastSubject instance + */ + public static UnicastSubject create(boolean delayError) { + State state = new State(16, delayError, null); return new UnicastSubject(state); } @@ -78,7 +91,28 @@ public static UnicastSubject create(int capacityHint) { * @return the created BufferUntilSubscriber instance */ public static UnicastSubject create(int capacityHint, Action0 onTerminated) { - State state = new State(capacityHint, onTerminated); + State state = new State(capacityHint, false, onTerminated); + return new UnicastSubject(state); + } + + /** + * Constructs an empty UnicastSubject instance with a capacity hint, delay error + * flag and Action0 instance to call if the subject reaches its terminal state + * or the single Subscriber unsubscribes mid-sequence. + *

The capacity hint determines the internal queue's island size: the larger + * it is the less frequent allocation will happen if there is no subscriber + * or the subscriber hasn't caught up. + * @param the input and output value type + * @param capacityHint the capacity hint for the internal queue + * @param onTerminated the optional callback to call when subject reaches its terminal state + * or the single Subscriber unsubscribes mid-sequence. It will be called + * at most once. + * @param delayError flag indicating whether to deliver pending next events before error. + * @return the created BufferUntilSubscriber instance + */ + public static UnicastSubject create(int capacityHint, + Action0 onTerminated, boolean delayError) { + State state = new State(capacityHint, delayError, onTerminated); return new UnicastSubject(state); } @@ -119,6 +153,8 @@ static final class State extends AtomicLong implements Producer, Observer, final AtomicReference> subscriber; /** The queue holding values until the subscriber arrives and catches up. */ final Queue queue; + /** Deliver pending next events before error. */ + final boolean delayError; /** Atomically set to true on terminal condition. */ final AtomicReference terminateOnce; /** In case the source emitted an error. */ @@ -137,10 +173,12 @@ static final class State extends AtomicLong implements Producer, Observer, * reduce allocation frequency * @param onTerminated the action to call when the subject reaches its terminal state or * the single subscriber unsubscribes. + * @param delayError deliver pending next events before error. */ - public State(int capacityHint, Action0 onTerminated) { + public State(int capacityHint, boolean delayError, Action0 onTerminated) { this.subscriber = new AtomicReference>(); this.terminateOnce = onTerminated != null ? new AtomicReference(onTerminated) : null; + this.delayError = delayError; Queue q; if (capacityHint > 1) { @@ -266,14 +304,14 @@ void replay() { emitting = true; } Queue q = queue; + boolean delayError = this.delayError; for (;;) { Subscriber s = subscriber.get(); boolean unlimited = false; if (s != null) { boolean d = done; boolean empty = q.isEmpty(); - - if (checkTerminated(d, empty, s)) { + if (checkTerminated(d, empty, delayError, s)) { return; } long r = get(); @@ -284,7 +322,7 @@ void replay() { d = done; Object v = q.poll(); empty = v == null; - if (checkTerminated(d, empty, s)) { + if (checkTerminated(d, empty, delayError, s)) { return; } if (empty) { @@ -348,23 +386,28 @@ public boolean isUnsubscribed() { * an error happened or the source terminated and the queue is empty * @param done indicates the source has called onCompleted * @param empty indicates if there are no more source values in the queue + * @param delayError indicates whether to deliver pending next events before error * @param s the target Subscriber to emit events to * @return true if this Subject reached a terminal state and the drain loop should quit */ - boolean checkTerminated(boolean done, boolean empty, Subscriber s) { + boolean checkTerminated(boolean done, boolean empty, boolean delayError, Subscriber s) { if (s.isUnsubscribed()) { queue.clear(); return true; } if (done) { Throwable e = error; - if (e != null) { + if (e != null && !delayError) { queue.clear(); s.onError(e); return true; - } else + } if (empty) { - s.onCompleted(); + if (e != null) { + s.onError(e); + } else { + s.onCompleted(); + } return true; } } diff --git a/src/perf/java/rx/OneItemPerf.java b/src/perf/java/rx/OneItemPerf.java index 43c6539992..7927268c69 100644 --- a/src/perf/java/rx/OneItemPerf.java +++ b/src/perf/java/rx/OneItemPerf.java @@ -70,7 +70,7 @@ public void call(SingleSubscriber t) { @Setup public void setup() { scalar = Observable.just(1); - one = Observable.create(new OnSubscribe() { + one = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { t.setProducer(new SingleProducer(t, 1)); diff --git a/src/perf/java/rx/ScalarJustPerf.java b/src/perf/java/rx/ScalarJustPerf.java index 548d1764fd..c50f945672 100644 --- a/src/perf/java/rx/ScalarJustPerf.java +++ b/src/perf/java/rx/ScalarJustPerf.java @@ -126,7 +126,7 @@ void runAsync(Blackhole bh, Observable source) { source.subscribe(lo); - while (lo.latch.getCount() != 0L); + while (lo.latch.getCount() != 0L) { } } @Benchmark diff --git a/src/perf/java/rx/SingleSourcePerf.java b/src/perf/java/rx/SingleSourcePerf.java index c735e4e2b0..743d8669fb 100644 --- a/src/perf/java/rx/SingleSourcePerf.java +++ b/src/perf/java/rx/SingleSourcePerf.java @@ -169,7 +169,7 @@ public void await() { } public void awaitSpin() { - while (cdl.getCount() != 0L) ; + while (cdl.getCount() != 0L) { } } } diff --git a/src/perf/java/rx/jmh/InputWithIncrementingInteger.java b/src/perf/java/rx/jmh/InputWithIncrementingInteger.java index ad0c350c2d..24468bb97b 100644 --- a/src/perf/java/rx/jmh/InputWithIncrementingInteger.java +++ b/src/perf/java/rx/jmh/InputWithIncrementingInteger.java @@ -44,7 +44,7 @@ public void setup(final Blackhole bh) { final int size = getSize(); observable = Observable.range(0, size); - firehose = Observable.create(new OnSubscribe() { + firehose = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -59,7 +59,7 @@ public void call(Subscriber s) { @Override public Iterator iterator() { return new Iterator() { - int i = 0; + int i; @Override public boolean hasNext() { diff --git a/src/perf/java/rx/jmh/PerfAsyncSingleSubscriber.java b/src/perf/java/rx/jmh/PerfAsyncSingleSubscriber.java index 01d7600d6f..0a579c3087 100644 --- a/src/perf/java/rx/jmh/PerfAsyncSingleSubscriber.java +++ b/src/perf/java/rx/jmh/PerfAsyncSingleSubscriber.java @@ -64,6 +64,6 @@ public void sleepAwait() { * Spins until the subscriber receives an events. */ public void spinAwait() { - while (cdl.getCount() != 0) ; + while (cdl.getCount() != 0) { } } } diff --git a/src/perf/java/rx/observables/SyncOnSubscribePerf.java b/src/perf/java/rx/observables/SyncOnSubscribePerf.java index 54377e433b..23c36e3d6b 100644 --- a/src/perf/java/rx/observables/SyncOnSubscribePerf.java +++ b/src/perf/java/rx/observables/SyncOnSubscribePerf.java @@ -49,7 +49,7 @@ private static Blackhole _jmh_tryInit_() { } private static OnSubscribe createSyncOnSubscribe(final Iterator iterator) { - return new SyncOnSubscribe(){ + return new SyncOnSubscribe() { @Override protected Void generateState() { @@ -60,12 +60,12 @@ protected Void generateState() { protected Void next(Void state, Observer observer) { if (iterator.hasNext()) { observer.onNext(iterator.next()); - } - else + } else { observer.onCompleted(); - return null; } - }; + return null; + } + }; } // @Benchmark diff --git a/src/perf/java/rx/operators/FromComparison.java b/src/perf/java/rx/operators/FromComparison.java index 3ba2d3c751..763a938cae 100644 --- a/src/perf/java/rx/operators/FromComparison.java +++ b/src/perf/java/rx/operators/FromComparison.java @@ -49,8 +49,8 @@ public void setup() { Arrays.fill(array, 1); - iterableSource = Observable.create(new OnSubscribeFromIterable(Arrays.asList(array))); - arraySource = Observable.create(new OnSubscribeFromArray(array)); + iterableSource = Observable.unsafeCreate(new OnSubscribeFromIterable(Arrays.asList(array))); + arraySource = Observable.unsafeCreate(new OnSubscribeFromArray(array)); } @Benchmark diff --git a/src/perf/java/rx/operators/OperatorRangePerf.java b/src/perf/java/rx/operators/OperatorRangePerf.java index 53e06a0c94..78711e3d58 100644 --- a/src/perf/java/rx/operators/OperatorRangePerf.java +++ b/src/perf/java/rx/operators/OperatorRangePerf.java @@ -43,7 +43,7 @@ public static class InputUsingRequest { @Setup public void setup(final Blackhole bh) { - observable = Observable.create(new OnSubscribeRange(0, size)); + observable = Observable.unsafeCreate(new OnSubscribeRange(0, size)); this.bh = bh; } @@ -91,7 +91,7 @@ public static class InputWithoutRequest { @Setup public void setup(final Blackhole bh) { - observable = Observable.create(new OnSubscribeRange(0, size)); + observable = Observable.unsafeCreate(new OnSubscribeRange(0, size)); this.bh = bh; } diff --git a/src/perf/java/rx/operators/OperatorSerializePerf.java b/src/perf/java/rx/operators/OperatorSerializePerf.java index cae310b72c..70b61421ec 100644 --- a/src/perf/java/rx/operators/OperatorSerializePerf.java +++ b/src/perf/java/rx/operators/OperatorSerializePerf.java @@ -59,7 +59,7 @@ public void serializedSingleStream(Input input) throws InterruptedException { @Benchmark public void serializedTwoStreamsHighlyContended(final Input input) throws InterruptedException { LatchedObserver o = input.newLatchedObserver(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -101,7 +101,7 @@ public Integer call(Long t1) { @Benchmark public void serializedTwoStreamsSlightlyContended(final InputWithInterval input) throws InterruptedException { LatchedObserver o = input.newLatchedObserver(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -118,7 +118,7 @@ public void call(Subscriber s) { @Benchmark public void serializedTwoStreamsOneFastOneSlow(final InputWithInterval input) throws InterruptedException { LatchedObserver o = input.newLatchedObserver(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber s) { diff --git a/src/perf/java/rx/operators/OperatorTakeLastOnePerf.java b/src/perf/java/rx/operators/OperatorTakeLastOnePerf.java index 3b54689c53..c96f9a2c66 100644 --- a/src/perf/java/rx/operators/OperatorTakeLastOnePerf.java +++ b/src/perf/java/rx/operators/OperatorTakeLastOnePerf.java @@ -46,7 +46,7 @@ public void takeLastOneUsingTakeLast(Input input) { @Benchmark public void takeLastOneUsingTakeLastOne(Input input) { - Observable.create(new OnSubscribeTakeLastOne(input.observable)).subscribe(input.observer); + Observable.unsafeCreate(new OnSubscribeTakeLastOne(input.observable)).subscribe(input.observer); } } diff --git a/src/perf/java/rx/operators/ZipPerf.java b/src/perf/java/rx/operators/ZipPerf.java index 36608aea53..1591c8e8d6 100644 --- a/src/perf/java/rx/operators/ZipPerf.java +++ b/src/perf/java/rx/operators/ZipPerf.java @@ -67,7 +67,7 @@ public void setup() { Integer[] array2 = new Integer[secondLen]; Arrays.fill(array2, 777); - baseline = Observable.from(firstLen < secondLen? array2 : array1); + baseline = Observable.from(firstLen < secondLen ? array2 : array1); Observable o1 = Observable.from(array1); @@ -107,7 +107,7 @@ public void syncAsync(Blackhole bh) throws Exception { firstSync.subscribe(o); if (small) { - while (o.latch.getCount() != 0); + while (o.latch.getCount() != 0) { } } else { o.latch.await(); } @@ -119,7 +119,7 @@ public void asyncSync(Blackhole bh) throws Exception { secondSync.subscribe(o); if (small) { - while (o.latch.getCount() != 0); + while (o.latch.getCount() != 0) { } } else { o.latch.await(); } @@ -131,7 +131,7 @@ public void asyncAsync(Blackhole bh) throws Exception { bothAsync.subscribe(o); if (small) { - while (o.latch.getCount() != 0); + while (o.latch.getCount() != 0) { } } else { o.latch.await(); } diff --git a/src/test/java/rx/BackpressureTests.java b/src/test/java/rx/BackpressureTests.java index 9556aff5e6..16a1565b0a 100644 --- a/src/test/java/rx/BackpressureTests.java +++ b/src/test/java/rx/BackpressureTests.java @@ -593,14 +593,14 @@ private static Observable incrementingIntegers(final AtomicInteger coun } private static Observable incrementingIntegers(final AtomicInteger counter, final ConcurrentLinkedQueue threadsSeen) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { final AtomicLong requested = new AtomicLong(); @Override public void call(final Subscriber s) { s.setProducer(new Producer() { - int i = 0; + int i; @Override public void request(long n) { @@ -637,9 +637,9 @@ public void request(long n) { * @return */ private static Observable firehose(final AtomicInteger counter) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { - int i = 0; + int i; @Override public void call(final Subscriber s) { diff --git a/src/test/java/rx/CapturingUncaughtExceptionHandler.java b/src/test/java/rx/CapturingUncaughtExceptionHandler.java index a44c6df080..27709943b8 100644 --- a/src/test/java/rx/CapturingUncaughtExceptionHandler.java +++ b/src/test/java/rx/CapturingUncaughtExceptionHandler.java @@ -19,7 +19,7 @@ import java.util.concurrent.CountDownLatch; public final class CapturingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { - public int count = 0; + public int count; public Throwable caught; public CountDownLatch completed = new CountDownLatch(1); diff --git a/src/test/java/rx/CompletableTest.java b/src/test/java/rx/CompletableTest.java index 98ee57b5b2..3f34a983ca 100644 --- a/src/test/java/rx/CompletableTest.java +++ b/src/test/java/rx/CompletableTest.java @@ -93,7 +93,7 @@ public void remove() { /** * A class containing a completable instance and counts the number of subscribers. */ - static final class NormalCompletable extends AtomicInteger { + public static final class NormalCompletable extends AtomicInteger { /** */ private static final long serialVersionUID = 7192337844700923752L; @@ -119,7 +119,7 @@ public void assertSubscriptions(int n) { * A class containing a completable instance that emits a TestException and counts * the number of subscribers. */ - static final class ErrorCompletable extends AtomicInteger { + public static final class ErrorCompletable extends AtomicInteger { /** */ private static final long serialVersionUID = 7192337844700923752L; @@ -391,7 +391,7 @@ public void call(CompletableSubscriber cs) { cs.onError(e); } }) - .andThen(Observable.create(new Observable.OnSubscribe() { + .andThen(Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { hasRun.set(true); @@ -2606,7 +2606,7 @@ public void call(Throwable e) { public void subscribeTwoCallbacksFirstNull() { normal.completable.subscribe(null, new Action1() { @Override - public void call(Throwable throwable) {} + public void call(Throwable throwable) { } }); } @@ -2894,9 +2894,9 @@ public Observable call(Completable c) { } }); - flow.toBlocking().forEach(new Action1(){ + flow.toBlocking().forEach(new Action1() { @Override - public void call(Object e){ } + public void call(Object e) { } }); } @@ -3887,8 +3887,12 @@ private static void expectUncaughtTestException(Action0 action) { assertEquals("Should have received exactly 1 exception", 1, handler.count); Throwable caught = handler.caught; while (caught != null) { - if (caught instanceof TestException) break; - if (caught == caught.getCause()) break; + if (caught instanceof TestException) { + break; + } + if (caught == caught.getCause()) { + break; + } caught = caught.getCause(); } assertTrue("A TestException should have been delivered to the handler", diff --git a/src/test/java/rx/ConcatTests.java b/src/test/java/rx/ConcatTests.java index 558f8af1dd..d00d812c52 100644 --- a/src/test/java/rx/ConcatTests.java +++ b/src/test/java/rx/ConcatTests.java @@ -149,7 +149,7 @@ public void testConcatCovariance4() { Media media = new Media(); HorrorMovie horrorMovie2 = new HorrorMovie(); - Observable o1 = Observable.create(new OnSubscribe() { + Observable o1 = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber o) { diff --git a/src/test/java/rx/EventStream.java b/src/test/java/rx/EventStream.java index 632e649601..91a6482a5c 100644 --- a/src/test/java/rx/EventStream.java +++ b/src/test/java/rx/EventStream.java @@ -32,7 +32,7 @@ private EventStream() { throw new IllegalStateException("No instances!"); } public static Observable getEventStream(final String type, final int numInstances) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber subscriber) { diff --git a/src/test/java/rx/MergeTests.java b/src/test/java/rx/MergeTests.java index 07bba783ea..c42579cc37 100644 --- a/src/test/java/rx/MergeTests.java +++ b/src/test/java/rx/MergeTests.java @@ -80,7 +80,7 @@ public void testMergeCovariance3() { @Test public void testMergeCovariance4() { - Observable o1 = Observable.create(new OnSubscribe() { + Observable o1 = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber o) { diff --git a/src/test/java/rx/NotificationTest.java b/src/test/java/rx/NotificationTest.java index df79092ff4..c25e28d136 100644 --- a/src/test/java/rx/NotificationTest.java +++ b/src/test/java/rx/NotificationTest.java @@ -29,49 +29,49 @@ public class NotificationTest { @Test - public void testOnNextIntegerNotificationDoesNotEqualNullNotification(){ + public void testOnNextIntegerNotificationDoesNotEqualNullNotification() { final Notification integerNotification = Notification.createOnNext(1); final Notification nullNotification = Notification.createOnNext(null); assertFalse(integerNotification.equals(nullNotification)); } @Test - public void testOnNextNullNotificationDoesNotEqualIntegerNotification(){ + public void testOnNextNullNotificationDoesNotEqualIntegerNotification() { final Notification integerNotification = Notification.createOnNext(1); final Notification nullNotification = Notification.createOnNext(null); assertFalse(nullNotification.equals(integerNotification)); } @Test - public void testOnNextIntegerNotificationsWhenEqual(){ + public void testOnNextIntegerNotificationsWhenEqual() { final Notification integerNotification = Notification.createOnNext(1); final Notification integerNotification2 = Notification.createOnNext(1); assertTrue(integerNotification.equals(integerNotification2)); } @Test - public void testOnNextIntegerNotificationsWhenNotEqual(){ + public void testOnNextIntegerNotificationsWhenNotEqual() { final Notification integerNotification = Notification.createOnNext(1); final Notification integerNotification2 = Notification.createOnNext(2); assertFalse(integerNotification.equals(integerNotification2)); } @Test - public void testOnErrorIntegerNotificationDoesNotEqualNullNotification(){ + public void testOnErrorIntegerNotificationDoesNotEqualNullNotification() { final Notification integerNotification = Notification.createOnError(new Exception()); final Notification nullNotification = Notification.createOnError(null); assertFalse(integerNotification.equals(nullNotification)); } @Test - public void testOnErrorNullNotificationDoesNotEqualIntegerNotification(){ + public void testOnErrorNullNotificationDoesNotEqualIntegerNotification() { final Notification integerNotification = Notification.createOnError(new Exception()); final Notification nullNotification = Notification.createOnError(null); assertFalse(nullNotification.equals(integerNotification)); } @Test - public void testOnErrorIntegerNotificationsWhenEqual(){ + public void testOnErrorIntegerNotificationsWhenEqual() { final Exception exception = new Exception(); final Notification onErrorNotification = Notification.createOnError(exception); final Notification onErrorNotification2 = Notification.createOnError(exception); @@ -79,7 +79,7 @@ public void testOnErrorIntegerNotificationsWhenEqual(){ } @Test - public void testOnErrorIntegerNotificationWhenNotEqual(){ + public void testOnErrorIntegerNotificationWhenNotEqual() { final Notification onErrorNotification = Notification.createOnError(new Exception()); final Notification onErrorNotification2 = Notification.createOnError(new Exception()); assertFalse(onErrorNotification.equals(onErrorNotification2)); @@ -256,5 +256,10 @@ public boolean equals(Object o) { } return false; } + + @Override + public int hashCode() { + return getMessage().hashCode(); + } } } diff --git a/src/test/java/rx/ObservableTests.java b/src/test/java/rx/ObservableTests.java index 433d56331b..1ad7daaa72 100644 --- a/src/test/java/rx/ObservableTests.java +++ b/src/test/java/rx/ObservableTests.java @@ -94,7 +94,7 @@ public void fromArityArgs1() { @Test public void testCreate() { - Observable observable = Observable.create(new OnSubscribe() { + Observable observable = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber Observer) { @@ -140,7 +140,7 @@ public void testCountZeroItems() { @Test public void testCountError() { - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber obsv) { obsv.onError(new RuntimeException()); @@ -289,7 +289,7 @@ public void testOnSubscribeFails() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); final RuntimeException re = new RuntimeException("bad impl"); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { @@ -330,7 +330,7 @@ public void testCustomObservableWithErrorInObserverAsynchronous() throws Interru final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger count = new AtomicInteger(); final AtomicReference error = new AtomicReference(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -395,7 +395,7 @@ public void onNext(String v) { public void testCustomObservableWithErrorInObserverSynchronous() { final AtomicInteger count = new AtomicInteger(); final AtomicReference error = new AtomicReference(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { @@ -445,7 +445,7 @@ public void onNext(String v) { public void testCustomObservableWithErrorInObservableSynchronous() { final AtomicInteger count = new AtomicInteger(); final AtomicReference error = new AtomicReference(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { @@ -484,7 +484,7 @@ public void onNext(String v) { @Test public void testPublishLast() throws InterruptedException { final AtomicInteger count = new AtomicInteger(); - ConnectableObservable connectable = Observable.create(new OnSubscribe() { + ConnectableObservable connectable = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { count.incrementAndGet(); @@ -522,7 +522,7 @@ public void call(String value) { @Test public void testReplay() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - ConnectableObservable o = Observable.create(new OnSubscribe() { + ConnectableObservable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -577,7 +577,7 @@ public void call(String v) { @Test public void testCache() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -625,7 +625,7 @@ public void call(String v) { @Test public void testCacheWithCapacity() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -711,7 +711,7 @@ public void call(Object t1) { public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference exception = new AtomicReference(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -1089,7 +1089,7 @@ public void testSubscribingSubscriberAsObserverMaintainsSubscriptionChain() { subscriber.assertUnsubscribed(); } - @Test(expected=OnErrorNotImplementedException.class) + @Test(expected = OnErrorNotImplementedException.class) public void testForEachWithError() { Observable.error(new Exception("boo")) // @@ -1100,7 +1100,7 @@ public void call(Object t) { }}); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testForEachWithNull() { Observable.error(new Exception("boo")) // @@ -1109,7 +1109,7 @@ public void testForEachWithNull() { @Test public void nullOnSubscribe() { - Observable source = Observable.create((OnSubscribe)null); + Observable source = Observable.unsafeCreate((OnSubscribe)null); try { source.subscribe(); @@ -1147,7 +1147,7 @@ public void nullSubscriber() { @Test public void testCacheHint() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { diff --git a/src/test/java/rx/SchedulerWorkerTest.java b/src/test/java/rx/SchedulerWorkerTest.java index 16d0c65553..159d3d34a7 100644 --- a/src/test/java/rx/SchedulerWorkerTest.java +++ b/src/test/java/rx/SchedulerWorkerTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import rx.functions.Action0; +import rx.internal.schedulers.SchedulePeriodicHelper; import rx.schedulers.Schedulers; public class SchedulerWorkerTest { @@ -85,7 +86,7 @@ public void call() { Thread.sleep(150); - s.drift = -1000 - TimeUnit.NANOSECONDS.toMillis(Scheduler.CLOCK_DRIFT_TOLERANCE_NANOS); + s.drift = -1000 - TimeUnit.NANOSECONDS.toMillis(SchedulePeriodicHelper.CLOCK_DRIFT_TOLERANCE_NANOS); Thread.sleep(400); @@ -127,7 +128,7 @@ public void call() { Thread.sleep(150); - s.drift = 1000 + TimeUnit.NANOSECONDS.toMillis(Scheduler.CLOCK_DRIFT_TOLERANCE_NANOS); + s.drift = 1000 + TimeUnit.NANOSECONDS.toMillis(SchedulePeriodicHelper.CLOCK_DRIFT_TOLERANCE_NANOS); Thread.sleep(400); diff --git a/src/test/java/rx/SingleTest.java b/src/test/java/rx/SingleTest.java index 41b75e73f7..9ae7fa0f90 100644 --- a/src/test/java/rx/SingleTest.java +++ b/src/test/java/rx/SingleTest.java @@ -2055,7 +2055,7 @@ public Single call(Integer v) { }) .subscribe(); - while (!ps.hasObservers() && !Thread.currentThread().isInterrupted()) ; + while (!ps.hasObservers() && !Thread.currentThread().isInterrupted()) { } s.unsubscribe(); @@ -2230,4 +2230,65 @@ public void call(Throwable t) { assertEquals(1, calls[0]); } + + @Test + public void unsubscribeOnSuccess() throws InterruptedException { + final AtomicReference name = new AtomicReference(); + + final CountDownLatch cdl = new CountDownLatch(1); + + TestSubscriber ts = TestSubscriber.create(); + + Single.fromCallable(new Callable() { + @Override + public Integer call() throws Exception { + return 1; + } + }) + .doOnUnsubscribe(new Action0() { + @Override + public void call() { + name.set(Thread.currentThread().getName()); + cdl.countDown(); + } + }) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.computation()) + .subscribe(ts); + + cdl.await(); + + ts.awaitTerminalEvent(); + ts.assertReceivedOnNext(Arrays.asList(1)); + + assertTrue(name.get().startsWith("RxComputation")); + } + + @Test + public void unsubscribeOnError() throws InterruptedException { + final AtomicReference name = new AtomicReference(); + + final CountDownLatch cdl = new CountDownLatch(1); + + TestSubscriber ts = TestSubscriber.create(); + + Single.error(new RuntimeException()) + .doOnUnsubscribe(new Action0() { + @Override + public void call() { + name.set(Thread.currentThread().getName()); + cdl.countDown(); + } + }) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.computation()) + .subscribe(ts); + + cdl.await(); + + ts.awaitTerminalEvent(); + ts.assertError(RuntimeException.class); + + assertTrue(name.get().startsWith("RxComputation")); + } } diff --git a/src/test/java/rx/SubscriberTest.java b/src/test/java/rx/SubscriberTest.java index 82abd5fee6..a57f2b0229 100644 --- a/src/test/java/rx/SubscriberTest.java +++ b/src/test/java/rx/SubscriberTest.java @@ -229,7 +229,7 @@ public void testRequestToObservable() { TestSubscriber ts = new TestSubscriber(); ts.request(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -252,7 +252,7 @@ public void testRequestThroughMap() { TestSubscriber ts = new TestSubscriber(); ts.request(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -282,7 +282,7 @@ public void testRequestThroughTakeThatReducesRequest() { TestSubscriber ts = new TestSubscriber(); ts.request(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -305,7 +305,7 @@ public void testRequestThroughTakeWhereRequestIsSmallerThanTake() { TestSubscriber ts = new TestSubscriber(); ts.request(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -489,7 +489,7 @@ public void testOnStartRequestsAreAdditiveAndOverflowBecomesMaxValue() { @Override public void onStart() { request(2); - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); } @Override diff --git a/src/test/java/rx/TestUtil.java b/src/test/java/rx/TestUtil.java index 7beb6d0f4a..15414b510e 100644 --- a/src/test/java/rx/TestUtil.java +++ b/src/test/java/rx/TestUtil.java @@ -67,7 +67,7 @@ public static void race(Action0 r1, final Action0 r2) { @Override public void call() { if (counter.decrementAndGet() != 0) { - while (counter.get() != 0); + while (counter.get() != 0) { } } try { @@ -81,7 +81,7 @@ public void call() { }); if (counter.decrementAndGet() != 0) { - while (counter.get() != 0); + while (counter.get() != 0) { } } try { diff --git a/src/test/java/rx/exceptions/CompositeExceptionTest.java b/src/test/java/rx/exceptions/CompositeExceptionTest.java index 5451a8cf10..e7f33251c5 100644 --- a/src/test/java/rx/exceptions/CompositeExceptionTest.java +++ b/src/test/java/rx/exceptions/CompositeExceptionTest.java @@ -15,18 +15,12 @@ */ package rx.exceptions; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.io.*; +import java.util.*; -import org.junit.Test; +import org.junit.*; import rx.exceptions.CompositeException.CompositeExceptionCausalChain; @@ -159,7 +153,7 @@ private static Throwable getRootCause(Throwable ex) { if (root == null) { return null; } else { - while(true) { + while (true) { if (root.getCause() == null) { return root; } else { @@ -257,7 +251,7 @@ public void complexCauses() { e5.initCause(e6); CompositeException compositeException = new CompositeException(e1, e3, e5); - assert(compositeException.getCause() instanceof CompositeExceptionCausalChain); + Assert.assertTrue(compositeException.getCause() instanceof CompositeExceptionCausalChain); List causeChain = new ArrayList(); Throwable cause = compositeException.getCause().getCause(); diff --git a/src/test/java/rx/exceptions/ExceptionsTest.java b/src/test/java/rx/exceptions/ExceptionsTest.java index 5fe0f18d69..07bcb644a8 100644 --- a/src/test/java/rx/exceptions/ExceptionsTest.java +++ b/src/test/java/rx/exceptions/ExceptionsTest.java @@ -254,10 +254,10 @@ public void onNext(Integer integer) { @Test(expected = OnErrorFailedException.class) public void testOnErrorExceptionIsThrownFromSubscribe() { - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s1) { - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s2) { throw new IllegalArgumentException("original exception"); @@ -270,10 +270,10 @@ public void call(Subscriber s2) { @Test(expected = OnErrorFailedException.class) public void testOnErrorExceptionIsThrownFromUnsafeSubscribe() { - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s1) { - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s2) { throw new IllegalArgumentException("original exception"); diff --git a/src/test/java/rx/exceptions/OnNextValueTest.java b/src/test/java/rx/exceptions/OnNextValueTest.java index 2e36c5bafd..31a37692f8 100644 --- a/src/test/java/rx/exceptions/OnNextValueTest.java +++ b/src/test/java/rx/exceptions/OnNextValueTest.java @@ -15,20 +15,16 @@ */ package rx.exceptions; +import static org.junit.Assert.*; + +import java.io.*; + import org.junit.Test; -import rx.Observable; -import rx.Observer; +import rx.*; import rx.exceptions.OnErrorThrowable.OnNextValue; import rx.functions.Func1; -import java.io.PrintWriter; -import java.io.StringWriter; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - /** * ```java * public OnNextValue(Object value) { @@ -166,4 +162,32 @@ public void testRenderDouble() { public void testRenderVoid() { assertEquals("null", OnNextValue.renderValue((Void) null)); } + + static class Value { + @Override + public String toString() { + return "Value"; + } + } + + @Test + public void nonSerializableValue() throws Exception { + Throwable e = OnErrorThrowable.addValueAsLastCause(new RuntimeException(), new Value()); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bout); + oos.writeObject(e); + oos.close(); + + ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bin); + + Throwable f = (Throwable)ois.readObject(); + + ois.close(); + + Object v = ((OnNextValue)f.getCause()).getValue(); + + assertEquals("Value", v); + } } diff --git a/src/test/java/rx/internal/operators/BlockingOperatorNextTest.java b/src/test/java/rx/internal/operators/BlockingOperatorNextTest.java index 45a109e885..69541f712b 100644 --- a/src/test/java/rx/internal/operators/BlockingOperatorNextTest.java +++ b/src/test/java/rx/internal/operators/BlockingOperatorNextTest.java @@ -234,69 +234,80 @@ public void testNextWithCallingHasNextMultipleTimes() { */ @Test public void testNoBufferingOrBlockingOfSequence() throws Throwable { - final CountDownLatch finished = new CountDownLatch(1); - final int COUNT = 30; - final CountDownLatch timeHasPassed = new CountDownLatch(COUNT); - final AtomicBoolean running = new AtomicBoolean(true); - final AtomicInteger count = new AtomicInteger(0); - final Observable obs = Observable.create(new Observable.OnSubscribe() { + int retries = 10; - @Override - public void call(final Subscriber o) { - new Thread(new Runnable() { + for (;;) { + try { + final CountDownLatch finished = new CountDownLatch(1); + final int COUNT = 30; + final CountDownLatch timeHasPassed = new CountDownLatch(COUNT); + final AtomicBoolean running = new AtomicBoolean(true); + final AtomicInteger count = new AtomicInteger(0); + final Observable obs = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override - public void run() { - try { - while (running.get()) { - o.onNext(count.incrementAndGet()); - timeHasPassed.countDown(); + public void call(final Subscriber o) { + new Thread(new Runnable() { + + @Override + public void run() { + try { + while (running.get()) { + o.onNext(count.incrementAndGet()); + timeHasPassed.countDown(); + } + o.onCompleted(); + } catch (Throwable e) { + o.onError(e); + } finally { + finished.countDown(); + } } - o.onCompleted(); - } catch (Throwable e) { - o.onError(e); - } finally { - finished.countDown(); - } + }).start(); } - }).start(); - } - }); + }); - try { - Iterator it = next(obs).iterator(); + try { + Iterator it = next(obs).iterator(); - assertTrue(it.hasNext()); - int a = it.next(); - assertTrue(it.hasNext()); - int b = it.next(); - // we should have a different value - assertTrue("a and b should be different", a != b); + assertTrue(it.hasNext()); + int a = it.next(); + assertTrue(it.hasNext()); + int b = it.next(); + // we should have a different value + assertTrue("a and b should be different", a != b); - // wait for some time (if times out we are blocked somewhere so fail ... set very high for very slow, constrained machines) - timeHasPassed.await(8000, TimeUnit.MILLISECONDS); + // wait for some time (if times out we are blocked somewhere so fail ... set very high for very slow, constrained machines) + timeHasPassed.await(8000, TimeUnit.MILLISECONDS); - assertTrue(it.hasNext()); - int c = it.next(); + assertTrue(it.hasNext()); + int c = it.next(); - assertTrue("c should not just be the next in sequence", c != (b + 1)); - assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); + assertTrue("c should not just be the next in sequence", c != (b + 1)); + assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); - assertTrue(it.hasNext()); - int d = it.next(); - assertTrue(d > c); + assertTrue(it.hasNext()); + int d = it.next(); + assertTrue(d > c); - // shut down the thread - running.set(false); + // shut down the thread + running.set(false); - finished.await(); + finished.await(); - assertFalse(it.hasNext()); + assertFalse(it.hasNext()); - System.out.println("a: " + a + " b: " + b + " c: " + c); - } finally { - running.set(false); // don't let the thread spin indefinitely + System.out.println("a: " + a + " b: " + b + " c: " + c); + } finally { + running.set(false); // don't let the thread spin indefinitely + } + return; + } catch (AssertionError ex) { + if (retries-- == 0) { + throw ex; + } + } } } diff --git a/src/test/java/rx/internal/operators/BlockingOperatorToFutureTest.java b/src/test/java/rx/internal/operators/BlockingOperatorToFutureTest.java index 5a85995d7b..81f54dcdca 100644 --- a/src/test/java/rx/internal/operators/BlockingOperatorToFutureTest.java +++ b/src/test/java/rx/internal/operators/BlockingOperatorToFutureTest.java @@ -58,14 +58,14 @@ public void testExceptionWithMoreThanOneElement() throws Throwable { // we expect an exception since there are more than 1 element f.get(); } - catch(ExecutionException e) { + catch (ExecutionException e) { throw e.getCause(); } } @Test public void testToFutureWithException() { - Observable obs = Observable.create(new OnSubscribe() { + Observable obs = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { @@ -83,18 +83,18 @@ public void call(Subscriber observer) { } } - @Test(expected=CancellationException.class) + @Test(expected = CancellationException.class) public void testGetAfterCancel() throws Exception { - Observable obs = Observable.create(new OperationNeverComplete()); + Observable obs = Observable.unsafeCreate(new OperationNeverComplete()); Future f = toFuture(obs); boolean cancelled = f.cancel(true); assertTrue(cancelled); // because OperationNeverComplete never does f.get(); // Future.get() docs require this to throw } - @Test(expected=CancellationException.class) + @Test(expected = CancellationException.class) public void testGetWithTimeoutAfterCancel() throws Exception { - Observable obs = Observable.create(new OperationNeverComplete()); + Observable obs = Observable.unsafeCreate(new OperationNeverComplete()); Future f = toFuture(obs); boolean cancelled = f.cancel(true); assertTrue(cancelled); // because OperationNeverComplete never does @@ -118,7 +118,7 @@ public void testGetWithEmptyObservable() throws Throwable { try { f.get(); } - catch(ExecutionException e) { + catch (ExecutionException e) { throw e.getCause(); } } diff --git a/src/test/java/rx/internal/operators/BlockingOperatorToIteratorTest.java b/src/test/java/rx/internal/operators/BlockingOperatorToIteratorTest.java index e641227f01..91902b2c77 100644 --- a/src/test/java/rx/internal/operators/BlockingOperatorToIteratorTest.java +++ b/src/test/java/rx/internal/operators/BlockingOperatorToIteratorTest.java @@ -55,7 +55,7 @@ public void testToIterator() { @Test(expected = TestException.class) public void testToIteratorWithException() { - Observable obs = Observable.create(new OnSubscribe() { + Observable obs = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { @@ -75,7 +75,7 @@ public void call(Subscriber observer) { @Test(expected = TestException.class) public void testExceptionThrownFromOnSubscribe() { - Iterable strings = Observable.create(new Observable.OnSubscribe() { + Iterable strings = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber subscriber) { throw new TestException("intentional"); diff --git a/src/test/java/rx/internal/operators/CachedObservableTest.java b/src/test/java/rx/internal/operators/CachedObservableTest.java index a642d80b03..40fe2e56de 100644 --- a/src/test/java/rx/internal/operators/CachedObservableTest.java +++ b/src/test/java/rx/internal/operators/CachedObservableTest.java @@ -85,7 +85,7 @@ public void testColdReplayBackpressure() { @Test public void testCache() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(new Observable.OnSubscribe() { + Observable o = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -220,7 +220,7 @@ public void testAsyncComeAndGo() { @Test public void testNoMissingBackpressureException() { final int m = 4 * 1000 * 1000; - Observable firehose = Observable.create(new OnSubscribe() { + Observable firehose = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { for (int i = 0; i < m; i++) { diff --git a/src/test/java/rx/internal/operators/CompletableConcatTest.java b/src/test/java/rx/internal/operators/CompletableConcatTest.java new file mode 100644 index 0000000000..f55898193c --- /dev/null +++ b/src/test/java/rx/internal/operators/CompletableConcatTest.java @@ -0,0 +1,138 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.*; + +import rx.*; +import rx.functions.*; +import rx.schedulers.Schedulers; + +public class CompletableConcatTest { + + @Test + public void asyncObservables() { + + final int[] calls = { 0 }; + + Completable.concat(Observable.range(1, 5).map(new Func1() { + @Override + public Completable call(final Integer v) { + System.out.println("Mapping " + v); + return Completable.fromAction(new Action0() { + @Override + public void call() { + System.out.println("Processing " + (calls[0] + 1)); + calls[0]++; + } + }) + .subscribeOn(Schedulers.io()) + .doOnCompleted(new Action0() { + @Override + public void call() { + System.out.println("Inner complete " + v); + } + }) + .observeOn(Schedulers.computation()); + } + }) + ).test() + .awaitTerminalEventAndUnsubscribeOnTimeout(5, TimeUnit.SECONDS) + .assertResult(); + + Assert.assertEquals(5, calls[0]); + } + + @Test + public void andThenNoInterrupt() throws InterruptedException { + for (int k = 0; k < 100; k++) { + final int count = 10; + final CountDownLatch latch = new CountDownLatch(count); + final AtomicBoolean interrupted = new AtomicBoolean(); + + for (int i = 0; i < count; i++) { + Completable.complete() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .andThen(Completable.fromAction(new Action0() { + @Override + public void call() { + try { + Thread.sleep(30); + } catch (InterruptedException e) { + System.out.println("Interrupted! " + Thread.currentThread()); + interrupted.set(true); + } + } + })) + .subscribe(new Action0() { + @Override + public void call() { + latch.countDown(); + } + }); + } + + latch.await(); + assertFalse("The second Completable was interrupted!", interrupted.get()); + } + } + + @Test + public void noInterrupt() throws InterruptedException { + for (int k = 0; k < 100; k++) { + final int count = 10; + final CountDownLatch latch = new CountDownLatch(count); + final AtomicBoolean interrupted = new AtomicBoolean(); + + for (int i = 0; i < count; i++) { + Completable c0 = Completable.fromAction(new Action0() { + @Override + public void call() { + try { + Thread.sleep(30); + } catch (InterruptedException e) { + System.out.println("Interrupted! " + Thread.currentThread()); + interrupted.set(true); + } + } + }); + Completable.concat(Arrays.asList(Completable.complete() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()), + c0) + ) + .subscribe(new Action0() { + @Override + public void call() { + latch.countDown(); + } + }); + } + + latch.await(); + assertFalse("The second Completable was interrupted!", interrupted.get()); + } + } + +} diff --git a/src/test/java/rx/internal/operators/CompletableMergeTest.java b/src/test/java/rx/internal/operators/CompletableMergeTest.java new file mode 100644 index 0000000000..a16518ab36 --- /dev/null +++ b/src/test/java/rx/internal/operators/CompletableMergeTest.java @@ -0,0 +1,61 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.concurrent.TimeUnit; + +import org.junit.*; + +import rx.*; +import rx.functions.*; +import rx.schedulers.Schedulers; + +public class CompletableMergeTest { + + @Test + public void asyncObservables() { + + final int[] calls = { 0 }; + + Completable.merge(Observable.range(1, 5).map(new Func1() { + @Override + public Completable call(final Integer v) { + System.out.println("Mapping " + v); + return Completable.fromAction(new Action0() { + @Override + public void call() { + System.out.println("Processing " + (calls[0] + 1)); + calls[0]++; + } + }) + .subscribeOn(Schedulers.io()) + .doOnCompleted(new Action0() { + @Override + public void call() { + System.out.println("Inner complete " + v); + } + }) + .observeOn(Schedulers.computation()); + } + }), 1 + ).test() + .awaitTerminalEventAndUnsubscribeOnTimeout(5, TimeUnit.SECONDS) + .assertResult(); + + Assert.assertEquals(5, calls[0]); + } +} diff --git a/src/test/java/rx/internal/operators/CompletableOnErrorXTest.java b/src/test/java/rx/internal/operators/CompletableOnErrorXTest.java new file mode 100644 index 0000000000..31cbff9df1 --- /dev/null +++ b/src/test/java/rx/internal/operators/CompletableOnErrorXTest.java @@ -0,0 +1,63 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import static org.junit.Assert.*; +import org.junit.Test; + +import rx.Completable; +import rx.functions.Func1; +import rx.observers.AssertableSubscriber; +import rx.subjects.PublishSubject; + +public class CompletableOnErrorXTest { + + @Test + public void nextUnsubscribe() { + PublishSubject ps = PublishSubject.create(); + + AssertableSubscriber as = ps.toCompletable() + .onErrorResumeNext(new Func1() { + @Override + public Completable call(Throwable e) { + return Completable.complete(); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + as.unsubscribe(); + + assertFalse("Still subscribed!", ps.hasObservers()); + } + + @Test + public void completeUnsubscribe() { + PublishSubject ps = PublishSubject.create(); + + AssertableSubscriber as = ps.toCompletable() + .onErrorComplete() + .test(); + + assertTrue(ps.hasObservers()); + + as.unsubscribe(); + + assertFalse("Still subscribed!", ps.hasObservers()); + } +} diff --git a/src/test/java/rx/internal/operators/DeferredScalarSubscriberTest.java b/src/test/java/rx/internal/operators/DeferredScalarSubscriberTest.java index 6d09cfb950..c3ad148e75 100644 --- a/src/test/java/rx/internal/operators/DeferredScalarSubscriberTest.java +++ b/src/test/java/rx/internal/operators/DeferredScalarSubscriberTest.java @@ -317,14 +317,14 @@ public void emissionRequestRace() { @Override public void call() { ready.decrementAndGet(); - while (ready.get() != 0) ; + while (ready.get() != 0) { } ts.requestMore(1); } }); ready.decrementAndGet(); - while (ready.get() != 0) ; + while (ready.get() != 0) { } ds.onCompleted(); @@ -361,7 +361,7 @@ public void emissionRequestRace2() { @Override public void call() { ready.decrementAndGet(); - while (ready.get() != 0) ; + while (ready.get() != 0) { } ts.requestMore(1); } @@ -371,14 +371,14 @@ public void call() { @Override public void call() { ready.decrementAndGet(); - while (ready.get() != 0) ; + while (ready.get() != 0) { } ts.requestMore(1); } }); ready.decrementAndGet(); - while (ready.get() != 0) ; + while (ready.get() != 0) { } ds.onCompleted(); diff --git a/src/test/java/rx/internal/operators/OnSubscribeAmbTest.java b/src/test/java/rx/internal/operators/OnSubscribeAmbTest.java index c3e7480fa7..7f9bf0ff49 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeAmbTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeAmbTest.java @@ -51,7 +51,7 @@ public void setUp() { private Observable createObservable(final String[] values, final long interval, final Throwable e) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber subscriber) { @@ -90,7 +90,7 @@ public void testAmb() { Observable observable3 = createObservable(new String[] { "3", "33", "333", "3333" }, 3000, null); - Observable o = Observable.create(amb(observable1, + Observable o = Observable.unsafeCreate(amb(observable1, observable2, observable3)); @SuppressWarnings("unchecked") @@ -119,7 +119,7 @@ public void testAmb2() { Observable observable3 = createObservable(new String[] {}, 3000, new IOException("fake exception")); - Observable o = Observable.create(amb(observable1, + Observable o = Observable.unsafeCreate(amb(observable1, observable2, observable3)); @SuppressWarnings("unchecked") @@ -146,7 +146,7 @@ public void testAmb3() { Observable observable3 = createObservable(new String[] { "3" }, 3000, null); - Observable o = Observable.create(amb(observable1, + Observable o = Observable.unsafeCreate(amb(observable1, observable2, observable3)); @SuppressWarnings("unchecked") @@ -165,7 +165,7 @@ public void testProducerRequestThroughAmb() { ts.requestMore(3); final AtomicLong requested1 = new AtomicLong(); final AtomicLong requested2 = new AtomicLong(); - Observable o1 = Observable.create(new OnSubscribe() { + Observable o1 = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -181,7 +181,7 @@ public void request(long n) { } }); - Observable o2 = Observable.create(new OnSubscribe() { + Observable o2 = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { diff --git a/src/test/java/rx/internal/operators/OnSubscribeCollectTest.java b/src/test/java/rx/internal/operators/OnSubscribeCollectTest.java index 859ecd63c9..75d6b51954 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeCollectTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeCollectTest.java @@ -130,7 +130,7 @@ public void call(Throwable t) { final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); TestSubscriber> ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -171,7 +171,7 @@ public void call(List t1, Integer t2) { public void testCollectorFailureDoesNotResultInErrorAndCompletedEmissions() { final RuntimeException e1 = new RuntimeException(); TestSubscriber> ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -209,7 +209,7 @@ public void testCollectorFailureDoesNotResultInErrorAndOnNextEmissions() { final RuntimeException e1 = new RuntimeException(); TestSubscriber> ts = TestSubscriber.create(); final AtomicBoolean added = new AtomicBoolean(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OnSubscribeCombineLatestTest.java b/src/test/java/rx/internal/operators/OnSubscribeCombineLatestTest.java index 1fb0518112..b5ebd07e5e 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeCombineLatestTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeCombineLatestTest.java @@ -831,7 +831,7 @@ public Long call(Long t1, Integer t2) { assertEquals(SIZE, count.get()); } - @Test(timeout=10000) + @Test(timeout = 10000) public void testCombineLatestRequestOverflow() throws InterruptedException { @SuppressWarnings("unchecked") List> sources = Arrays.asList(Observable.from(Arrays.asList(1,2,3,4)), Observable.from(Arrays.asList(5,6,7,8))); @@ -862,7 +862,7 @@ public void onError(Throwable e) { @Override public void onNext(Integer t) { latch.countDown(); - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); }}); assertTrue(latch.await(10, TimeUnit.SECONDS)); } diff --git a/src/test/java/rx/internal/operators/OnSubscribeFromEmitterTest.java b/src/test/java/rx/internal/operators/OnSubscribeCreateTest.java similarity index 80% rename from src/test/java/rx/internal/operators/OnSubscribeFromEmitterTest.java rename to src/test/java/rx/internal/operators/OnSubscribeCreateTest.java index b7579b5b6f..2d8e5f843c 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeFromEmitterTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeCreateTest.java @@ -32,7 +32,7 @@ import rx.plugins.RxJavaHooks; import rx.subjects.PublishSubject; -public class OnSubscribeFromEmitterTest { +public class OnSubscribeCreateTest { PublishEmitter source; @@ -49,7 +49,7 @@ public void before() { @Test public void normalBuffered() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); source.onNext(1); source.onNext(2); @@ -70,7 +70,7 @@ public void normalBuffered() { @Test public void normalDrop() { - Observable.fromEmitter(source, Emitter.BackpressureMode.DROP).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.DROP).subscribe(ts); source.onNext(1); @@ -86,7 +86,7 @@ public void normalDrop() { @Test public void normalLatest() { - Observable.fromEmitter(source, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.LATEST).subscribe(ts); source.onNext(1); @@ -102,7 +102,7 @@ public void normalLatest() { @Test public void normalNone() { - Observable.fromEmitter(source, Emitter.BackpressureMode.NONE).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.NONE).subscribe(ts); source.onNext(1); source.onNext(2); @@ -115,7 +115,7 @@ public void normalNone() { @Test public void normalNoneRequested() { - Observable.fromEmitter(source, Emitter.BackpressureMode.NONE).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.NONE).subscribe(ts); ts.requestMore(2); source.onNext(1); @@ -130,7 +130,7 @@ public void normalNoneRequested() { @Test public void normalError() { - Observable.fromEmitter(source, Emitter.BackpressureMode.ERROR).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.ERROR).subscribe(ts); source.onNext(1); source.onNext(2); @@ -140,12 +140,12 @@ public void normalError() { ts.assertError(MissingBackpressureException.class); ts.assertNotCompleted(); - Assert.assertEquals("fromEmitter: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); + Assert.assertEquals("create: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); } @Test public void overflowErrorIsNotFollowedByAnotherErrorDueToOnNextFromUpstream() { - Action1> source = new Action1>(){ + Action1> source = new Action1>() { @Override public void call(Emitter emitter) { @@ -153,18 +153,18 @@ public void call(Emitter emitter) { //don't check for unsubscription emitter.onNext(2); }}; - Observable.fromEmitter(source, Emitter.BackpressureMode.ERROR).unsafeSubscribe(ts); + Observable.create(source, Emitter.BackpressureMode.ERROR).unsafeSubscribe(ts); ts.assertNoValues(); ts.assertError(MissingBackpressureException.class); ts.assertNotCompleted(); - Assert.assertEquals("fromEmitter: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); + Assert.assertEquals("create: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); } @Test public void overflowErrorIsNotFollowedByAnotherCompletedDueToCompletedFromUpstream() { - Action1> source = new Action1>(){ + Action1> source = new Action1>() { @Override public void call(Emitter emitter) { @@ -172,13 +172,13 @@ public void call(Emitter emitter) { //don't check for unsubscription emitter.onCompleted(); }}; - Observable.fromEmitter(source, Emitter.BackpressureMode.ERROR).unsafeSubscribe(ts); + Observable.create(source, Emitter.BackpressureMode.ERROR).unsafeSubscribe(ts); ts.assertNoValues(); ts.assertError(MissingBackpressureException.class); ts.assertNotCompleted(); - Assert.assertEquals("fromEmitter: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); + Assert.assertEquals("create: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); } @Test @@ -191,7 +191,7 @@ public void call(Throwable t) { list.add(t); }}); final RuntimeException e = new RuntimeException(); - Action1> source = new Action1>(){ + Action1> source = new Action1>() { @Override public void call(Emitter emitter) { @@ -199,13 +199,13 @@ public void call(Emitter emitter) { //don't check for unsubscription emitter.onError(e); }}; - Observable.fromEmitter(source, Emitter.BackpressureMode.ERROR).unsafeSubscribe(ts); + Observable.create(source, Emitter.BackpressureMode.ERROR).unsafeSubscribe(ts); ts.assertNoValues(); ts.assertError(MissingBackpressureException.class); ts.assertNotCompleted(); - Assert.assertEquals("fromEmitter: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); + Assert.assertEquals("create: could not emit value due to lack of requests", ts.getOnErrorEvents().get(0).getMessage()); assertEquals(Arrays.asList(e), list); } finally { RxJavaHooks.reset(); @@ -214,7 +214,7 @@ public void call(Emitter emitter) { @Test public void errorBuffered() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); source.onNext(1); source.onNext(2); @@ -233,7 +233,7 @@ public void errorBuffered() { @Test public void errorLatest() { - Observable.fromEmitter(source, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.LATEST).subscribe(ts); source.onNext(1); source.onNext(2); @@ -248,7 +248,7 @@ public void errorLatest() { @Test public void errorNone() { - Observable.fromEmitter(source, Emitter.BackpressureMode.NONE).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.NONE).subscribe(ts); source.onNext(1); source.onNext(2); @@ -263,7 +263,7 @@ public void errorNone() { @Test public void unsubscribedBuffer() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); ts.unsubscribe(); source.onNext(1); @@ -279,7 +279,7 @@ public void unsubscribedBuffer() { @Test public void unsubscribedLatest() { - Observable.fromEmitter(source, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.LATEST).subscribe(ts); ts.unsubscribe(); source.onNext(1); @@ -295,7 +295,7 @@ public void unsubscribedLatest() { @Test public void unsubscribedError() { - Observable.fromEmitter(source, Emitter.BackpressureMode.ERROR).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.ERROR).subscribe(ts); ts.unsubscribe(); source.onNext(1); @@ -311,7 +311,7 @@ public void unsubscribedError() { @Test public void unsubscribedDrop() { - Observable.fromEmitter(source, Emitter.BackpressureMode.DROP).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.DROP).subscribe(ts); ts.unsubscribe(); source.onNext(1); @@ -327,7 +327,7 @@ public void unsubscribedDrop() { @Test public void unsubscribedNone() { - Observable.fromEmitter(source, Emitter.BackpressureMode.NONE).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.NONE).subscribe(ts); ts.unsubscribe(); source.onNext(1); @@ -343,7 +343,7 @@ public void unsubscribedNone() { @Test public void unsubscribedNoCancelBuffer() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); ts.unsubscribe(); sourceNoCancel.onNext(1); @@ -359,7 +359,7 @@ public void unsubscribedNoCancelBuffer() { @Test public void unsubscribedNoCancelLatest() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); ts.unsubscribe(); sourceNoCancel.onNext(1); @@ -375,7 +375,7 @@ public void unsubscribedNoCancelLatest() { @Test public void unsubscribedNoCancelError() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.ERROR).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.ERROR).subscribe(ts); ts.unsubscribe(); sourceNoCancel.onNext(1); @@ -391,7 +391,7 @@ public void unsubscribedNoCancelError() { @Test public void unsubscribedNoCancelDrop() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.DROP).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.DROP).subscribe(ts); ts.unsubscribe(); sourceNoCancel.onNext(1); @@ -407,7 +407,7 @@ public void unsubscribedNoCancelDrop() { @Test public void unsubscribedNoCancelNone() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.NONE).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.NONE).subscribe(ts); ts.unsubscribe(); sourceNoCancel.onNext(1); @@ -423,7 +423,7 @@ public void unsubscribedNoCancelNone() { @Test public void deferredRequest() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); source.onNext(1); source.onNext(2); @@ -438,7 +438,7 @@ public void deferredRequest() { @Test public void take() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).take(2).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).take(2).subscribe(ts); source.onNext(1); source.onNext(2); @@ -453,7 +453,7 @@ public void take() { @Test public void takeOne() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).take(1).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).take(1).subscribe(ts); ts.requestMore(2); source.onNext(1); @@ -467,7 +467,7 @@ public void takeOne() { @Test public void requestExact() { - Observable.fromEmitter(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(source, Emitter.BackpressureMode.BUFFER).subscribe(ts); ts.requestMore(2); source.onNext(1); @@ -481,7 +481,7 @@ public void requestExact() { @Test public void takeNoCancel() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).take(2).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).take(2).subscribe(ts); sourceNoCancel.onNext(1); sourceNoCancel.onNext(2); @@ -496,7 +496,7 @@ public void takeNoCancel() { @Test public void takeOneNoCancel() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).take(1).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).take(1).subscribe(ts); ts.requestMore(2); sourceNoCancel.onNext(1); @@ -510,7 +510,7 @@ public void takeOneNoCancel() { @Test public void unsubscribeNoCancel() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); ts.requestMore(2); sourceNoCancel.onNext(1); @@ -535,7 +535,7 @@ public void onNext(Integer t) { } }; - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts1); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts1); sourceNoCancel.onNext(1); @@ -546,7 +546,7 @@ public void onNext(Integer t) { @Test public void completeInline() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); sourceNoCancel.onNext(1); sourceNoCancel.onCompleted(); @@ -560,7 +560,7 @@ public void completeInline() { @Test public void errorInline() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts); sourceNoCancel.onNext(1); sourceNoCancel.onError(new TestException()); @@ -582,7 +582,7 @@ public void onNext(Integer t) { } }; - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts1); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.BUFFER).subscribe(ts1); sourceNoCancel.onNext(1); sourceNoCancel.onNext(2); @@ -602,7 +602,7 @@ public void onNext(Integer t) { } }; - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts1); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts1); sourceNoCancel.onNext(1); @@ -621,7 +621,7 @@ public void onNext(Integer t) { } }; - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts1); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts1); sourceNoCancel.onNext(1); @@ -632,7 +632,7 @@ public void onNext(Integer t) { @Test public void completeInlineLatest() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); sourceNoCancel.onNext(1); sourceNoCancel.onCompleted(); @@ -646,7 +646,7 @@ public void completeInlineLatest() { @Test public void completeInlineExactLatest() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); sourceNoCancel.onNext(1); sourceNoCancel.onCompleted(); @@ -660,7 +660,7 @@ public void completeInlineExactLatest() { @Test public void errorInlineLatest() { - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts); sourceNoCancel.onNext(1); sourceNoCancel.onError(new TestException()); @@ -682,7 +682,7 @@ public void onNext(Integer t) { } }; - Observable.fromEmitter(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts1); + Observable.create(sourceNoCancel, Emitter.BackpressureMode.LATEST).subscribe(ts1); sourceNoCancel.onNext(1); sourceNoCancel.onNext(2); diff --git a/src/test/java/rx/internal/operators/OnSubscribeDetachTest.java b/src/test/java/rx/internal/operators/OnSubscribeDetachTest.java index 6d06019425..7d435bff79 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeDetachTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeDetachTest.java @@ -138,7 +138,7 @@ public void deferredUpstreamProducer() { TestSubscriber ts = new TestSubscriber(0); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { subscriber.set(t); diff --git a/src/test/java/rx/internal/operators/OnSubscribeDoOnEachTest.java b/src/test/java/rx/internal/operators/OnSubscribeDoOnEachTest.java index 397b6496be..bec5c4daab 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeDoOnEachTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeDoOnEachTest.java @@ -124,7 +124,7 @@ public void testIssue1451Case1() { // https://github.com/Netflix/RxJava/issues/1451 final int expectedCount = 3; final AtomicInteger count = new AtomicInteger(); - for (int i=0; i < expectedCount; i++) { + for (int i = 0; i < expectedCount; i++) { Observable .just(Boolean.TRUE, Boolean.FALSE) .takeWhile(new Func1() { @@ -150,7 +150,7 @@ public void testIssue1451Case2() { // https://github.com/Netflix/RxJava/issues/1451 final int expectedCount = 3; final AtomicInteger count = new AtomicInteger(); - for (int i=0; i < expectedCount; i++) { + for (int i = 0; i < expectedCount; i++) { Observable .just(Boolean.TRUE, Boolean.FALSE, Boolean.FALSE) .takeWhile(new Func1() { @@ -178,7 +178,7 @@ public void testFatalError() { .flatMap(new Func1>() { @Override public Observable call(Integer integer) { - return Observable.create(new Observable.OnSubscribe() { + return Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber o) { throw new NullPointerException("Test NPE"); @@ -229,7 +229,7 @@ public void call(Throwable e) { public void testIfOnNextActionFailsEmitsErrorAndDoesNotFollowWithCompleted() { TestSubscriber ts = TestSubscriber.create(); final RuntimeException e1 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber subscriber) { @@ -259,7 +259,7 @@ public void call(Integer t) { public void testIfOnNextActionFailsEmitsErrorAndDoesNotFollowWithOnNext() { TestSubscriber ts = TestSubscriber.create(); final RuntimeException e1 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber subscriber) { @@ -288,7 +288,7 @@ public void call(Integer t) { @Test public void testIfOnNextActionFailsEmitsErrorAndReportsMoreErrorsToRxJavaHooksNotDownstream() { try { - final List list= new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList(); RxJavaHooks.setOnError(new Action1() { @Override @@ -298,7 +298,7 @@ public void call(Throwable e) { TestSubscriber ts = TestSubscriber.create(); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber subscriber) { diff --git a/src/test/java/rx/internal/operators/OnSubscribeFlatMapCompletableTest.java b/src/test/java/rx/internal/operators/OnSubscribeFlatMapCompletableTest.java new file mode 100644 index 0000000000..2b88387400 --- /dev/null +++ b/src/test/java/rx/internal/operators/OnSubscribeFlatMapCompletableTest.java @@ -0,0 +1,557 @@ +/** + * Copyright 2017 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import static org.junit.Assert.*; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.*; + +import rx.*; +import rx.CompletableTest.*; +import rx.Observable; +import rx.exceptions.*; +import rx.functions.*; +import rx.internal.util.UtilityFunctions; +import rx.observers.*; +import rx.schedulers.Schedulers; +import rx.subjects.PublishSubject; +import rx.subscriptions.Subscriptions; + +public class OnSubscribeFlatMapCompletableTest implements Action0, Action1 { + + final AtomicInteger calls = new AtomicInteger(); + + /** A normal Completable object. */ + final NormalCompletable normal = new NormalCompletable(); + + /** An error Completable object. */ + final ErrorCompletable error = new ErrorCompletable(); + + final Func1 identity = UtilityFunctions.identity(); + + @Override + public void call() { + calls.getAndIncrement(); + } + + @Override + public void call(Object t) { + calls.getAndIncrement(); + } + + void assertCalls(int n) { + assertEquals(n, calls.get()); + } + + @Test + public void normal() { + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.complete().doOnCompleted(OnSubscribeFlatMapCompletableTest.this); + } + }) + .test() + .assertResult(); + + assertCalls(10); + } + + @Test + public void normalMaxConcurrent() { + for (int i = 1; i < 10; i++) { + calls.set(0); + + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.complete() + .observeOn(Schedulers.computation()) + .doOnCompleted(OnSubscribeFlatMapCompletableTest.this); + } + }, false, i) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS) + .assertResult(); + + assertCalls(10); + } + } + + @Test + public void error() { + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.error(new TestException()).doOnError(OnSubscribeFlatMapCompletableTest.this); + } + }) + .test() + .assertFailure(TestException.class); + + assertCalls(1); + } + + @Test + public void errorDelayed() { + AssertableSubscriber as = Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.error(new TestException()).doOnError(OnSubscribeFlatMapCompletableTest.this); + } + }, true) + .test() + .assertFailure(CompositeException.class); + + List onErrorEvents = as.getOnErrorEvents(); + + assertEquals(onErrorEvents.toString(), 1, onErrorEvents.size()); + + onErrorEvents = ((CompositeException)onErrorEvents.get(0)).getExceptions(); + + assertEquals(onErrorEvents.toString(), 10, onErrorEvents.size()); + + for (Throwable ex : onErrorEvents) { + assertTrue(ex.toString(), ex instanceof TestException); + } + + assertCalls(10); + } + + @Test + public void errorDelayedMaxConcurrency() { + AssertableSubscriber as = Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.error(new TestException()).doOnError(OnSubscribeFlatMapCompletableTest.this); + } + }, true, 1) + .test() + .assertFailure(CompositeException.class); + + List onErrorEvents = as.getOnErrorEvents(); + + assertEquals(onErrorEvents.toString(), 1, onErrorEvents.size()); + + onErrorEvents = ((CompositeException)onErrorEvents.get(0)).getExceptions(); + + assertEquals(onErrorEvents.toString(), 10, onErrorEvents.size()); + + for (Throwable ex : onErrorEvents) { + assertTrue(ex.toString(), ex instanceof TestException); + } + + assertCalls(10); + } + + @Test + public void mapperThrows() { + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperNull() { + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return null; + } + }) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void paramValidation() { + try { + Observable.range(1, 10) + .flatMapCompletable(null); + fail("Should have thrown"); + } catch (NullPointerException ex) { + assertEquals("mapper is null", ex.getMessage()); + } + + try { + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.complete(); + } + }, false, 0); + fail("Should have thrown"); + } catch (IllegalArgumentException ex) { + assertEquals("maxConcurrency > 0 required but it was 0", ex.getMessage()); + } + + try { + Observable.range(1, 10) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.complete(); + } + }, true, -99); + fail("Should have thrown"); + } catch (IllegalArgumentException ex) { + assertEquals("maxConcurrency > 0 required but it was -99", ex.getMessage()); + } + } + + @Test + public void mainErrorDelayed() { + Observable.range(1, 10).concatWith(Observable.error(new TestException())) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return Completable.complete().doOnCompleted(OnSubscribeFlatMapCompletableTest.this); + } + }, true) + .test() + .assertFailure(TestException.class); + + assertCalls(10); + } + + @Test + public void innerDoubleOnSubscribe() { + final CompletableSubscriber[] inner = { null }; + + AssertableSubscriber as = Observable.just(1) + .flatMapCompletable(new Func1() { + @Override + public Completable call(Integer t) { + return Completable.create(new Completable.OnSubscribe() { + @Override + public void call(CompletableSubscriber t) { + OnSubscribeFlatMapCompletableTest.this.call(); + Subscription s1 = Subscriptions.empty(); + + t.onSubscribe(s1); + + Subscription s2 = Subscriptions.empty(); + + t.onSubscribe(s2); + + if (s2.isUnsubscribed()) { + OnSubscribeFlatMapCompletableTest.this.call(); + } + + t.onCompleted(); + + inner[0] = t; + } + }); + } + }) + .test() + .assertResult(); + + assertCalls(2); + + inner[0].onError(new TestException()); + + as.assertResult(); + } + + @Test + public void mainErrorUnsubscribes() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(); + + ps0.flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return v == 0 ? ps1.toCompletable() : ps2.toCompletable(); + } + }).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onError(new TestException()); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + @Test + public void innerErrorUnsubscribes() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(); + + ps0.flatMapCompletable(new Func1() { + @Override + public Completable call(Integer v) { + return v == 0 ? ps1.toCompletable() : ps2.toCompletable(); + } + }).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps1.onError(new TestException()); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + + @Test(timeout = 5000) + public void mergeObservableEmpty() { + Completable c = Observable.empty().flatMapCompletable(identity).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000, expected = TestException.class) + public void mergeObservableError() { + Completable c = Observable.error(new TestException()).flatMapCompletable(identity).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000) + public void mergeObservableSingle() { + Completable c = Observable.just(normal.completable).flatMapCompletable(identity).toCompletable(); + + c.await(); + + normal.assertSubscriptions(1); + } + + @Test(timeout = 5000, expected = TestException.class) + public void mergeObservableSingleThrows() { + Completable c = Observable.just(error.completable).flatMapCompletable(identity).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000) + public void mergeObservableMany() { + Completable c = Observable.just(normal.completable).repeat(3).flatMapCompletable(identity).toCompletable(); + + c.await(); + + normal.assertSubscriptions(3); + } + + @Test(timeout = 5000, expected = TestException.class) + public void mergeObservableManyOneThrows() { + Completable c = Observable.just(normal.completable, error.completable).flatMapCompletable(identity).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000) + public void mergeObservableMaxConcurrent() { + final List requested = new ArrayList(); + Observable cs = Observable + .just(normal.completable) + .repeat(10) + .doOnRequest(new Action1() { + @Override + public void call(Long v) { + requested.add(v); + } + }); + + Completable c = cs.flatMapCompletable(identity, false, 5).toCompletable(); + + c.await(); + + // FIXME this request pattern looks odd because all 10 completions trigger 1 requests + Assert.assertEquals(Arrays.asList(5L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), requested); + } + + @Test(timeout = 5000) + public void mergeDelayErrorObservableEmpty() { + Completable c = Observable.empty().flatMapCompletable(identity, true).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000, expected = TestException.class) + public void mergeDelayErrorObservableError() { + Completable c = Observable.error(new TestException()).flatMapCompletable(identity, true).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000) + public void mergeDelayErrorObservableSingle() { + Completable c = Observable.just(normal.completable).flatMapCompletable(identity, true).toCompletable(); + + c.await(); + + normal.assertSubscriptions(1); + } + + @Test(timeout = 5000, expected = TestException.class) + public void mergeDelayErrorObservableSingleThrows() { + Completable c = Observable.just(error.completable).flatMapCompletable(identity, true).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000) + public void mergeDelayErrorObservableMany() { + Completable c = Observable.just(normal.completable).repeat(3).flatMapCompletable(identity, true).toCompletable(); + + c.await(); + + normal.assertSubscriptions(3); + } + + @Test(timeout = 5000, expected = TestException.class) + public void mergeDelayErrorObservableManyOneThrows() { + Completable c = Observable.just(normal.completable, error.completable).flatMapCompletable(identity, true).toCompletable(); + + c.await(); + } + + @Test(timeout = 5000) + public void mergeDelayErrorObservableMaxConcurrent() { + final List requested = new ArrayList(); + Observable cs = Observable + .just(normal.completable) + .repeat(10) + .doOnRequest(new Action1() { + @Override + public void call(Long v) { + requested.add(v); + } + }); + + Completable c = cs.flatMapCompletable(identity, true, 5).toCompletable(); + + c.await(); + + // FIXME this request pattern looks odd because all 10 completions trigger 1 requests + Assert.assertEquals(Arrays.asList(5L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), requested); + } + + @Test + public void asyncObservables() { + + final int[] calls = { 0 }; + + Observable.range(1, 5).map(new Func1() { + @Override + public Completable call(final Integer v) { + System.out.println("Mapping " + v); + return Completable.fromAction(new Action0() { + @Override + public void call() { + System.out.println("Processing " + (calls[0] + 1)); + calls[0]++; + } + }) + .subscribeOn(Schedulers.io()) + .doOnCompleted(new Action0() { + @Override + public void call() { + System.out.println("Inner complete " + v); + } + }) + .observeOn(Schedulers.computation()); + } + }).flatMapCompletable(identity, false, 1).toCompletable() + .test() + .awaitTerminalEventAndUnsubscribeOnTimeout(5, TimeUnit.SECONDS) + .assertResult(); + + Assert.assertEquals(5, calls[0]); + } + +} diff --git a/src/test/java/rx/internal/operators/OnSubscribeFlatMapSingleTest.java b/src/test/java/rx/internal/operators/OnSubscribeFlatMapSingleTest.java new file mode 100644 index 0000000000..3da2ac389c --- /dev/null +++ b/src/test/java/rx/internal/operators/OnSubscribeFlatMapSingleTest.java @@ -0,0 +1,782 @@ +/** + * Copyright 2017 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; +import org.junit.Test; + +import rx.*; +import rx.Observable; +import rx.exceptions.*; +import rx.functions.*; +import rx.observers.*; +import rx.schedulers.Schedulers; +import rx.subjects.PublishSubject; + +public class OnSubscribeFlatMapSingleTest implements Action0, Action1 { + + final AtomicInteger calls = new AtomicInteger(); + + final Action1 errorConsumer = new Action1() { + @Override + public void call(Throwable e) { + OnSubscribeFlatMapSingleTest.this.call(e); + } + }; + + @Override + public void call() { + calls.getAndIncrement(); + } + + @Override + public void call(Object t) { + calls.getAndIncrement(); + } + + void assertCalls(int n) { + assertEquals(n, calls.get()); + } + + @Test + public void normal() { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }) + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void normalBackpressured() { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }) + .test(0L) + .assertNoValues() + .requestMore(1) + .assertValues(1) + .requestMore(2) + .assertValues(1, 2, 3) + .requestMore(3) + .assertValues(1, 2, 3, 4, 5, 6) + .requestMore(4) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void normalMaxConcurrencyBackpressured() { + for (int i = 1; i < 16; i++) { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }, false, i) + .test(0L) + .assertNoValues() + .requestMore(1) + .assertValues(1) + .requestMore(2) + .assertValues(1, 2, 3) + .requestMore(3) + .assertValues(1, 2, 3, 4, 5, 6) + .requestMore(4) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + } + + @Test + public void normalMaxConcurrent() { + for (int i = 1; i < 16; i++) { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }, false, i) + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + } + + @Test + public void normalMaxConcurrentAsync() { + for (int i = 1; i < 2; i++) { + AssertableSubscriber as = Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v).observeOn(Schedulers.computation()); + } + }, false, i) + .test(); + + as.awaitTerminalEvent(5, TimeUnit.SECONDS) + .assertValueCount(10) + .assertNoErrors() + .assertCompleted(); + + Set set = new HashSet(as.getOnNextEvents()); + + for (int j = 1; j < 11; j++) { + assertTrue("" + set, set.contains(j)); + } + } + } + + @Test + public void justMaxConcurrentAsync() { + AssertableSubscriber as = Observable.just(1) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v).observeOn(Schedulers.computation()); + } + }, false, 1) + .test(); + + as.awaitTerminalEvent(5, TimeUnit.SECONDS) + .assertResult(1); + } + + @Test + public void error() { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.error(new TestException()).doOnError(errorConsumer); + } + }) + .test() + .assertFailure(TestException.class); + + assertCalls(1); + } + + @Test + public void errorDelayed() { + AssertableSubscriber as = Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.error(new TestException()).doOnError(errorConsumer); + } + }, true) + .test() + .assertFailure(CompositeException.class); + + List onErrorEvents = as.getOnErrorEvents(); + + assertEquals(onErrorEvents.toString(), 1, onErrorEvents.size()); + + onErrorEvents = ((CompositeException)onErrorEvents.get(0)).getExceptions(); + + assertEquals(onErrorEvents.toString(), 10, onErrorEvents.size()); + + for (Throwable ex : onErrorEvents) { + assertTrue(ex.toString(), ex instanceof TestException); + } + + assertCalls(10); + } + + @Test + public void errorDelayedMaxConcurrency() { + AssertableSubscriber as = Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.error(new TestException()).doOnError(errorConsumer); + } + }, true, 1) + .test() + .assertFailure(CompositeException.class); + + List onErrorEvents = as.getOnErrorEvents(); + + assertEquals(onErrorEvents.toString(), 1, onErrorEvents.size()); + + onErrorEvents = ((CompositeException)onErrorEvents.get(0)).getExceptions(); + + assertEquals(onErrorEvents.toString(), 10, onErrorEvents.size()); + + for (Throwable ex : onErrorEvents) { + assertTrue(ex.toString(), ex instanceof TestException); + } + + assertCalls(10); + } + + @Test + public void mapperThrows() { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperNull() { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return null; + } + }) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void paramValidation() { + try { + Observable.range(1, 10) + .flatMapSingle(null); + fail("Should have thrown"); + } catch (NullPointerException ex) { + assertEquals("mapper is null", ex.getMessage()); + } + + try { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }, false, 0); + fail("Should have thrown"); + } catch (IllegalArgumentException ex) { + assertEquals("maxConcurrency > 0 required but it was 0", ex.getMessage()); + } + + try { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }, true, -99); + fail("Should have thrown"); + } catch (IllegalArgumentException ex) { + assertEquals("maxConcurrency > 0 required but it was -99", ex.getMessage()); + } + } + + @Test + public void mainErrorDelayed() { + Observable.range(1, 10).concatWith(Observable.error(new TestException())) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v).doOnSuccess(OnSubscribeFlatMapSingleTest.this); + } + }, true) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + assertCalls(10); + } + + @Test + public void mainErrorUnsubscribes() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(); + + ps0.flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return v == 0 ? ps1.toSingle() : ps2.toSingle(); + } + }).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onError(new TestException()); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + @Test + public void innerErrorUnsubscribes() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(); + + ps0.flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return v == 0 ? ps1.toSingle() : ps2.toSingle(); + } + }).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps1.onError(new TestException()); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + @Test + public void take() { + Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }) + .take(5) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + + @Test + public void unsubscribe() { + AssertableSubscriber as = Observable.range(1, 10) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }) + .test(0) + ; + + as.unsubscribe(); + + as.assertNoValues().assertNoErrors().assertNotCompleted(); + } + + @Test + public void mainErrorUnsubscribesNoRequest() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(0L); + + ps0.flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return v == 0 ? ps1.toSingle() : ps2.toSingle(); + } + }).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onError(new TestException()); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + @Test + public void innerErrorUnsubscribesNoRequest() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(0L); + + ps0.flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return v == 0 ? ps1.toSingle() : ps2.toSingle(); + } + }).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps1.onError(new TestException()); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + @Test + public void mainErrorUnsubscribesNoRequestDelayError() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(0L); + + ps0.flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return v == 0 ? ps1.toSingle() : ps2.toSingle(); + } + }, true).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onError(new TestException()); + ps1.onNext(3); + ps1.onCompleted(); + ps2.onNext(4); + ps2.onCompleted(); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + + as.requestMore(2); + as.assertValues(3, 4); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + @Test + public void innerErrorUnsubscribesNoRequestDelayError() { + PublishSubject ps0 = PublishSubject.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + + TestSubscriber as = TestSubscriber.create(0L); + + ps0.flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return v == 0 ? ps1.toSingle() : ps2.toSingle(); + } + }, true).unsafeSubscribe(as); + + assertTrue(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(0); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onNext(1); + + assertTrue(ps0.hasObservers()); + assertTrue(ps1.hasObservers()); + assertTrue(ps2.hasObservers()); + as.assertNoValues(); + as.assertNoErrors(); + as.assertNotCompleted(); + + ps0.onCompleted(); + ps1.onError(new TestException()); + ps2.onNext(4); + ps2.onCompleted(); + + assertFalse(ps0.hasObservers()); + assertFalse(ps1.hasObservers()); + assertFalse(ps2.hasObservers()); + + as.requestMore(1); + as.assertValues(4); + as.assertError(TestException.class); + as.assertNotCompleted(); + } + + @Test + public void justBackpressured() { + Observable.just(1) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }) + .test(1L) + .assertResult(1); + } + + @Test + public void justBackpressuredDelayError() { + Observable.just(1) + .flatMapSingle(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + }, true) + .test(1L) + .assertResult(1); + } + + @Test + public void singleMerge() { + Single.merge(Observable.range(1, 10).map(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + })) + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void singleMergeMaxConcurrent() { + AssertableSubscriber as = Single.merge(Observable.range(1, 10).map(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v).observeOn(Schedulers.computation()); + } + }), 2) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS); + + Set set = new HashSet(as.getOnNextEvents()); + + assertEquals("" + set, 10, set.size()); + for (int j = 1; j < 11; j++) { + assertTrue("" + set, set.contains(j)); + } + } + + @Test + public void singleMergeDelayError() { + Single.mergeDelayError(Observable.range(1, 10).map(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + })) + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void singleMergeDelayErrorMaxConcurrent() { + AssertableSubscriber as = Single.mergeDelayError(Observable.range(1, 10).map(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v).observeOn(Schedulers.computation()); + } + }), 2) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS); + + Set set = new HashSet(as.getOnNextEvents()); + + assertEquals("" + set, 10, set.size()); + for (int j = 1; j < 11; j++) { + assertTrue("" + set, set.contains(j)); + } + } + + @Test + public void singleMergeDelayErrorWithError() { + Single.mergeDelayError(Observable.range(1, 10) + .concatWith(Observable.error(new TestException())) + .map(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v); + } + })) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void singleMergeDelayMaxConcurrentErrorWithError() { + AssertableSubscriber as = Single.mergeDelayError(Observable.range(1, 10) + .concatWith(Observable.error(new TestException())) + .map(new Func1>() { + @Override + public Single call(Integer v) { + return Single.just(v).observeOn(Schedulers.computation()); + } + }), 2) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS); + + Set set = new HashSet(as.getOnNextEvents()); + + assertEquals("" + set, 10, set.size()); + for (int j = 1; j < 11; j++) { + assertTrue("" + set, set.contains(j)); + } + } +} diff --git a/src/test/java/rx/internal/operators/OnSubscribeFromArrayTest.java b/src/test/java/rx/internal/operators/OnSubscribeFromArrayTest.java index cd117169d2..1d5368f097 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeFromArrayTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeFromArrayTest.java @@ -29,7 +29,7 @@ Observable create(int n) { for (int i = 0; i < n; i++) { array[i] = i; } - return Observable.create(new OnSubscribeFromArray(array)); + return Observable.unsafeCreate(new OnSubscribeFromArray(array)); } @Test public void simple() { diff --git a/src/test/java/rx/internal/operators/OnSubscribeFromIterableTest.java b/src/test/java/rx/internal/operators/OnSubscribeFromIterableTest.java index 4feca72cdd..0464ed8dc0 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeFromIterableTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeFromIterableTest.java @@ -38,12 +38,12 @@ public class OnSubscribeFromIterableTest { @Test(expected = NullPointerException.class) public void testNull() { - Observable.create(new OnSubscribeFromIterable(null)); + Observable.unsafeCreate(new OnSubscribeFromIterable(null)); } @Test public void testListIterable() { - Observable observable = Observable.create(new OnSubscribeFromIterable(Arrays. asList("one", "two", "three"))); + Observable observable = Observable.unsafeCreate(new OnSubscribeFromIterable(Arrays. asList("one", "two", "three"))); @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @@ -66,7 +66,7 @@ public void testRawIterable() { public Iterator iterator() { return new Iterator() { - int i = 0; + int i; @Override public boolean hasNext() { @@ -86,7 +86,7 @@ public void remove() { } }; - Observable observable = Observable.create(new OnSubscribeFromIterable(it)); + Observable observable = Observable.unsafeCreate(new OnSubscribeFromIterable(it)); @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @@ -184,7 +184,7 @@ public void onError(Throwable e) { @Override public void onNext(Integer t) { latch.countDown(); - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); }}); assertTrue(latch.await(10, TimeUnit.SECONDS)); } @@ -237,8 +237,9 @@ public boolean hasNext() { if (count > 1) { called.set(true); return false; - } else + } else { return true; + } } @Override @@ -274,8 +275,9 @@ public boolean hasNext() { if (count > 1) { called.set(true); return false; - } else + } else { return true; + } } @Override diff --git a/src/test/java/rx/internal/operators/OnSubscribeMapTest.java b/src/test/java/rx/internal/operators/OnSubscribeMapTest.java index 4cc589a7cc..12c24cb303 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeMapTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeMapTest.java @@ -278,7 +278,7 @@ public Observable call(Object object) { }; Func1 mapper = new Func1() { - private int count = 0; + private int count; @Override public Object call(Object object) { @@ -299,7 +299,7 @@ public void call(Object object) { }; try { - Observable.create(creator).flatMap(manyMapper).map(mapper).subscribe(onNext); + Observable.unsafeCreate(creator).flatMap(manyMapper).map(mapper).subscribe(onNext); } catch (RuntimeException e) { e.printStackTrace(); throw e; diff --git a/src/test/java/rx/internal/operators/OnSubscribeReduceTest.java b/src/test/java/rx/internal/operators/OnSubscribeReduceTest.java index 9ae07b6112..7d0f1a6cc3 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeReduceTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeReduceTest.java @@ -145,7 +145,7 @@ public void testBackpressureWithInitialValue() throws InterruptedException { @Test public void testNoInitialValueDoesNotEmitMultipleTerminalEvents() { TestSubscriber ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -176,7 +176,7 @@ public Integer call(Integer a, Integer b) { @Test public void testNoInitialValueUpstreamEmitsMoreOnNextDespiteUnsubscription() { TestSubscriber ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -226,7 +226,7 @@ public void call(Throwable t) { TestSubscriber ts = TestSubscriber.create(); final RuntimeException e1 = new RuntimeException("e1"); final Throwable e2 = new RuntimeException("e2"); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OnSubscribeRefCountTest.java b/src/test/java/rx/internal/operators/OnSubscribeRefCountTest.java index e0761a576a..a824f3191b 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeRefCountTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeRefCountTest.java @@ -19,6 +19,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; +import java.lang.management.ManagementFactory; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; @@ -31,6 +32,7 @@ import rx.Observable.OnSubscribe; import rx.Observer; import rx.functions.*; +import rx.observables.ConnectableObservable; import rx.observers.*; import rx.schedulers.*; import rx.subjects.ReplaySubject; @@ -322,7 +324,7 @@ public void call() { } private Observable synchronousInterval() { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { @@ -341,7 +343,7 @@ public void call(Subscriber subscriber) { public void onlyFirstShouldSubscribeAndLastUnsubscribe() { final AtomicInteger subscriptionCount = new AtomicInteger(); final AtomicInteger unsubscriptionCount = new AtomicInteger(); - Observable observable = Observable.create(new OnSubscribe() { + Observable observable = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { subscriptionCount.incrementAndGet(); @@ -611,4 +613,155 @@ public void call(Throwable t) { assertNotNull("First subscriber didn't get the error", err1); assertNotNull("Second subscriber didn't get the error", err2); } + + Observable source; + + @Test + public void replayNoLeak() throws Exception { + System.gc(); + Thread.sleep(100); + + long start = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = Observable.fromCallable(new Callable() { + @Override + public Object call() throws Exception { + return new byte[100 * 1000 * 1000]; + } + }) + .replay(1) + .refCount(); + + source.subscribe(); + + System.gc(); + Thread.sleep(100); + + long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = null; + assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after); + } + + @Test + public void replayNoLeak2() throws Exception { + System.gc(); + Thread.sleep(100); + + long start = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = Observable.fromCallable(new Callable() { + @Override + public Object call() throws Exception { + return new byte[100 * 1000 * 1000]; + } + }).concatWith(Observable.never()) + .replay(1) + .refCount(); + + Subscription s1 = source.subscribe(); + Subscription s2 = source.subscribe(); + + s1.unsubscribe(); + s2.unsubscribe(); + + s1 = null; + s2 = null; + + System.gc(); + Thread.sleep(100); + + long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = null; + assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after); + } + + static final class ExceptionData extends Exception { + private static final long serialVersionUID = -6763898015338136119L; + + public final Object data; + + public ExceptionData(Object data) { + this.data = data; + } + } + + @Test + public void publishNoLeak() throws Exception { + System.gc(); + Thread.sleep(100); + + long start = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = Observable.fromCallable(new Callable() { + @Override + public Object call() throws Exception { + throw new ExceptionData(new byte[100 * 1000 * 1000]); + } + }) + .publish() + .refCount(); + + Action1 err = Actions.empty(); + source.subscribe(Actions.empty(), err); + + System.gc(); + Thread.sleep(100); + + long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = null; + assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after); + } + + @Test + public void publishNoLeak2() throws Exception { + System.gc(); + Thread.sleep(100); + + long start = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = Observable.fromCallable(new Callable() { + @Override + public Object call() throws Exception { + return new byte[100 * 1000 * 1000]; + } + }).concatWith(Observable.never()) + .publish() + .refCount(); + + Subscription s1 = source.test(0); + Subscription s2 = source.test(0); + + s1.unsubscribe(); + s2.unsubscribe(); + + s1 = null; + s2 = null; + + System.gc(); + Thread.sleep(100); + + long after = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + + source = null; + assertTrue(String.format("%,3d -> %,3d%n", start, after), start + 20 * 1000 * 1000 > after); + } + + @Test + public void replayIsUnsubscribed() { + ConnectableObservable co = Observable.just(1) + .replay(); + + assertTrue(((Subscription)co).isUnsubscribed()); + + Subscription s = co.connect(); + + assertFalse(((Subscription)co).isUnsubscribed()); + + s.unsubscribe(); + + assertTrue(((Subscription)co).isUnsubscribed()); + } } diff --git a/src/test/java/rx/internal/operators/OnSubscribeToMapTest.java b/src/test/java/rx/internal/operators/OnSubscribeToMapTest.java index 1ed6a542a8..97b7f33673 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeToMapTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeToMapTest.java @@ -301,7 +301,7 @@ public Map call() { public void testFactoryFailureDoesNotAllowErrorAndCompletedEmissions() { TestSubscriber> ts = TestSubscriber.create(0); final RuntimeException e = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -342,7 +342,7 @@ public void call(Throwable t) { TestSubscriber> ts = TestSubscriber.create(0); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -377,7 +377,7 @@ public Integer call(Integer t) { public void testFactoryFailureDoesNotAllowErrorThenOnNextEmissions() { TestSubscriber> ts = TestSubscriber.create(0); final RuntimeException e = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OnSubscribeToMultimapTest.java b/src/test/java/rx/internal/operators/OnSubscribeToMultimapTest.java index 91fb03c0be..a27c2ae662 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeToMultimapTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeToMultimapTest.java @@ -395,7 +395,7 @@ public Collection call(Integer k) { public void testKeySelectorFailureDoesNotAllowErrorAndCompletedEmissions() { TestSubscriber>> ts = TestSubscriber.create(0); final RuntimeException e = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -436,7 +436,7 @@ public void call(Throwable t) { TestSubscriber>> ts = TestSubscriber.create(0); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -471,7 +471,7 @@ public Integer call(Integer t) { public void testFactoryFailureDoesNotAllowErrorThenOnNextEmissions() { TestSubscriber>> ts = TestSubscriber.create(0); final RuntimeException e = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OnSubscribeUsingTest.java b/src/test/java/rx/internal/operators/OnSubscribeUsingTest.java index 68b860d97f..3a93d4cc8f 100644 --- a/src/test/java/rx/internal/operators/OnSubscribeUsingTest.java +++ b/src/test/java/rx/internal/operators/OnSubscribeUsingTest.java @@ -254,7 +254,7 @@ public Subscription call() { Func1> observableFactory = new Func1>() { @Override public Observable call(Subscription subscription) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { throw new TestException(); @@ -280,7 +280,7 @@ public void testUsingDisposesEagerlyBeforeCompletion() { final List events = new ArrayList(); Func0 resourceFactory = createResourceFactory(events); final Action0 completion = createOnCompletedAction(events); - final Action0 unsub =createUnsubAction(events); + final Action0 unsub = createUnsubAction(events); Func1> observableFactory = new Func1>() { @Override @@ -305,7 +305,7 @@ public void testUsingDoesNotDisposesEagerlyBeforeCompletion() { final List events = new ArrayList(); Func0 resourceFactory = createResourceFactory(events); final Action0 completion = createOnCompletedAction(events); - final Action0 unsub =createUnsubAction(events); + final Action0 unsub = createUnsubAction(events); Func1> observableFactory = new Func1>() { @Override diff --git a/src/test/java/rx/internal/operators/OperatorAllTest.java b/src/test/java/rx/internal/operators/OperatorAllTest.java index 264c0abc88..5ac74526fa 100644 --- a/src/test/java/rx/internal/operators/OperatorAllTest.java +++ b/src/test/java/rx/internal/operators/OperatorAllTest.java @@ -186,7 +186,7 @@ public Boolean call(Object object) { @Test public void testDoesNotEmitMultipleTerminalEvents() { TestSubscriber ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -216,7 +216,7 @@ public Boolean call(Integer t) { @Test public void testUpstreamEmitsOnNextAfterFailureWithoutCheckingSubscription() { TestSubscriber ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -236,9 +236,9 @@ public void request(long n) { boolean once = true; @Override public Boolean call(Integer t) { - if (once) + if (once) { throw new RuntimeException("boo"); - else { + } else { once = false; return true; } @@ -263,7 +263,7 @@ public void call(Throwable t) { TestSubscriber ts = TestSubscriber.create(); final RuntimeException e1 = new RuntimeException(); final Throwable e2 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OperatorAnyTest.java b/src/test/java/rx/internal/operators/OperatorAnyTest.java index 7ddbfa6a23..0eaf6b07ec 100644 --- a/src/test/java/rx/internal/operators/OperatorAnyTest.java +++ b/src/test/java/rx/internal/operators/OperatorAnyTest.java @@ -278,7 +278,7 @@ public Boolean call(Object object) { @Test public void testUpstreamEmitsOnNextAfterFailureWithoutCheckingSubscription() { TestSubscriber ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -298,9 +298,9 @@ public void request(long n) { boolean once = true; @Override public Boolean call(Integer t) { - if (once) + if (once) { throw new RuntimeException("boo"); - else { + } else { once = false; return true; } @@ -314,7 +314,7 @@ public Boolean call(Integer t) { @Test public void testUpstreamEmitsOnNextWithoutCheckingSubscription() { TestSubscriber ts = TestSubscriber.create(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -356,7 +356,7 @@ public void call(Throwable t) { TestSubscriber ts = TestSubscriber.create(); final RuntimeException e1 = new RuntimeException(); final Throwable e2 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OperatorBufferTest.java b/src/test/java/rx/internal/operators/OperatorBufferTest.java index 2a14f7797e..5cd3b2fa96 100644 --- a/src/test/java/rx/internal/operators/OperatorBufferTest.java +++ b/src/test/java/rx/internal/operators/OperatorBufferTest.java @@ -52,7 +52,7 @@ public void before() { @Test public void testComplete() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { observer.onCompleted(); @@ -69,7 +69,7 @@ public void call(Subscriber observer) { @Test public void testSkipAndCountOverlappingBuffers() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { observer.onNext("one"); @@ -94,7 +94,7 @@ public void call(Subscriber observer) { @Test public void testSkipAndCountGaplessBuffers() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { observer.onNext("one"); @@ -119,7 +119,7 @@ public void call(Subscriber observer) { @Test public void testSkipAndCountBuffersWithGaps() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { observer.onNext("one"); @@ -144,7 +144,7 @@ public void call(Subscriber observer) { @Test public void testTimedAndCount() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 10); @@ -175,7 +175,7 @@ public void call(Subscriber observer) { @Test public void testTimed() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 97); @@ -208,7 +208,7 @@ public void call(Subscriber observer) { @Test public void testObservableBasedOpenerAndCloser() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 10); @@ -220,7 +220,7 @@ public void call(Subscriber observer) { } }); - Observable openings = Observable.create(new Observable.OnSubscribe() { + Observable openings = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, new Object(), 50); @@ -232,7 +232,7 @@ public void call(Subscriber observer) { Func1> closer = new Func1>() { @Override public Observable call(Object opening) { - return Observable.create(new Observable.OnSubscribe() { + return Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, new Object(), 100); @@ -256,7 +256,7 @@ public void call(Subscriber observer) { @Test public void testObservableBasedCloser() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 10); @@ -271,7 +271,7 @@ public void call(Subscriber observer) { Func0> closer = new Func0>() { @Override public Observable call() { - return Observable.create(new Observable.OnSubscribe() { + return Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, new Object(), 100); @@ -309,7 +309,7 @@ public void testLongTimeAction() throws InterruptedException { private static class LongTimeAction implements Action1> { CountDownLatch latch; - boolean fail = false; + boolean fail; public LongTimeAction(CountDownLatch latch) { this.latch = latch; @@ -786,7 +786,7 @@ public void testProducerRequestThroughBufferWithSize1() { TestSubscriber> ts = new TestSubscriber>(); ts.requestMore(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { @@ -811,7 +811,7 @@ public void request(long n) { public void testProducerRequestThroughBufferWithSize2() { TestSubscriber> ts = new TestSubscriber>(); final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { @@ -834,7 +834,7 @@ public void testProducerRequestThroughBufferWithSize3() { TestSubscriber> ts = new TestSubscriber>(); ts.requestMore(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { @@ -858,7 +858,7 @@ public void request(long n) { public void testProducerRequestThroughBufferWithSize4() { TestSubscriber> ts = new TestSubscriber>(); final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { @@ -882,7 +882,7 @@ public void testProducerRequestOverflowThroughBufferWithSize1() { TestSubscriber> ts = new TestSubscriber>(); ts.requestMore(Long.MAX_VALUE / 2); final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { @@ -905,7 +905,7 @@ public void testProducerRequestOverflowThroughBufferWithSize2() { TestSubscriber> ts = new TestSubscriber>(); ts.requestMore(Long.MAX_VALUE / 2); final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber s) { @@ -926,7 +926,7 @@ public void request(long n) { @Test public void testProducerRequestOverflowThroughBufferWithSize3() { final AtomicLong requested = new AtomicLong(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber s) { diff --git a/src/test/java/rx/internal/operators/OperatorConcatTest.java b/src/test/java/rx/internal/operators/OperatorConcatTest.java index 99f4ae1a25..1e818accda 100644 --- a/src/test/java/rx/internal/operators/OperatorConcatTest.java +++ b/src/test/java/rx/internal/operators/OperatorConcatTest.java @@ -84,11 +84,11 @@ public void testConcatMapIterable() { final String[] l = { "a", "b", "c", "d", "e" }; Func1,List> identity = new Func1, List>() { - @Override - public List call(List t) { - return t; - } - }; + @Override + public List call(List t) { + return t; + } + }; final Observable> listObs = Observable.just(Arrays.asList(l)); final Observable concatMap = listObs.concatMapIterable(identity); @@ -115,7 +115,7 @@ public void testConcatObservableOfObservables() { final Observable odds = Observable.from(o); final Observable even = Observable.from(e); - Observable> observableOfObservables = Observable.create(new Observable.OnSubscribe>() { + Observable> observableOfObservables = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { @@ -144,7 +144,7 @@ public void testSimpleAsyncConcat() { TestObservable o1 = new TestObservable("one", "two", "three"); TestObservable o2 = new TestObservable("four", "five", "six"); - Observable.concat(Observable.create(o1), Observable.create(o2)).subscribe(observer); + Observable.concat(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2)).subscribe(observer); try { // wait for async observables to complete @@ -179,7 +179,7 @@ public void testNestedAsyncConcat() throws Throwable { final AtomicReference parent = new AtomicReference(); final CountDownLatch parentHasStarted = new CountDownLatch(1); - Observable> observableOfObservables = Observable.create(new Observable.OnSubscribe>() { + Observable> observableOfObservables = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(final Subscriber> observer) { @@ -193,12 +193,12 @@ public void run() { // emit first if (!s.isUnsubscribed()) { System.out.println("Emit o1"); - observer.onNext(Observable.create(o1)); + observer.onNext(Observable.unsafeCreate(o1)); } // emit second if (!s.isUnsubscribed()) { System.out.println("Emit o2"); - observer.onNext(Observable.create(o2)); + observer.onNext(Observable.unsafeCreate(o2)); } // wait until sometime later and emit third @@ -209,7 +209,7 @@ public void run() { } if (!s.isUnsubscribed()) { System.out.println("Emit o3"); - observer.onNext(Observable.create(o3)); + observer.onNext(Observable.unsafeCreate(o3)); } } catch (Throwable e) { @@ -285,7 +285,7 @@ public void testBlockedObservableOfObservables() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even); - Observable concatF = Observable.concat(Observable.create(observableOfObservables)); + Observable concatF = Observable.concat(Observable.unsafeCreate(observableOfObservables)); concatF.subscribe(observer); try { //Block main thread to allow observables to serve up o1. @@ -323,8 +323,8 @@ public void testConcatConcurrentWithInfinity() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2)); - Observable concatF = Observable.concat(Observable.create(observableOfObservables)); + TestObservable> observableOfObservables = new TestObservable>(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); + Observable concatF = Observable.concat(Observable.unsafeCreate(observableOfObservables)); concatF.take(50).subscribe(observer); @@ -357,13 +357,13 @@ public void testConcatNonBlockingObservables() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable> observableOfObservables = Observable.create(new Observable.OnSubscribe>() { + Observable> observableOfObservables = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { // simulate what would happen in an observable - observer.onNext(Observable.create(w1)); - observer.onNext(Observable.create(w2)); + observer.onNext(Observable.unsafeCreate(w1)); + observer.onNext(Observable.unsafeCreate(w2)); observer.onCompleted(); } @@ -408,7 +408,7 @@ public void testConcatUnsubscribe() { @SuppressWarnings("unchecked") final Observer observer = mock(Observer.class); - final Observable concat = Observable.concat(Observable.create(w1), Observable.create(w2)); + final Observable concat = Observable.concat(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); try { // Subscribe @@ -450,8 +450,8 @@ public void testConcatUnsubscribeConcurrent() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2)); - Observable concatF = Observable.concat(Observable.create(observableOfObservables)); + TestObservable> observableOfObservables = new TestObservable>(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); + Observable concatF = Observable.concat(Observable.unsafeCreate(observableOfObservables)); Subscription s1 = concatF.subscribe(observer); @@ -497,8 +497,8 @@ public boolean isUnsubscribed() { }; private final List values; - private Thread t = null; - private int count = 0; + private Thread t; + private int count; private boolean subscribed = true; private final CountDownLatch once; private final CountDownLatch okToContinue; @@ -535,20 +535,24 @@ public void call(final Subscriber observer) { public void run() { try { while (count < size && subscribed) { - if (null != values) + if (null != values) { observer.onNext(values.get(count)); - else + } else { observer.onNext(seed); + } count++; //Unblock the main thread to call unsubscribe. - if (null != once) + if (null != once) { once.countDown(); + } //Block until the main thread has called unsubscribe. - if (null != okToContinue) + if (null != okToContinue) { okToContinue.await(5, TimeUnit.SECONDS); + } } - if (subscribed) + if (subscribed) { observer.onCompleted(); + } } catch (InterruptedException e) { e.printStackTrace(); fail(e.getMessage()); @@ -614,7 +618,7 @@ public void testMultipleObservers() { @Test public void concatVeryLongObservableOfObservables() { final int n = 10000; - Observable> source = Observable.create(new OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new OnSubscribe>() { @Override public void call(Subscriber> s) { for (int i = 0; i < n; i++) { @@ -646,7 +650,7 @@ public void call(Subscriber> s) { @Test public void concatVeryLongObservableOfObservablesTakeHalf() { final int n = 10000; - Observable> source = Observable.create(new OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new OnSubscribe>() { @Override public void call(Subscriber> s) { for (int i = 0; i < n; i++) { @@ -720,7 +724,7 @@ public void testInnerBackpressureWithoutAlignedBoundaries() { // https://github.com/ReactiveX/RxJava/issues/1818 @Test public void testConcatWithNonCompliantSourceDoubleOnComplete() { - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -821,8 +825,9 @@ public void concatMapRangeAsyncLoopIssue2876() { final long startTime = System.currentTimeMillis(); for (int i = 0;; i++) { //only run this for a max of ten seconds - if (System.currentTimeMillis()-startTime > TimeUnit.SECONDS.toMillis(durationSeconds)) + if (System.currentTimeMillis() - startTime > TimeUnit.SECONDS.toMillis(durationSeconds)) { return; + } if (i % 1000 == 0) { System.out.println("concatMapRangeAsyncLoop > " + i); } diff --git a/src/test/java/rx/internal/operators/OperatorDebounceTest.java b/src/test/java/rx/internal/operators/OperatorDebounceTest.java index ae509c743d..bf3cad52d5 100644 --- a/src/test/java/rx/internal/operators/OperatorDebounceTest.java +++ b/src/test/java/rx/internal/operators/OperatorDebounceTest.java @@ -57,7 +57,7 @@ public void before() { @Test public void testDebounceWithCompleted() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. @@ -82,7 +82,7 @@ public void call(Subscriber observer) { @Test public void testDebounceNeverEmits() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { // all should be skipped since they are happening faster than the 200ms timeout @@ -111,7 +111,7 @@ public void call(Subscriber observer) { @Test public void testDebounceWithError() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { Exception error = new TestException(); diff --git a/src/test/java/rx/internal/operators/OperatorDelayTest.java b/src/test/java/rx/internal/operators/OperatorDelayTest.java index d92aac8569..8c2bfbf9f2 100644 --- a/src/test/java/rx/internal/operators/OperatorDelayTest.java +++ b/src/test/java/rx/internal/operators/OperatorDelayTest.java @@ -671,7 +671,7 @@ public void testBackpressureWithTimedDelay() { .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t) { @@ -700,7 +700,7 @@ public void testBackpressureWithSubscriptionTimedDelay() { .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t) { @@ -735,7 +735,7 @@ public Observable call(Integer i) { .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t) { @@ -776,7 +776,7 @@ public Observable call(Integer i) { .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t) { diff --git a/src/test/java/rx/internal/operators/OperatorDoOnRequestTest.java b/src/test/java/rx/internal/operators/OperatorDoOnRequestTest.java index e471376e93..d1ddd22c85 100644 --- a/src/test/java/rx/internal/operators/OperatorDoOnRequestTest.java +++ b/src/test/java/rx/internal/operators/OperatorDoOnRequestTest.java @@ -99,7 +99,7 @@ public void dontRequestIfDownstreamRequestsLate() { final AtomicReference producer = new AtomicReference(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { t.setProducer(new Producer() { diff --git a/src/test/java/rx/internal/operators/OperatorDoOnSubscribeTest.java b/src/test/java/rx/internal/operators/OperatorDoOnSubscribeTest.java index 4765a5dfe3..2f60a9e8c5 100644 --- a/src/test/java/rx/internal/operators/OperatorDoOnSubscribeTest.java +++ b/src/test/java/rx/internal/operators/OperatorDoOnSubscribeTest.java @@ -76,7 +76,7 @@ public void testDoOnUnSubscribeWorksWithRefCount() throws Exception { final AtomicInteger countBefore = new AtomicInteger(); final AtomicInteger countAfter = new AtomicInteger(); final AtomicReference> sref = new AtomicReference>(); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { diff --git a/src/test/java/rx/internal/operators/OperatorFlatMapTest.java b/src/test/java/rx/internal/operators/OperatorFlatMapTest.java index 61805d7372..286a2a03bc 100644 --- a/src/test/java/rx/internal/operators/OperatorFlatMapTest.java +++ b/src/test/java/rx/internal/operators/OperatorFlatMapTest.java @@ -468,7 +468,7 @@ public Observable call(Integer t) { } } } - @Test(timeout = 30000) + @Test(timeout = 60000) public void flatMapRangeMixedAsyncLoop() { for (int i = 0; i < 2000; i++) { if (i % 10 == 0) { diff --git a/src/test/java/rx/internal/operators/OperatorGroupByTest.java b/src/test/java/rx/internal/operators/OperatorGroupByTest.java index 2b9328795d..b9c2bc6ece 100644 --- a/src/test/java/rx/internal/operators/OperatorGroupByTest.java +++ b/src/test/java/rx/internal/operators/OperatorGroupByTest.java @@ -39,8 +39,10 @@ import rx.functions.*; import rx.internal.util.*; import rx.observables.GroupedObservable; +import rx.observers.AssertableSubscriber; import rx.observers.TestSubscriber; import rx.schedulers.Schedulers; +import rx.subjects.PublishSubject; public class OperatorGroupByTest { @@ -189,7 +191,7 @@ public void testGroupedEventStream() throws Throwable { final int count = 100; final int groupCount = 2; - Observable es = Observable.create(new Observable.OnSubscribe() { + Observable es = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -602,7 +604,7 @@ public void call(String s) { public void testFirstGroupsCompleteAndParentSlowToThenEmitFinalGroupsAndThenComplete() throws InterruptedException { final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete final ArrayList results = new ArrayList(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { @@ -680,7 +682,7 @@ public void testFirstGroupsCompleteAndParentSlowToThenEmitFinalGroupsWhichThenSu System.err.println("----------------------------------------------------------------------------------------------"); final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete final ArrayList results = new ArrayList(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { @@ -771,7 +773,7 @@ public void call(String s) { public void testFirstGroupsCompleteAndParentSlowToThenEmitFinalGroupsWhichThenObservesOnAndDelaysAndThenCompletes() throws InterruptedException { final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete final ArrayList results = new ArrayList(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { @@ -847,7 +849,7 @@ public void call(String s) { @Test public void testGroupsWithNestedSubscribeOn() throws InterruptedException { final ArrayList results = new ArrayList(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { @@ -903,7 +905,7 @@ public void call(String s) { @Test public void testGroupsWithNestedObserveOn() throws InterruptedException { final ArrayList results = new ArrayList(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { @@ -963,7 +965,7 @@ Observable ASYNC_INFINITE_OBSERVABLE_OF_EVENT(final int numGroups, final } Observable SYNC_INFINITE_OBSERVABLE_OF_EVENT(final int numGroups, final AtomicInteger subscribeCounter, final AtomicInteger sentEventCounter) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber op) { @@ -1142,7 +1144,7 @@ public Observable call(final GroupedObservable g) { System.out.println("-----------> NEXT: " + g.getKey()); return g.take(2).map(new Func1() { - int count = 0; + int count; @Override public String call(String v) { @@ -1264,7 +1266,7 @@ public void call() { }).observeOn(Schedulers.computation()).map(new Func1() { - int c = 0; + int c; @Override public String call(Integer l) { @@ -1375,7 +1377,7 @@ public void call(String s) { @Test public void testGroupByUnsubscribe() { final Subscription s = mock(Subscription.class); - Observable o = Observable.create( + Observable o = Observable.unsafeCreate( new OnSubscribe() { @Override public void call(Subscriber subscriber) { @@ -1419,7 +1421,7 @@ public void onNext(GroupedObservable o) { } } }); - Observable.create( + Observable.unsafeCreate( new OnSubscribe() { @Override public void call(Subscriber subscriber) { @@ -1481,7 +1483,7 @@ public void onError(Throwable e) { public void onNext(Integer t) { System.out.println(t); //provoke possible request overflow - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); }}); assertTrue(completed.get()); } @@ -1812,10 +1814,10 @@ public Integer call(Integer pair) { @Test public void mapFactoryEvictionWorks() { - Func1 keySelector = new Func1 (){ + Func1 keySelector = new Func1 () { @Override public Integer call(Integer t) { - return t /10; + return t / 10; }}; Func1 elementSelector = UtilityFunctions.identity(); final List evictedKeys = new ArrayList(); @@ -1831,7 +1833,7 @@ public Map call(final Action1 evicted) { return new ConcurrentHashMap() { private static final long serialVersionUID = -7519109652858021153L; - Integer lastKey = null; + Integer lastKey; @Override public Object put(Integer key, Object value) { @@ -1867,7 +1869,7 @@ public String call(Integer x) { .map(new Func1() { @Override public String call(Integer x) { - return (x /10) + ":" + x; + return (x / 10) + ":" + x; } }) .toList().toBlocking().single(); @@ -1876,10 +1878,10 @@ public String call(Integer x) { private static final Func1 EVICTING_MAP_ELEMENT_SELECTOR = UtilityFunctions.identity(); - private static final Func1 EVICTING_MAP_KEY_SELECTOR = new Func1 (){ + private static final Func1 EVICTING_MAP_KEY_SELECTOR = new Func1 () { @Override public Integer call(Integer t) { - return t /10; + return t / 10; }}; @Test @@ -1969,7 +1971,7 @@ public String call(Integer x) { .map(new Func1() { @Override public String call(Integer x) { - return (x /10) + ":" + x; + return (x / 10) + ":" + x; } }) .toList().toBlocking().single(); @@ -1996,7 +1998,7 @@ public void testEvictingMapFactoryIfMapCreateThrowsRuntimeExceptionThenErrorEmit ts.assertError(exception); } - @Test(expected=OnErrorNotImplementedException.class) + @Test(expected = OnErrorNotImplementedException.class) public void testEvictingMapFactoryIfMapCreateThrowsFatalErrorThenSubscribeThrows() { final OnErrorNotImplementedException exception = new OnErrorNotImplementedException("boo", new RuntimeException()); Func1, Map> mapFactory = createMapFactoryThatThrowsOnCreate(exception); @@ -2017,4 +2019,260 @@ public Map call(Action1 t) { throw exception; }}; } + + @Test + public void outerConsumedInABoundedManner() { + final int[] counter = { 0 }; + + Observable.range(1, 10000) + .doOnRequest(new Action1() { + @Override + public void call(Long v) { + counter[0] += v; + } + }) + .groupBy(new Func1() { + @Override + public Integer call(Integer v) { + return 1; + } + }) + .flatMap(new Func1, Observable>() { + @Override + public Observable call(GroupedObservable v) { + return v; + } + }) + .test(0); + + int c = counter[0]; + assertTrue("" + c, c > 0); + assertTrue("" + c, c < 10000); + } + + @Test + public void groupByEvictingMapFactoryThrows() { + final RuntimeException ex = new RuntimeException("boo"); + Func1, Map> evictingMapFactory = // + new Func1, Map>() { + + @Override + public Map call(final Action1 notify) { + throw ex; + } + }; + Observable.just(1) + .groupBy(UtilityFunctions.identity(), UtilityFunctions.identity(), 16, true, evictingMapFactory) + .test() + .assertNoValues() + .assertError(ex); + } + + @Test + public void groupByEvictingMapFactoryExpiryCompletesGroupedFlowable() { + final List completed = new CopyOnWriteArrayList(); + Func1, Map> evictingMapFactory = createEvictingMapFactorySynchronousOnly(1); + PublishSubject subject = PublishSubject.create(); + AssertableSubscriber ts = subject + .groupBy(UtilityFunctions.identity(), UtilityFunctions.identity(), 16, true, evictingMapFactory) + .flatMap(addCompletedKey(completed)) + .test(); + subject.onNext(1); + subject.onNext(2); + subject.onNext(3); + ts.assertValues(1, 2, 3) + .assertNoTerminalEvent(); + assertEquals(Arrays.asList(1, 2), completed); + //ensure coverage of the code that clears the evicted queue + subject.onCompleted(); + ts.assertCompleted(); + ts.assertValueCount(3); + } + + private static final Func1 mod5 = new Func1() { + + @Override + public Integer call(Integer n) { + return n % 5; + } + }; + + @Test + public void groupByEvictingMapFactoryWithExpiringGuavaCacheDemonstrationCodeForUseInJavadoc() { + //javadoc will be a version of this using lambdas and without assertions + final List completed = new CopyOnWriteArrayList(); + //size should be less than 5 to notice the effect + Func1, Map> evictingMapFactory = createEvictingMapFactoryGuava(3); + int numValues = 1000; + Observable.range(1, numValues) + .groupBy(mod5, UtilityFunctions.identity(), 16, true, evictingMapFactory) + .flatMap(addCompletedKey(completed)) + .test() + .assertCompleted() + .assertValueCount(numValues); + //the exact eviction behaviour of the guava cache is not specified so we make some approximate tests + assertTrue(completed.size() > numValues * 0.9); + } + + @Test + public void groupByEvictingMapFactoryEvictionQueueClearedOnErrorCoverageOnly() { + Func1, Map> evictingMapFactory = createEvictingMapFactorySynchronousOnly(1); + PublishSubject subject = PublishSubject.create(); + AssertableSubscriber ts = subject + .groupBy(UtilityFunctions.identity(), UtilityFunctions.identity(), 16, true, evictingMapFactory) + .flatMap(new Func1, Observable>() { + @Override + public Observable call(GroupedObservable g) { + return g; + } + }) + .test(); + RuntimeException ex = new RuntimeException(); + //ensure coverage of the code that clears the evicted queue + subject.onError(ex); + ts.assertNoValues() + .assertError(ex); + } + + private static Func1, Observable> addCompletedKey( + final List completed) { + return new Func1, Observable>() { + @Override + public Observable call(final GroupedObservable g) { + return g.doOnCompleted(new Action0() { + @Override + public void call() { + completed.add(g.getKey()); + } + }); + } + }; + } + + //not thread safe + private static final class SingleThreadEvictingHashMap implements Map { + + private final List list = new ArrayList(); + private final Map map = new HashMap(); + private final int maxSize; + private final Action1 evictedListener; + + SingleThreadEvictingHashMap(int maxSize, Action1 evictedListener) { + this.maxSize = maxSize; + this.evictedListener = evictedListener; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public V get(Object key) { + return map.get(key); + } + + @Override + public V put(K key, V value) { + list.remove(key); + V v; + if (maxSize > 0 && list.size() == maxSize) { + //remove first + K k = list.get(0); + list.remove(0); + v = map.remove(k); + } else { + v = null; + } + list.add(key); + V result = map.put(key, value); + if (v != null) { + evictedListener.call(v); + } + return result; + } + + @Override + public V remove(Object key) { + list.remove(key); + return map.remove(key); + } + + @Override + public void putAll(Map m) { + for (Entry entry: m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + list.clear(); + map.clear(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } + } + + private static Func1, Map> createEvictingMapFactoryGuava(final int maxSize) { + Func1, Map> evictingMapFactory = // + new Func1, Map>() { + + @Override + public Map call(final Action1 notify) { + return CacheBuilder.newBuilder() // + .maximumSize(maxSize) // + .removalListener(new RemovalListener() { + @Override + public void onRemoval(RemovalNotification notification) { + notify.call(notification.getValue()); + }}) + . build() + .asMap(); + }}; + return evictingMapFactory; + } + + private static Func1, Map> createEvictingMapFactorySynchronousOnly(final int maxSize) { + Func1, Map> evictingMapFactory = // + new Func1, Map>() { + + @Override + public Map call(final Action1 notify) { + return new SingleThreadEvictingHashMap(maxSize, new Action1() { + @Override + public void call(Object object) { + notify.call(object); + }}); + }}; + return evictingMapFactory; + } } diff --git a/src/test/java/rx/internal/operators/OperatorMaterializeTest.java b/src/test/java/rx/internal/operators/OperatorMaterializeTest.java index 1ddaa6c45d..a55758d25c 100644 --- a/src/test/java/rx/internal/operators/OperatorMaterializeTest.java +++ b/src/test/java/rx/internal/operators/OperatorMaterializeTest.java @@ -29,9 +29,12 @@ import rx.Notification; import rx.Observable; import rx.Subscriber; +import rx.TestUtil; +import rx.functions.Action0; import rx.functions.Action1; import rx.observers.TestSubscriber; import rx.schedulers.Schedulers; +import rx.subjects.PublishSubject; public class OperatorMaterializeTest { @@ -43,7 +46,7 @@ public void testMaterialize1() { "three"); TestObserver Observer = new TestObserver(); - Observable> m = Observable.create(o1).materialize(); + Observable> m = Observable.unsafeCreate(o1).materialize(); m.subscribe(Observer); try { @@ -69,7 +72,7 @@ public void testMaterialize2() { final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); TestObserver Observer = new TestObserver(); - Observable> m = Observable.create(o1).materialize(); + Observable> m = Observable.unsafeCreate(o1).materialize(); m.subscribe(Observer); try { @@ -94,7 +97,7 @@ public void testMaterialize2() { public void testMultipleSubscribes() throws InterruptedException, ExecutionException { final TestAsyncErrorObservable o = new TestAsyncErrorObservable("one", "two", null, "three"); - Observable> m = Observable.create(o).materialize(); + Observable> m = Observable.unsafeCreate(o).materialize(); assertEquals(3, m.toList().toBlocking().toFuture().get().size()); assertEquals(3, m.toList().toBlocking().toFuture().get().size()); @@ -201,10 +204,37 @@ public void testUnsubscribeJustBeforeCompletionNotificationShouldPreventThatNoti ts.assertUnsubscribed(); } + @Test + public void testConcurrency() { + for (int i = 0; i < 1000; i++) { + final TestSubscriber> ts = TestSubscriber.create(0); + final PublishSubject ps = PublishSubject.create(); + Action0 publishAction = new Action0() { + @Override + public void call() { + ps.onCompleted(); + } + }; + + Action0 requestAction = new Action0() { + @Override + public void call() { + ts.requestMore(1); + } + }; + + ps.materialize().subscribe(ts); + TestUtil.race(publishAction, requestAction); + ts.assertValueCount(1); + ts.assertTerminalEvent(); + ts.assertNoErrors(); + } + } + private static class TestObserver extends Subscriber> { - boolean onCompleted = false; - boolean onError = false; + boolean onCompleted; + boolean onError; List> notifications = new Vector>(); @Override diff --git a/src/test/java/rx/internal/operators/OperatorMergeDelayErrorTest.java b/src/test/java/rx/internal/operators/OperatorMergeDelayErrorTest.java index 7943e30dee..05326cf75a 100644 --- a/src/test/java/rx/internal/operators/OperatorMergeDelayErrorTest.java +++ b/src/test/java/rx/internal/operators/OperatorMergeDelayErrorTest.java @@ -55,8 +55,8 @@ public void before() { @Test public void testErrorDelayed1() { - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("one", "two", "three")); Observable m = Observable.mergeDelayError(o1, o2); m.subscribe(stringObserver); @@ -76,10 +76,10 @@ public void testErrorDelayed1() { @Test public void testErrorDelayed2() { - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null)); - final Observable o4 = Observable.create(new TestErrorObservable("nine")); + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o3 = Observable.unsafeCreate(new TestErrorObservable("seven", "eight", null)); + final Observable o4 = Observable.unsafeCreate(new TestErrorObservable("nine")); Observable m = Observable.mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); @@ -101,10 +101,10 @@ public void testErrorDelayed2() { @Test public void testErrorDelayed3() { - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six")); - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null)); - final Observable o4 = Observable.create(new TestErrorObservable("nine")); + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("four", "five", "six")); + final Observable o3 = Observable.unsafeCreate(new TestErrorObservable("seven", "eight", null)); + final Observable o4 = Observable.unsafeCreate(new TestErrorObservable("nine")); Observable m = Observable.mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); @@ -124,10 +124,10 @@ public void testErrorDelayed3() { @Test public void testErrorDelayed4() { - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six")); - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight")); - final Observable o4 = Observable.create(new TestErrorObservable("nine", null)); + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("four", "five", "six")); + final Observable o3 = Observable.unsafeCreate(new TestErrorObservable("seven", "eight")); + final Observable o4 = Observable.unsafeCreate(new TestErrorObservable("nine", null)); Observable m = Observable.mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); @@ -153,7 +153,7 @@ public void testErrorDelayed4WithThreading() { // throw the error at the very end so no onComplete will be called after it final TestAsyncErrorObservable o4 = new TestAsyncErrorObservable("nine", null); - Observable m = Observable.mergeDelayError(Observable.create(o1), Observable.create(o2), Observable.create(o3), Observable.create(o4)); + Observable m = Observable.mergeDelayError(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2), Observable.unsafeCreate(o3), Observable.unsafeCreate(o4)); m.subscribe(stringObserver); try { @@ -180,8 +180,8 @@ public void testErrorDelayed4WithThreading() { @Test public void testCompositeErrorDelayed1() { - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null)); + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("one", "two", null)); Observable m = Observable.mergeDelayError(o1, o2); m.subscribe(stringObserver); @@ -200,8 +200,8 @@ public void testCompositeErrorDelayed1() { @Test public void testCompositeErrorDelayed2() { - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null)); + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("one", "two", null)); Observable m = Observable.mergeDelayError(o1, o2); CaptureObserver w = new CaptureObserver(); @@ -223,10 +223,10 @@ public void testCompositeErrorDelayed2() { @Test public void testMergeObservableOfObservables() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); - Observable> observableOfObservables = Observable.create(new Observable.OnSubscribe>() { + Observable> observableOfObservables = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { @@ -247,8 +247,8 @@ public void call(Subscriber> observer) { @Test public void testMergeArray() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); Observable m = Observable.mergeDelayError(o1, o2); m.subscribe(stringObserver); @@ -260,8 +260,8 @@ public void testMergeArray() { @Test public void testMergeList() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); List> listOfObservables = new ArrayList>(); listOfObservables.add(o1); listOfObservables.add(o2); @@ -277,8 +277,8 @@ public void testMergeList() { // This is pretty much a clone of testMergeList but with the overloaded MergeDelayError for Iterables @Test public void mergeIterable() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); List> listOfObservables = new ArrayList>(); listOfObservables.add(o1); listOfObservables.add(o2); @@ -296,7 +296,7 @@ public void testMergeArrayWithThreading() { final TestASynchronousObservable o1 = new TestASynchronousObservable(); final TestASynchronousObservable o2 = new TestASynchronousObservable(); - Observable m = Observable.mergeDelayError(Observable.create(o1), Observable.create(o2)); + Observable m = Observable.mergeDelayError(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2)); m.subscribe(stringObserver); try { @@ -455,7 +455,7 @@ public void onNext(String args) { } @Test public void testMergeSourceWhichDoesntPropagateExceptionBack() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { try { @@ -527,11 +527,11 @@ public void testErrorInParentObservableDelayed() throws Exception { for (int i = 0; i < 50; i++) { final TestASynchronous1sDelayedObservable o1 = new TestASynchronous1sDelayedObservable(); final TestASynchronous1sDelayedObservable o2 = new TestASynchronous1sDelayedObservable(); - Observable> parentObservable = Observable.create(new Observable.OnSubscribe>() { + Observable> parentObservable = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> op) { - op.onNext(Observable.create(o1)); - op.onNext(Observable.create(o2)); + op.onNext(Observable.unsafeCreate(o1)); + op.onNext(Observable.unsafeCreate(o2)); op.onError(new NullPointerException("throwing exception in parent")); } }); diff --git a/src/test/java/rx/internal/operators/OperatorMergeMaxConcurrentTest.java b/src/test/java/rx/internal/operators/OperatorMergeMaxConcurrentTest.java index 56aa690b84..5be03a3b6f 100644 --- a/src/test/java/rx/internal/operators/OperatorMergeMaxConcurrentTest.java +++ b/src/test/java/rx/internal/operators/OperatorMergeMaxConcurrentTest.java @@ -72,7 +72,7 @@ public void testMaxConcurrent() { for (int i = 0; i < observableCount; i++) { SubscriptionCheckObservable sco = new SubscriptionCheckObservable(subscriptionCount, maxConcurrent); scos.add(sco); - os.add(Observable.create(sco)); + os.add(Observable.unsafeCreate(sco)); } Iterator iter = Observable.merge(os, maxConcurrent).toBlocking().toIterable().iterator(); @@ -92,7 +92,7 @@ private static class SubscriptionCheckObservable implements Observable.OnSubscri private final AtomicInteger subscriptionCount; private final int maxConcurrent; - volatile boolean failed = false; + volatile boolean failed; SubscriptionCheckObservable(AtomicInteger subscriptionCount, int maxConcurrent) { this.subscriptionCount = subscriptionCount; @@ -197,7 +197,7 @@ public void testSimpleAsyncLoop() { testSimpleAsync(); } } - @Test(timeout = 10000) + @Test(timeout = 30000) public void testSimpleAsync() { for (int i = 1; i < 50; i++) { TestSubscriber ts = new TestSubscriber(); @@ -217,7 +217,7 @@ public void testSimpleAsync() { assertEquals(expected, actual); } } - @Test(timeout = 10000) + @Test(timeout = 30000) public void testSimpleOneLessAsyncLoop() { int max = 200; if (PlatformDependent.isAndroid()) { @@ -227,7 +227,7 @@ public void testSimpleOneLessAsyncLoop() { testSimpleOneLessAsync(); } } - @Test(timeout = 10000) + @Test(timeout = 30000) public void testSimpleOneLessAsync() { long t = System.currentTimeMillis(); for (int i = 2; i < 50; i++) { diff --git a/src/test/java/rx/internal/operators/OperatorMergeTest.java b/src/test/java/rx/internal/operators/OperatorMergeTest.java index 746f794158..bc13673f5e 100644 --- a/src/test/java/rx/internal/operators/OperatorMergeTest.java +++ b/src/test/java/rx/internal/operators/OperatorMergeTest.java @@ -53,10 +53,10 @@ public void before() { @Test public void testMergeObservableOfObservables() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); - Observable> observableOfObservables = Observable.create(new Observable.OnSubscribe>() { + Observable> observableOfObservables = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { @@ -77,8 +77,8 @@ public void call(Subscriber> observer) { @Test public void testMergeArray() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); Observable m = Observable.merge(o1, o2); m.subscribe(stringObserver); @@ -90,8 +90,8 @@ public void testMergeArray() { @Test public void testMergeList() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); + final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); + final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); List> listOfObservables = new ArrayList>(); listOfObservables.add(o1); listOfObservables.add(o2); @@ -110,7 +110,7 @@ public void testUnSubscribeObservableOfObservables() throws InterruptedException final AtomicBoolean unsubscribed = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(final Subscriber> observer) { @@ -172,7 +172,7 @@ public void testMergeArrayWithThreading() { final TestASynchronousObservable o1 = new TestASynchronousObservable(); final TestASynchronousObservable o2 = new TestASynchronousObservable(); - Observable m = Observable.merge(Observable.create(o1), Observable.create(o2)); + Observable m = Observable.merge(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2)); TestSubscriber ts = new TestSubscriber(stringObserver); m.subscribe(ts); @@ -195,7 +195,7 @@ public void testSynchronizationOfMultipleSequences() throws Throwable { final AtomicInteger concurrentCounter = new AtomicInteger(); final AtomicInteger totalCounter = new AtomicInteger(); - Observable m = Observable.merge(Observable.create(o1), Observable.create(o2)); + Observable m = Observable.merge(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2)); m.subscribe(new Subscriber() { @Override @@ -262,8 +262,8 @@ public void onNext(String v) { @Test public void testError1() { // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); // we expect to lose all of these since o1 is done first and fails + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("four", null, "six")); // we expect to lose "six" + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("one", "two", "three")); // we expect to lose all of these since o1 is done first and fails Observable m = Observable.merge(o1, o2); m.subscribe(stringObserver); @@ -284,10 +284,10 @@ public void testError1() { @Test public void testError2() { // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));// we expect to lose all of these since o2 is done first and fails - final Observable o4 = Observable.create(new TestErrorObservable("nine"));// we expect to lose all of these since o2 is done first and fails + final Observable o1 = Observable.unsafeCreate(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.unsafeCreate(new TestErrorObservable("four", null, "six")); // we expect to lose "six" + final Observable o3 = Observable.unsafeCreate(new TestErrorObservable("seven", "eight", null));// we expect to lose all of these since o2 is done first and fails + final Observable o4 = Observable.unsafeCreate(new TestErrorObservable("nine"));// we expect to lose all of these since o2 is done first and fails Observable m = Observable.merge(o1, o2, o3, o4); m.subscribe(stringObserver); @@ -308,7 +308,7 @@ public void testError2() { @Test public void testThrownErrorHandling() { TestSubscriber ts = new TestSubscriber(); - Observable o1 = Observable.create(new OnSubscribe() { + Observable o1 = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -460,7 +460,7 @@ public void testEarlyUnsubscribe() { } private Observable createObservableOf5IntervalsOf1SecondIncrementsWithSubscriptionHook(final Scheduler scheduler, final AtomicBoolean unsubscribed) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -499,7 +499,7 @@ public void testConcurrency() { @Test public void testConcurrencyWithSleeping() { - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber s) { @@ -543,7 +543,7 @@ public void call() { @Test public void testConcurrencyWithBrokenOnCompleteContract() { - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber s) { @@ -667,13 +667,14 @@ public void testBackpressureDownstreamWithConcurrentStreams() throws Interrupted TestSubscriber testSubscriber = new TestSubscriber() { @Override public void onNext(Integer t) { - if (t < 100) + if (t < 100) { try { // force a slow consumer Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } + } // System.err.println("testSubscriber received => " + t + " on thread " + Thread.currentThread()); super.onNext(t); } @@ -708,13 +709,14 @@ public Observable call(Integer t1) { TestSubscriber testSubscriber = new TestSubscriber() { @Override public void onNext(Integer t) { - if (t < 100) + if (t < 100) { try { // force a slow consumer Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } + } // System.err.println("testSubscriber received => " + t + " on thread " + Thread.currentThread()); super.onNext(t); } @@ -759,17 +761,18 @@ public Observable call(Integer t1) { }); TestSubscriber testSubscriber = new TestSubscriber() { - int i = 0; + int i; @Override public void onNext(Integer t) { - if (i++ < 400) + if (i++ < 400) { try { // force a slow consumer Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } + } // System.err.println("testSubscriber received => " + t + " on thread " + Thread.currentThread()); super.onNext(t); } @@ -804,7 +807,7 @@ public void mergeWithNullValues() { public void mergeWithTerminalEventAfterUnsubscribe() { System.out.println("mergeWithTerminalEventAfterUnsubscribe"); TestSubscriber ts = new TestSubscriber(); - Observable bad = Observable.create(new OnSubscribe() { + Observable bad = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -981,7 +984,7 @@ public void mergeManyAsyncSingle() { @Override public Observable call(final Integer i) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -1197,11 +1200,39 @@ public void onError(Throwable e) { public void onNext(Integer t) { latch.countDown(); request(2); - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); }}); assertTrue(latch.await(10, TimeUnit.SECONDS)); } + @Test + public void testConcurrentMergeInnerError() { + for (int i = 0; i < 1000; i++) { + final TestSubscriber ts = TestSubscriber.create(); + final PublishSubject ps1 = PublishSubject.create(); + final PublishSubject ps2 = PublishSubject.create(); + final Exception error = new NullPointerException(); + Action0 action1 = new Action0() { + @Override + public void call() { + ps1.onNext(1); + ps1.onCompleted(); + } + }; + Action0 action2 = new Action0() { + @Override + public void call() { + ps2.onError(error); + } + }; + + Observable.mergeDelayError(ps1, ps2).subscribe(ts); + TestUtil.race(action1, action2); + ts.assertTerminalEvent(); + ts.assertError(error); + } + } + private static Action1 printCount() { return new Action1() { long count; @@ -1311,7 +1342,7 @@ public void onNext(Integer t) { @Test public void testUnboundedDefaultConcurrency() { List> os = new ArrayList>(); - for(int i=0; i < 2000; i++) { + for (int i = 0; i < 2000; i++) { os.add(Observable.never()); } os.add(Observable.range(0, 100)); @@ -1326,7 +1357,7 @@ public void testUnboundedDefaultConcurrency() { @Test public void testConcurrencyLimit() { List> os = new ArrayList>(); - for(int i=0; i < 2000; i++) { + for (int i = 0; i < 2000; i++) { os.add(Observable.never()); } os.add(Observable.range(0, 100)); diff --git a/src/test/java/rx/internal/operators/OperatorObserveOnTest.java b/src/test/java/rx/internal/operators/OperatorObserveOnTest.java index c5a29e04a2..df2e21e5c8 100644 --- a/src/test/java/rx/internal/operators/OperatorObserveOnTest.java +++ b/src/test/java/rx/internal/operators/OperatorObserveOnTest.java @@ -526,7 +526,7 @@ public boolean hasNext() { @Test public void testQueueFullEmitsError() { final CountDownLatch latch = new CountDownLatch(1); - Observable observable = Observable.create(new OnSubscribe() { + Observable observable = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber o) { diff --git a/src/test/java/rx/internal/operators/OperatorOnBackpressureBufferTest.java b/src/test/java/rx/internal/operators/OperatorOnBackpressureBufferTest.java index aef7303848..b76405b12c 100644 --- a/src/test/java/rx/internal/operators/OperatorOnBackpressureBufferTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnBackpressureBufferTest.java @@ -129,7 +129,7 @@ public void call() { int size = ts.getOnNextEvents().size(); assertTrue(size <= 150); // will get up to 50 more - assertTrue(ts.getOnNextEvents().get(size-1) == size-1); + assertTrue(ts.getOnNextEvents().get(size - 1) == size - 1); assertTrue(s.isUnsubscribed()); } @@ -227,7 +227,7 @@ public void onNext(T t) { }); } - static final Observable infinite = Observable.create(new OnSubscribe() { + static final Observable infinite = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { diff --git a/src/test/java/rx/internal/operators/OperatorOnBackpressureDropTest.java b/src/test/java/rx/internal/operators/OperatorOnBackpressureDropTest.java index 947af54695..07174570b5 100644 --- a/src/test/java/rx/internal/operators/OperatorOnBackpressureDropTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnBackpressureDropTest.java @@ -121,7 +121,7 @@ public void onError(Throwable e) { public void onNext(Long t) { count.incrementAndGet(); //cause overflow of requested if not handled properly in onBackpressureDrop operator - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); }}); assertEquals(n, count.get()); } @@ -151,7 +151,7 @@ public void testOnDropMethodIsCalled() { final List list = new ArrayList(); // request 0 TestSubscriber ts = TestSubscriber.create(0); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -180,7 +180,7 @@ public void call(Integer t) { public void testUpstreamEmitsOnCompletedAfterFailureWithoutCheckingSubscription() { TestSubscriber ts = TestSubscriber.create(0); final RuntimeException e = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -221,7 +221,7 @@ public void call(Throwable t) { TestSubscriber ts = TestSubscriber.create(0); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -255,7 +255,7 @@ public void call(Integer t) { public void testUpstreamEmitsOnNextAfterFailureWithoutCheckingSubscription() { TestSubscriber ts = TestSubscriber.create(0); final RuntimeException e = new RuntimeException(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber sub) { @@ -291,7 +291,7 @@ public void call(Long n) { } }; - static final Observable infinite = Observable.create(new OnSubscribe() { + static final Observable infinite = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -304,11 +304,11 @@ public void call(Subscriber s) { }); private static final Observable range(final long n) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { - for (long i=0;i < n;i++) { + for (long i = 0;i < n;i++) { if (s.isUnsubscribed()) { break; } diff --git a/src/test/java/rx/internal/operators/OperatorOnBackpressureLatestTest.java b/src/test/java/rx/internal/operators/OperatorOnBackpressureLatestTest.java index cbcd3d26f8..a226159529 100644 --- a/src/test/java/rx/internal/operators/OperatorOnBackpressureLatestTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnBackpressureLatestTest.java @@ -124,7 +124,7 @@ public void onNext(Integer t) { if (rnd.nextDouble() < 0.001) { try { Thread.sleep(1); - } catch(InterruptedException ex) { + } catch (InterruptedException ex) { ex.printStackTrace(); } } diff --git a/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaFunctionTest.java b/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaFunctionTest.java index d4ec5d756d..e127334588 100644 --- a/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaFunctionTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaFunctionTest.java @@ -44,7 +44,7 @@ public class OperatorOnErrorResumeNextViaFunctionTest { @Test public void testResumeNextWithSynchronousExecution() { final AtomicReference receivedException = new AtomicReference(); - Observable w = Observable.create(new Observable.OnSubscribe() { + Observable w = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { @@ -94,7 +94,7 @@ public Observable call(Throwable t1) { } }; - Observable observable = Observable.create(w).onErrorResumeNext(resume); + Observable observable = Observable.unsafeCreate(w).onErrorResumeNext(resume); @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @@ -131,7 +131,7 @@ public Observable call(Throwable t1) { } }; - Observable observable = Observable.create(w).onErrorResumeNext(resume); + Observable observable = Observable.unsafeCreate(w).onErrorResumeNext(resume); @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @@ -242,8 +242,9 @@ public void testMapResumeAsyncNext() { w = w.map(new Func1() { @Override public String call(String s) { - if ("fail".equals(s)) + if ("fail".equals(s)) { throw new RuntimeException("Forced Failure"); + } System.out.println("BadMapper:" + s); return s; } @@ -277,7 +278,7 @@ private static class TestObservable implements Observable.OnSubscribe { final Subscription s; final String[] values; - Thread t = null; + Thread t; public TestObservable(Subscription s, String... values) { this.s = s; @@ -326,7 +327,7 @@ public Observable call(Throwable t1) { }) .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t1) { diff --git a/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaObservableTest.java b/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaObservableTest.java index 418324f7f4..41f910c2fc 100644 --- a/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaObservableTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnErrorResumeNextViaObservableTest.java @@ -42,7 +42,7 @@ public void testResumeNext() { Subscription s = mock(Subscription.class); // Trigger failure on second element TestObservable f = new TestObservable(s, "one", "fail", "two", "three"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); Observable resume = Observable.just("twoResume", "threeResume"); Observable observable = w.onErrorResumeNext(resume); @@ -72,15 +72,16 @@ public void testMapResumeAsyncNext() { Observable w = Observable.just("one", "fail", "two", "three", "fail"); // Resume Observable is async TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); - Observable resume = Observable.create(f); + Observable resume = Observable.unsafeCreate(f); // Introduce map function that fails intermittently (Map does not prevent this when the observer is a // rx.operator incl onErrorResumeNextViaObservable) w = w.map(new Func1() { @Override public String call(String s) { - if ("fail".equals(s)) + if ("fail".equals(s)) { throw new RuntimeException("Forced Failure"); + } System.out.println("BadMapper:" + s); return s; } @@ -109,7 +110,7 @@ public String call(String s) { @Test public void testResumeNextWithFailedOnSubscribe() { - Observable testObservable = Observable.create(new OnSubscribe() { + Observable testObservable = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { @@ -131,7 +132,7 @@ public void call(Subscriber t1) { @Test public void testResumeNextWithFailedOnSubscribeAsync() { - Observable testObservable = Observable.create(new OnSubscribe() { + Observable testObservable = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { @@ -158,7 +159,7 @@ private static class TestObservable implements Observable.OnSubscribe { final Subscription s; final String[] values; - Thread t = null; + Thread t; public TestObservable(Subscription s, String... values) { this.s = s; @@ -176,8 +177,9 @@ public void run() { try { System.out.println("running TestObservable thread"); for (String s : values) { - if ("fail".equals(s)) + if ("fail".equals(s)) { throw new RuntimeException("Forced Failure"); + } System.out.println("TestObservable onNext: " + s); observer.onNext(s); } @@ -203,7 +205,7 @@ public void testBackpressure() { .onErrorResumeNext(Observable.just(1)) .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t1) { diff --git a/src/test/java/rx/internal/operators/OperatorOnErrorReturnTest.java b/src/test/java/rx/internal/operators/OperatorOnErrorReturnTest.java index 1b08ced1ca..196d5d49ce 100644 --- a/src/test/java/rx/internal/operators/OperatorOnErrorReturnTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnErrorReturnTest.java @@ -41,7 +41,7 @@ public class OperatorOnErrorReturnTest { @Test public void testResumeNext() { TestObservable f = new TestObservable("one"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); final AtomicReference capturedException = new AtomicReference(); Observable observable = w.onErrorReturn(new Func1() { @@ -77,7 +77,7 @@ public String call(Throwable e) { @Test public void testFunctionThrowsError() { TestObservable f = new TestObservable("one"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); final AtomicReference capturedException = new AtomicReference(); Observable observable = w.onErrorReturn(new Func1() { @@ -119,8 +119,9 @@ public void testMapResumeAsyncNext() { w = w.map(new Func1() { @Override public String call(String s) { - if ("fail".equals(s)) + if ("fail".equals(s)) { throw new RuntimeException("Forced Failure"); + } System.out.println("BadMapper:" + s); return s; } @@ -163,7 +164,7 @@ public Integer call(Throwable t1) { }) .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t1) { @@ -187,7 +188,7 @@ public Integer call(Integer t1) { private static class TestObservable implements Observable.OnSubscribe { final String[] values; - Thread t = null; + Thread t; public TestObservable(String... values) { this.values = values; diff --git a/src/test/java/rx/internal/operators/OperatorOnExceptionResumeNextViaObservableTest.java b/src/test/java/rx/internal/operators/OperatorOnExceptionResumeNextViaObservableTest.java index 7522ae6d86..b54c7d4c46 100644 --- a/src/test/java/rx/internal/operators/OperatorOnExceptionResumeNextViaObservableTest.java +++ b/src/test/java/rx/internal/operators/OperatorOnExceptionResumeNextViaObservableTest.java @@ -35,7 +35,7 @@ public class OperatorOnExceptionResumeNextViaObservableTest { public void testResumeNextWithException() { // Trigger failure on second element TestObservable f = new TestObservable("one", "EXCEPTION", "two", "three"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); Observable resume = Observable.just("twoResume", "threeResume"); Observable observable = w.onExceptionResumeNext(resume); @@ -63,7 +63,7 @@ public void testResumeNextWithException() { public void testResumeNextWithRuntimeException() { // Trigger failure on second element TestObservable f = new TestObservable("one", "RUNTIMEEXCEPTION", "two", "three"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); Observable resume = Observable.just("twoResume", "threeResume"); Observable observable = w.onExceptionResumeNext(resume); @@ -91,7 +91,7 @@ public void testResumeNextWithRuntimeException() { public void testThrowablePassesThru() { // Trigger failure on second element TestObservable f = new TestObservable("one", "THROWABLE", "two", "three"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); Observable resume = Observable.just("twoResume", "threeResume"); Observable observable = w.onExceptionResumeNext(resume); @@ -119,7 +119,7 @@ public void testThrowablePassesThru() { public void testErrorPassesThru() { // Trigger failure on second element TestObservable f = new TestObservable("one", "ON_OVERFLOW_ERROR", "two", "three"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); Observable resume = Observable.just("twoResume", "threeResume"); Observable observable = w.onExceptionResumeNext(resume); @@ -149,15 +149,16 @@ public void testMapResumeAsyncNext() { Observable w = Observable.just("one", "fail", "two", "three", "fail"); // Resume Observable is async TestObservable f = new TestObservable("twoResume", "threeResume"); - Observable resume = Observable.create(f); + Observable resume = Observable.unsafeCreate(f); // Introduce map function that fails intermittently (Map does not prevent this when the observer is a // rx.operator incl onErrorResumeNextViaObservable) w = w.map(new Func1() { @Override public String call(String s) { - if ("fail".equals(s)) + if ("fail".equals(s)) { throw new RuntimeException("Forced Failure"); + } System.out.println("BadMapper:" + s); return s; } @@ -195,7 +196,7 @@ public void testBackpressure() { .onExceptionResumeNext(Observable.just(1)) .observeOn(Schedulers.computation()) .map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer t1) { @@ -220,7 +221,7 @@ public Integer call(Integer t1) { private static class TestObservable implements Observable.OnSubscribe { final String[] values; - Thread t = null; + Thread t; public TestObservable(String... values) { this.values = values; @@ -236,14 +237,15 @@ public void run() { try { System.out.println("running TestObservable thread"); for (String s : values) { - if ("EXCEPTION".equals(s)) + if ("EXCEPTION".equals(s)) { throw new Exception("Forced Exception"); - else if ("RUNTIMEEXCEPTION".equals(s)) + } else if ("RUNTIMEEXCEPTION".equals(s)) { throw new RuntimeException("Forced RuntimeException"); - else if ("ON_OVERFLOW_ERROR".equals(s)) + } else if ("ON_OVERFLOW_ERROR".equals(s)) { throw new Error("Forced Error"); - else if ("THROWABLE".equals(s)) + } else if ("THROWABLE".equals(s)) { throw new Throwable("Forced Throwable"); + } System.out.println("TestObservable onNext: " + s); observer.onNext(s); } diff --git a/src/test/java/rx/internal/operators/OperatorPublishTest.java b/src/test/java/rx/internal/operators/OperatorPublishTest.java index ba2f114742..9855143447 100644 --- a/src/test/java/rx/internal/operators/OperatorPublishTest.java +++ b/src/test/java/rx/internal/operators/OperatorPublishTest.java @@ -37,7 +37,7 @@ public class OperatorPublishTest { @Test public void testPublish() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - ConnectableObservable o = Observable.create(new OnSubscribe() { + ConnectableObservable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -98,7 +98,7 @@ public void call() { }); Observable slow = is.observeOn(Schedulers.computation()).map(new Func1() { - int c = 0; + int c; @Override public Integer call(Integer i) { @@ -368,7 +368,7 @@ public void onStart() { @Test public void testConnectIsIdempotent() { final AtomicInteger calls = new AtomicInteger(); - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { calls.getAndIncrement(); diff --git a/src/test/java/rx/internal/operators/OperatorRepeatTest.java b/src/test/java/rx/internal/operators/OperatorRepeatTest.java index c736b2ef3f..1a37f23c05 100644 --- a/src/test/java/rx/internal/operators/OperatorRepeatTest.java +++ b/src/test/java/rx/internal/operators/OperatorRepeatTest.java @@ -40,7 +40,7 @@ public class OperatorRepeatTest { public void testRepetition() { int NUM = 10; final AtomicInteger count = new AtomicInteger(); - int value = Observable.create(new OnSubscribe() { + int value = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber o) { @@ -68,7 +68,7 @@ public void testNoStackOverFlow() { public void testRepeatTakeWithSubscribeOn() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - Observable oi = Observable.create(new OnSubscribe() { + Observable oi = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { diff --git a/src/test/java/rx/internal/operators/OperatorReplayTest.java b/src/test/java/rx/internal/operators/OperatorReplayTest.java index e98c542cd8..fbe256ef01 100644 --- a/src/test/java/rx/internal/operators/OperatorReplayTest.java +++ b/src/test/java/rx/internal/operators/OperatorReplayTest.java @@ -915,7 +915,7 @@ public void testColdReplayBackpressure() { @Test public void testCache() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(new Observable.OnSubscribe() { + Observable o = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -1055,7 +1055,7 @@ public void testNoMissingBackpressureException() { m = 4 * 1000 * 1000; } - Observable firehose = Observable.create(new OnSubscribe() { + Observable firehose = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { for (int i = 0; i < m; i++) { @@ -1577,4 +1577,20 @@ public ConnectableObservable call(Observable o) { }); } + @Test + public void noOldEntries() { + TestScheduler scheduler = new TestScheduler(); + + Observable source = Observable.just(1) + .replay(2, TimeUnit.SECONDS, scheduler) + .autoConnect(); + + source.test().assertResult(1); + + source.test().assertResult(1); + + scheduler.advanceTimeBy(3, TimeUnit.SECONDS); + + source.test().assertResult(); + } } \ No newline at end of file diff --git a/src/test/java/rx/internal/operators/OperatorRetryTest.java b/src/test/java/rx/internal/operators/OperatorRetryTest.java index 100891b792..d4cf161dc5 100644 --- a/src/test/java/rx/internal/operators/OperatorRetryTest.java +++ b/src/test/java/rx/internal/operators/OperatorRetryTest.java @@ -45,7 +45,7 @@ public class OperatorRetryTest { public void iterativeBackoff() { @SuppressWarnings("unchecked") Observer consumer = mock(Observer.class); - Observable producer = Observable.create(new OnSubscribe() { + Observable producer = Observable.unsafeCreate(new OnSubscribe() { private AtomicInteger count = new AtomicInteger(4); long last = System.currentTimeMillis(); @@ -57,9 +57,9 @@ public void call(Subscriber t1) { if (count.getAndDecrement() == 0) { t1.onNext("hello"); t1.onCompleted(); - } - else + } else { t1.onError(new RuntimeException()); + } } }); @@ -75,7 +75,7 @@ public Observable call(Observable attempts) { public Tuple call(Throwable n) { return new Tuple(new Long(1), n); }}) - .scan(new Func2(){ + .scan(new Func2() { @Override public Tuple call(Tuple t, Tuple n) { return new Tuple(t.count + n.count, n.n); @@ -83,10 +83,10 @@ public Tuple call(Tuple t, Tuple n) { .flatMap(new Func1>() { @Override public Observable call(Tuple t) { - System.out.println("Retry # "+t.count); + System.out.println("Retry # " + t.count); return t.count > 20 ? Observable.error(t.n) : - Observable.timer(t.count *1L, TimeUnit.MILLISECONDS); + Observable.timer(t.count * 1L, TimeUnit.MILLISECONDS); }}); } }).subscribe(ts); @@ -116,7 +116,7 @@ public void testRetryIndefinitely() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); int NUM_RETRIES = 20; - Observable origin = Observable.create(new FuncWithErrors(NUM_RETRIES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); origin.retry().unsafeSubscribe(new TestSubscriber(observer)); InOrder inOrder = inOrder(observer); @@ -136,7 +136,7 @@ public void testSchedulingNotificationHandler() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); int NUM_RETRIES = 2; - Observable origin = Observable.create(new FuncWithErrors(NUM_RETRIES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); TestSubscriber subscriber = new TestSubscriber(observer); origin.retryWhen(new Func1, Observable>() { @Override @@ -168,7 +168,7 @@ public void testOnNextFromNotificationHandler() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); int NUM_RETRIES = 2; - Observable origin = Observable.create(new FuncWithErrors(NUM_RETRIES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); origin.retryWhen(new Func1, Observable>() { @Override public Observable call(Observable t1) { @@ -198,7 +198,7 @@ public Void call(Throwable t1) { public void testOnCompletedFromNotificationHandler() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(1)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(1)); TestSubscriber subscriber = new TestSubscriber(observer); origin.retryWhen(new Func1, Observable>() { @Override @@ -219,7 +219,7 @@ public Observable call(Observable t1) { public void testOnErrorFromNotificationHandler() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(2)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(2)); origin.retryWhen(new Func1, Observable>() { @Override public Observable call(Observable t1) { @@ -247,7 +247,7 @@ public void call(Subscriber subscriber) { } }; - int first = Observable.create(onSubscribe) + int first = Observable.unsafeCreate(onSubscribe) .retryWhen(new Func1, Observable>() { @Override public Observable call(Observable attempt) { @@ -270,7 +270,7 @@ public Void call(Throwable o, Integer integer) { public void testOriginFails() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(1)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(1)); origin.subscribe(observer); InOrder inOrder = inOrder(observer); @@ -286,7 +286,7 @@ public void testRetryFail() { int NUM_FAILURES = 2; @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_FAILURES)); origin.retry(NUM_RETRIES).subscribe(observer); InOrder inOrder = inOrder(observer); @@ -305,7 +305,7 @@ public void testRetrySuccess() { int NUM_FAILURES = 1; @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_FAILURES)); origin.retry(3).subscribe(observer); InOrder inOrder = inOrder(observer); @@ -325,7 +325,7 @@ public void testInfiniteRetry() { int NUM_FAILURES = 20; @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_FAILURES)); origin.retry().subscribe(observer); InOrder inOrder = inOrder(observer); @@ -398,7 +398,7 @@ public void call(final Subscriber o) { final AtomicLong req = new AtomicLong(); // 0 = not set, 1 = fast path, 2 = backpressure final AtomicInteger path = new AtomicInteger(0); - volatile boolean done = false; + volatile boolean done; @Override public void request(long n) { @@ -463,7 +463,7 @@ public void testRetryAllowsSubscriptionAfterAllSubscriptionsUnsubscribed() throw public void call(Subscriber s) { subsCount.incrementAndGet(); s.add(new Subscription() { - boolean unsubscribed = false; + boolean unsubscribed; @Override public void unsubscribe() { @@ -478,7 +478,7 @@ public boolean isUnsubscribed() { }); } }; - Observable stream = Observable.create(onSubscribe); + Observable stream = Observable.unsafeCreate(onSubscribe); Observable streamWithRetry = stream.retry(); Subscription sub = streamWithRetry.subscribe(); assertEquals(1, subsCount.get()); @@ -512,7 +512,7 @@ public void call(Subscriber s) { } }; - Observable.create(onSubscribe).retry(3).subscribe(ts); + Observable.unsafeCreate(onSubscribe).retry(3).subscribe(ts); assertEquals(4, subsCount.get()); // 1 + 3 retries } @@ -530,7 +530,7 @@ public void call(Subscriber s) { } }; - Observable.create(onSubscribe).retry(1).subscribe(ts); + Observable.unsafeCreate(onSubscribe).retry(1).subscribe(ts); assertEquals(2, subsCount.get()); } @@ -548,7 +548,7 @@ public void call(Subscriber s) { } }; - Observable.create(onSubscribe).retry(0).subscribe(ts); + Observable.unsafeCreate(onSubscribe).retry(0).subscribe(ts); assertEquals(1, subsCount.get()); } @@ -651,7 +651,7 @@ public void testUnsubscribeAfterError() { // Observable that always fails after 100ms SlowObservable so = new SlowObservable(100, 0); - Observable o = Observable.create(so).retry(5); + Observable o = Observable.unsafeCreate(so).retry(5); AsyncObserver async = new AsyncObserver(observer); @@ -676,7 +676,7 @@ public void testTimeoutWithRetry() { // Observable that sends every 100ms (timeout fails instead) SlowObservable so = new SlowObservable(100, 10); - Observable o = Observable.create(so).timeout(80, TimeUnit.MILLISECONDS).retry(5); + Observable o = Observable.unsafeCreate(so).timeout(80, TimeUnit.MILLISECONDS).retry(5); AsyncObserver async = new AsyncObserver(observer); @@ -695,12 +695,12 @@ public void testTimeoutWithRetry() { @Test//(timeout = 15000) public void testRetryWithBackpressure() throws InterruptedException { final int NUM_LOOPS = 1; - for (int j=0;j observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_RETRIES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); TestSubscriber ts = new TestSubscriber(observer); origin.retry().observeOn(Schedulers.computation()).unsafeSubscribe(ts); ts.awaitTerminalEvent(5, TimeUnit.SECONDS); @@ -743,7 +743,7 @@ public void testRetryWithBackpressureParallel() throws InterruptedException { public void run() { final AtomicInteger nexts = new AtomicInteger(); try { - Observable origin = Observable.create(new FuncWithErrors(NUM_RETRIES)); + Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); TestSubscriber ts = new TestSubscriber(); origin.retry() .observeOn(Schedulers.computation()).unsafeSubscribe(ts); @@ -866,11 +866,11 @@ public void testIssue1900SourceNotSupportingBackpressure() { final int NUM_MSG = 1034; final AtomicInteger count = new AtomicInteger(); - Observable origin = Observable.create(new Observable.OnSubscribe() { + Observable origin = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber o) { - for(int i=0; i source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { int count; @Override public void call(Subscriber t1) { @@ -116,7 +116,7 @@ public void call(Subscriber t1) { } @Test public void testRetryTwiceAndGiveUp() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { t1.onNext(0); @@ -143,7 +143,7 @@ public void call(Subscriber t1) { } @Test public void testRetryOnSpecificException() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { int count; @Override public void call(Subscriber t1) { @@ -179,7 +179,7 @@ public void call(Subscriber t1) { public void testRetryOnSpecificExceptionAndNotOther() { final IOException ioe = new IOException(); final TestException te = new TestException(); - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { int count; @Override public void call(Subscriber t1) { @@ -238,7 +238,7 @@ public void testUnsubscribeAfterError() { // Observable that always fails after 100ms OperatorRetryTest.SlowObservable so = new OperatorRetryTest.SlowObservable(100, 0); Observable o = Observable - .create(so) + .unsafeCreate(so) .retry(retry5); OperatorRetryTest.AsyncObserver async = new OperatorRetryTest.AsyncObserver(observer); @@ -265,7 +265,7 @@ public void testTimeoutWithRetry() { // Observable that sends every 100ms (timeout fails instead) OperatorRetryTest.SlowObservable so = new OperatorRetryTest.SlowObservable(100, 10); Observable o = Observable - .create(so) + .unsafeCreate(so) .timeout(80, TimeUnit.MILLISECONDS) .retry(retry5); @@ -320,7 +320,7 @@ public Integer call(Integer t1) { public void testIssue3008RetryWithPredicate() { final List list = new CopyOnWriteArrayList(); final AtomicBoolean isFirst = new AtomicBoolean(true); - Observable. just(1L, 2L, 3L).map(new Func1(){ + Observable. just(1L, 2L, 3L).map(new Func1() { @Override public Long call(Long x) { System.out.println("map " + x); @@ -348,7 +348,7 @@ public void call(Long t) { public void testIssue3008RetryInfinite() { final List list = new CopyOnWriteArrayList(); final AtomicBoolean isFirst = new AtomicBoolean(true); - Observable. just(1L, 2L, 3L).map(new Func1(){ + Observable. just(1L, 2L, 3L).map(new Func1() { @Override public Long call(Long x) { System.out.println("map " + x); diff --git a/src/test/java/rx/internal/operators/OperatorSampleTest.java b/src/test/java/rx/internal/operators/OperatorSampleTest.java index 04000973ec..20d59d8523 100644 --- a/src/test/java/rx/internal/operators/OperatorSampleTest.java +++ b/src/test/java/rx/internal/operators/OperatorSampleTest.java @@ -50,7 +50,7 @@ public void before() { @Test public void testSample() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer1) { innerScheduler.schedule(new Action0() { @@ -111,7 +111,7 @@ public void call() { @Test public void sampleWithTimeEmitAndTerminate() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer1) { innerScheduler.schedule(new Action0() { @@ -303,7 +303,7 @@ public void sampleWithSamplerThrows() { @Test public void testSampleUnsubscribe() { final Subscription s = mock(Subscription.class); - Observable o = Observable.create( + Observable o = Observable.unsafeCreate( new OnSubscribe() { @Override public void call(Subscriber subscriber) { @@ -387,7 +387,7 @@ public void dontUnsubscribeChild2() { @Test public void neverSetProducer() { - Observable neverBackpressure = Observable.create(new OnSubscribe() { + Observable neverBackpressure = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { t.setProducer(new Producer() { @@ -430,7 +430,7 @@ public void setProducer(Producer p) { public void unsubscribeMainAfterCompleted() { final AtomicBoolean unsubscribed = new AtomicBoolean(); - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { t.add(Subscriptions.create(new Action0() { @@ -467,7 +467,7 @@ public void onCompleted() { public void unsubscribeSamplerAfterCompleted() { final AtomicBoolean unsubscribed = new AtomicBoolean(); - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { t.add(Subscriptions.create(new Action0() { diff --git a/src/test/java/rx/internal/operators/OperatorScanTest.java b/src/test/java/rx/internal/operators/OperatorScanTest.java index c6bf08b718..e20519ed17 100644 --- a/src/test/java/rx/internal/operators/OperatorScanTest.java +++ b/src/test/java/rx/internal/operators/OperatorScanTest.java @@ -306,7 +306,7 @@ public Integer call(Integer t1, Integer t2) { @Test public void testScanShouldNotRequestZero() { final AtomicReference producer = new AtomicReference(); - Observable o = Observable.create(new Observable.OnSubscribe() { + Observable o = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber subscriber) { Producer p = spy(new Producer() { @@ -371,7 +371,7 @@ public Integer call(Integer t1, Integer t2) { @Test public void testInitialValueEmittedWithProducer() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t) { t.setProducer(new Producer() { diff --git a/src/test/java/rx/internal/operators/OperatorSerializeTest.java b/src/test/java/rx/internal/operators/OperatorSerializeTest.java index 153df50fa2..14d38dd76b 100644 --- a/src/test/java/rx/internal/operators/OperatorSerializeTest.java +++ b/src/test/java/rx/internal/operators/OperatorSerializeTest.java @@ -51,7 +51,7 @@ public void before() { @Test public void testSingleThreadedBasic() { TestSingleThreadedObservable onSubscribe = new TestSingleThreadedObservable("one", "two", "three"); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); w.serialize().subscribe(observer); onSubscribe.waitToFinish(); @@ -69,7 +69,7 @@ public void testSingleThreadedBasic() { @Test public void testMultiThreadedBasic() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three"); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); BusyObserver busyobserver = new BusyObserver(); @@ -92,7 +92,7 @@ public void testMultiThreadedBasic() { @Test public void testMultiThreadedWithNPE() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); BusyObserver busyobserver = new BusyObserver(); @@ -123,7 +123,7 @@ public void testMultiThreadedWithNPEinMiddle() { boolean lessThan9 = false; for (int i = 0; i < 3; i++) { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); BusyObserver busyobserver = new BusyObserver(); @@ -223,7 +223,7 @@ private enum TestConcurrencyobserverEvent { private static class TestSingleThreadedObservable implements Observable.OnSubscribe { final String[] values; - private Thread t = null; + private Thread t; public TestSingleThreadedObservable(final String... values) { this.values = values; @@ -270,7 +270,7 @@ public void waitToFinish() { */ private static class TestMultiThreadedObservable implements Observable.OnSubscribe { final String[] values; - Thread t = null; + Thread t; AtomicInteger threadsRunning = new AtomicInteger(); AtomicInteger maxConcurrentThreads = new AtomicInteger(); ExecutorService threadPool; @@ -302,8 +302,9 @@ public void run() { System.out.println("TestMultiThreadedObservable onNext: null"); // force an error throw npe; - } else + } else { System.out.println("TestMultiThreadedObservable onNext: " + s); + } observer.onNext(s); // capture 'maxThreads' int concurrentThreads = threadsRunning.get(); @@ -350,8 +351,8 @@ public void waitToFinish() { } private static class BusyObserver extends Subscriber { - volatile boolean onCompleted = false; - volatile boolean onError = false; + volatile boolean onCompleted; + volatile boolean onError; AtomicInteger onNextCount = new AtomicInteger(); AtomicInteger threadsRunning = new AtomicInteger(); AtomicInteger maxConcurrentThreads = new AtomicInteger(); diff --git a/src/test/java/rx/internal/operators/OperatorSkipTest.java b/src/test/java/rx/internal/operators/OperatorSkipTest.java index 649c562bb2..fdfd12de1d 100644 --- a/src/test/java/rx/internal/operators/OperatorSkipTest.java +++ b/src/test/java/rx/internal/operators/OperatorSkipTest.java @@ -172,7 +172,7 @@ public void call(Long n) { @Test public void testRequestOverflowDoesNotOccur() { - TestSubscriber ts = new TestSubscriber(Long.MAX_VALUE-1); + TestSubscriber ts = new TestSubscriber(Long.MAX_VALUE - 1); Observable.range(1, 10).skip(5).subscribe(ts); ts.assertTerminalEvent(); ts.assertCompleted(); diff --git a/src/test/java/rx/internal/operators/OperatorSkipWhileTest.java b/src/test/java/rx/internal/operators/OperatorSkipWhileTest.java index f17b3ac07a..205ed930bb 100644 --- a/src/test/java/rx/internal/operators/OperatorSkipWhileTest.java +++ b/src/test/java/rx/internal/operators/OperatorSkipWhileTest.java @@ -44,14 +44,15 @@ public class OperatorSkipWhileTest { private static final Func1 LESS_THAN_FIVE = new Func1() { @Override public Boolean call(Integer v) { - if (v == 42) + if (v == 42) { throw new RuntimeException("that's not the answer to everything!"); + } return v < 5; } }; private static final Func1 INDEX_LESS_THAN_THREE = new Func1() { - int index = 0; + int index; @Override public Boolean call(Integer value) { return index++ < 3; diff --git a/src/test/java/rx/internal/operators/OperatorSubscribeOnTest.java b/src/test/java/rx/internal/operators/OperatorSubscribeOnTest.java index 65f1b9870e..b28b3fc27b 100644 --- a/src/test/java/rx/internal/operators/OperatorSubscribeOnTest.java +++ b/src/test/java/rx/internal/operators/OperatorSubscribeOnTest.java @@ -26,16 +26,12 @@ import org.junit.Test; -import rx.Observable; +import rx.*; import rx.Observable.OnSubscribe; import rx.Observable.Operator; -import rx.Observer; -import rx.Producer; -import rx.Scheduler; -import rx.Subscriber; -import rx.Subscription; -import rx.functions.Action0; -import rx.observers.TestSubscriber; +import rx.functions.*; +import rx.internal.util.*; +import rx.observers.*; import rx.schedulers.Schedulers; public class OperatorSubscribeOnTest { @@ -50,7 +46,7 @@ public void testIssue813() throws InterruptedException { TestSubscriber observer = new TestSubscriber(); final Subscription subscription = Observable - .create(new Observable.OnSubscribe() { + .unsafeCreate(new Observable.OnSubscribe() { @Override public void call( final Subscriber subscriber) { @@ -85,7 +81,7 @@ public void call( @Test public void testThrownErrorHandling() { TestSubscriber ts = new TestSubscriber(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -100,7 +96,7 @@ public void call(Subscriber s) { @Test public void testOnError() { TestSubscriber ts = new TestSubscriber(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -170,7 +166,7 @@ public Subscription schedule(final Action0 action, final long delayTime, final T public void testUnsubscribeInfiniteStream() throws InterruptedException { TestSubscriber ts = new TestSubscriber(); final AtomicInteger count = new AtomicInteger(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber sub) { @@ -267,4 +263,85 @@ public void onNext(Integer t) { ts.assertNoErrors(); } + @Test + public void noSamepoolDeadlock() { + final int n = 4 * RxRingBuffer.SIZE; + + Observable.create(new Action1>() { + @Override + public void call(Emitter e) { + for (int i = 0; i < n; i++) { + e.onNext(i); + try { + Thread.sleep(1); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + e.onCompleted(); + } + }, Emitter.BackpressureMode.DROP) + .map(UtilityFunctions.identity()) + .subscribeOn(Schedulers.io(), false) + .observeOn(Schedulers.computation()) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS) + .assertValueCount(n) + .assertNoErrors() + .assertCompleted(); + } + + @Test + public void noSamepoolDeadlockRequestOn() { + final int n = 4 * RxRingBuffer.SIZE; + + Observable.create(new Action1>() { + @Override + public void call(Emitter e) { + for (int i = 0; i < n; i++) { + e.onNext(i); + try { + Thread.sleep(1); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + e.onCompleted(); + } + }, Emitter.BackpressureMode.DROP) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.computation()) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS) + .assertValueCount(n) + .assertNoErrors() + .assertCompleted(); + } + + @Test + public void noSamepoolDeadlockRequestOn2() { + final int n = 4 * RxRingBuffer.SIZE; + + Observable.create(new Action1>() { + @Override + public void call(Emitter e) { + for (int i = 0; i < n; i++) { + e.onNext(i); + try { + Thread.sleep(1); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + e.onCompleted(); + } + }, Emitter.BackpressureMode.DROP) + .subscribeOn(Schedulers.io(), true) + .observeOn(Schedulers.computation()) + .test() + .awaitTerminalEvent(5, TimeUnit.SECONDS) + .assertValueCount(RxRingBuffer.SIZE) + .assertNoErrors() + .assertCompleted(); + } } diff --git a/src/test/java/rx/internal/operators/OperatorSwitchIfEmptyTest.java b/src/test/java/rx/internal/operators/OperatorSwitchIfEmptyTest.java index 70225a53eb..235cac495b 100644 --- a/src/test/java/rx/internal/operators/OperatorSwitchIfEmptyTest.java +++ b/src/test/java/rx/internal/operators/OperatorSwitchIfEmptyTest.java @@ -17,16 +17,15 @@ import static org.junit.Assert.*; -import java.util.*; +import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import rx.*; -import rx.Observable; import rx.Observable.OnSubscribe; -import rx.functions.Action0; +import rx.functions.*; import rx.observers.TestSubscriber; import rx.schedulers.Schedulers; import rx.subscriptions.Subscriptions; @@ -58,7 +57,7 @@ public void testSwitchWhenEmpty() throws Exception { @Test public void testSwitchWithProducer() throws Exception { final AtomicBoolean emitted = new AtomicBoolean(false); - Observable withProducer = Observable.create(new Observable.OnSubscribe() { + Observable withProducer = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber subscriber) { subscriber.setProducer(new Producer() { @@ -82,7 +81,7 @@ public void request(long n) { public void testSwitchTriggerUnsubscribe() throws Exception { final Subscription empty = Subscriptions.empty(); - Observable withProducer = Observable.create(new Observable.OnSubscribe() { + Observable withProducer = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber subscriber) { subscriber.add(empty); @@ -121,7 +120,7 @@ public void onNext(Long aLong) { public void testSwitchShouldTriggerUnsubscribe() { final Subscription s = Subscriptions.empty(); - Observable.create(new Observable.OnSubscribe() { + Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber subscriber) { subscriber.add(s); @@ -176,7 +175,7 @@ public void testBackpressureOnFirstObservable() { @Test(timeout = 10000) public void testRequestsNotLost() throws InterruptedException { final TestSubscriber ts = new TestSubscriber(0); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber subscriber) { @@ -212,4 +211,29 @@ public void call() { public void testAlternateNull() { Observable.just(1).switchIfEmpty(null); } + + Observable recursiveSwitch(final int level) { + if (level == 100) { + return Observable.just(Thread.currentThread().getStackTrace()); + } + return Observable.empty().switchIfEmpty(Observable.defer(new Func0>() { + @Override + public Observable call() { + return recursiveSwitch(level + 1); + } + })); + } + + @Test + public void stackDepth() { + StackTraceElement[] trace = recursiveSwitch(0) + .toBlocking().last(); + + if (trace.length > 1000 || trace.length < 100) { + for (StackTraceElement ste : trace) { + System.out.println(ste); + } + fail("Stack too deep: " + trace.length); + } + } } \ No newline at end of file diff --git a/src/test/java/rx/internal/operators/OperatorSwitchTest.java b/src/test/java/rx/internal/operators/OperatorSwitchTest.java index 9b1d1be8b4..00887db5e1 100644 --- a/src/test/java/rx/internal/operators/OperatorSwitchTest.java +++ b/src/test/java/rx/internal/operators/OperatorSwitchTest.java @@ -53,10 +53,10 @@ public void before() { @Test public void testSwitchWhenOuterCompleteBeforeInner() { - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 50, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 70, "one"); @@ -80,10 +80,10 @@ public void call(Subscriber observer) { @Test public void testSwitchWhenInnerCompleteBeforeOuter() { - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 10, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 10, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 0, "one"); @@ -92,7 +92,7 @@ public void call(Subscriber observer) { } })); - publishNext(observer, 100, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 100, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 0, "three"); @@ -123,10 +123,10 @@ public void call(Subscriber observer) { @Test public void testSwitchWithComplete() { - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 50, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { publishNext(observer, 60, "one"); @@ -134,7 +134,7 @@ public void call(final Subscriber observer) { } })); - publishNext(observer, 200, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 200, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { publishNext(observer, 0, "three"); @@ -179,10 +179,10 @@ public void call(final Subscriber observer) { @Test public void testSwitchWithError() { - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 50, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { publishNext(observer, 50, "one"); @@ -190,7 +190,7 @@ public void call(final Subscriber observer) { } })); - publishNext(observer, 200, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 200, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 0, "three"); @@ -235,10 +235,10 @@ public void call(Subscriber observer) { @Test public void testSwitchWithSubsequenceComplete() { - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 50, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 50, "one"); @@ -246,14 +246,14 @@ public void call(Subscriber observer) { } })); - publishNext(observer, 130, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 130, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishCompleted(observer, 0); } })); - publishNext(observer, 150, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 150, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 50, "three"); @@ -285,10 +285,10 @@ public void call(Subscriber observer) { @Test public void testSwitchWithSubsequenceError() { - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 50, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 50, "one"); @@ -296,14 +296,14 @@ public void call(Subscriber observer) { } })); - publishNext(observer, 130, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 130, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishError(observer, 0, new TestException()); } })); - publishNext(observer, 150, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 150, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 50, "three"); @@ -364,10 +364,10 @@ public void call() { @Test public void testSwitchIssue737() { // https://github.com/ReactiveX/RxJava/issues/737 - Observable> source = Observable.create(new Observable.OnSubscribe>() { + Observable> source = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { - publishNext(observer, 0, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 0, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 10, "1-one"); @@ -377,7 +377,7 @@ public void call(Subscriber observer) { publishCompleted(observer, 40); } })); - publishNext(observer, 25, Observable.create(new Observable.OnSubscribe() { + publishNext(observer, 25, Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 10, "2-one"); @@ -407,69 +407,69 @@ public void call(Subscriber observer) { @Test public void testBackpressure() { - final Observable o1 = Observable.create(new Observable.OnSubscribe() { + final Observable o1 = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { observer.setProducer(new Producer() { - private int emitted = 0; + private int emitted; @Override public void request(long n) { - for(int i = 0; i < n && emitted < 10 && !observer.isUnsubscribed(); i++) { + for (int i = 0; i < n && emitted < 10 && !observer.isUnsubscribed(); i++) { scheduler.advanceTimeBy(5, TimeUnit.MILLISECONDS); emitted++; observer.onNext("a" + emitted); } - if(emitted == 10) { + if (emitted == 10) { observer.onCompleted(); } } }); } }); - final Observable o2 = Observable.create(new Observable.OnSubscribe() { + final Observable o2 = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { observer.setProducer(new Producer() { - private int emitted = 0; + private int emitted; @Override public void request(long n) { - for(int i = 0; i < n && emitted < 10 && !observer.isUnsubscribed(); i++) { + for (int i = 0; i < n && emitted < 10 && !observer.isUnsubscribed(); i++) { scheduler.advanceTimeBy(5, TimeUnit.MILLISECONDS); emitted++; observer.onNext("b" + emitted); } - if(emitted == 10) { + if (emitted == 10) { observer.onCompleted(); } } }); } }); - final Observable o3 = Observable.create(new Observable.OnSubscribe() { + final Observable o3 = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { observer.setProducer(new Producer() { - private int emitted = 0; + private int emitted; @Override public void request(long n) { - for(int i = 0; i < n && emitted < 10 && !observer.isUnsubscribed(); i++) { + for (int i = 0; i < n && emitted < 10 && !observer.isUnsubscribed(); i++) { emitted++; observer.onNext("c" + emitted); } - if(emitted == 10) { + if (emitted == 10) { observer.onCompleted(); } } }); } }); - Observable> o = Observable.create(new Observable.OnSubscribe>() { + Observable> o = Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(Subscriber> observer) { publishNext(observer, 10, o1); @@ -481,7 +481,7 @@ public void call(Subscriber> observer) { final TestSubscriber testSubscriber = new TestSubscriber(); Observable.switchOnNext(o).subscribe(new Subscriber() { - private int requested = 0; + private int requested; @Override public void onStart() { @@ -503,7 +503,7 @@ public void onError(Throwable e) { public void onNext(String s) { testSubscriber.onNext(s); requested--; - if(requested == 0) { + if (requested == 0) { requested = 3; request(3); } @@ -519,7 +519,7 @@ public void onNext(String s) { public void testUnsubscribe() { final AtomicBoolean isUnsubscribed = new AtomicBoolean(); Observable.switchOnNext( - Observable.create(new Observable.OnSubscribe>() { + Observable.unsafeCreate(new Observable.OnSubscribe>() { @Override public void call(final Subscriber> subscriber) { subscriber.onNext(Observable.just(1)); @@ -657,7 +657,7 @@ public Observable call(Long t) { ts.awaitTerminalEvent(); assertTrue(ts.getOnNextEvents().size() > 0); assertEquals(4, requests.size()); // depends on the request pattern - assertEquals(Long.MAX_VALUE, (long) requests.get(requests.size()-1)); + assertEquals(Long.MAX_VALUE, (long) requests.get(requests.size() - 1)); } @Test @@ -894,7 +894,7 @@ public void asyncInner() throws Throwable { .switchMap(UtilityFunctions.>identity()) .observeOn(Schedulers.computation()) .ignoreElements() - .timeout(5, TimeUnit.SECONDS) + .timeout(15, TimeUnit.SECONDS) .toBlocking() .subscribe(Actions.empty(), new Action1() { @Override diff --git a/src/test/java/rx/internal/operators/OperatorTakeLastTest.java b/src/test/java/rx/internal/operators/OperatorTakeLastTest.java index 23a4361f0f..1a2eddec64 100644 --- a/src/test/java/rx/internal/operators/OperatorTakeLastTest.java +++ b/src/test/java/rx/internal/operators/OperatorTakeLastTest.java @@ -133,7 +133,7 @@ public void testBackpressure2() { private Func1 newSlowProcessor() { return new Func1() { - int c = 0; + int c; @Override public Integer call(Integer i) { @@ -292,7 +292,7 @@ public void onNext(Integer integer) { assertEquals(1,count.get()); } - @Test(timeout=10000) + @Test(timeout = 10000) public void testRequestOverflow() { final List list = new ArrayList(); Observable.range(1, 100).takeLast(50).subscribe(new Subscriber() { @@ -315,7 +315,7 @@ public void onError(Throwable e) { @Override public void onNext(Integer t) { list.add(t); - request(Long.MAX_VALUE-1); + request(Long.MAX_VALUE - 1); }}); assertEquals(50, list.size()); } @@ -343,7 +343,7 @@ public void completionRequestRace() { w.schedule(new Action0() { @Override public void call() { - while (!go.get()); + while (!go.get()) { } ts.requestMore(n + 1); } }); diff --git a/src/test/java/rx/internal/operators/OperatorTakeLastTimedTest.java b/src/test/java/rx/internal/operators/OperatorTakeLastTimedTest.java index 70794d1aa1..491825d3f9 100644 --- a/src/test/java/rx/internal/operators/OperatorTakeLastTimedTest.java +++ b/src/test/java/rx/internal/operators/OperatorTakeLastTimedTest.java @@ -237,7 +237,7 @@ public void completionRequestRace() { w.schedule(new Action0() { @Override public void call() { - while (!go.get()); + while (!go.get()) { } ts.requestMore(n + 1); } }); diff --git a/src/test/java/rx/internal/operators/OperatorTakeTest.java b/src/test/java/rx/internal/operators/OperatorTakeTest.java index c056e8b8f8..0885a8b343 100644 --- a/src/test/java/rx/internal/operators/OperatorTakeTest.java +++ b/src/test/java/rx/internal/operators/OperatorTakeTest.java @@ -19,7 +19,7 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import java.util.Arrays; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; @@ -27,10 +27,13 @@ import org.mockito.InOrder; import rx.*; +import rx.Observable; import rx.Observable.OnSubscribe; +import rx.Observer; import rx.exceptions.TestException; import rx.functions.*; import rx.observers.*; +import rx.plugins.RxJavaHooks; import rx.schedulers.Schedulers; import rx.subjects.PublishSubject; @@ -112,7 +115,7 @@ public Integer call(Integer t1) { @Test public void testTakeDoesntLeakErrors() { - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { observer.onNext("one"); @@ -136,7 +139,7 @@ public void call(Subscriber observer) { public void testTakeZeroDoesntLeakError() { final AtomicBoolean subscribed = new AtomicBoolean(false); final AtomicBoolean unSubscribed = new AtomicBoolean(false); - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { subscribed.set(true); @@ -173,7 +176,7 @@ public boolean isUnsubscribed() { public void testUnsubscribeAfterTake() { final Subscription s = mock(Subscription.class); TestObservableFunc f = new TestObservableFunc("one", "two", "three"); - Observable w = Observable.create(f); + Observable w = Observable.unsafeCreate(f); @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); @@ -218,7 +221,7 @@ public void call(Long l) { @Test(timeout = 2000) public void testMultiTake() { final AtomicInteger count = new AtomicInteger(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -245,7 +248,7 @@ public void call(Integer t1) { private static class TestObservableFunc implements Observable.OnSubscribe { final String[] values; - Thread t = null; + Thread t; public TestObservableFunc(String... values) { this.values = values; @@ -277,7 +280,7 @@ public void run() { } } - private static Observable INFINITE_OBSERVABLE = Observable.create(new OnSubscribe() { + private static Observable INFINITE_OBSERVABLE = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber op) { @@ -311,7 +314,7 @@ public void testProducerRequestThroughTake() { TestSubscriber ts = new TestSubscriber(); ts.requestMore(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -334,7 +337,7 @@ public void testProducerRequestThroughTakeIsModified() { TestSubscriber ts = new TestSubscriber(); ts.requestMore(3); final AtomicLong requested = new AtomicLong(); - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -457,4 +460,39 @@ public void takeZero() { ts.assertCompleted(); } + @Test + public void crashReportedToHooks() { + final List errors = Collections.synchronizedList(new ArrayList()); + RxJavaHooks.setOnError(new Action1() { + @Override + public void call(Throwable error) { + errors.add(error); + } + }); + + try { + Observable.just("1") + .take(1) + .toSingle() + .subscribe( + new Action1() { + @Override + public void call(String it) { + throw new TestException("bla"); + } + }, + new Action1() { + @Override + public void call(Throwable error) { + errors.add(new AssertionError()); + } + } + ); + + assertEquals("" + errors, 1, errors.size()); + assertTrue("" + errors.get(0), errors.get(0).getMessage().equals("bla")); + } finally { + RxJavaHooks.setOnError(null); + } + } } diff --git a/src/test/java/rx/internal/operators/OperatorTakeUntilTest.java b/src/test/java/rx/internal/operators/OperatorTakeUntilTest.java index f51aa9cd26..5c590c68c5 100644 --- a/src/test/java/rx/internal/operators/OperatorTakeUntilTest.java +++ b/src/test/java/rx/internal/operators/OperatorTakeUntilTest.java @@ -40,7 +40,7 @@ public void testTakeUntil() { TestObservable other = new TestObservable(sOther); Observer result = mock(Observer.class); - Observable stringObservable = Observable.create(source).takeUntil(Observable.create(other)); + Observable stringObservable = Observable.unsafeCreate(source).takeUntil(Observable.unsafeCreate(other)); stringObservable.subscribe(result); source.sendOnNext("one"); source.sendOnNext("two"); @@ -67,7 +67,7 @@ public void testTakeUntilSourceCompleted() { TestObservable other = new TestObservable(sOther); Observer result = mock(Observer.class); - Observable stringObservable = Observable.create(source).takeUntil(Observable.create(other)); + Observable stringObservable = Observable.unsafeCreate(source).takeUntil(Observable.unsafeCreate(other)); stringObservable.subscribe(result); source.sendOnNext("one"); source.sendOnNext("two"); @@ -90,7 +90,7 @@ public void testTakeUntilSourceError() { Throwable error = new Throwable(); Observer result = mock(Observer.class); - Observable stringObservable = Observable.create(source).takeUntil(Observable.create(other)); + Observable stringObservable = Observable.unsafeCreate(source).takeUntil(Observable.unsafeCreate(other)); stringObservable.subscribe(result); source.sendOnNext("one"); source.sendOnNext("two"); @@ -116,7 +116,7 @@ public void testTakeUntilOtherError() { Throwable error = new Throwable(); Observer result = mock(Observer.class); - Observable stringObservable = Observable.create(source).takeUntil(Observable.create(other)); + Observable stringObservable = Observable.unsafeCreate(source).takeUntil(Observable.unsafeCreate(other)); stringObservable.subscribe(result); source.sendOnNext("one"); source.sendOnNext("two"); @@ -145,7 +145,7 @@ public void testTakeUntilOtherCompleted() { TestObservable other = new TestObservable(sOther); Observer result = mock(Observer.class); - Observable stringObservable = Observable.create(source).takeUntil(Observable.create(other)); + Observable stringObservable = Observable.unsafeCreate(source).takeUntil(Observable.unsafeCreate(other)); stringObservable.subscribe(result); source.sendOnNext("one"); source.sendOnNext("two"); @@ -163,7 +163,7 @@ public void testTakeUntilOtherCompleted() { private static class TestObservable implements Observable.OnSubscribe { - Observer observer = null; + Observer observer; Subscription s; public TestObservable(Subscription s) { diff --git a/src/test/java/rx/internal/operators/OperatorTakeWhileTest.java b/src/test/java/rx/internal/operators/OperatorTakeWhileTest.java index f80b863f9c..2537e0e807 100644 --- a/src/test/java/rx/internal/operators/OperatorTakeWhileTest.java +++ b/src/test/java/rx/internal/operators/OperatorTakeWhileTest.java @@ -87,7 +87,7 @@ public Boolean call(Integer input) { public void testTakeWhile2() { Observable w = Observable.just("one", "two", "three"); Observable take = w.takeWhile(new Func1() { - int index = 0; + int index; @Override public Boolean call(String input) { @@ -107,7 +107,7 @@ public Boolean call(String input) { @Test public void testTakeWhileDoesntLeakErrors() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { observer.onNext("one"); @@ -130,7 +130,7 @@ public void testTakeWhileProtectsPredicateCall() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable take = Observable.create(source).takeWhile(new Func1() { + Observable take = Observable.unsafeCreate(source).takeWhile(new Func1() { @Override public Boolean call(String s) { throw testException; @@ -157,8 +157,8 @@ public void testUnsubscribeAfterTake() { @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); - Observable take = Observable.create(w).takeWhile(new Func1() { - int index = 0; + Observable take = Observable.unsafeCreate(w).takeWhile(new Func1() { + int index; @Override public Boolean call(String s) { @@ -186,7 +186,7 @@ private static class TestObservable implements Observable.OnSubscribe { final Subscription s; final String[] values; - Thread t = null; + Thread t; public TestObservable(Subscription s, String... values) { this.s = s; diff --git a/src/test/java/rx/internal/operators/OperatorThrottleFirstTest.java b/src/test/java/rx/internal/operators/OperatorThrottleFirstTest.java index f608cb22c6..f0fc6527a6 100644 --- a/src/test/java/rx/internal/operators/OperatorThrottleFirstTest.java +++ b/src/test/java/rx/internal/operators/OperatorThrottleFirstTest.java @@ -16,26 +16,18 @@ package rx.internal.operators; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.*; import java.util.concurrent.TimeUnit; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import org.mockito.InOrder; -import rx.Observable; +import rx.*; import rx.Observable.OnSubscribe; -import rx.Observer; -import rx.Scheduler; -import rx.Subscriber; import rx.exceptions.TestException; import rx.functions.Action0; -import rx.observers.TestSubscriber; +import rx.observers.*; import rx.schedulers.TestScheduler; import rx.subjects.PublishSubject; @@ -55,7 +47,7 @@ public void before() { @Test public void testThrottlingWithCompleted() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { publishNext(observer, 100, "one"); // publish as it's first @@ -82,7 +74,7 @@ public void call(Subscriber observer) { @Test public void testThrottlingWithError() { - Observable source = Observable.create(new OnSubscribe() { + Observable source = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { Exception error = new TestException(); @@ -214,4 +206,28 @@ public void throttleWithTestSchedulerTimeOfZero() { verify(observer).onCompleted(); verifyNoMoreInteractions(observer); } + + @Test + public void nowDrift() { + TestScheduler s = new TestScheduler(); + s.advanceTimeBy(2, TimeUnit.SECONDS); + + PublishSubject o = PublishSubject.create(); + + AssertableSubscriber as = o.throttleFirst(500, TimeUnit.MILLISECONDS, s) + .test(); + + o.onNext(1); + s.advanceTimeBy(100, TimeUnit.MILLISECONDS); + o.onNext(2); + s.advanceTimeBy(100, TimeUnit.MILLISECONDS); + o.onNext(3); + s.advanceTimeBy(-1000, TimeUnit.MILLISECONDS); + o.onNext(4); + s.advanceTimeBy(100, TimeUnit.MILLISECONDS); + o.onNext(5); + o.onCompleted(); + + as.assertResult(1, 4); + } } diff --git a/src/test/java/rx/internal/operators/OperatorTimeoutTests.java b/src/test/java/rx/internal/operators/OperatorTimeoutTests.java index 3071d78f6d..6be7706d0f 100644 --- a/src/test/java/rx/internal/operators/OperatorTimeoutTests.java +++ b/src/test/java/rx/internal/operators/OperatorTimeoutTests.java @@ -15,21 +15,26 @@ */ package rx.internal.operators; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.io.IOException; +import java.util.*; import java.util.concurrent.*; import org.junit.*; import org.mockito.*; import rx.*; +import rx.Observable; import rx.Observable.OnSubscribe; -import rx.functions.Func1; -import rx.observers.TestSubscriber; +import rx.Observer; +import rx.exceptions.TestException; +import rx.functions.*; +import rx.observers.*; import rx.schedulers.TestScheduler; -import rx.subjects.PublishSubject; +import rx.subjects.*; public class OperatorTimeoutTests { private PublishSubject underlyingSubject; @@ -234,7 +239,7 @@ public void shouldTimeoutIfSynchronizedObservableEmitFirstOnNextNotWithinTimeout @Override public void run() { - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { @@ -268,7 +273,7 @@ public void shouldUnsubscribeFromUnderlyingSubscriptionOnTimeout() throws Interr // From https://github.com/ReactiveX/RxJava/pull/951 final Subscription s = mock(Subscription.class); - Observable never = Observable.create(new OnSubscribe() { + Observable never = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { subscriber.add(s); @@ -296,7 +301,7 @@ public void shouldUnsubscribeFromUnderlyingSubscriptionOnImmediatelyComplete() { // From https://github.com/ReactiveX/RxJava/pull/951 final Subscription s = mock(Subscription.class); - Observable immediatelyComplete = Observable.create(new OnSubscribe() { + Observable immediatelyComplete = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { subscriber.add(s); @@ -326,7 +331,7 @@ public void shouldUnsubscribeFromUnderlyingSubscriptionOnImmediatelyErrored() th // From https://github.com/ReactiveX/RxJava/pull/951 final Subscription s = mock(Subscription.class); - Observable immediatelyError = Observable.create(new OnSubscribe() { + Observable immediatelyError = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { subscriber.add(s); @@ -427,4 +432,71 @@ public void withDefaultSchedulerAndOther() { ts.assertNoErrors(); ts.assertCompleted(); } + + @Test + public void disconnectOnTimeout() { + final List list = Collections.synchronizedList(new ArrayList()); + + TestScheduler sch = new TestScheduler(); + + Subject subject = PublishSubject.create(); + Observable initialObservable = subject.share() + .map(new Func1() { + @Override + public Long call(Long value) { + list.add("Received value " + value); + return value; + } + }); + + Observable timeoutObservable = initialObservable + .map(new Func1() { + @Override + public Long call(Long value) { + list.add("Timeout received value " + value); + return value; + } + }); + + TestSubscriber subscriber = new TestSubscriber(); + initialObservable + .doOnUnsubscribe(new Action0() { + @Override + public void call() { + list.add("Unsubscribed"); + } + }) + .timeout(1, TimeUnit.SECONDS, timeoutObservable, sch).subscribe(subscriber); + + subject.onNext(5L); + + sch.advanceTimeBy(2, TimeUnit.SECONDS); + + subject.onNext(10L); + subject.onCompleted(); + + subscriber.awaitTerminalEvent(); + subscriber.assertNoErrors(); + subscriber.assertValues(5L, 10L); + + assertEquals(Arrays.asList( + "Received value 5", + "Unsubscribed", + "Received value 10", + "Timeout received value 10" + ), list); + } + + @Test + public void fallbackIsError() { + TestScheduler sch = new TestScheduler(); + + AssertableSubscriber as = Observable.never() + .timeout(1, TimeUnit.SECONDS, Observable.error(new TestException()), sch) + .test(); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + as.assertFailure(TestException.class); + } } diff --git a/src/test/java/rx/internal/operators/OperatorTimeoutWithSelectorTest.java b/src/test/java/rx/internal/operators/OperatorTimeoutWithSelectorTest.java index 456f48a998..628d1eca55 100644 --- a/src/test/java/rx/internal/operators/OperatorTimeoutWithSelectorTest.java +++ b/src/test/java/rx/internal/operators/OperatorTimeoutWithSelectorTest.java @@ -19,7 +19,8 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import java.util.Arrays; +import java.io.IOException; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,13 +29,15 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import rx.*; +import rx.Observable; import rx.Observable.OnSubscribe; +import rx.Observer; +import rx.Subscriber; import rx.exceptions.TestException; import rx.functions.*; -import rx.observers.TestSubscriber; -import rx.schedulers.Schedulers; -import rx.subjects.PublishSubject; +import rx.observers.*; +import rx.schedulers.*; +import rx.subjects.*; public class OperatorTimeoutWithSelectorTest { @Test(timeout = 2000) @@ -334,7 +337,7 @@ public void testTimeoutSelectorWithTimeoutAndOnNextRaceCondition() throws Interr public Observable call(Integer t1) { if (t1 == 1) { // Force "unsubscribe" run on another thread - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { enteredTimeoutOne.countDown(); @@ -393,7 +396,7 @@ public void run() { source.timeout(timeoutFunc, Observable.just(3)).subscribe(ts); source.onNext(1); // start timeout try { - if(!enteredTimeoutOne.await(30, TimeUnit.SECONDS)) { + if (!enteredTimeoutOne.await(30, TimeUnit.SECONDS)) { latchTimeout.set(true); } } catch (InterruptedException e) { @@ -401,7 +404,7 @@ public void run() { } source.onNext(2); // disable timeout try { - if(!timeoutEmittedOne.await(30, TimeUnit.SECONDS)) { + if (!timeoutEmittedOne.await(30, TimeUnit.SECONDS)) { latchTimeout.set(true); } } catch (InterruptedException e) { @@ -412,7 +415,7 @@ public void run() { }).start(); - if(!observerCompleted.await(30, TimeUnit.SECONDS)) { + if (!observerCompleted.await(30, TimeUnit.SECONDS)) { latchTimeout.set(true); } @@ -439,4 +442,169 @@ public Observable call() { assertEquals("timeoutSelector is null", ex.getMessage()); } } + + @Test + public void disconnectOnTimeout() { + final List list = Collections.synchronizedList(new ArrayList()); + + final TestScheduler sch = new TestScheduler(); + + Subject subject = PublishSubject.create(); + Observable initialObservable = subject.share() + .map(new Func1() { + @Override + public Long call(Long value) { + list.add("Received value " + value); + return value; + } + }); + + Observable timeoutObservable = initialObservable + .map(new Func1() { + @Override + public Long call(Long value) { + list.add("Timeout received value " + value); + return value; + } + }); + + TestSubscriber subscriber = new TestSubscriber(); + initialObservable + .doOnUnsubscribe(new Action0() { + @Override + public void call() { + list.add("Unsubscribed"); + } + }) + .timeout( + new Func0>() { + @Override + public Observable call() { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, + new Func1>() { + @Override + public Observable call(Long v) { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, + timeoutObservable).subscribe(subscriber); + + subject.onNext(5L); + + sch.advanceTimeBy(2, TimeUnit.SECONDS); + + subject.onNext(10L); + subject.onCompleted(); + + subscriber.awaitTerminalEvent(); + subscriber.assertNoErrors(); + subscriber.assertValues(5L, 10L); + + assertEquals(Arrays.asList( + "Received value 5", + "Unsubscribed", + "Received value 10", + "Timeout received value 10" + ), list); + } + + @Test + public void fallbackIsError() { + final TestScheduler sch = new TestScheduler(); + + AssertableSubscriber as = Observable.never() + .timeout(new Func0>() { + @Override + public Observable call() { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, + new Func1>() { + @Override + public Observable call(Object v) { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, Observable.error(new TestException())) + .test(); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + as.assertFailure(TestException.class); + } + + @Test + public void mainErrors() { + final TestScheduler sch = new TestScheduler(); + + AssertableSubscriber as = Observable.error(new IOException()) + .timeout(new Func0>() { + @Override + public Observable call() { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, + new Func1>() { + @Override + public Observable call(Object v) { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, Observable.error(new TestException())) + .test(); + + as.assertFailure(IOException.class); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + as.assertFailure(IOException.class); + } + + @Test + public void timeoutCompletesWithFallback() { + final TestScheduler sch = new TestScheduler(); + + AssertableSubscriber as = Observable.never() + .timeout(new Func0>() { + @Override + public Observable call() { + return Observable.timer(1, TimeUnit.SECONDS, sch).ignoreElements(); + } + }, + new Func1>() { + @Override + public Observable call(Object v) { + return Observable.timer(1, TimeUnit.SECONDS, sch); + } + }, Observable.just(1)) + .test(); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + as.assertResult(1); + } + + @Test + public void nullItemTimeout() { + final TestScheduler sch = new TestScheduler(); + + AssertableSubscriber as = Observable.just(1).concatWith(Observable.never()) + .timeout(new Func0>() { + @Override + public Observable call() { + return Observable.timer(1, TimeUnit.SECONDS, sch).ignoreElements(); + } + }, + new Func1>() { + @Override + public Observable call(Object v) { + return null; + } + }, Observable.just(1)) + .test(); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + as.assertFailureAndMessage(NullPointerException.class, "The itemTimeoutIndicator returned a null Observable", 1); + } } diff --git a/src/test/java/rx/internal/operators/OperatorToObservableSortedListTest.java b/src/test/java/rx/internal/operators/OperatorToObservableSortedListTest.java index 2fb7bc4d28..5081c2cb63 100644 --- a/src/test/java/rx/internal/operators/OperatorToObservableSortedListTest.java +++ b/src/test/java/rx/internal/operators/OperatorToObservableSortedListTest.java @@ -259,11 +259,11 @@ public void testSortedWithNonComparable() { testSubscriber.assertNotCompleted(); } - final static class NonComparable{ + final static class NonComparable { public int i; public String s; - NonComparable(int i, String s){ + NonComparable(int i, String s) { this.i = i; this.s = s; } diff --git a/src/test/java/rx/internal/operators/OperatorUnsubscribeOnTest.java b/src/test/java/rx/internal/operators/OperatorUnsubscribeOnTest.java index 8e4317af40..c409a467bf 100644 --- a/src/test/java/rx/internal/operators/OperatorUnsubscribeOnTest.java +++ b/src/test/java/rx/internal/operators/OperatorUnsubscribeOnTest.java @@ -39,7 +39,7 @@ public void testUnsubscribeWhenSubscribeOnAndUnsubscribeOnAreOnSameThread() thro try { final ThreadSubscription subscription = new ThreadSubscription(); final AtomicReference subscribeThread = new AtomicReference(); - Observable w = Observable.create(new OnSubscribe() { + Observable w = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { @@ -84,7 +84,7 @@ public void testUnsubscribeWhenSubscribeOnAndUnsubscribeOnAreOnDifferentThreads( try { final ThreadSubscription subscription = new ThreadSubscription(); final AtomicReference subscribeThread = new AtomicReference(); - Observable w = Observable.create(new OnSubscribe() { + Observable w = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber t1) { @@ -204,4 +204,31 @@ public Thread getThread() { } } + + @Test + public void backpressure() { + AssertableSubscriber as = Observable.range(1, 10) + .unsubscribeOn(Schedulers.trampoline()) + .test(0); + + as.assertNoValues() + .assertNoErrors() + .assertNotCompleted(); + + as.requestMore(1); + + as.assertValue(1) + .assertNoErrors() + .assertNotCompleted(); + + as.requestMore(3); + + as.assertValues(1, 2, 3, 4) + .assertNoErrors() + .assertNotCompleted(); + + as.requestMore(10); + + as.assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } } \ No newline at end of file diff --git a/src/test/java/rx/internal/operators/OperatorWindowWithSizeTest.java b/src/test/java/rx/internal/operators/OperatorWindowWithSizeTest.java index 7f74cd1ee0..2e9b718e67 100644 --- a/src/test/java/rx/internal/operators/OperatorWindowWithSizeTest.java +++ b/src/test/java/rx/internal/operators/OperatorWindowWithSizeTest.java @@ -249,7 +249,7 @@ public void onCompleted() { } public static Observable hotStream() { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { while (!s.isUnsubscribed()) { diff --git a/src/test/java/rx/internal/operators/OperatorWindowWithStartEndObservableTest.java b/src/test/java/rx/internal/operators/OperatorWindowWithStartEndObservableTest.java index 858465a877..eeb47b89be 100644 --- a/src/test/java/rx/internal/operators/OperatorWindowWithStartEndObservableTest.java +++ b/src/test/java/rx/internal/operators/OperatorWindowWithStartEndObservableTest.java @@ -46,7 +46,7 @@ public void testObservableBasedOpenerAndCloser() { final List list = new ArrayList(); final List> lists = new ArrayList>(); - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 10); @@ -58,7 +58,7 @@ public void call(Subscriber observer) { } }); - Observable openings = Observable.create(new Observable.OnSubscribe() { + Observable openings = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, new Object(), 50); @@ -70,7 +70,7 @@ public void call(Subscriber observer) { Func1> closer = new Func1>() { @Override public Observable call(Object opening) { - return Observable.create(new Observable.OnSubscribe() { + return Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, new Object(), 100); @@ -94,7 +94,7 @@ public void testObservableBasedCloser() { final List list = new ArrayList(); final List> lists = new ArrayList>(); - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 10); @@ -110,7 +110,7 @@ public void call(Subscriber observer) { int calls; @Override public Observable call() { - return Observable.create(new Observable.OnSubscribe() { + return Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { int c = calls++; diff --git a/src/test/java/rx/internal/operators/OperatorWindowWithTimeTest.java b/src/test/java/rx/internal/operators/OperatorWindowWithTimeTest.java index 71a59ff510..78500db578 100644 --- a/src/test/java/rx/internal/operators/OperatorWindowWithTimeTest.java +++ b/src/test/java/rx/internal/operators/OperatorWindowWithTimeTest.java @@ -45,7 +45,7 @@ public void testTimedAndCount() { final List list = new ArrayList(); final List> lists = new ArrayList>(); - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 10); @@ -78,7 +78,7 @@ public void testTimed() { final List list = new ArrayList(); final List> lists = new ArrayList>(); - Observable source = Observable.create(new Observable.OnSubscribe() { + Observable source = Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { push(observer, "one", 98); diff --git a/src/test/java/rx/internal/operators/OperatorZipIterableTest.java b/src/test/java/rx/internal/operators/OperatorZipIterableTest.java index d82465428d..bfcd20c09d 100644 --- a/src/test/java/rx/internal/operators/OperatorZipIterableTest.java +++ b/src/test/java/rx/internal/operators/OperatorZipIterableTest.java @@ -366,8 +366,8 @@ static final class SquareStr implements Func1 { @Override public String call(Integer t1) { counter.incrementAndGet(); - System.out.println("Omg I'm calculating so hard: " + t1 + "*" + t1 + "=" + (t1*t1)); - return " " + (t1*t1); + System.out.println("Omg I'm calculating so hard: " + t1 + "*" + t1 + "=" + (t1 * t1)); + return " " + (t1 * t1); } } diff --git a/src/test/java/rx/internal/operators/OperatorZipTest.java b/src/test/java/rx/internal/operators/OperatorZipTest.java index 950d506201..5f4b06bcbe 100644 --- a/src/test/java/rx/internal/operators/OperatorZipTest.java +++ b/src/test/java/rx/internal/operators/OperatorZipTest.java @@ -94,7 +94,7 @@ public void testStartpingDifferentLengthObservableSequences1() { TestObservable w2 = new TestObservable(); TestObservable w3 = new TestObservable(); - Observable zipW = Observable.zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr()); + Observable zipW = Observable.zip(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2), Observable.unsafeCreate(w3), getConcat3StringsZipr()); zipW.subscribe(w); /* simulate sending data */ @@ -128,7 +128,7 @@ public void testStartpingDifferentLengthObservableSequences2() { TestObservable w2 = new TestObservable(); TestObservable w3 = new TestObservable(); - Observable zipW = Observable.zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr()); + Observable zipW = Observable.zip(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2), Observable.unsafeCreate(w3), getConcat3StringsZipr()); zipW.subscribe(w); /* simulate sending data */ @@ -1212,7 +1212,7 @@ public boolean hasNext() { Observable OBSERVABLE_OF_5_INTEGERS = OBSERVABLE_OF_5_INTEGERS(new AtomicInteger()); Observable OBSERVABLE_OF_5_INTEGERS(final AtomicInteger numEmitted) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber o) { @@ -1231,7 +1231,7 @@ public void call(final Subscriber o) { } Observable ASYNC_OBSERVABLE_OF_INFINITE_INTEGERS(final CountDownLatch latch) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber o) { @@ -1315,7 +1315,7 @@ public void testZipRace() { // time limit of 9 seconds ( 1 second less than the test timeout) is // used so that this test will not timeout on slow machines. int i = 0; - while (System.currentTimeMillis()-startTime < 9000 && i++ < 100000) { + while (System.currentTimeMillis() - startTime < 9000 && i++ < 100000) { int value = Observable.zip(src, src, new Func2() { @Override public Integer call(Integer t1, Integer t2) { diff --git a/src/test/java/rx/internal/operators/SafeSubscriberTest.java b/src/test/java/rx/internal/operators/SafeSubscriberTest.java index 8cd7c18031..57fc3446bc 100644 --- a/src/test/java/rx/internal/operators/SafeSubscriberTest.java +++ b/src/test/java/rx/internal/operators/SafeSubscriberTest.java @@ -38,7 +38,7 @@ public class SafeSubscriberTest { @Test public void testOnNextAfterOnError() { TestObservable t = new TestObservable(); - Observable st = Observable.create(t); + Observable st = Observable.unsafeCreate(t); @SuppressWarnings("unchecked") Observer w = mock(Observer.class); @@ -60,7 +60,7 @@ public void testOnNextAfterOnError() { @Test public void testOnCompletedAfterOnError() { TestObservable t = new TestObservable(); - Observable st = Observable.create(t); + Observable st = Observable.unsafeCreate(t); @SuppressWarnings("unchecked") Observer w = mock(Observer.class); @@ -82,7 +82,7 @@ public void testOnCompletedAfterOnError() { @Test public void testOnNextAfterOnCompleted() { TestObservable t = new TestObservable(); - Observable st = Observable.create(t); + Observable st = Observable.unsafeCreate(t); @SuppressWarnings("unchecked") Observer w = mock(Observer.class); @@ -105,7 +105,7 @@ public void testOnNextAfterOnCompleted() { @Test public void testOnErrorAfterOnCompleted() { TestObservable t = new TestObservable(); - Observable st = Observable.create(t); + Observable st = Observable.unsafeCreate(t); @SuppressWarnings("unchecked") Observer w = mock(Observer.class); @@ -126,7 +126,7 @@ public void testOnErrorAfterOnCompleted() { */ private static class TestObservable implements Observable.OnSubscribe { - Observer observer = null; + Observer observer; /* used to simulate subscription */ public void sendOnCompleted() { diff --git a/src/test/java/rx/internal/operators/SingleFromEmitterTest.java b/src/test/java/rx/internal/operators/SingleFromEmitterTest.java new file mode 100644 index 0000000000..a9f8dfd5b1 --- /dev/null +++ b/src/test/java/rx/internal/operators/SingleFromEmitterTest.java @@ -0,0 +1,221 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.*; + +import rx.*; +import rx.exceptions.TestException; +import rx.functions.*; +import rx.observers.AssertableSubscriber; +import rx.plugins.RxJavaHooks; + +public class SingleFromEmitterTest implements Cancellable, Action1 { + + int calls; + + final List errors = Collections.synchronizedList(new ArrayList()); + + @Before + public void before() { + RxJavaHooks.setOnError(this); + } + + @After + public void after() { + RxJavaHooks.reset(); + } + + @Override + public void cancel() throws Exception { + calls++; + } + + @Override + public void call(Throwable t) { + errors.add(t); + } + + @Test + public void normal() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onSuccess(1); + } + }) + .test() + .assertResult(1); + + assertEquals(1, calls); + } + + @Test + public void nullSuccess() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onSuccess(null); + } + }) + .test() + .assertResult((Object)null); + + assertEquals(1, calls); + } + + @Test + public void error() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onError(new TestException()); + } + }) + .test() + .assertFailure(TestException.class); + + assertEquals(1, calls); + } + + @Test + public void nullError() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onError(null); + } + }) + .test() + .assertFailure(NullPointerException.class); + + assertEquals(1, calls); + } + + @Test + public void crash() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + + assertEquals(1, calls); + } + + @Test + public void crash2() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onError(null); + throw new TestException(); + } + }) + .test() + .assertFailure(NullPointerException.class); + + assertEquals(1, calls); + + assertTrue(errors.get(0).toString(), errors.get(0) instanceof TestException); + } + + @Test + public void unsubscribe() { + @SuppressWarnings("unchecked") + final SingleEmitter[] emitter = new SingleEmitter[1]; + + AssertableSubscriber ts = Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + emitter[0] = e; + } + }) + .test(); + + ts.unsubscribe(); + + emitter[0].onSuccess(1); + emitter[0].onError(new TestException()); + + ts.assertNoErrors().assertNotCompleted().assertNoValues(); + + assertTrue(errors.get(0).toString(), errors.get(0) instanceof TestException); + } + + @Test + public void onSuccessThrows() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onSuccess(1); + } + }) + .subscribe(new SingleSubscriber() { + @Override + public void onSuccess(Object value) { + throw new TestException(); + } + @Override + public void onError(Throwable error) { + } + }); + + assertEquals(1, calls); + + assertTrue(errors.get(0).toString(), errors.get(0) instanceof TestException); + } + + @Test + public void onErrorThrows() { + Single.fromEmitter(new Action1>() { + @Override + public void call(SingleEmitter e) { + e.setCancellation(SingleFromEmitterTest.this); + e.onError(new IOException()); + } + }) + .subscribe(new SingleSubscriber() { + @Override + public void onSuccess(Object value) { + } + @Override + public void onError(Throwable error) { + throw new TestException(); + } + }); + + assertEquals(1, calls); + + assertTrue(errors.get(0).toString(), errors.get(0) instanceof TestException); + } +} diff --git a/src/test/java/rx/internal/operators/SingleOnSubscribeUsingTest.java b/src/test/java/rx/internal/operators/SingleOnSubscribeUsingTest.java index 61c2015b4e..e8fc60ead5 100644 --- a/src/test/java/rx/internal/operators/SingleOnSubscribeUsingTest.java +++ b/src/test/java/rx/internal/operators/SingleOnSubscribeUsingTest.java @@ -273,7 +273,7 @@ public void disposesEagerlyBeforeCompletion() { final List events = new ArrayList(); Func0 resourceFactory = createResourceFactory(events); final Action1 completion = createOnSuccessAction(events); - final Action0 unsub =createUnsubAction(events); + final Action0 unsub = createUnsubAction(events); Func1> observableFactory = new Func1>() { @Override @@ -298,7 +298,7 @@ public void doesNotDisposesEagerlyBeforeCompletion() { final List events = new ArrayList(); Func0 resourceFactory = createResourceFactory(events); final Action1 completion = createOnSuccessAction(events); - final Action0 unsub =createUnsubAction(events); + final Action0 unsub = createUnsubAction(events); Func1> observableFactory = new Func1>() { @Override diff --git a/src/test/java/rx/internal/operators/SingleOperatorCastTest.java b/src/test/java/rx/internal/operators/SingleOperatorCastTest.java new file mode 100644 index 0000000000..6d60c8b411 --- /dev/null +++ b/src/test/java/rx/internal/operators/SingleOperatorCastTest.java @@ -0,0 +1,51 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package rx.internal.operators; + +import org.junit.Test; +import rx.Observer; +import rx.Single; + +import static org.mockito.Mockito.*; + +public class SingleOperatorCastTest { + + @Test + public void testSingleCast() { + Single source = Single.just(1); + Single single = source.cast(Integer.class); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + single.subscribe(observer); + verify(observer, times(1)).onNext(1); + verify(observer, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(observer, times(1)).onCompleted(); + } + + @Test + public void testSingleCastWithWrongType() { + Single source = Single.just(1); + Single single = source.cast(Boolean.class); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + single.subscribe(observer); + verify(observer, times(1)).onError( + org.mockito.Matchers.any(ClassCastException.class)); + } +} diff --git a/src/test/java/rx/internal/operators/SingleTakeUntilTest.java b/src/test/java/rx/internal/operators/SingleTakeUntilTest.java new file mode 100644 index 0000000000..45a9bb23db --- /dev/null +++ b/src/test/java/rx/internal/operators/SingleTakeUntilTest.java @@ -0,0 +1,61 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.internal.operators; + +import java.util.concurrent.CancellationException; + +import static org.junit.Assert.*; +import org.junit.Test; + +import rx.*; +import rx.observers.AssertableSubscriber; + +public class SingleTakeUntilTest { + + @Test + public void withSingleMessage() { + AssertableSubscriber ts = Single.just(1).takeUntil(Single.just(2)) + .test() + .assertFailure(CancellationException.class); + + String message = ts.getOnErrorEvents().get(0).getMessage(); + + assertTrue(message, message.startsWith("Single::takeUntil(Single)")); + } + + @Test + public void withCompletableMessage() { + AssertableSubscriber ts = Single.just(1).takeUntil(Completable.complete()) + .test() + .assertFailure(CancellationException.class); + + String message = ts.getOnErrorEvents().get(0).getMessage(); + + assertTrue(message, message.startsWith("Single::takeUntil(Completable)")); + } + + @Test + public void withObservableMessage() { + AssertableSubscriber ts = Single.just(1).takeUntil(Observable.just(1)) + .test() + .assertFailure(CancellationException.class); + + String message = ts.getOnErrorEvents().get(0).getMessage(); + + assertTrue(message, message.startsWith("Single::takeUntil(Observable)")); + } +} diff --git a/src/test/java/rx/internal/producers/ProducersTest.java b/src/test/java/rx/internal/producers/ProducersTest.java index f41fcda71a..46533b8ada 100644 --- a/src/test/java/rx/internal/producers/ProducersTest.java +++ b/src/test/java/rx/internal/producers/ProducersTest.java @@ -365,7 +365,7 @@ public void testObserverArbiterAsync() { .map(plus(40)) ); - Observable source = Observable.create( + Observable source = Observable.unsafeCreate( new SwitchTimer(timers, 550, TimeUnit.MILLISECONDS, test)); diff --git a/src/test/java/rx/internal/producers/SingleDelayedProducerTest.java b/src/test/java/rx/internal/producers/SingleDelayedProducerTest.java index d3e074df04..f21753f4a3 100644 --- a/src/test/java/rx/internal/producers/SingleDelayedProducerTest.java +++ b/src/test/java/rx/internal/producers/SingleDelayedProducerTest.java @@ -56,14 +56,14 @@ public void requestCompleteRace() throws Exception { @Override public void call() { waiter.decrementAndGet(); - while (waiter.get() != 0) ; + while (waiter.get() != 0) { } pa.request(1); cdl.countDown(); } }); waiter.decrementAndGet(); - while (waiter.get() != 0) ; + while (waiter.get() != 0) { } pa.setValue(1); if (!cdl.await(5, TimeUnit.SECONDS)) { Assert.fail("The wait for completion timed out"); diff --git a/src/test/java/rx/internal/schedulers/NewThreadWorkerTest.java b/src/test/java/rx/internal/schedulers/NewThreadWorkerTest.java index 4368953241..f162ad958b 100644 --- a/src/test/java/rx/internal/schedulers/NewThreadWorkerTest.java +++ b/src/test/java/rx/internal/schedulers/NewThreadWorkerTest.java @@ -49,7 +49,7 @@ public void findSetRemoveOnCancelPolicyMethodShouldNotFindMethod() { private static abstract class ScheduledExecutorServiceWithSetRemoveOnCancelPolicy implements ScheduledExecutorService { // Just declaration of required method to allow run tests on JDK 6 - public void setRemoveOnCancelPolicy(boolean value) {} + public void setRemoveOnCancelPolicy(boolean value) { } } @Test diff --git a/src/test/java/rx/internal/util/IndexedRingBufferTest.java b/src/test/java/rx/internal/util/IndexedRingBufferTest.java index 5ed75ea5e5..233557ac08 100644 --- a/src/test/java/rx/internal/util/IndexedRingBufferTest.java +++ b/src/test/java/rx/internal/util/IndexedRingBufferTest.java @@ -186,7 +186,7 @@ public Boolean call(String t1) { list.clear(); nextIndex = buffer.forEach(new Func1() { - int i = 0; + int i; @Override public Boolean call(String t1) { diff --git a/src/test/java/rx/internal/util/JCToolsQueueTests.java b/src/test/java/rx/internal/util/JCToolsQueueTests.java index 1ec3c202bb..eb89381d94 100644 --- a/src/test/java/rx/internal/util/JCToolsQueueTests.java +++ b/src/test/java/rx/internal/util/JCToolsQueueTests.java @@ -169,7 +169,7 @@ public void run() { Integer j; for (int i = 0; i < 1000 * 1000; i++) { - while ((j = q.poll()) == null); + while ((j = q.poll()) == null) { } assertTrue("Value " + j + " already removed", set.remove(j)); } assertTrue("Set is not empty", set.isEmpty()); @@ -242,7 +242,7 @@ public void run() { Integer j; for (int i = 0; i < 1000 * 1000; i++) { - while ((j = q.poll()) == null); + while ((j = q.poll()) == null) { } assertTrue("Value " + j + " already removed", set.remove(j)); } assertTrue("Set is not empty", set.isEmpty()); @@ -366,7 +366,7 @@ public void testSpscLinkedAtomicQueuePipelined() throws InterruptedException { public void run() { Integer j; for (int i = 0; i < 1000 * 1000; i++) { - while ((j = q.poll()) == null); + while ((j = q.poll()) == null) { } if (j == i) { count.getAndIncrement(); } @@ -424,7 +424,7 @@ public void testSpscLinkedQueuePipelined() throws InterruptedException { public void run() { Integer j; for (int i = 0; i < 1000 * 1000; i++) { - while ((j = q.poll()) == null); + while ((j = q.poll()) == null) { } if (j == i) { count.getAndIncrement(); } diff --git a/src/test/java/rx/internal/util/PlatformDependentTest.java b/src/test/java/rx/internal/util/PlatformDependentTest.java index 64f47915b1..b868145a83 100644 --- a/src/test/java/rx/internal/util/PlatformDependentTest.java +++ b/src/test/java/rx/internal/util/PlatformDependentTest.java @@ -19,9 +19,18 @@ import rx.TestUtil; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + public class PlatformDependentTest { @Test public void constructorShouldBePrivate() { TestUtil.checkUtilityClass(PlatformDependent.class); } + + @Test + public void platformShouldNotBeAndroid() { + assertFalse(PlatformDependent.isAndroid()); + assertEquals(0, PlatformDependent.getAndroidApiVersion()); + } } diff --git a/src/test/java/rx/internal/util/SynchronizedQueueTest.java b/src/test/java/rx/internal/util/SynchronizedQueueTest.java deleted file mode 100644 index 977c458d7c..0000000000 --- a/src/test/java/rx/internal/util/SynchronizedQueueTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package rx.internal.util; - -import static org.junit.Assert.assertTrue; - -import java.util.*; - -import org.junit.*; - -public class SynchronizedQueueTest { - SynchronizedQueue q = new SynchronizedQueue(); - - @Test - public void testEquals() { - - assertTrue(q.equals(q)); - } - - @Test - public void contains() { - q.offer(1); - Assert.assertTrue(q.add(2)); - - Assert.assertEquals(2, q.size()); - Assert.assertTrue(q.contains(1)); - Assert.assertTrue(q.contains(2)); - Assert.assertFalse(q.contains(3)); - } - - @Test - public void iterator() { - q.offer(1); - - Assert.assertEquals(1, q.iterator().next()); - } - - @Test - public void remove() { - q.offer(1); - q.offer(2); - q.offer(3); - - Assert.assertTrue(q.remove(2)); - Assert.assertFalse(q.remove(2)); - } - - @Test - public void addAllContainsAll() { - q.addAll(Arrays.asList(1, 2, 3, 4)); - - q.removeAll(Arrays.asList(2, 3)); - - Assert.assertEquals(2, q.size()); - Assert.assertTrue(q.contains(1)); - Assert.assertFalse(q.contains(2)); - Assert.assertFalse(q.contains(3)); - Assert.assertTrue(q.contains(4)); - - Assert.assertTrue(q.containsAll(Arrays.asList(1, 4))); - Assert.assertFalse(q.containsAll(Arrays.asList(2, 3))); - } - - @Test - public void retainAll() { - q.addAll(Arrays.asList(1, 2, 3, 4)); - - q.retainAll(Arrays.asList(2, 3)); - - Assert.assertEquals(2, q.size()); - Assert.assertFalse(q.contains(1)); - Assert.assertTrue(q.contains(2)); - Assert.assertTrue(q.contains(3)); - Assert.assertFalse(q.contains(4)); - } - - @Test - public void clear() { - q.addAll(Arrays.asList(1, 2, 3, 4)); - - q.clear(); - - Assert.assertEquals(0, q.size()); - Assert.assertTrue(q.isEmpty()); - } - - @Test - public void toStringValue() { - q.offer(1); - - Assert.assertEquals("[1]", q.toString()); - } - - @Test - public void equalsTo() { - q.offer(1); - - SynchronizedQueue q2 = new SynchronizedQueue(); - q2.offer(1); - - SynchronizedQueue q3 = new SynchronizedQueue(); - q3.offer(2); - - Assert.assertEquals(q, q2); - Assert.assertEquals(q.hashCode(), q2.hashCode()); - Assert.assertNotEquals(q, q3); - - Assert.assertFalse(q.equals(null)); - Assert.assertFalse(q.equals(1)); - - Assert.assertEquals(q, q.clone()); - - } - - @Test - public void toArray() { - q.offer(1); - - Object[] a = q.toArray(); - - Object[] b = q.toArray(new Integer[1]); - - Assert.assertEquals(1, a[0]); - Assert.assertEquals(1, b[0]); - } - - @Test - public void peekElement() { - q.offer(1); - - Assert.assertEquals(1, q.peek()); - Assert.assertEquals(1, q.element()); - Assert.assertEquals(1, q.remove()); - try { - q.element(); - Assert.fail("Failed to throw on empty queue"); - } catch (NoSuchElementException ex) { - // expected - } - } -} diff --git a/src/test/java/rx/observables/AsyncOnSubscribeTest.java b/src/test/java/rx/observables/AsyncOnSubscribeTest.java index 931b7c1d3f..72afaad285 100644 --- a/src/test/java/rx/observables/AsyncOnSubscribeTest.java +++ b/src/test/java/rx/observables/AsyncOnSubscribeTest.java @@ -60,12 +60,12 @@ public void setup() { @Test public void testSerializesConcurrentObservables() throws InterruptedException { final TestScheduler scheduler = new TestScheduler(); - AsyncOnSubscribe os = AsyncOnSubscribe.createStateful(new Func0(){ + AsyncOnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { if (state == 1) { @@ -76,9 +76,9 @@ public Integer call(Integer state, Long requested, Observer o = Observable.just(5, 6, 7, 8); observer.onNext(o); - } - else + } else { observer.onCompleted(); + } return state + 1; }}); // initial request emits [[1, 2, 3, 4]] on delay @@ -105,12 +105,12 @@ else if (state == 2) { public void testSubscribedByBufferingOperator() { final TestScheduler scheduler = new TestScheduler(); OnSubscribe os = AsyncOnSubscribe.createStateless( - new Action2>>(){ + new Action2>>() { @Override public void call(Long requested, Observer> observer) { observer.onNext(Observable.range(1, requested.intValue())); }}); - Observable.create(os).observeOn(scheduler).subscribe(subscriber); + Observable.unsafeCreate(os).observeOn(scheduler).subscribe(subscriber); subscriber.requestMore(RxRingBuffer.SIZE); scheduler.advanceTimeBy(10, TimeUnit.DAYS); subscriber.assertNoErrors(); @@ -121,19 +121,19 @@ public void call(Long requested, Observer> observe @Test public void testOnUnsubscribeHasCorrectState() throws InterruptedException { final AtomicInteger lastState = new AtomicInteger(-1); - OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { if (state < 3) { observer.onNext(Observable.just(state)); - } - else + } else { observer.onCompleted(); + } return state + 1; }}, new Action1() { @@ -141,7 +141,7 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateless(new Action2>>(){ + OnSubscribe os = AsyncOnSubscribe.createStateless(new Action2>>() { @Override public void call(Long requested, Observer> observer) { observer.onCompleted(); }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertNoErrors(); subscriber.assertCompleted(); @@ -167,14 +167,14 @@ public void call(Long requested, Observer> observe @Test public void testTryOnNextTwice() throws InterruptedException { - OnSubscribe os = AsyncOnSubscribe.createStateless(new Action2>>(){ + OnSubscribe os = AsyncOnSubscribe.createStateless(new Action2>>() { @Override public void call(Long requested, Observer> observer) { observer.onNext(Observable.just(1)); observer.onNext(Observable.just(2)); } }); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertError(IllegalStateException.class); subscriber.assertNotCompleted(); @@ -184,12 +184,12 @@ public void call(Long requested, Observer> observe @Test public void testThrowException() throws InterruptedException { OnSubscribe os = AsyncOnSubscribe.createStateless( - new Action2>>(){ + new Action2>>() { @Override public void call(Long requested, Observer> observer) { throw new TestException(); }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertError(TestException.class); subscriber.assertNotCompleted(); @@ -198,18 +198,18 @@ public void call(Long requested, Observer> observe @Test public void testThrowExceptionAfterTerminal() throws InterruptedException { - OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { observer.onCompleted(); throw new TestException(); }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertNoErrors(); subscriber.assertCompleted(); @@ -218,19 +218,19 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { observer.onCompleted(); observer.onNext(Observable.just(1)); return 1; }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertNoErrors(); subscriber.assertCompleted(); @@ -239,19 +239,19 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { observer.onError(new TestException()); observer.onNext(Observable.just(1)); return 1; }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertError(TestException.class); subscriber.assertNotCompleted(); @@ -260,19 +260,19 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { observer.onNext(Observable.empty()); observer.onCompleted(); return state; }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertNoErrors(); subscriber.assertCompleted(); @@ -281,19 +281,19 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { observer.onError(new TestException()); return state; } }); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); subscriber.assertError(TestException.class); subscriber.assertNotCompleted(); @@ -302,12 +302,12 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { observer.onCompleted(); @@ -315,7 +315,7 @@ public Integer call(Integer state, Long requested, Observer os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { Observable o1; switch (state) { case 1: o1 = Observable.just(1) - .doOnUnsubscribe(new Action0(){ + .doOnUnsubscribe(new Action0() { @Override public void call() { l1.incrementAndGet(); @@ -346,7 +346,7 @@ public void call() { break; case 2: o1 = Observable.just(2) - .doOnUnsubscribe(new Action0(){ + .doOnUnsubscribe(new Action0() { @Override public void call() { l2.incrementAndGet(); @@ -359,7 +359,7 @@ public void call() { observer.onNext(o1); return state + 1; }}); - Observable.create(os).subscribe(subscriber); + Observable.unsafeCreate(os).subscribe(subscriber); subscriber.requestMore(1); // [[1]] subscriber.requestMore(2); // [[2]] subscriber.requestMore(2); // onCompleted @@ -380,19 +380,19 @@ public void testUnsubscribesFromAllNonTerminatedObservables() throws Interrupted final AtomicInteger l2 = new AtomicInteger(); final TestScheduler scheduler = new TestScheduler(); final AtomicReference sub = new AtomicReference(); - OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { switch (state) { case 1: observer.onNext(Observable.range(1, requested.intValue()) .subscribeOn(scheduler) - .doOnUnsubscribe(new Action0(){ + .doOnUnsubscribe(new Action0() { @Override public void call() { l1.incrementAndGet(); @@ -402,7 +402,7 @@ public void call() { observer.onNext(Observable.just(1) .concatWith(Observable.never()) .subscribeOn(scheduler) - .doOnUnsubscribe(new Action0(){ + .doOnUnsubscribe(new Action0() { @Override public void call() { l2.incrementAndGet(); @@ -413,7 +413,7 @@ public void call() { } return state + 1; }}); - Subscription subscription = Observable.create(os) + Subscription subscription = Observable.unsafeCreate(os) .observeOn(scheduler, 1) .subscribe(subscriber); sub.set(subscription); @@ -429,29 +429,30 @@ public void call() { assertEquals("did not unsub from Observable.never() inner obs", 1, l2.get()); } - private static class Foo {} - private static class Bar extends Foo {} + private static class Foo { } + private static class Bar extends Foo { } @Test public void testGenerics() { - AsyncOnSubscribe.createStateless(new Action2>>(){ + AsyncOnSubscribe.createStateless(new Action2>>() { @Override public void call(Long state, Observer> observer) { - if (state == null) + if (state == null) { observer.onNext(Observable.just(new Foo())); - else + } else { observer.onNext(Observable.just(new Bar())); + } }}); } @Test public void testUnderdeliveryCorrection() { - OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = AsyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; }}, - new Func3>, Integer>(){ + new Func3>, Integer>() { @Override public Integer call(Integer state, Long requested, Observer> observer) { switch (state) { @@ -464,7 +465,7 @@ public Integer call(Integer state, Long requested, Observer os = Observable.create(AsyncOnSubscribe. createStateful( + new Func0() { + + @Override + public Integer call() { + return 0; + } + + }, + new Func3>, Integer>() { + + @Override + public Integer call(Integer state, Long requested, Observer> emitter) { + if (state == 0) { + emitter.onNext(Observable.range(0,100).delay(1, TimeUnit.SECONDS, scheduler)); + } else { + emitter.onCompleted(); + } + return state + 1; + } + + })); + + TestSubscriber ts = new TestSubscriber(); + os.mergeWith(Observable.just(0)).subscribe(ts); + scheduler.advanceTimeBy(1, TimeUnit.HOURS); + ts.assertCompleted(); + ts.assertValueCount(101); + } + } \ No newline at end of file diff --git a/src/test/java/rx/observables/BlockingObservableTest.java b/src/test/java/rx/observables/BlockingObservableTest.java index 43e0889318..fc802f978a 100644 --- a/src/test/java/rx/observables/BlockingObservableTest.java +++ b/src/test/java/rx/observables/BlockingObservableTest.java @@ -259,7 +259,7 @@ public void testToIterableManyTimes() { @Test(expected = TestException.class) public void testToIterableWithException() { - BlockingObservable obs = BlockingObservable.from(Observable.create(new Observable.OnSubscribe() { + BlockingObservable obs = BlockingObservable.from(Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(Subscriber observer) { @@ -281,7 +281,7 @@ public void call(Subscriber observer) { @Test public void testForEachWithError() { try { - BlockingObservable.from(Observable.create(new Observable.OnSubscribe() { + BlockingObservable.from(Observable.unsafeCreate(new Observable.OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -382,7 +382,7 @@ public Boolean call(String args) { @Test public void testSingleOrDefaultUnsubscribe() throws InterruptedException { final CountDownLatch unsubscribe = new CountDownLatch(1); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber subscriber) { subscriber.add(Subscriptions.create(new Action0() { @@ -565,7 +565,7 @@ private Observable createSynchronousObservable() { @Override public Iterator iterator() { return new Iterator() { - private boolean nextCalled = false; + private boolean nextCalled; @Override public boolean hasNext() { diff --git a/src/test/java/rx/observables/SyncOnSubscribeTest.java b/src/test/java/rx/observables/SyncOnSubscribeTest.java index 398c1a4d45..a219e1d7b7 100644 --- a/src/test/java/rx/observables/SyncOnSubscribeTest.java +++ b/src/test/java/rx/observables/SyncOnSubscribeTest.java @@ -52,7 +52,7 @@ public void call(Observer subscriber) { TestSubscriber ts = new TestSubscriber(); - Observable.create(os).subscribe(ts); + Observable.unsafeCreate(os).subscribe(ts); ts.assertNoErrors(); ts.assertTerminalEvent(); @@ -62,7 +62,7 @@ public void call(Observer subscriber) { @Test public void testStateAfterTerminal() { final AtomicInteger finalStateValue = new AtomicInteger(-1); - OnSubscribe os = SyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = SyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return 1; @@ -82,7 +82,7 @@ public void call(Integer t) { TestSubscriber ts = new TestSubscriber(); - Observable.create(os).subscribe(ts); + Observable.unsafeCreate(os).subscribe(ts); ts.assertNoErrors(); ts.assertTerminalEvent(); @@ -103,7 +103,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, times(1)).onNext(1); verify(o, never()).onNext(2); @@ -124,7 +124,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, times(1)).onNext(1); verify(o, times(1)).onCompleted(); @@ -144,7 +144,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, times(1)).onNext(1); verify(o, times(1)).onCompleted(); @@ -171,7 +171,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, times(1)).onNext(1); verify(o, never()).onCompleted(); @@ -190,7 +190,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, never()).onNext(any(Integer.class)); verify(o, never()).onError(any(Throwable.class)); @@ -206,7 +206,7 @@ public void call(Observer subscriber) { }}); - Observable neverObservable = Observable.create(os).subscribeOn(Schedulers.newThread()); + Observable neverObservable = Observable.unsafeCreate(os).subscribeOn(Schedulers.newThread()); Observable merged = Observable.amb(neverObservable, Observable.timer(100, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.newThread())); Iterator values = merged.toBlocking().toIterable().iterator(); @@ -225,7 +225,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, never()).onNext(any(Integer.class)); verify(o, never()).onCompleted(); @@ -243,7 +243,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, never()).onNext(any(Integer.class)); verify(o, times(1)).onCompleted(); @@ -268,7 +268,7 @@ public void onStart() { } }; - Observable.create(os).subscribe(ts); + Observable.unsafeCreate(os).subscribe(ts); ts.requestMore(1); @@ -288,7 +288,7 @@ public void call(Observer subscriber) { @SuppressWarnings("unchecked") Observer o = mock(Observer.class); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, never()).onNext(any(Integer.class)); verify(o).onError(any(TestException.class)); @@ -299,7 +299,7 @@ public void call(Observer subscriber) { public void testRange() { final int start = 1; final int count = 4000; - OnSubscribe os = SyncOnSubscribe.createStateful(new Func0(){ + OnSubscribe os = SyncOnSubscribe.createStateful(new Func0() { @Override public Integer call() { return start; @@ -319,7 +319,7 @@ public Integer call(Integer state, Observer subscriber) { Observer o = mock(Observer.class); InOrder inOrder = inOrder(o); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, never()).onError(any(TestException.class)); inOrder.verify(o, times(count)).onNext(any(Integer.class)); @@ -357,7 +357,7 @@ public Iterator call(Iterator it, Observer ob Observer o = mock(Observer.class); InOrder inOrder = inOrder(o); - Observable.create(os).subscribe(o); + Observable.unsafeCreate(os).subscribe(o); verify(o, never()).onError(any(TestException.class)); inOrder.verify(o, times(n)).onNext(any()); @@ -385,7 +385,7 @@ public Integer call(Integer state, Observer observer) { Observer o = mock(Observer.class); InOrder inOrder = inOrder(o); - Observable.create(os).take(finalCount).subscribe(o); + Observable.unsafeCreate(os).take(finalCount).subscribe(o); verify(o, never()).onError(any(Throwable.class)); inOrder.verify(o, times(finalCount)).onNext(any()); @@ -416,7 +416,7 @@ public Integer call(Integer state, Observer observer) { onUnSubscribe); TestSubscriber ts = new TestSubscriber(0); - Observable.create(os).subscribe(ts); + Observable.unsafeCreate(os).subscribe(ts); ts.requestMore(finalCount); @@ -451,7 +451,7 @@ public Integer call(Integer state, Observer observer) { TestSubscriber ts = new TestSubscriber(o); - Observable.create(os).take(1).subscribe(ts); + Observable.unsafeCreate(os).take(1).subscribe(ts); verify(o, never()).onError(any(Throwable.class)); verify(onUnSubscribe, times(1)).call(any(Integer.class)); @@ -521,7 +521,7 @@ public Integer call(Integer state, Observer observer) { InOrder inOrder = inOrder(o); final TestSubscriber ts = new TestSubscriber(o); - Observable.create(os).subscribeOn(Schedulers.newThread()).subscribe(ts); + Observable.unsafeCreate(os).subscribeOn(Schedulers.newThread()).subscribe(ts); // wait until the first request has started processing if (!l2.await(2, TimeUnit.SECONDS)) { @@ -557,7 +557,7 @@ public void call(Observer observer) { observer.onNext(null); currentlyEvaluating.set(false); }}, - new Action0(){ + new Action0() { @Override public void call() { calledUnsubscribe.incrementAndGet(); @@ -569,10 +569,10 @@ public void call() { final CountDownLatch latch = new CountDownLatch(1); final TestSubscriber ts = new TestSubscriber(o); - Observable.create(os).lift(new Operator(){ + Observable.unsafeCreate(os).lift(new Operator() { @Override public Subscriber call(final Subscriber subscriber) { - return new Subscriber(subscriber){ + return new Subscriber(subscriber) { @Override public void setProducer(Producer p) { p.request(1); @@ -590,7 +590,7 @@ public void onError(Throwable e) { @Override public void onNext(final Void t) { subscriber.onNext(t); - new Thread(new Runnable(){ + new Thread(new Runnable() { @Override public void run() { try { @@ -634,7 +634,7 @@ public Map call(Map state, Observer source = Observable.create(os); + Observable source = Observable.unsafeCreate(os); for (int i = 0; i < count; i++) { source.subscribe(); } @@ -678,7 +678,7 @@ public void call(Integer t) { subs.add(ts); } TestScheduler scheduler = new TestScheduler(); - Observable o2 = Observable.create(os).subscribeOn(scheduler); + Observable o2 = Observable.unsafeCreate(os).subscribeOn(scheduler); for (Subscriber ts : subs) { o2.subscribe(ts); } @@ -709,8 +709,9 @@ public void testObserveOn() { @Override public Integer call(Integer calls, Observer observer) { observer.onNext(calls); - if (calls == count) + if (calls == count) { observer.onCompleted(); + } return calls + 1; }}, onUnSubscribe); @@ -718,7 +719,7 @@ public Integer call(Integer calls, Observer observer) { TestSubscriber ts = new TestSubscriber(); TestScheduler scheduler = new TestScheduler(); - Observable.create(os).observeOn(scheduler).subscribe(ts); + Observable.unsafeCreate(os).observeOn(scheduler).subscribe(ts); scheduler.triggerActions(); ts.awaitTerminalEvent(); @@ -747,7 +748,7 @@ public void call(Observer observer) { }}, onUnSubscribe); final AtomicReference exception = new AtomicReference(); - Observable.create(os).subscribe(new Subscriber() { + Observable.unsafeCreate(os).subscribe(new Subscriber() { @Override public void onCompleted() { @@ -812,10 +813,10 @@ protected void onUnsubscribe(Object state) { assertEquals(1, countUnsubs.get()); } - private interface FooQux {} - private static class Foo implements FooQux {} - private interface BarQux extends FooQux {} - private static class Bar extends Foo implements BarQux {} + private interface FooQux { } + private static class Foo implements FooQux { } + private interface BarQux extends FooQux { } + private static class Bar extends Foo implements BarQux { } @Test public void testGenericsCreateSingleState() { @@ -932,7 +933,7 @@ private void assertJustBehavior(OnSubscribe os) { } @Test - public void testConcurrentUnsubscribe3000Iterations() throws InterruptedException, BrokenBarrierException, ExecutionException{ + public void testConcurrentUnsubscribe3000Iterations() throws InterruptedException, BrokenBarrierException, ExecutionException { ExecutorService exec = null; try { exec = Executors.newSingleThreadExecutor(); @@ -986,7 +987,9 @@ public Object call() throws Exception { assertEquals("Unsubscribe supposed to be called once", 1, wip.get()); } } finally { - if (exec != null) exec.shutdownNow(); + if (exec != null) { + exec.shutdownNow(); + } } } diff --git a/src/test/java/rx/observers/AssertableSubscriberTest.java b/src/test/java/rx/observers/AssertableSubscriberTest.java index 838f57d6ec..4c6adbb06d 100644 --- a/src/test/java/rx/observers/AssertableSubscriberTest.java +++ b/src/test/java/rx/observers/AssertableSubscriberTest.java @@ -151,7 +151,7 @@ public void testSingle() { assertEquals(Thread.currentThread().getName(), ts.getLastSeenThread().getName()); assertTrue(ts.getOnErrorEvents().isEmpty()); assertTrue(ts.getOnNextEvents().isEmpty()); - assertEquals(1, ts.getValueCount()); + assertEquals(0, ts.getValueCount()); } @Test diff --git a/src/test/java/rx/observers/SafeSubscriberTest.java b/src/test/java/rx/observers/SafeSubscriberTest.java index 7d60a71c9f..3fdd8bbf66 100644 --- a/src/test/java/rx/observers/SafeSubscriberTest.java +++ b/src/test/java/rx/observers/SafeSubscriberTest.java @@ -80,7 +80,7 @@ public void onCompleted() { assertTrue(safe.isUnsubscribed()); } - @Test(expected=OnCompletedFailedException.class) + @Test(expected = OnCompletedFailedException.class) public void testPluginException() { RxJavaPlugins.getInstance().registerErrorHandler(new RxJavaErrorHandler() { @Override @@ -261,7 +261,7 @@ public void call() { try { safe.onCompleted(); Assert.fail(); - } catch(UnsubscribeFailedException e) { + } catch (UnsubscribeFailedException e) { assertEquals(1, calls.get()); assertEquals(0, errors.get()); } @@ -301,7 +301,7 @@ public void call() { try { safe.onCompleted(); Assert.fail(); - } catch(UnsubscribeFailedException e) { + } catch (UnsubscribeFailedException e) { assertEquals(2, calls.get()); assertEquals(0, errors.get()); } diff --git a/src/test/java/rx/observers/SerializedObserverTest.java b/src/test/java/rx/observers/SerializedObserverTest.java index 41d6d2da6d..83f49ecd52 100644 --- a/src/test/java/rx/observers/SerializedObserverTest.java +++ b/src/test/java/rx/observers/SerializedObserverTest.java @@ -50,7 +50,7 @@ private Observer serializedObserver(Observer o) { @Test public void testSingleThreadedBasic() { TestSingleThreadedObservable onSubscribe = new TestSingleThreadedObservable("one", "two", "three"); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); Observer aw = serializedObserver(observer); @@ -70,7 +70,7 @@ public void testSingleThreadedBasic() { @Test public void testMultiThreadedBasic() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three"); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); BusyObserver busyObserver = new BusyObserver(); Observer aw = serializedObserver(busyObserver); @@ -94,7 +94,7 @@ public void testMultiThreadedBasic() { @Test(timeout = 1000) public void testMultiThreadedWithNPE() throws InterruptedException { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); BusyObserver busyObserver = new BusyObserver(); Observer aw = serializedObserver(busyObserver); @@ -128,7 +128,7 @@ public void testMultiThreadedWithNPEinMiddle() { for (int i = 0; i < n; i++) { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - Observable w = Observable.create(onSubscribe); + Observable w = Observable.unsafeCreate(onSubscribe); BusyObserver busyObserver = new BusyObserver(); Observer aw = serializedObserver(busyObserver); @@ -391,7 +391,7 @@ private static void waitOnThreads(Future... futures) { } private static Observable infinite(final AtomicInteger produced) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber s) { @@ -589,7 +589,7 @@ public int assertEvents(TestConcurrencyObserverEvent expectedEndingEvent) throws private static class TestSingleThreadedObservable implements Observable.OnSubscribe { final String[] values; - private Thread t = null; + private Thread t; public TestSingleThreadedObservable(final String... values) { this.values = values; @@ -637,7 +637,7 @@ public void waitToFinish() { private static class TestMultiThreadedObservable implements Observable.OnSubscribe { final String[] values; - Thread t = null; + Thread t; AtomicInteger threadsRunning = new AtomicInteger(); AtomicInteger maxConcurrentThreads = new AtomicInteger(); ExecutorService threadPool; @@ -726,8 +726,8 @@ public void waitToFinish() { } private static class BusyObserver extends Subscriber { - volatile boolean onCompleted = false; - volatile boolean onError = false; + volatile boolean onCompleted; + volatile boolean onError; AtomicInteger onNextCount = new AtomicInteger(); AtomicInteger threadsRunning = new AtomicInteger(); AtomicInteger maxConcurrentThreads = new AtomicInteger(); diff --git a/src/test/java/rx/observers/TestSubscriberTest.java b/src/test/java/rx/observers/TestSubscriberTest.java index ee94f12297..a78890aa74 100644 --- a/src/test/java/rx/observers/TestSubscriberTest.java +++ b/src/test/java/rx/observers/TestSubscriberTest.java @@ -72,7 +72,7 @@ public void testAssertNotMatchValue() { oi.subscribe(o); thrown.expect(AssertionError.class); - thrown.expectMessage("Value at index: 1 expected to be [3] (Integer) but was: [2] (Integer)"); + thrown.expectMessage("Value at index: 1 expected: [3] (Integer) but was: [2] (Integer)"); o.assertReceivedOnNext(Arrays.asList(1, 3)); @@ -815,4 +815,15 @@ public void assertAndConsume() { ts.assertNoValues(); } + + @Test + public void assertAndClearResetsValueCount() { + TestSubscriber ts = TestSubscriber.create(); + + ts.onNext(1); + ts.assertValuesAndClear(1); + + ts.assertNoValues(); + Assert.assertEquals(0, ts.getValueCount()); + } } diff --git a/src/test/java/rx/plugins/RxJavaPluginsTest.java b/src/test/java/rx/plugins/RxJavaPluginsTest.java index ebcee62325..50d6944a08 100644 --- a/src/test/java/rx/plugins/RxJavaPluginsTest.java +++ b/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.mock; +import java.security.Permission; import java.util.*; import java.util.concurrent.TimeUnit; @@ -72,7 +73,7 @@ public void testErrorHandlerViaProperty() { public static class RxJavaErrorHandlerTestImpl extends RxJavaErrorHandler { private volatile Throwable e; - private volatile int count = 0; + private volatile int count; @Override public void handleError(Throwable e) { @@ -344,4 +345,75 @@ public void onNext(Object o) { assertEquals(re, errorHandler.e); assertEquals(1, errorHandler.count); } + + @Test + public void systemPropertiesSecurityException() { + assertNull(RxJavaPlugins.getPluginImplementationViaProperty(Object.class, new Properties() { + + private static final long serialVersionUID = -4291534158508255616L; + + @Override + public Set> entrySet() { + return new HashSet>() { + + private static final long serialVersionUID = -7714005655772619143L; + + @Override + public Iterator> iterator() { + return new Iterator>() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public Map.Entry next() { + throw new SecurityException(); + }; + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + @Override + public synchronized Object clone() { + return this; + } + })); + } + + @Test + public void securityManagerDenySystemProperties() { + SecurityManager old = System.getSecurityManager(); + try { + SecurityManager sm = new SecurityManager() { + @Override + public void checkPropertiesAccess() { + throw new SecurityException(); + } + + @Override + public void checkPermission(Permission perm) { + // allow restoring the security manager + } + + @Override + public void checkPermission(Permission perm, Object context) { + // allow restoring the security manager + } + }; + + System.setSecurityManager(sm); + assertTrue(RxJavaPlugins.getSystemPropertiesSafe().isEmpty()); + } finally { + System.setSecurityManager(old); + } + + assertFalse(RxJavaPlugins.getSystemPropertiesSafe().isEmpty()); + } } diff --git a/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java b/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java index 5e5686b396..1a9541997c 100644 --- a/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java +++ b/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java @@ -111,7 +111,7 @@ public void testUnsubscribeRecursiveScheduleFromOutside() throws InterruptedExce public void call() { inner.schedule(new Action0() { - int i = 0; + int i; @Override public void call() { @@ -159,7 +159,7 @@ public void testUnsubscribeRecursiveScheduleFromInside() throws InterruptedExcep public void call() { inner.schedule(new Action0() { - int i = 0; + int i; @Override public void call() { @@ -239,7 +239,7 @@ public void recursionFromOuterActionAndUnsubscribeInside() throws InterruptedExc try { inner.schedule(new Action0() { - int i = 0; + int i; @Override public void call() { @@ -268,7 +268,7 @@ public void testRecursion() throws InterruptedException { try { inner.schedule(new Action0() { - private long i = 0; + private long i; @Override public void call() { @@ -297,7 +297,7 @@ public void testRecursionAndOuterUnsubscribe() throws InterruptedException { final CountDownLatch completionLatch = new CountDownLatch(1); final Worker inner = getScheduler().createWorker(); try { - Observable obs = Observable.create(new OnSubscribe() { + Observable obs = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { inner.schedule(new Action0() { diff --git a/src/test/java/rx/schedulers/AbstractSchedulerTests.java b/src/test/java/rx/schedulers/AbstractSchedulerTests.java index c0502a6fe0..ff5c94071a 100644 --- a/src/test/java/rx/schedulers/AbstractSchedulerTests.java +++ b/src/test/java/rx/schedulers/AbstractSchedulerTests.java @@ -299,7 +299,7 @@ public final void testRecursiveExecutionWithDelayTime() throws InterruptedExcept inner.schedule(new Action0() { - int state = 0; + int state; @Override public void call() { @@ -322,13 +322,13 @@ public void call() { @Test public final void testRecursiveSchedulerInObservable() { - Observable obs = Observable.create(new OnSubscribe() { + Observable obs = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { final Scheduler.Worker inner = getScheduler().createWorker(); observer.add(inner); inner.schedule(new Action0() { - int i = 0; + int i; @Override public void call() { @@ -362,7 +362,7 @@ public void call(Integer v) { public final void testConcurrentOnNextFailsValidation() throws InterruptedException { final int count = 10; final CountDownLatch latch = new CountDownLatch(count); - Observable o = Observable.create(new OnSubscribe() { + Observable o = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber observer) { @@ -423,7 +423,7 @@ public final void testSubscribeOnNestedConcurrency() throws InterruptedException @Override public Observable call(final String v) { - return Observable.create(new OnSubscribe() { + return Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber observer) { @@ -508,7 +508,7 @@ public void periodicTaskCancelsItself() throws Exception { @Override public void call() { executions[0]++; - while (cancel.get() == null); + while (cancel.get() == null) { } cancel.get().unsubscribe(); cdl.countDown(); diff --git a/src/test/java/rx/schedulers/ComputationSchedulerTests.java b/src/test/java/rx/schedulers/ComputationSchedulerTests.java index 0841d815a9..d83a456f56 100644 --- a/src/test/java/rx/schedulers/ComputationSchedulerTests.java +++ b/src/test/java/rx/schedulers/ComputationSchedulerTests.java @@ -52,7 +52,7 @@ public void testThreadSafetyWhenSchedulerIsHoppingBetweenThreads() { inner.schedule(new Action0() { private HashMap statefulMap = map; - int nonThreadSafeCounter = 0; + int nonThreadSafeCounter; @Override public void call() { diff --git a/src/test/java/rx/schedulers/SchedulerTests.java b/src/test/java/rx/schedulers/SchedulerTests.java index c216580284..f750a47d4d 100644 --- a/src/test/java/rx/schedulers/SchedulerTests.java +++ b/src/test/java/rx/schedulers/SchedulerTests.java @@ -65,8 +65,12 @@ public static void testUnhandledErrorIsDeliveredToThreadHandler(Scheduler schedu assertEquals("Should have received exactly 1 exception", 1, handler.count); Throwable cause = handler.caught; while (cause != null) { - if (error.equals(cause)) break; - if (cause == cause.getCause()) break; + if (error.equals(cause)) { + break; + } + if (cause == cause.getCause()) { + break; + } cause = cause.getCause(); } assertEquals("Our error should have been delivered to the handler", error, cause); @@ -105,8 +109,12 @@ public static void testHandledErrorIsNotDeliveredToThreadHandler(Scheduler sched Throwable cause = observer.error; while (cause != null) { - if (error.equals(cause)) break; - if (cause == cause.getCause()) break; + if (error.equals(cause)) { + break; + } + if (cause == cause.getCause()) { + break; + } cause = cause.getCause(); } assertEquals("Our error should have been delivered to the observer", error, cause); @@ -183,8 +191,8 @@ public void call() { private static final class CapturingObserver implements Observer { CountDownLatch completed = new CountDownLatch(1); - int errorCount = 0; - int nextCount = 0; + int errorCount; + int nextCount; Throwable error; @Override diff --git a/src/test/java/rx/schedulers/SchedulerWhenTest.java b/src/test/java/rx/schedulers/SchedulerWhenTest.java index ed0e2f3792..c3b9761e64 100644 --- a/src/test/java/rx/schedulers/SchedulerWhenTest.java +++ b/src/test/java/rx/schedulers/SchedulerWhenTest.java @@ -30,184 +30,184 @@ import rx.observers.TestSubscriber; public class SchedulerWhenTest { - @Test - public void testAsyncMaxConcurrent() { - TestScheduler tSched = new TestScheduler(); - SchedulerWhen sched = maxConcurrentScheduler(tSched); - TestSubscriber tSub = TestSubscriber.create(); + @Test + public void testAsyncMaxConcurrent() { + TestScheduler tSched = new TestScheduler(); + SchedulerWhen sched = maxConcurrentScheduler(tSched); + TestSubscriber tSub = TestSubscriber.create(); - asyncWork(sched).subscribe(tSub); + asyncWork(sched).subscribe(tSub); - tSub.assertValueCount(0); + tSub.assertValueCount(0); - tSched.advanceTimeBy(0, SECONDS); - tSub.assertValueCount(0); + tSched.advanceTimeBy(0, SECONDS); + tSub.assertValueCount(0); - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(2); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(2); - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(4); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(4); - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(5); - tSub.assertCompleted(); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(5); + tSub.assertCompleted(); - sched.unsubscribe(); - } + sched.unsubscribe(); + } + + @Test + public void testAsyncDelaySubscription() { + final TestScheduler tSched = new TestScheduler(); + SchedulerWhen sched = throttleScheduler(tSched); + TestSubscriber tSub = TestSubscriber.create(); + + asyncWork(sched).subscribe(tSub); + + tSub.assertValueCount(0); + + tSched.advanceTimeBy(0, SECONDS); + tSub.assertValueCount(0); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(1); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(1); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(2); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(2); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(3); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(3); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(4); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(4); + + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(5); + tSub.assertCompleted(); + + sched.unsubscribe(); + } + + @Test + public void testSyncMaxConcurrent() { + TestScheduler tSched = new TestScheduler(); + SchedulerWhen sched = maxConcurrentScheduler(tSched); + TestSubscriber tSub = TestSubscriber.create(); + + syncWork(sched).subscribe(tSub); + + tSub.assertValueCount(0); + tSched.advanceTimeBy(0, SECONDS); + + // since all the work is synchronous nothing is blocked and its all done + tSub.assertValueCount(5); + tSub.assertCompleted(); - @Test - public void testAsyncDelaySubscription() { - final TestScheduler tSched = new TestScheduler(); - SchedulerWhen sched = throttleScheduler(tSched); - TestSubscriber tSub = TestSubscriber.create(); + sched.unsubscribe(); + } + + @Test + public void testSyncDelaySubscription() { + final TestScheduler tSched = new TestScheduler(); + SchedulerWhen sched = throttleScheduler(tSched); + TestSubscriber tSub = TestSubscriber.create(); + + syncWork(sched).subscribe(tSub); + + tSub.assertValueCount(0); + + tSched.advanceTimeBy(0, SECONDS); + tSub.assertValueCount(1); - asyncWork(sched).subscribe(tSub); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(2); - tSub.assertValueCount(0); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(3); - tSched.advanceTimeBy(0, SECONDS); - tSub.assertValueCount(0); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(4); - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(1); + tSched.advanceTimeBy(1, SECONDS); + tSub.assertValueCount(5); + tSub.assertCompleted(); - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(1); + sched.unsubscribe(); + } - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(2); + private Observable asyncWork(final Scheduler sched) { + return Observable.range(1, 5).flatMap(new Func1>() { + @Override + public Observable call(Integer t) { + return Observable.timer(1, SECONDS, sched); + } + }); + } - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(2); + private Observable syncWork(final Scheduler sched) { + return Observable.range(1, 5).flatMap(new Func1>() { + @Override + public Observable call(Integer t) { + return Observable.defer(new Func0>() { + @Override + public Observable call() { + return Observable.just(0l); + } + }).subscribeOn(sched); + } + }); + } - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(3); + private SchedulerWhen maxConcurrentScheduler(TestScheduler tSched) { + SchedulerWhen sched = new SchedulerWhen(new Func1>, Completable>() { + @Override + public Completable call(Observable> workerActions) { + Observable workers = workerActions.map(new Func1, Completable>() { + @Override + public Completable call(Observable actions) { + return Completable.concat(actions); + } + }); + return Completable.merge(workers, 2); + } + }, tSched); + return sched; + } - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(3); + private SchedulerWhen throttleScheduler(final TestScheduler tSched) { + SchedulerWhen sched = new SchedulerWhen(new Func1>, Completable>() { + @Override + public Completable call(Observable> workerActions) { + Observable workers = workerActions.map(new Func1, Completable>() { + @Override + public Completable call(Observable actions) { + return Completable.concat(actions); + } + }); + return Completable.concat(workers.map(new Func1() { + @Override + public Completable call(Completable worker) { + return worker.delay(1, SECONDS, tSched); + } + })); + } + }, tSched); + return sched; + } - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(4); - - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(4); - - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(5); - tSub.assertCompleted(); - - sched.unsubscribe(); - } - - @Test - public void testSyncMaxConcurrent() { - TestScheduler tSched = new TestScheduler(); - SchedulerWhen sched = maxConcurrentScheduler(tSched); - TestSubscriber tSub = TestSubscriber.create(); - - syncWork(sched).subscribe(tSub); - - tSub.assertValueCount(0); - tSched.advanceTimeBy(0, SECONDS); - - // since all the work is synchronous nothing is blocked and its all done - tSub.assertValueCount(5); - tSub.assertCompleted(); - - sched.unsubscribe(); - } - - @Test - public void testSyncDelaySubscription() { - final TestScheduler tSched = new TestScheduler(); - SchedulerWhen sched = throttleScheduler(tSched); - TestSubscriber tSub = TestSubscriber.create(); - - syncWork(sched).subscribe(tSub); - - tSub.assertValueCount(0); - - tSched.advanceTimeBy(0, SECONDS); - tSub.assertValueCount(1); - - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(2); - - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(3); - - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(4); - - tSched.advanceTimeBy(1, SECONDS); - tSub.assertValueCount(5); - tSub.assertCompleted(); - - sched.unsubscribe(); - } - - private Observable asyncWork(final Scheduler sched) { - return Observable.range(1, 5).flatMap(new Func1>() { - @Override - public Observable call(Integer t) { - return Observable.timer(1, SECONDS, sched); - } - }); - } - - private Observable syncWork(final Scheduler sched) { - return Observable.range(1, 5).flatMap(new Func1>() { - @Override - public Observable call(Integer t) { - return Observable.defer(new Func0>() { - @Override - public Observable call() { - return Observable.just(0l); - } - }).subscribeOn(sched); - } - }); - } - - private SchedulerWhen maxConcurrentScheduler(TestScheduler tSched) { - SchedulerWhen sched = new SchedulerWhen(new Func1>, Completable>() { - @Override - public Completable call(Observable> workerActions) { - Observable workers = workerActions.map(new Func1, Completable>() { - @Override - public Completable call(Observable actions) { - return Completable.concat(actions); - } - }); - return Completable.merge(workers, 2); - } - }, tSched); - return sched; - } - - private SchedulerWhen throttleScheduler(final TestScheduler tSched) { - SchedulerWhen sched = new SchedulerWhen(new Func1>, Completable>() { - @Override - public Completable call(Observable> workerActions) { - Observable workers = workerActions.map(new Func1, Completable>() { - @Override - public Completable call(Observable actions) { - return Completable.concat(actions); - } - }); - return Completable.concat(workers.map(new Func1() { - @Override - public Completable call(Completable worker) { - return worker.delay(1, SECONDS, tSched); - } - })); - } - }, tSched); - return sched; - } - - @Test(timeout=1000) + @Test(timeout = 1000) public void testRaceConditions() { Scheduler comp = Schedulers.computation(); Scheduler limited = comp.when(new Func1>, Completable>() { diff --git a/src/test/java/rx/schedulers/TestSchedulerTest.java b/src/test/java/rx/schedulers/TestSchedulerTest.java index ba95fd1df9..f26939d5bf 100644 --- a/src/test/java/rx/schedulers/TestSchedulerTest.java +++ b/src/test/java/rx/schedulers/TestSchedulerTest.java @@ -17,25 +17,18 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; +import org.mockito.*; -import rx.Observable; +import rx.*; import rx.Observable.OnSubscribe; -import rx.Scheduler; -import rx.Subscriber; -import rx.Subscription; -import rx.functions.Action0; -import rx.functions.Func1; +import rx.functions.*; +import rx.observers.TestSubscriber; public class TestSchedulerTest { @@ -188,7 +181,7 @@ public final void testNestedSchedule() { final Action0 calledOp = mock(Action0.class); Observable poller; - poller = Observable.create(new OnSubscribe() { + poller = Observable.unsafeCreate(new OnSubscribe() { @Override public void call(final Subscriber aSubscriber) { inner.schedule(new Action0() { @@ -222,4 +215,24 @@ public void call() { inner.unsubscribe(); } } + + @Test + public void resolution() { + for (final TimeUnit unit : TimeUnit.values()) { + TestScheduler scheduler = new TestScheduler(); + TestSubscriber testSubscriber = new TestSubscriber(); + + Observable.interval(30, unit, scheduler) + .map(new Func1() { + @Override + public String call(Long v) { + return v + "-" + unit; + } + }) + .subscribe(testSubscriber); + scheduler.advanceTimeTo(60, unit); + + testSubscriber.assertValues("0-" + unit, "1-" + unit); + } + } } diff --git a/src/test/java/rx/schedulers/TrampolineSchedulerTest.java b/src/test/java/rx/schedulers/TrampolineSchedulerTest.java index f191fa9819..61e6b4f3e2 100644 --- a/src/test/java/rx/schedulers/TrampolineSchedulerTest.java +++ b/src/test/java/rx/schedulers/TrampolineSchedulerTest.java @@ -125,7 +125,7 @@ public Subscription call(Long count) { return trampolineWorker.schedule(new Action0() { @Override - public void call() {} + public void call() { } }); } diff --git a/src/test/java/rx/subjects/BufferUntilSubscriberTest.java b/src/test/java/rx/subjects/BufferUntilSubscriberTest.java index d1ad426a96..6d8aebe167 100644 --- a/src/test/java/rx/subjects/BufferUntilSubscriberTest.java +++ b/src/test/java/rx/subjects/BufferUntilSubscriberTest.java @@ -33,8 +33,9 @@ public class BufferUntilSubscriberTest { public void testIssue1677() throws InterruptedException { final AtomicLong counter = new AtomicLong(); final Integer[] numbers = new Integer[5000]; - for (int i = 0; i < numbers.length; i++) + for (int i = 0; i < numbers.length; i++) { numbers[i] = i + 1; + } final int NITERS = 250; final CountDownLatch latch = new CountDownLatch(NITERS); for (int iters = 0; iters < NITERS; iters++) { @@ -72,13 +73,15 @@ public void call(List integers) { } }) .subscribe(); - if (!innerLatch.await(30, TimeUnit.SECONDS)) + if (!innerLatch.await(30, TimeUnit.SECONDS)) { Assert.fail("Failed inner latch wait, iteration " + iters); + } } - if (!latch.await(30, TimeUnit.SECONDS)) + if (!latch.await(30, TimeUnit.SECONDS)) { Assert.fail("Incomplete! Went through " + latch.getCount() + " iterations"); - else + } else { Assert.assertEquals(NITERS, counter.get()); + } } @Test diff --git a/src/test/java/rx/subjects/ReplaySubjectBoundedConcurrencyTest.java b/src/test/java/rx/subjects/ReplaySubjectBoundedConcurrencyTest.java index 72149ee677..954a9b8950 100644 --- a/src/test/java/rx/subjects/ReplaySubjectBoundedConcurrencyTest.java +++ b/src/test/java/rx/subjects/ReplaySubjectBoundedConcurrencyTest.java @@ -52,7 +52,7 @@ public void testReplaySubjectConcurrentSubscribersDoingReplayDontBlockEachOther( @Override public void run() { - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber o) { diff --git a/src/test/java/rx/subjects/ReplaySubjectConcurrencyTest.java b/src/test/java/rx/subjects/ReplaySubjectConcurrencyTest.java index 5e1c350462..69b4c8cf69 100644 --- a/src/test/java/rx/subjects/ReplaySubjectConcurrencyTest.java +++ b/src/test/java/rx/subjects/ReplaySubjectConcurrencyTest.java @@ -52,7 +52,7 @@ public void testReplaySubjectConcurrentSubscribersDoingReplayDontBlockEachOther( @Override public void run() { - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber o) { @@ -174,7 +174,7 @@ public static void concurrencyTest(final ReplaySubject replay) throws Inte @Override public void run() { - Observable.create(new OnSubscribe() { + Observable.unsafeCreate(new OnSubscribe() { @Override public void call(Subscriber o) { diff --git a/src/test/java/rx/subjects/ReplaySubjectTest.java b/src/test/java/rx/subjects/ReplaySubjectTest.java index 8464cf1b3f..63b63ea16c 100644 --- a/src/test/java/rx/subjects/ReplaySubjectTest.java +++ b/src/test/java/rx/subjects/ReplaySubjectTest.java @@ -1175,4 +1175,22 @@ public Boolean call(Integer v) { ts2.assertValues(1, 2, 3, 6, 7); } + @Test + public void noOldEntries() { + TestScheduler scheduler = new TestScheduler(); + + ReplaySubject source = ReplaySubject.createWithTime(2, TimeUnit.SECONDS, scheduler); + + source.onNext(1); + source.onCompleted(); + + source.test().assertResult(1); + + source.test().assertResult(1); + + scheduler.advanceTimeBy(3, TimeUnit.SECONDS); + + source.test().assertResult(); + } + } diff --git a/src/test/java/rx/subjects/UnicastSubjectTest.java b/src/test/java/rx/subjects/UnicastSubjectTest.java new file mode 100644 index 0000000000..af9ef33468 --- /dev/null +++ b/src/test/java/rx/subjects/UnicastSubjectTest.java @@ -0,0 +1,101 @@ +package rx.subjects; + +import org.junit.Test; +import rx.functions.Action0; +import rx.observers.TestSubscriber; + +public class UnicastSubjectTest { + + @Test + public void testOneArgFactoryDelayError() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(true); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(2); + subscriber.assertError(RuntimeException.class); + } + + @Test + public void testOneArgFactoryNoDelayError() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(false); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(0); + subscriber.assertError(RuntimeException.class); + } + + @Test + public void testThreeArgsFactoryDelayError() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(16, new NoopAction0(), true); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(2); + subscriber.assertError(RuntimeException.class); + } + + @Test + public void testThreeArgsFactoryNoDelayError() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(16, new NoopAction0(), false); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(0); + subscriber.assertError(RuntimeException.class); + } + + @Test + public void testZeroArgsFactory() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(0); + subscriber.assertError(RuntimeException.class); + } + + @Test + public void testOneArgFactory() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(16); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(0); + subscriber.assertError(RuntimeException.class); + } + + @Test + public void testTwoArgsFactory() throws Exception { + TestSubscriber subscriber = TestSubscriber.create(); + UnicastSubject s = UnicastSubject.create(16, new NoopAction0()); + s.onNext(1L); + s.onNext(2L); + s.onError(new RuntimeException()); + s.subscribe(subscriber); + subscriber.assertValueCount(0); + subscriber.assertError(RuntimeException.class); + } + + + + private static final class NoopAction0 implements Action0 { + + @Override + public void call() { + } + } +} diff --git a/src/test/java/rx/util/AssertObservable.java b/src/test/java/rx/util/AssertObservable.java index 41ddaaad1e..d1a0072a34 100644 --- a/src/test/java/rx/util/AssertObservable.java +++ b/src/test/java/rx/util/AssertObservable.java @@ -102,24 +102,30 @@ public Notification call(Notification expectedNotfication, Notificati if (expectedNotfication.equals(actualNotification)) { StringBuilder message = new StringBuilder(); message.append(expectedNotfication.getKind()); - if (expectedNotfication.hasValue()) + if (expectedNotfication.hasValue()) { message.append(" ").append(expectedNotfication.getValue()); - if (expectedNotfication.hasThrowable()) + } + if (expectedNotfication.hasThrowable()) { message.append(" ").append(expectedNotfication.getThrowable()); + } return Notification.createOnNext("equals " + message.toString()); } else { StringBuilder error = new StringBuilder(); error.append("expected:<").append(expectedNotfication.getKind()); - if (expectedNotfication.hasValue()) + if (expectedNotfication.hasValue()) { error.append(" ").append(expectedNotfication.getValue()); - if (expectedNotfication.hasThrowable()) + } + if (expectedNotfication.hasThrowable()) { error.append(" ").append(expectedNotfication.getThrowable()); + } error.append("> but was:<").append(actualNotification.getKind()); - if (actualNotification.hasValue()) + if (actualNotification.hasValue()) { error.append(" ").append(actualNotification.getValue()); - if (actualNotification.hasThrowable()) + } + if (actualNotification.hasThrowable()) { error.append(" ").append(actualNotification.getThrowable()); + } error.append(">"); return Notification.createOnError(new AssertionError(error.toString())); @@ -136,10 +142,11 @@ public Notification call(Notification a, Notification b) message += "\n\t" + (b.isOnError() ? b.getThrowable().getMessage() : b.getValue()); fail |= b.isOnError(); - if (fail) + if (fail) { return Notification.createOnError(new AssertionError(message)); - else + } else { return Notification.createOnNext(message); + } } };