From d91794b7123f58d5dfc85fa7edfd8c33ffd9655d Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Fri, 31 Jan 2014 12:35:25 -0800 Subject: [PATCH 1/2] Scheduler Outer/Inner --- rxjava-core/src/main/java/rx/Observable.java | 2 +- rxjava-core/src/main/java/rx/Scheduler.java | 203 ++++----------- .../concurrency/CurrentThreadScheduler.java | 54 ---- .../rx/concurrency/ExecutorScheduler.java | 39 --- .../rx/concurrency/ImmediateScheduler.java | 54 ---- .../rx/concurrency/NewThreadScheduler.java | 54 ---- .../src/main/java/rx/concurrency/README.txt | 19 -- .../main/java/rx/concurrency/Schedulers.java | 115 --------- .../java/rx/concurrency/TestScheduler.java | 29 --- .../java/rx/operators/ChunkedOperation.java | 17 +- .../java/rx/operators/OperationDebounce.java | 9 +- .../java/rx/operators/OperationDelay.java | 8 +- .../java/rx/operators/OperationInterval.java | 8 +- .../java/rx/operators/OperationObserveOn.java | 28 +-- .../java/rx/operators/OperationRepeat.java | 39 +-- .../java/rx/operators/OperationRetry.java | 41 ++- .../java/rx/operators/OperationSample.java | 2 +- .../main/java/rx/operators/OperationSkip.java | 8 +- .../rx/operators/OperationSubscribeOn.java | 30 ++- .../java/rx/operators/OperationTakeTimed.java | 10 +- .../rx/operators/OperationThrottleFirst.java | 2 +- .../java/rx/operators/OperationTimeout.java | 12 +- .../java/rx/operators/OperationTimer.java | 14 +- .../rx/schedulers/CurrentThreadScheduler.java | 168 ------------- .../java/rx/schedulers/DiscardableAction.java | 62 ----- .../java/rx/schedulers/ExecutorScheduler.java | 234 ++++++------------ .../GenericScheduledExecutorService.java | 2 +- .../rx/schedulers/ImmediateScheduler.java | 49 +++- .../rx/schedulers/NewThreadScheduler.java | 168 ++++++------- .../main/java/rx/schedulers/Schedulers.java | 15 +- .../java/rx/schedulers/SleepingAction.java | 23 +- .../java/rx/schedulers/TestScheduler.java | 81 +++--- .../rx/schedulers/TrampolineScheduler.java | 134 ++++++++++ .../schedulers/SchedulerPerformanceTests.java | 4 +- rxjava-core/src/test/java/rx/EventStream.java | 36 +-- .../java/rx/operators/OperationAmbTest.java | 23 +- .../rx/operators/OperationBufferTest.java | 9 +- .../rx/operators/OperationDebounceTest.java | 14 +- .../rx/operators/OperationSampleTest.java | 14 +- .../operators/OperationSubscribeOnTest.java | 7 +- .../rx/operators/OperationSwitchTest.java | 14 +- .../operators/OperationThrottleFirstTest.java | 15 +- .../rx/operators/OperationWindowTest.java | 9 +- .../java/rx/operators/OperatorMergeTest.java | 2 +- .../AbstractSchedulerConcurrencyTests.java | 177 ++++++++----- .../rx/schedulers/AbstractSchedulerTests.java | 192 +++++--------- .../rx/schedulers/ExecutorSchedulerTests.java | 83 +++---- .../java/rx/schedulers/TestSchedulerTest.java | 14 +- ...Test.java => TrampolineSchedulerTest.java} | 4 +- .../src/test/java/rx/test/OperatorTester.java | 33 +-- 50 files changed, 889 insertions(+), 1494 deletions(-) delete mode 100644 rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java delete mode 100644 rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java delete mode 100644 rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java delete mode 100644 rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java delete mode 100644 rxjava-core/src/main/java/rx/concurrency/README.txt delete mode 100644 rxjava-core/src/main/java/rx/concurrency/Schedulers.java delete mode 100644 rxjava-core/src/main/java/rx/concurrency/TestScheduler.java delete mode 100644 rxjava-core/src/main/java/rx/schedulers/CurrentThreadScheduler.java delete mode 100644 rxjava-core/src/main/java/rx/schedulers/DiscardableAction.java create mode 100644 rxjava-core/src/main/java/rx/schedulers/TrampolineScheduler.java rename rxjava-core/src/test/java/rx/schedulers/{CurrentThreadSchedulerTest.java => TrampolineSchedulerTest.java} (93%) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 429d0d02c0..872460b43d 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -5300,7 +5300,7 @@ public final Observable onExceptionResumeNext(final Observable r } /** - * Perform work on the source {@code Observable} in parallel by sharding it on a {@link Schedulers#threadPoolForComputation()} {@link Scheduler}, and return the resulting {@code Observable}. + * Perform work on the source {@code Observable} in parallel by sharding it on a {@link Schedulers#computation()} {@link Scheduler}, and return the resulting {@code Observable}. *

* * diff --git a/rxjava-core/src/main/java/rx/Scheduler.java b/rxjava-core/src/main/java/rx/Scheduler.java index 19059d1f75..fd93a92972 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 Netflix, Inc. + * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +15,9 @@ */ package rx; -import java.util.Date; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.MultipleAssignmentSubscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; import rx.util.functions.Action1; -import rx.util.functions.Func2; /** * Represents an object that schedules units of work. @@ -48,30 +41,24 @@ public abstract class Scheduler { /** - * Schedules a cancelable action to be executed. + * Schedules an Action on a new Scheduler instance (typically another thread) for execution. * - * @param state - * State to pass into the action. * @param action * Action to schedule. * @return a subscription to be able to unsubscribe from action. */ - public abstract Subscription schedule(T state, Func2 action); + + public abstract Subscription schedule(Action1 action); /** - * Schedules a cancelable action to be executed in delayTime. + * Schedules an Action on a new Scheduler instance (typically another thread) for execution at some point in the future. * - * @param state - * State to pass into the action. * @param action - * Action to schedule. * @param delayTime - * Time the action is to be delayed before executing. * @param unit - * Time unit of the delay time. - * @return a subscription to be able to unsubscribe from action. + * @return */ - public abstract Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit); + public abstract Subscription schedule(final Action1 action, final long delayTime, final TimeUnit unit); /** * Schedules a cancelable action to be executed periodically. @@ -90,152 +77,58 @@ public abstract class Scheduler { * The time unit the interval above is given in. * @return A subscription to be able to unsubscribe from action. */ - public Subscription schedulePeriodically(T state, final Func2 action, long initialDelay, long period, TimeUnit unit) { + public Subscription schedulePeriodically(final Action1 action, long initialDelay, long period, TimeUnit unit) { final long periodInNanos = unit.toNanos(period); - final AtomicBoolean complete = new AtomicBoolean(); - final Func2 recursiveAction = new Func2() { + final Action1 recursiveAction = new Action1() { @Override - public Subscription call(Scheduler scheduler, T state0) { - if (!complete.get()) { + public void call(Inner inner) { + if (!inner.isUnsubscribed()) { long startedAt = now(); - final Subscription sub1 = action.call(scheduler, state0); + action.call(inner); long timeTakenByActionInNanos = TimeUnit.MILLISECONDS.toNanos(now() - startedAt); - final Subscription sub2 = schedule(state0, this, periodInNanos - timeTakenByActionInNanos, TimeUnit.NANOSECONDS); - return Subscriptions.create(new Action0() { - @Override - public void call() { - sub1.unsubscribe(); - sub2.unsubscribe(); - } - }); + inner.schedule(this, periodInNanos - timeTakenByActionInNanos, TimeUnit.NANOSECONDS); } - return Subscriptions.empty(); } }; - final Subscription sub = schedule(state, recursiveAction, initialDelay, unit); - return Subscriptions.create(new Action0() { - @Override - public void call() { - complete.set(true); - sub.unsubscribe(); - } - }); + return schedule(recursiveAction, initialDelay, unit); } - /** - * Schedules a cancelable action to be executed at dueTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param dueTime - * Time the action is to be executed. If in the past it will be executed immediately. - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(T state, Func2 action, Date dueTime) { - long scheduledTime = dueTime.getTime(); - long timeInFuture = scheduledTime - now(); - if (timeInFuture <= 0) { - return schedule(state, action); - } else { - return schedule(state, action, timeInFuture, TimeUnit.MILLISECONDS); + public abstract static class Inner implements Subscription { + + /** + * Schedules an action to be executed in delayTime. + * + * @param delayTime + * Time the action is to be delayed before executing. + * @param unit + * Time unit of the delay time. + */ + public abstract void schedule(Action1 action, long delayTime, TimeUnit unit); + + /** + * Schedules a cancelable action to be executed in delayTime. + * + */ + public abstract void schedule(Action1 action); + + /** + * @return the scheduler's notion of current absolute time in milliseconds. + */ + public long now() { + return System.currentTimeMillis(); } } /** - * Schedules an action and receives back an action for recursive execution. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Action1 action) { - final CompositeSubscription parentSubscription = new CompositeSubscription(); - final MultipleAssignmentSubscription childSubscription = new MultipleAssignmentSubscription(); - parentSubscription.add(childSubscription); - - final Func2 parentAction = new Func2() { - - @Override - public Subscription call(final Scheduler scheduler, final Func2 parentAction) { - action.call(new Action0() { - - @Override - public void call() { - if (!parentSubscription.isUnsubscribed()) { - childSubscription.set(scheduler.schedule(parentAction, parentAction)); - } - } - - }); - return childSubscription; - } - }; - - parentSubscription.add(schedule(parentAction, parentAction)); - - return parentSubscription; - } - - /** - * Schedules an action to be executed. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Action0 action) { - return schedule(null, new Func2() { - - @Override - public Subscription call(Scheduler scheduler, Void state) { - action.call(); - return Subscriptions.empty(); - } - }); - } - - /** - * Schedules an action to be executed in delayTime. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) { - return schedule(null, new Func2() { - - @Override - public Subscription call(Scheduler scheduler, Void state) { - action.call(); - return Subscriptions.empty(); - } - }, delayTime, unit); - } - - /** - * Schedules an action to be executed periodically. + * Parallelism available to a Scheduler. + *

+ * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @param unit - * The time unit the interval above is given in. - * @return A subscription to be able to unsubscribe from action. + * @return the scheduler's available degree of parallelism. */ - public Subscription schedulePeriodically(final Action0 action, long initialDelay, long period, TimeUnit unit) { - return schedulePeriodically(null, new Func2() { - @Override - public Subscription call(Scheduler scheduler, Void state) { - action.call(); - return Subscriptions.empty(); - } - }, initialDelay, period, unit); + public int degreeOfParallelism() { + return Runtime.getRuntime().availableProcessors(); } /** @@ -245,14 +138,4 @@ public long now() { return System.currentTimeMillis(); } - /** - * Parallelism available to a Scheduler. - *

- * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. - * - * @return the scheduler's available degree of parallelism. - */ - public int degreeOfParallelism() { - return Runtime.getRuntime().availableProcessors(); - } } diff --git a/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java b/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java deleted file mode 100644 index b5e5fd1d14..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java +++ /dev/null @@ -1,54 +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.concurrency; - -import java.util.concurrent.TimeUnit; - -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Func2; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.CurrentThreadScheduler} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class CurrentThreadScheduler extends Scheduler { - - private final static CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler(); - - public static CurrentThreadScheduler getInstance() { - return INSTANCE; - } - - private final rx.schedulers.CurrentThreadScheduler actual; - - private CurrentThreadScheduler() { - actual = rx.schedulers.CurrentThreadScheduler.getInstance(); - } - - @Override - public Subscription schedule(T state, Func2 action) { - return actual.schedule(state, action); - } - - @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - return actual.schedule(state, action, delayTime, unit); - } - -} diff --git a/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java b/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java deleted file mode 100644 index 4f43643451..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java +++ /dev/null @@ -1,39 +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.concurrency; - -import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.ExecutorScheduler} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class ExecutorScheduler extends rx.schedulers.ExecutorScheduler { - - @Deprecated - public ExecutorScheduler(Executor executor) { - super(executor); - } - - @Deprecated - public ExecutorScheduler(ScheduledExecutorService executor) { - super(executor); - } - -} diff --git a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java b/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java deleted file mode 100644 index b37a7db13e..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java +++ /dev/null @@ -1,54 +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.concurrency; - -import java.util.concurrent.TimeUnit; - -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Func2; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.ImmediateScheduler} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class ImmediateScheduler extends Scheduler { - - private final static ImmediateScheduler INSTANCE = new ImmediateScheduler(); - - public static ImmediateScheduler getInstance() { - return INSTANCE; - } - - private final rx.schedulers.ImmediateScheduler actual; - - private ImmediateScheduler() { - actual = rx.schedulers.ImmediateScheduler.getInstance(); - } - - @Override - public Subscription schedule(T state, Func2 action) { - return actual.schedule(state, action); - } - - @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - return actual.schedule(state, action, delayTime, unit); - } - -} diff --git a/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java b/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java deleted file mode 100644 index 39ce1be3ae..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java +++ /dev/null @@ -1,54 +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.concurrency; - -import java.util.concurrent.TimeUnit; - -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Func2; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.NewThreadScheduler} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class NewThreadScheduler extends Scheduler { - - private final static NewThreadScheduler INSTANCE = new NewThreadScheduler(); - - public static NewThreadScheduler getInstance() { - return INSTANCE; - } - - private final rx.schedulers.NewThreadScheduler actual; - - private NewThreadScheduler() { - actual = rx.schedulers.NewThreadScheduler.getInstance(); - } - - @Override - public Subscription schedule(T state, Func2 action) { - return actual.schedule(state, action); - } - - @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - return actual.schedule(state, action, delayTime, unit); - } - -} diff --git a/rxjava-core/src/main/java/rx/concurrency/README.txt b/rxjava-core/src/main/java/rx/concurrency/README.txt deleted file mode 100644 index 41ed405b26..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/README.txt +++ /dev/null @@ -1,19 +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. -==== - -This package is deprecated and will be removed prior to a 1.0 release. - -Use rx.schedulers.* instead of rx.concurrency.* \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/concurrency/Schedulers.java b/rxjava-core/src/main/java/rx/concurrency/Schedulers.java deleted file mode 100644 index 0147aa5b36..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/Schedulers.java +++ /dev/null @@ -1,115 +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.concurrency; - -import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; - -import rx.Scheduler; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.Schedulers} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class Schedulers { - - /** - * {@link Scheduler} that executes work immediately on the current thread. - * - * @return {@link ImmediateScheduler} instance - */ - @Deprecated - public static Scheduler immediate() { - return rx.schedulers.ImmediateScheduler.getInstance(); - } - - /** - * {@link Scheduler} that queues work on the current thread to be executed after the current work completes. - * - * @return {@link CurrentThreadScheduler} instance - */ - @Deprecated - public static Scheduler currentThread() { - return rx.schedulers.CurrentThreadScheduler.getInstance(); - } - - /** - * {@link Scheduler} that creates a new {@link Thread} for each unit of work. - * - * @return {@link NewThreadScheduler} instance - */ - @Deprecated - public static Scheduler newThread() { - return rx.schedulers.NewThreadScheduler.getInstance(); - } - - /** - * {@link Scheduler} that queues work on an {@link Executor}. - *

- * Note that this does not support scheduled actions with a delay. - * - * @return {@link ExecutorScheduler} instance - */ - @Deprecated - public static Scheduler executor(Executor executor) { - return new rx.schedulers.ExecutorScheduler(executor); - } - - /** - * {@link Scheduler} that queues work on an {@link ScheduledExecutorService}. - * - * @return {@link ExecutorScheduler} instance - */ - @Deprecated - public static Scheduler executor(ScheduledExecutorService executor) { - return new rx.schedulers.ExecutorScheduler(executor); - } - - /** - * {@link Scheduler} intended for computational work. - *

- * The implementation is backed by a {@link ScheduledExecutorService} thread-pool sized to the number of CPU cores. - *

- * This can be used for event-loops, processing callbacks and other computational work. - *

- * Do not perform IO-bound work on this scheduler. Use {@link #threadPoolForComputation()} instead. - * - * @return {@link ExecutorScheduler} for computation-bound work. - */ - @Deprecated - public static Scheduler threadPoolForComputation() { - return rx.schedulers.Schedulers.threadPoolForComputation(); - } - - /** - * {@link Scheduler} intended for IO-bound work. - *

- * The implementation is backed by an {@link Executor} thread-pool that will grow as needed. - *

- * This can be used for asynchronously performing blocking IO. - *

- * Do not perform computational work on this scheduler. Use {@link #threadPoolForComputation()} instead. - * - * @return {@link ExecutorScheduler} for IO-bound work. - */ - @Deprecated - public static Scheduler threadPoolForIO() { - return rx.schedulers.Schedulers.threadPoolForIO(); - } - -} diff --git a/rxjava-core/src/main/java/rx/concurrency/TestScheduler.java b/rxjava-core/src/main/java/rx/concurrency/TestScheduler.java deleted file mode 100644 index 696975d753..0000000000 --- a/rxjava-core/src/main/java/rx/concurrency/TestScheduler.java +++ /dev/null @@ -1,29 +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.concurrency; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.TestScheduler} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class TestScheduler extends rx.schedulers.TestScheduler { - - public TestScheduler() { - } - -} diff --git a/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java b/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java index 77606a03e6..42a096212f 100644 --- a/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java +++ b/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java @@ -27,6 +27,7 @@ import rx.Observable; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.util.functions.Action0; import rx.util.functions.Action1; @@ -169,9 +170,9 @@ public TimeAndSizeBasedChunks(Observer observer, Func0 createChunk() { final Chunk chunk = super.createChunk(); - subscriptions.put(chunk, scheduler.schedule(new Action0() { + subscriptions.put(chunk, scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { emitChunk(chunk); } }, maxTime, unit)); @@ -251,9 +252,9 @@ public TimeBasedChunks(Observer observer, Func0 @Override public Chunk createChunk() { final Chunk chunk = super.createChunk(); - subscriptions.put(chunk, scheduler.schedule(new Action0() { + subscriptions.put(chunk, scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { emitChunk(chunk); } }, time, unit)); @@ -567,18 +568,18 @@ protected static class TimeBasedChunkCreator implements ChunkCreator { private final SafeObservableSubscription subscription = new SafeObservableSubscription(); public TimeBasedChunkCreator(final NonOverlappingChunks chunks, long time, TimeUnit unit, Scheduler scheduler) { - this.subscription.wrap(scheduler.schedulePeriodically(new Action0() { + this.subscription.wrap(scheduler.schedulePeriodically(new Action1() { @Override - public void call() { + public void call(Inner inner) { chunks.emitAndReplaceChunk(); } }, 0, time, unit)); } public TimeBasedChunkCreator(final OverlappingChunks chunks, long time, TimeUnit unit, Scheduler scheduler) { - this.subscription.wrap(scheduler.schedulePeriodically(new Action0() { + this.subscription.wrap(scheduler.schedulePeriodically(new Action1() { @Override - public void call() { + public void call(Inner inner) { chunks.createChunk(); } }, 0, time, unit)); diff --git a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java index 9cad959c80..a04566b057 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java @@ -22,12 +22,14 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.observers.SynchronizedObserver; import rx.schedulers.Schedulers; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.SerialSubscription; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func1; /** @@ -51,7 +53,7 @@ public final class OperationDebounce { * @return A {@link Func1} which performs the throttle operation. */ public static OnSubscribeFunc debounce(Observable items, long timeout, TimeUnit unit) { - return debounce(items, timeout, unit, Schedulers.threadPoolForComputation()); + return debounce(items, timeout, unit, Schedulers.computation()); } /** @@ -140,14 +142,15 @@ public void onError(Throwable e) { @Override public void onNext(final T v) { - Subscription previousSubscription = lastScheduledNotification.getAndSet(scheduler.schedule(new Action0() { + Subscription previousSubscription = lastScheduledNotification.getAndSet(scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(v); } }, timeout, unit)); + // cancel previous if not already executed if (previousSubscription != null) { previousSubscription.unsubscribe(); diff --git a/rxjava-core/src/main/java/rx/operators/OperationDelay.java b/rxjava-core/src/main/java/rx/operators/OperationDelay.java index 5bd391758e..104dd850bc 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDelay.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDelay.java @@ -21,12 +21,14 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.observables.ConnectableObservable; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.SerialSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; @@ -74,11 +76,11 @@ public DelaySubscribeFunc(Observable source, long time, TimeUnit un public Subscription onSubscribe(final Observer t1) { final SerialSubscription ssub = new SerialSubscription(); - ssub.setSubscription(scheduler.schedule(new Action0() { + ssub.set(scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { if (!ssub.isUnsubscribed()) { - ssub.setSubscription(source.subscribe(t1)); + ssub.set(source.subscribe(t1)); } } }, time, unit)); diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index 1d3bd15715..c3936afc09 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -20,10 +20,12 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.Schedulers; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Returns an observable sequence that produces a value after each period. @@ -35,7 +37,7 @@ public final class OperationInterval { * Creates an event each time interval. */ public static OnSubscribeFunc interval(long interval, TimeUnit unit) { - return interval(interval, unit, Schedulers.threadPoolForComputation()); + return interval(interval, unit, Schedulers.computation()); } /** @@ -66,9 +68,9 @@ private Interval(long period, TimeUnit unit, Scheduler scheduler) { @Override public Subscription onSubscribe(final Observer observer) { - final Subscription wrapped = scheduler.schedulePeriodically(new Action0() { + final Subscription wrapped = scheduler.schedulePeriodically(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(currentValue); currentValue++; } diff --git a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java index b024a44de9..c3addd9933 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java @@ -23,14 +23,12 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; -import rx.schedulers.CurrentThreadScheduler; import rx.schedulers.ImmediateScheduler; +import rx.schedulers.TrampolineScheduler; import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.MultipleAssignmentSubscription; -import rx.util.functions.Action0; import rx.util.functions.Action1; -import rx.util.functions.Func2; /** * Asynchronously notify Observers on the specified Scheduler. @@ -57,7 +55,7 @@ public Subscription onSubscribe(final Observer observer) { if (scheduler instanceof ImmediateScheduler) { // do nothing if we request ImmediateScheduler so we don't invoke overhead return source.subscribe(observer); - } else if (scheduler instanceof CurrentThreadScheduler) { + } else if (scheduler instanceof TrampolineScheduler) { // do nothing if we request CurrentThreadScheduler so we don't invoke overhead return source.subscribe(observer); } else { @@ -69,10 +67,9 @@ public Subscription onSubscribe(final Observer observer) { private class Observation { final Observer observer; final CompositeSubscription compositeSubscription = new CompositeSubscription(); - final MultipleAssignmentSubscription recursiveSubscription = new MultipleAssignmentSubscription(); final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); final AtomicLong counter = new AtomicLong(0); - private volatile Scheduler recursiveScheduler; + private volatile Scheduler.Inner recursiveScheduler; public Observation(Observer observer) { this.observer = observer; @@ -91,15 +88,16 @@ public void call(Notification e) { if (counter.getAndIncrement() == 0) { if (recursiveScheduler == null) { // compositeSubscription for the outer scheduler, recursive for inner - compositeSubscription.add(scheduler.schedule(null, new Func2() { + compositeSubscription.add(scheduler.schedule(new Action1() { + @Override - public Subscription call(Scheduler innerScheduler, T state) { + public void call(Inner inner) { // record innerScheduler so 'processQueue' can use it for all subsequent executions - recursiveScheduler = innerScheduler; + recursiveScheduler = inner; // once we have the innerScheduler we can start doing real work processQueue(); - return recursiveSubscription; } + })); } else { processQueue(); @@ -108,9 +106,9 @@ public Subscription call(Scheduler innerScheduler, T state) { } void processQueue() { - recursiveSubscription.set(recursiveScheduler.schedule(new Action1() { + recursiveScheduler.schedule(new Action1() { @Override - public void call(Action0 self) { + public void call(Inner inner) { Notification not = queue.poll(); if (not != null) { not.accept(observer); @@ -119,10 +117,10 @@ public void call(Action0 self) { // decrement count and if we still have work to do // recursively schedule ourselves to process again if (counter.decrementAndGet() > 0) { - self.call(); + inner.schedule(this); } } - })); + }); } } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRepeat.java b/rxjava-core/src/main/java/rx/operators/OperationRepeat.java index ade09aecce..c84cc781f2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRepeat.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRepeat.java @@ -19,6 +19,7 @@ import rx.Observable; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; @@ -44,27 +45,35 @@ public Subscription onSubscribe(final Observer observer) { final CompositeSubscription compositeSubscription = new CompositeSubscription(); final MultipleAssignmentSubscription innerSubscription = new MultipleAssignmentSubscription(); compositeSubscription.add(innerSubscription); - compositeSubscription.add(scheduler.schedule(new Action1() { - @Override - public void call(final Action0 self) { - innerSubscription.set(source.subscribe(new Observer() { + compositeSubscription.add(scheduler.schedule(new Action1() { + @Override + public void call(Inner inner) { + inner.schedule(new Action1() { @Override - public void onCompleted() { - self.call(); - } + public void call(final Inner inner) { + final Action1 _self = this; + innerSubscription.set(source.subscribe(new Observer() { - @Override - public void onError(Throwable error) { - observer.onError(error); - } + @Override + public void onCompleted() { + inner.schedule(_self); + } - @Override - public void onNext(T value) { - observer.onNext(value); + @Override + public void onError(Throwable error) { + observer.onError(error); + } + + @Override + public void onNext(T value) { + observer.onNext(value); + } + })); } - })); + }); } + })); return compositeSubscription; } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRetry.java b/rxjava-core/src/main/java/rx/operators/OperationRetry.java index 3e55077849..a84ab90aac 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRetry.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRetry.java @@ -1,12 +1,12 @@ /** * 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 - * + * + * 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. @@ -36,12 +36,10 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; -import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.Schedulers; -import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.MultipleAssignmentSubscription; -import rx.util.functions.Func2; +import rx.util.functions.Action1; public class OperationRetry { @@ -60,7 +58,6 @@ private static class Retry implements OnSubscribeFunc { private final Observable source; private final int retryCount; private final AtomicInteger attempts = new AtomicInteger(0); - private final CompositeSubscription subscription = new CompositeSubscription(); public Retry(Observable source, int retryCount) { this.source = source; @@ -68,20 +65,14 @@ public Retry(Observable source, int retryCount) { } @Override - public Subscription onSubscribe(Observer observer) { - MultipleAssignmentSubscription rescursiveSubscription = new MultipleAssignmentSubscription(); - subscription.add(Schedulers.currentThread().schedule(rescursiveSubscription, attemptSubscription(observer))); - subscription.add(rescursiveSubscription); - return subscription; - } - - private Func2 attemptSubscription(final Observer observer) { - return new Func2() { + public Subscription onSubscribe(final Observer observer) { + return Schedulers.trampoline().schedule(new Action1() { @Override - public Subscription call(final Scheduler scheduler, final MultipleAssignmentSubscription rescursiveSubscription) { + public void call(final Inner inner) { + final Action1 _self = this; attempts.incrementAndGet(); - return source.subscribe(new Observer() { + source.subscribe(new Observer() { @Override public void onCompleted() { @@ -90,10 +81,9 @@ public void onCompleted() { @Override public void onError(Throwable e) { - if ((retryCount == INFINITE_RETRY || attempts.get() <= retryCount) && !subscription.isUnsubscribed()) { + if ((retryCount == INFINITE_RETRY || attempts.get() <= retryCount) && !inner.isUnsubscribed()) { // retry again - // add the new subscription and schedule a retry recursively - rescursiveSubscription.setSubscription(scheduler.schedule(rescursiveSubscription, attemptSubscription(observer))); + inner.schedule(_self); } else { // give up and pass the failure observer.onError(e); @@ -104,11 +94,10 @@ public void onError(Throwable e) { public void onNext(T v) { observer.onNext(v); } - }); + }); } - - }; + }); } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSample.java b/rxjava-core/src/main/java/rx/operators/OperationSample.java index 57ceb97d22..a48c0a512a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSample.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSample.java @@ -41,7 +41,7 @@ public final class OperationSample { * Samples the observable sequence at each interval. */ public static OnSubscribeFunc sample(final Observable source, long period, TimeUnit unit) { - return new Sample(source, period, unit, Schedulers.threadPoolForComputation()); + return new Sample(source, period, unit, Schedulers.computation()); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkip.java b/rxjava-core/src/main/java/rx/operators/OperationSkip.java index 706ae08c80..b74c53160d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkip.java @@ -23,9 +23,11 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.subscriptions.CompositeSubscription; import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Returns an Observable that skips the first num items emitted by the source @@ -140,7 +142,7 @@ public Subscription onSubscribe(Observer t1) { CompositeSubscription csub = new CompositeSubscription(timer, data); - SourceObserver so = new SourceObserver(t1, csub); + final SourceObserver so = new SourceObserver(t1, csub); data.wrap(source.subscribe(so)); if (!data.isUnsubscribed()) { timer.wrap(scheduler.schedule(so, time, unit)); @@ -155,7 +157,7 @@ public Subscription onSubscribe(Observer t1) { * @param * the observed value type */ - private static final class SourceObserver implements Observer, Action0 { + private static final class SourceObserver implements Observer, Action1 { final AtomicBoolean gate; final Observer observer; final Subscription cancel; @@ -193,7 +195,7 @@ public void onCompleted() { } @Override - public void call() { + public void call(Inner inner) { gate.set(true); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java index 1d244312e2..5f1329bda5 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java @@ -15,13 +15,18 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import rx.util.functions.Func2; +import rx.util.functions.Action1; /** * Asynchronously subscribes and unsubscribes Observers on the specified Scheduler. @@ -45,21 +50,28 @@ public SubscribeOn(Observable source, Scheduler scheduler) { @Override public Subscription onSubscribe(final Observer observer) { - return scheduler.schedule(null, new Func2() { + final CompositeSubscription s = new CompositeSubscription(); + scheduler.schedule(new Action1() { + @Override - public Subscription call(Scheduler s, T t) { - return new ScheduledSubscription(source.subscribe(observer), scheduler); + public void call(final Inner inner) { + s.add(new ScheduledSubscription(source.subscribe(observer), inner)); } + }); + // only include the ScheduledSubscription + // but not the actual Subscription from the Scheduler as we need to schedule the unsubscribe action + // and therefore can't unsubscribe the scheduler until after the unsubscribe happens + return s; } } private static class ScheduledSubscription implements Subscription { private final Subscription underlying; - private final Scheduler scheduler; private volatile boolean unsubscribed = false; + private final Scheduler.Inner scheduler; - private ScheduledSubscription(Subscription underlying, Scheduler scheduler) { + private ScheduledSubscription(Subscription underlying, Inner scheduler) { this.underlying = underlying; this.scheduler = scheduler; } @@ -67,10 +79,12 @@ private ScheduledSubscription(Subscription underlying, Scheduler scheduler) { @Override public void unsubscribe() { unsubscribed = true; - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { underlying.unsubscribe(); + // tear down this subscription as well now that we're done + inner.unsubscribe(); } }); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeTimed.java b/rxjava-core/src/main/java/rx/operators/OperationTakeTimed.java index 2894edabbf..abd63d1f34 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeTimed.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeTimed.java @@ -22,10 +22,12 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Returns an Observable that emits the first num items emitted by the source @@ -41,7 +43,7 @@ public final class OperationTakeTimed { //TODO this has not been migrated to use bind yet - + /** * Returns a specified number of contiguous values from the start of an observable sequence. * @@ -195,7 +197,7 @@ public Subscription onSubscribe(Observer t1) { CompositeSubscription csub = new CompositeSubscription(timer, data); - SourceObserver so = new SourceObserver(t1, csub); + final SourceObserver so = new SourceObserver(t1, csub); data.wrap(source.subscribe(so)); if (!data.isUnsubscribed()) { timer.wrap(scheduler.schedule(so, time, unit)); @@ -210,7 +212,7 @@ public Subscription onSubscribe(Observer t1) { * @param * the observed value type */ - private static final class SourceObserver implements Observer, Action0 { + private static final class SourceObserver implements Observer, Action1 { final Observer observer; final Subscription cancel; final AtomicInteger state = new AtomicInteger(); @@ -281,7 +283,7 @@ public void onCompleted() { } @Override - public void call() { + public void call(Inner inner) { onCompleted(); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java index 6b009e1e27..b942065c48 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java +++ b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java @@ -43,7 +43,7 @@ public final class OperationThrottleFirst { * @return A {@link Func1} which performs the throttle operation. */ public static OnSubscribeFunc throttleFirst(Observable items, long windowDuration, TimeUnit unit) { - return throttleFirst(items, windowDuration, unit, Schedulers.threadPoolForComputation()); + return throttleFirst(items, windowDuration, unit, Schedulers.computation()); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java index cf838a6061..224048788e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java @@ -24,12 +24,14 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.Schedulers; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.SerialSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; @@ -43,11 +45,11 @@ public final class OperationTimeout { public static OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit) { - return new Timeout(source, timeout, timeUnit, null, Schedulers.threadPoolForComputation()); + return new Timeout(source, timeout, timeUnit, null, Schedulers.computation()); } public static OnSubscribeFunc timeout(Observable sequence, long timeout, TimeUnit timeUnit, Observable other) { - return new Timeout(sequence, timeout, timeUnit, other, Schedulers.threadPoolForComputation()); + return new Timeout(sequence, timeout, timeUnit, other, Schedulers.computation()); } public static OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { @@ -84,9 +86,9 @@ public Subscription onSubscribe(final Observer observer) { @Override public Subscription call() { final long expected = actual.get(); - return scheduler.schedule(new Action0() { + return scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { boolean timeoutWins = false; synchronized (gate) { if (expected == actual.get() && !terminated.getAndSet(true)) { @@ -98,7 +100,7 @@ public void call() { observer.onError(new TimeoutException()); } else { - serial.setSubscription(other.subscribe(observer)); + serial.set(other.subscribe(observer)); } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimer.java b/rxjava-core/src/main/java/rx/operators/OperationTimer.java index f5e5197a1a..ee19ee7512 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimer.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimer.java @@ -20,8 +20,10 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Operation Timer with several overloads. @@ -49,9 +51,9 @@ public TimerOnce(long dueTime, TimeUnit unit, Scheduler scheduler) { @Override public Subscription onSubscribe(final Observer t1) { - return scheduler.schedule(new Action0() { + return scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { t1.onNext(0L); t1.onCompleted(); } @@ -78,16 +80,14 @@ public TimerPeriodically(long initialDelay, long period, TimeUnit unit, Schedule @Override public Subscription onSubscribe(final Observer t1) { - return scheduler.schedulePeriodically(new Action0() { + return scheduler.schedulePeriodically(new Action1() { long count; @Override - public void call() { + public void call(Inner inner) { t1.onNext(count++); } - }, - initialDelay, period, unit - ); + }, initialDelay, period, unit); } } } diff --git a/rxjava-core/src/main/java/rx/schedulers/CurrentThreadScheduler.java b/rxjava-core/src/main/java/rx/schedulers/CurrentThreadScheduler.java deleted file mode 100644 index effc5101be..0000000000 --- a/rxjava-core/src/main/java/rx/schedulers/CurrentThreadScheduler.java +++ /dev/null @@ -1,168 +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.schedulers; - -import java.util.PriorityQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import rx.Scheduler; -import rx.Subscription; -import rx.subscriptions.MultipleAssignmentSubscription; -import rx.util.functions.Func1; -import rx.util.functions.Func2; - -/** - * Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed. - */ -public class CurrentThreadScheduler extends Scheduler { - private static final CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler(); - private static final AtomicLong counter = new AtomicLong(0); - - public static CurrentThreadScheduler getInstance() { - return INSTANCE; - } - - private static final ThreadLocal> QUEUE = new ThreadLocal>() { - protected java.util.PriorityQueue initialValue() { - return new PriorityQueue(); - }; - }; - - private static final ThreadLocal PROCESSING = new ThreadLocal() { - protected Boolean initialValue() { - return Boolean.FALSE; - }; - }; - - /* package accessible for unit tests */CurrentThreadScheduler() { - } - - @Override - public Subscription schedule(T state, Func2 action) { - // immediately move to the InnerCurrentThreadScheduler - InnerCurrentThreadScheduler innerScheduler = new InnerCurrentThreadScheduler(); - innerScheduler.schedule(state, action); - enqueueFromOuter(innerScheduler, now()); - return innerScheduler; - } - - @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - long execTime = now() + unit.toMillis(delayTime); - - // create an inner scheduler and queue it for execution - InnerCurrentThreadScheduler innerScheduler = new InnerCurrentThreadScheduler(); - innerScheduler.schedule(state, action, delayTime, unit); - enqueueFromOuter(innerScheduler, execTime); - return innerScheduler; - } - - /* - * This will accept InnerCurrentThreadScheduler instances and execute them in order they are received - * and on each of them will loop internally until each is complete. - */ - private void enqueueFromOuter(final InnerCurrentThreadScheduler innerScheduler, long execTime) { - // Note that everything here is single-threaded so we won't have race conditions - PriorityQueue queue = QUEUE.get(); - queue.add(new TimedAction(new Func1() { - - @Override - public Subscription call(Scheduler _) { - // when the InnerCurrentThreadScheduler gets scheduled we want to process its tasks - return innerScheduler.startProcessing(); - } - }, execTime, counter.incrementAndGet())); - - // first time through starts the loop - if (!PROCESSING.get()) { - PROCESSING.set(Boolean.TRUE); - while (!queue.isEmpty()) { - queue.poll().action.call(innerScheduler); - } - PROCESSING.set(Boolean.FALSE); - } - } - - private static class InnerCurrentThreadScheduler extends Scheduler implements Subscription { - private final MultipleAssignmentSubscription childSubscription = new MultipleAssignmentSubscription(); - private final PriorityQueue innerQueue = new PriorityQueue(); - - @Override - public Subscription schedule(T state, Func2 action) { - DiscardableAction discardableAction = new DiscardableAction(state, action); - childSubscription.set(discardableAction); - enqueue(discardableAction, now()); - return childSubscription; - } - - @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - long execTime = now() + unit.toMillis(delayTime); - - DiscardableAction discardableAction = new DiscardableAction(state, new SleepingAction(action, this, execTime)); - childSubscription.set(discardableAction); - enqueue(discardableAction, execTime); - return childSubscription; - } - - private void enqueue(Func1 action, long execTime) { - innerQueue.add(new TimedAction(action, execTime, counter.incrementAndGet())); - } - - private Subscription startProcessing() { - while (!innerQueue.isEmpty()) { - innerQueue.poll().action.call(this); - } - return this; - } - - @Override - public void unsubscribe() { - childSubscription.unsubscribe(); - } - - @Override - public boolean isUnsubscribed() { - return childSubscription.isUnsubscribed(); - } - - } - - /** - * Use time to sort items so delayed actions are sorted to their appropriate position in the queue. - */ - private static class TimedAction implements Comparable { - final Func1 action; - final Long execTime; - final Long count; // In case if time between enqueueing took less than 1ms - - private TimedAction(Func1 action, Long execTime, Long count) { - this.action = action; - this.execTime = execTime; - this.count = count; - } - - @Override - public int compareTo(TimedAction that) { - int result = execTime.compareTo(that.execTime); - if (result == 0) { - return count.compareTo(that.count); - } - return result; - } - } -} diff --git a/rxjava-core/src/main/java/rx/schedulers/DiscardableAction.java b/rxjava-core/src/main/java/rx/schedulers/DiscardableAction.java deleted file mode 100644 index eafc825012..0000000000 --- a/rxjava-core/src/main/java/rx/schedulers/DiscardableAction.java +++ /dev/null @@ -1,62 +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.schedulers; - -import java.util.concurrent.atomic.AtomicBoolean; - -import rx.Scheduler; -import rx.Subscription; -import rx.operators.SafeObservableSubscription; -import rx.util.functions.Func1; -import rx.util.functions.Func2; - -/** - * Combines standard {@link Subscription#unsubscribe()} functionality with ability to skip execution if an unsubscribe occurs before the {@link #call()} method is invoked. - */ -/* package */class DiscardableAction implements Func1, Subscription { - private final Func2 underlying; - private final T state; - - private final SafeObservableSubscription wrapper = new SafeObservableSubscription(); - private final AtomicBoolean ready = new AtomicBoolean(true); - - public DiscardableAction(T state, Func2 underlying) { - this.state = state; - this.underlying = underlying; - } - - @Override - public Subscription call(Scheduler scheduler) { - if (ready.compareAndSet(true, false)) { - Subscription subscription = underlying.call(scheduler, state); - wrapper.wrap(subscription); - return subscription; - } - return wrapper; - } - - @Override - public void unsubscribe() { - ready.set(false); - wrapper.unsubscribe(); - } - - @Override - public boolean isUnsubscribed() { - return wrapper.isUnsubscribed(); - } - -} diff --git a/rxjava-core/src/main/java/rx/schedulers/ExecutorScheduler.java b/rxjava-core/src/main/java/rx/schedulers/ExecutorScheduler.java index bd31760395..b0c0607456 100644 --- a/rxjava-core/src/main/java/rx/schedulers/ExecutorScheduler.java +++ b/rxjava-core/src/main/java/rx/schedulers/ExecutorScheduler.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 Netflix, Inc. + * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,9 @@ import rx.Scheduler; import rx.Subscription; -import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; -import rx.util.functions.Func2; +import rx.util.functions.Action1; /** * A {@link Scheduler} implementation that uses an {@link Executor} or {@link ScheduledExecutorService} implementation. @@ -46,180 +45,74 @@ public ExecutorScheduler(ScheduledExecutorService executor) { } @Override - public Subscription schedulePeriodically(final T state, final Func2 action, long initialDelay, long period, TimeUnit unit) { - if (executor instanceof ScheduledExecutorService) { - final CompositeSubscription subscriptions = new CompositeSubscription(); - - ScheduledFuture f = ((ScheduledExecutorService) executor).scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - Subscription s = action.call(ExecutorScheduler.this, state); - subscriptions.add(s); - } - }, initialDelay, period, unit); - - subscriptions.add(Subscriptions.from(f)); - return subscriptions; - - } else { - return super.schedulePeriodically(state, action, initialDelay, period, unit); - } + public Subscription schedule(Action1 action) { + InnerExecutorScheduler inner = new InnerExecutorScheduler(); + inner.schedule(action); + return inner.innerSubscription; } + @Override - public Subscription schedule(final T state, final Func2 action, long delayTime, TimeUnit unit) { - final DiscardableAction discardableAction = new DiscardableAction(state, action); - final InnerExecutorScheduler _scheduler = new InnerExecutorScheduler(executor); - - // all subscriptions that may need to be unsubscribed - final CompositeSubscription subscription = new CompositeSubscription(discardableAction, _scheduler); + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + InnerExecutorScheduler inner = new InnerExecutorScheduler(); + inner.schedule(action, delayTime, unit); + return inner.innerSubscription; + } + @Override + public Subscription schedulePeriodically(final Action1 action, long initialDelay, long period, TimeUnit unit) { if (executor instanceof ScheduledExecutorService) { - // we are a ScheduledExecutorService so can do proper scheduling - ScheduledFuture f = ((ScheduledExecutorService) executor).schedule(new Runnable() { + final InnerExecutorScheduler inner = new InnerExecutorScheduler(); + ScheduledFuture f = ((ScheduledExecutorService) executor).scheduleAtFixedRate(new Runnable() { @Override public void run() { - // when the delay has passed we now do the work on the actual scheduler - discardableAction.call(_scheduler); - } - }, delayTime, unit); - // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.from(f)); - } else { - // we are not a ScheduledExecutorService so can't directly schedule - if (delayTime == 0) { - // no delay so put on the thread-pool right now - return schedule(state, action); - } else { - // there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService - // to handle the scheduling and once it's ready then execute on this Executor - ScheduledFuture f = GenericScheduledExecutorService.getInstance().schedule(new Runnable() { - - @Override - public void run() { - // now execute on the real Executor (by using the other overload that schedules for immediate execution) - _scheduler.schedule(state, action); + if (inner.isUnsubscribed()) { + // don't execute if unsubscribed + return; } - }, delayTime, unit); - // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.from(f)); - } - } - return subscription; - } - - @Override - public Subscription schedule(T state, Func2 action) { - CompositeSubscription s = new CompositeSubscription(); - final DiscardableAction discardableAction = new DiscardableAction(state, action); - s.add(discardableAction); - - final InnerExecutorScheduler _scheduler = new InnerExecutorScheduler(executor); - s.add(_scheduler); - - s.add(execute(executor, new Runnable() { - @Override - public void run() { - discardableAction.call(_scheduler); - } - })); - - return s; - } + action.call(inner); + } + }, initialDelay, period, unit); - /** - * Execute on the given Executor and retrieve a Subscription - * - * @param executor - * @param r - * @return - */ - private static Subscription execute(Executor executor, Runnable r) { - // submit for immediate execution - if (executor instanceof ExecutorService) { - // we are an ExecutorService so get a Future back that supports unsubscribe - Future f = ((ExecutorService) executor).submit(r); - // add the Future as a subscription so we can cancel the scheduled action if an unsubscribe happens - return Subscriptions.from(f); + inner.innerSubscription.set(Subscriptions.from(f)); + return inner; } else { - // we are the lowest common denominator so can't unsubscribe once we execute - executor.execute(r); - return Subscriptions.empty(); + return super.schedulePeriodically(action, initialDelay, period, unit); } } - private static class InnerExecutorScheduler extends Scheduler implements Subscription { - - private final MultipleAssignmentSubscription childSubscription = new MultipleAssignmentSubscription(); - private final Executor executor; - - InnerExecutorScheduler(Executor executor) { - this.executor = executor; - } - - @Override - public Subscription schedule(T state, Func2 action) { - if (childSubscription.isUnsubscribed()) { - return childSubscription; - } + private class InnerExecutorScheduler extends Scheduler.Inner { - CompositeSubscription s = new CompositeSubscription(); - final DiscardableAction discardableAction = new DiscardableAction(state, action); - s.add(discardableAction); - - final Scheduler _scheduler = this; - - s.add(execute(executor, new Runnable() { - - @Override - public void run() { - discardableAction.call(_scheduler); - } - })); - - // replace the InnerExecutorScheduler child subscription with this one - childSubscription.set(s); - /* - * TODO: Consider what will happen if `schedule` is run concurrently instead of recursively - * and we lose subscriptions as the `childSubscription` only remembers the last one scheduled. - * - * Not obvious that this should ever happen. Can it? - * - * benjchristensen => Haven't been able to come up with a valid test case to prove this as an issue - * so it may not be. - */ - - return childSubscription; - } + private final MultipleAssignmentSubscription innerSubscription = new MultipleAssignmentSubscription(); @Override - public Subscription schedule(final T state, final Func2 action, long delayTime, TimeUnit unit) { - if (childSubscription.isUnsubscribed()) { - return childSubscription; + public void schedule(final Action1 action, long delayTime, TimeUnit unit) { + if (innerSubscription.isUnsubscribed()) { + // don't schedule, we are unsubscribed + return; } - CompositeSubscription s = new CompositeSubscription(); - final DiscardableAction discardableAction = new DiscardableAction(state, action); - s.add(discardableAction); - - final Scheduler _scheduler = this; - + final Inner _inner = this; if (executor instanceof ScheduledExecutorService) { // we are a ScheduledExecutorService so can do proper scheduling ScheduledFuture f = ((ScheduledExecutorService) executor).schedule(new Runnable() { @Override public void run() { + if (innerSubscription.isUnsubscribed()) { + // don't execute if unsubscribed + return; + } // when the delay has passed we now do the work on the actual scheduler - discardableAction.call(_scheduler); + action.call(_inner); } }, delayTime, unit); - // replace the InnerExecutorScheduler child subscription with this one - childSubscription.set(Subscriptions.from(f)); + // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens + innerSubscription.set(Subscriptions.from(f)); } else { // we are not a ScheduledExecutorService so can't directly schedule if (delayTime == 0) { // no delay so put on the thread-pool right now - return schedule(state, action); + schedule(action); } else { // there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService // to handle the scheduling and once it's ready then execute on this Executor @@ -227,25 +120,60 @@ public void run() { @Override public void run() { + if (innerSubscription.isUnsubscribed()) { + // don't execute if unsubscribed + return; + } // now execute on the real Executor (by using the other overload that schedules for immediate execution) - _scheduler.schedule(state, action); + schedule(action); } }, delayTime, unit); - // replace the InnerExecutorScheduler child subscription with this one - childSubscription.set(Subscriptions.from(f)); + // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens + innerSubscription.set(Subscriptions.from(f)); } } - return childSubscription; + } + + @Override + public void schedule(final Action1 action) { + if (innerSubscription.isUnsubscribed()) { + // don't schedule, we are unsubscribed + return; + } + + // work to be done on a thread + final Inner _inner = this; + Runnable r = new Runnable() { + @Override + public void run() { + if (innerSubscription.isUnsubscribed()) { + // don't execute if unsubscribed + return; + } + action.call(_inner); + } + }; + + // submit for immediate execution + if (executor instanceof ExecutorService) { + // we are an ExecutorService so get a Future back that supports unsubscribe + Future f = ((ExecutorService) executor).submit(r); + // add the Future as a subscription so we can cancel the scheduled action if an unsubscribe happens + innerSubscription.set(Subscriptions.from(f)); + } else { + // we are the lowest common denominator so can't unsubscribe once we execute + executor.execute(r); + } } @Override public void unsubscribe() { - childSubscription.unsubscribe(); + innerSubscription.unsubscribe(); } @Override public boolean isUnsubscribed() { - return childSubscription.isUnsubscribed(); + return innerSubscription.isUnsubscribed(); } } diff --git a/rxjava-core/src/main/java/rx/schedulers/GenericScheduledExecutorService.java b/rxjava-core/src/main/java/rx/schedulers/GenericScheduledExecutorService.java index 0ed3bec28c..f9b3bbe402 100644 --- a/rxjava-core/src/main/java/rx/schedulers/GenericScheduledExecutorService.java +++ b/rxjava-core/src/main/java/rx/schedulers/GenericScheduledExecutorService.java @@ -31,7 +31,7 @@ *

* NOTE: No actual work should be done on tasks submitted to this executor. Submit a task with the appropriate delay which then in turn invokes * the work asynchronously on the appropriate {@link Scheduler} implementation. This means for example that you would not use this approach - * along with {@link CurrentThreadScheduler} or {@link ImmediateScheduler}. + * along with {@link TrampolineScheduler} or {@link ImmediateScheduler}. */ /* package */class GenericScheduledExecutorService { diff --git a/rxjava-core/src/main/java/rx/schedulers/ImmediateScheduler.java b/rxjava-core/src/main/java/rx/schedulers/ImmediateScheduler.java index 939402cec8..810123b3ce 100644 --- a/rxjava-core/src/main/java/rx/schedulers/ImmediateScheduler.java +++ b/rxjava-core/src/main/java/rx/schedulers/ImmediateScheduler.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 Netflix, Inc. + * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import rx.Scheduler; import rx.Subscription; -import rx.util.functions.Func2; +import rx.subscriptions.BooleanSubscription; +import rx.util.functions.Action1; /** * Executes work immediately on the current thread. @@ -35,15 +36,47 @@ public static ImmediateScheduler getInstance() { } @Override - public Subscription schedule(T state, Func2 action) { - return action.call(this, state); + public Subscription schedule(Action1 action) { + InnerImmediateScheduler inner = new InnerImmediateScheduler(); + inner.schedule(action); + return inner.innerSubscription; } @Override - public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { - // since we are executing immediately on this thread we must cause this thread to sleep - long execTime = now() + unit.toMillis(dueTime); + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + InnerImmediateScheduler inner = new InnerImmediateScheduler(); + inner.schedule(action, delayTime, unit); + return inner.innerSubscription; + } + + + private class InnerImmediateScheduler extends Scheduler.Inner implements Subscription { + + final BooleanSubscription innerSubscription = new BooleanSubscription(); + + @Override + public void schedule(Action1 action, long delayTime, TimeUnit unit) { + // since we are executing immediately on this thread we must cause this thread to sleep + long execTime = now() + unit.toMillis(delayTime); + + schedule(new SleepingAction(action, ImmediateScheduler.this, execTime)); + } + + @Override + public void schedule(Action1 action) { + action.call(this); + } + + @Override + public void unsubscribe() { + innerSubscription.unsubscribe(); + } + + @Override + public boolean isUnsubscribed() { + return innerSubscription.isUnsubscribed(); + } - return schedule(state, new SleepingAction(action, this, execTime)); } + } diff --git a/rxjava-core/src/main/java/rx/schedulers/NewThreadScheduler.java b/rxjava-core/src/main/java/rx/schedulers/NewThreadScheduler.java index 1a34b460ed..157ed660e8 100644 --- a/rxjava-core/src/main/java/rx/schedulers/NewThreadScheduler.java +++ b/rxjava-core/src/main/java/rx/schedulers/NewThreadScheduler.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 Netflix, Inc. + * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,14 +21,13 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import rx.Scheduler; import rx.Subscription; import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; -import rx.util.functions.Func2; +import rx.util.functions.Action1; /** * Schedules work on a new thread. @@ -37,6 +36,13 @@ public class NewThreadScheduler extends Scheduler { private final static NewThreadScheduler INSTANCE = new NewThreadScheduler(); private final static AtomicLong count = new AtomicLong(); + private final static ThreadFactory THREAD_FACTORY = new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "RxNewThreadScheduler-" + count.incrementAndGet()); + } + }; public static NewThreadScheduler getInstance() { return INSTANCE; @@ -46,127 +52,101 @@ private NewThreadScheduler() { } - private static class EventLoopScheduler extends Scheduler { + @Override + public Subscription schedule(Action1 action) { + EventLoopScheduler innerScheduler = new EventLoopScheduler(); + innerScheduler.schedule(action); + return innerScheduler.innerSubscription; + } + + @Override + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + EventLoopScheduler innerScheduler = new EventLoopScheduler(); + innerScheduler.schedule(action, delayTime, unit); + return innerScheduler.innerSubscription; + } + + private class EventLoopScheduler extends Scheduler.Inner implements Subscription { + private final CompositeSubscription innerSubscription = new CompositeSubscription(); private final ExecutorService executor; - private final MultipleAssignmentSubscription childSubscription = new MultipleAssignmentSubscription(); + private final Inner _inner = this; private EventLoopScheduler() { - executor = Executors.newFixedThreadPool(1, new ThreadFactory() { - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r, "RxNewThreadScheduler-" + count.incrementAndGet()); - t.setDaemon(true); - return t; - } - }); + executor = Executors.newSingleThreadExecutor(THREAD_FACTORY); } @Override - public Subscription schedule(T state, Func2 action) { - if (childSubscription.isUnsubscribed()) { - return childSubscription; + public void schedule(final Action1 action) { + if (innerSubscription.isUnsubscribed()) { + // don't schedule, we are unsubscribed + return; } - CompositeSubscription s = new CompositeSubscription(); - final DiscardableAction discardableAction = new DiscardableAction(state, action); - s.add(discardableAction); - - final Scheduler _scheduler = this; - s.add(Subscriptions.from(executor.submit(new Runnable() { + final AtomicReference sf = new AtomicReference(); + Subscription s = Subscriptions.from(executor.submit(new Runnable() { @Override public void run() { - discardableAction.call(_scheduler); + try { + if (innerSubscription.isUnsubscribed()) { + return; + } + action.call(_inner); + } finally { + // remove the subscription now that we're completed + Subscription s = sf.get(); + if (s != null) { + innerSubscription.remove(s); + } + } } - }))); - - // replace the EventLoopScheduler child subscription with this one - childSubscription.set(s); - /* - * If `schedule` is run concurrently instead of recursively then we'd lose subscriptions as the `childSubscription` - * only remembers the last one scheduled. However, the parent subscription will shutdown the entire EventLoopScheduler - * and the ExecutorService which will terminate all outstanding tasks so this childSubscription is actually somewhat - * superfluous for stopping and cleanup ... though childSubscription does ensure exactness as can be seen by - * the `testUnSubscribeForScheduler()` unit test which fails if the `childSubscription` does not exist. - */ - - return childSubscription; + })); + + sf.set(s); + innerSubscription.add(s); } @Override - public Subscription schedule(final T state, final Func2 action, final long delayTime, final TimeUnit unit) { - if (childSubscription.isUnsubscribed()) { - return childSubscription; - } - + public void schedule(final Action1 action, long delayTime, TimeUnit unit) { + final AtomicReference sf = new AtomicReference(); // we will use the system scheduler since it doesn't make sense to launch a new Thread and then sleep // we will instead schedule the event then launch the thread after the delay has passed - final Scheduler _scheduler = this; - final CompositeSubscription subscription = new CompositeSubscription(); ScheduledFuture f = GenericScheduledExecutorService.getInstance().schedule(new Runnable() { @Override public void run() { - if (!subscription.isUnsubscribed()) { - // when the delay has passed we now do the work on the actual scheduler - Subscription s = _scheduler.schedule(state, action); - // add the subscription to the CompositeSubscription so it is unsubscribed - subscription.add(s); + try { + if (innerSubscription.isUnsubscribed()) { + return; + } + // now that the delay is past schedule the work to be done for real on the UI thread + schedule(action); + } finally { + // remove the subscription now that we're completed + Subscription s = sf.get(); + if (s != null) { + innerSubscription.remove(s); + } } } }, delayTime, unit); // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.from(f)); + Subscription s = Subscriptions.from(f); + sf.set(s); + innerSubscription.add(s); + } - return subscription; + @Override + public void unsubscribe() { + innerSubscription.unsubscribe(); } - private void shutdownNow() { - executor.shutdownNow(); + @Override + public boolean isUnsubscribed() { + return innerSubscription.isUnsubscribed(); } } - @Override - public Subscription schedule(T state, Func2 action) { - final EventLoopScheduler s = new EventLoopScheduler(); - CompositeSubscription cs = new CompositeSubscription(); - cs.add(s.schedule(state, action)); - cs.add(Subscriptions.create(new Action0() { - - @Override - public void call() { - // shutdown the executor, all tasks queued to run and clean up resources - s.shutdownNow(); - } - })); - return cs; - } - - @Override - public Subscription schedule(final T state, final Func2 action, long delay, TimeUnit unit) { - // we will use the system scheduler since it doesn't make sense to launch a new Thread and then sleep - // we will instead schedule the event then launch the thread after the delay has passed - final Scheduler _scheduler = this; - final CompositeSubscription subscription = new CompositeSubscription(); - ScheduledFuture f = GenericScheduledExecutorService.getInstance().schedule(new Runnable() { - - @Override - public void run() { - if (!subscription.isUnsubscribed()) { - // when the delay has passed we now do the work on the actual scheduler - Subscription s = _scheduler.schedule(state, action); - // add the subscription to the CompositeSubscription so it is unsubscribed - subscription.add(s); - } - } - }, delay, unit); - - // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.from(f)); - - return subscription; - } } diff --git a/rxjava-core/src/main/java/rx/schedulers/Schedulers.java b/rxjava-core/src/main/java/rx/schedulers/Schedulers.java index 0cac10a48c..d9ad39aabb 100644 --- a/rxjava-core/src/main/java/rx/schedulers/Schedulers.java +++ b/rxjava-core/src/main/java/rx/schedulers/Schedulers.java @@ -47,12 +47,23 @@ public static Scheduler immediate() { /** * {@link Scheduler} that queues work on the current thread to be executed after the current work completes. * - * @return {@link CurrentThreadScheduler} instance + * @return {@link TrampolineScheduler} instance + * @deprecated Use trampoline() instead */ + @Deprecated public static Scheduler currentThread() { - return CurrentThreadScheduler.getInstance(); + return TrampolineScheduler.getInstance(); } + /** + * {@link Scheduler} that queues work on the current thread to be executed after the current work completes. + * + * @return {@link TrampolineScheduler} instance + */ + public static Scheduler trampoline() { + return TrampolineScheduler.getInstance(); + } + /** * {@link Scheduler} that creates a new {@link Thread} for each unit of work. * diff --git a/rxjava-core/src/main/java/rx/schedulers/SleepingAction.java b/rxjava-core/src/main/java/rx/schedulers/SleepingAction.java index 51afadabfd..ec87050336 100644 --- a/rxjava-core/src/main/java/rx/schedulers/SleepingAction.java +++ b/rxjava-core/src/main/java/rx/schedulers/SleepingAction.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 Netflix, Inc. + * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +16,25 @@ package rx.schedulers; import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Func2; +import rx.Scheduler.Inner; +import rx.util.functions.Action1; -/* package */class SleepingAction implements Func2 { - private final Func2 underlying; +/* package */class SleepingAction implements Action1 { + private final Action1 underlying; private final Scheduler scheduler; private final long execTime; - public SleepingAction(Func2 underlying, Scheduler scheduler, long execTime) { + public SleepingAction(Action1 underlying, Scheduler scheduler, long execTime) { this.underlying = underlying; this.scheduler = scheduler; this.execTime = execTime; } @Override - public Subscription call(Scheduler s, T state) { + public void call(Inner s) { + if (s.isUnsubscribed()) { + return; + } if (execTime > scheduler.now()) { long delay = execTime - scheduler.now(); if (delay > 0) { @@ -44,6 +47,10 @@ public Subscription call(Scheduler s, T state) { } } - return underlying.call(s, state); + // after waking up check the subscription + if (s.isUnsubscribed()) { + return; + } + underlying.call(s); } } diff --git a/rxjava-core/src/main/java/rx/schedulers/TestScheduler.java b/rxjava-core/src/main/java/rx/schedulers/TestScheduler.java index cb6126b83b..2c10fa4efb 100644 --- a/rxjava-core/src/main/java/rx/schedulers/TestScheduler.java +++ b/rxjava-core/src/main/java/rx/schedulers/TestScheduler.java @@ -23,41 +23,36 @@ import rx.Scheduler; import rx.Subscription; +import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func2; public class TestScheduler extends Scheduler { - private final Queue> queue = new PriorityQueue>(11, new CompareActionsByTime()); + private final Queue queue = new PriorityQueue(11, new CompareActionsByTime()); - private static class TimedAction { + private static class TimedAction { private final long time; - private final Func2 action; - private final T state; - private final TestScheduler scheduler; - private final AtomicBoolean isCancelled = new AtomicBoolean(false); + private final Action1 action; + private final Inner scheduler; - private TimedAction(TestScheduler scheduler, long time, Func2 action, T state) { + private TimedAction(Inner scheduler, long time, Action1 action) { this.time = time; this.action = action; - this.state = state; this.scheduler = scheduler; } - public void cancel() { - isCancelled.set(true); - } - @Override public String toString() { return String.format("TimedAction(time = %d, action = %s)", time, action.toString()); } } - private static class CompareActionsByTime implements Comparator> { + private static class CompareActionsByTime implements Comparator { @Override - public int compare(TimedAction action1, TimedAction action2) { + public int compare(TimedAction action1, TimedAction action2) { return Long.valueOf(action1.time).compareTo(Long.valueOf(action2.time)); } } @@ -83,41 +78,65 @@ public void triggerActions() { triggerActions(time); } - @SuppressWarnings("unchecked") private void triggerActions(long targetTimeInNanos) { while (!queue.isEmpty()) { - TimedAction current = queue.peek(); + TimedAction current = queue.peek(); if (current.time > targetTimeInNanos) { break; } time = current.time; queue.remove(); - // Only execute if the TimedAction has not yet been cancelled - if (!current.isCancelled.get()) { - // because the queue can have wildcards we have to ignore the type T for the state - ((Func2) current.action).call(current.scheduler, current.state); + // Only execute if not unsubscribed + if (!current.scheduler.isUnsubscribed()) { + current.action.call(current.scheduler); } } time = targetTimeInNanos; } @Override - public Subscription schedule(T state, Func2 action) { - return schedule(state, action, 0, TimeUnit.MILLISECONDS); + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + InnerTestScheduler inner = new InnerTestScheduler(); + final TimedAction timedAction = new TimedAction(inner, time + unit.toNanos(delayTime), action); + queue.add(timedAction); + return inner; } @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - final TimedAction timedAction = new TimedAction(this, time + unit.toNanos(delayTime), action, state); + public Subscription schedule(Action1 action) { + InnerTestScheduler inner = new InnerTestScheduler(); + final TimedAction timedAction = new TimedAction(inner, 0, action); queue.add(timedAction); + return inner; + } - return Subscriptions.create(new Action0() { + private final class InnerTestScheduler extends Inner { + + private BooleanSubscription s = new BooleanSubscription(); + + @Override + public void unsubscribe() { + s.unsubscribe(); + } + + @Override + public boolean isUnsubscribed() { + return s.isUnsubscribed(); + } + + @Override + public void schedule(Action1 action, long delayTime, TimeUnit unit) { + final TimedAction timedAction = new TimedAction(this, time + unit.toNanos(delayTime), action); + queue.add(timedAction); + } + + @Override + public void schedule(Action1 action) { + final TimedAction timedAction = new TimedAction(this, 0, action); + queue.add(timedAction); + } - @Override - public void call() { - timedAction.cancel(); - } - }); } -} + +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/schedulers/TrampolineScheduler.java b/rxjava-core/src/main/java/rx/schedulers/TrampolineScheduler.java new file mode 100644 index 0000000000..0e0e8ccd0f --- /dev/null +++ b/rxjava-core/src/main/java/rx/schedulers/TrampolineScheduler.java @@ -0,0 +1,134 @@ +/** + * Copyright 2013 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.schedulers; + +import java.util.PriorityQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import rx.Scheduler; +import rx.Subscription; +import rx.subscriptions.BooleanSubscription; +import rx.util.functions.Action1; + +/** + * Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed. + */ +public class TrampolineScheduler extends Scheduler { + private static final TrampolineScheduler INSTANCE = new TrampolineScheduler(); + + public static TrampolineScheduler getInstance() { + return INSTANCE; + } + + @Override + public Subscription schedule(Action1 action) { + InnerCurrentThreadScheduler inner = new InnerCurrentThreadScheduler(); + inner.schedule(action); + return inner.innerSubscription; + } + + @Override + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + InnerCurrentThreadScheduler inner = new InnerCurrentThreadScheduler(); + inner.schedule(action, delayTime, unit); + return inner.innerSubscription; + } + + /* package accessible for unit tests */TrampolineScheduler() { + } + + private static final ThreadLocal> QUEUE = new ThreadLocal>(); + + private final AtomicInteger counter = new AtomicInteger(0); + + private class InnerCurrentThreadScheduler extends Scheduler.Inner implements Subscription { + + private final BooleanSubscription innerSubscription = new BooleanSubscription(); + + @Override + public void schedule(Action1 action) { + enqueue(action, now()); + } + + @Override + public void schedule(Action1 action, long delayTime, TimeUnit unit) { + long execTime = now() + unit.toMillis(delayTime); + + enqueue(new SleepingAction(action, TrampolineScheduler.this, execTime), execTime); + } + + private void enqueue(Action1 action, long execTime) { + if (innerSubscription.isUnsubscribed()) { + return; + } + PriorityQueue queue = QUEUE.get(); + boolean exec = queue == null; + + if (exec) { + queue = new PriorityQueue(); + QUEUE.set(queue); + } + + queue.add(new TimedAction(action, execTime, counter.incrementAndGet())); + + if (exec) { + while (!queue.isEmpty()) { + if (innerSubscription.isUnsubscribed()) { + return; + } + queue.poll().action.call(this); + } + + QUEUE.set(null); + } + } + + @Override + public void unsubscribe() { + QUEUE.set(null); // this assumes we are calling unsubscribe from the same thread + innerSubscription.unsubscribe(); + } + + @Override + public boolean isUnsubscribed() { + return innerSubscription.isUnsubscribed(); + } + + } + + private static class TimedAction implements Comparable { + final Action1 action; + final Long execTime; + final Integer count; // In case if time between enqueueing took less than 1ms + + private TimedAction(Action1 action, Long execTime, Integer count) { + this.action = action; + this.execTime = execTime; + this.count = count; + } + + @Override + public int compareTo(TimedAction that) { + int result = execTime.compareTo(that.execTime); + if (result == 0) { + return count.compareTo(that.count); + } + return result; + } + } + +} diff --git a/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java b/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java index d31f0d2452..231199faee 100644 --- a/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java +++ b/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java @@ -37,11 +37,11 @@ public static void main(String args[]) { public void call() { spt.singleResponse(Schedulers.immediate()); // spt.singleResponse(Schedulers.currentThread()); - // spt.singleResponse(Schedulers.threadPoolForComputation()); + // spt.singleResponse(Schedulers.computation()); // spt.arrayResponse(Schedulers.immediate()); // spt.arrayResponse(Schedulers.currentThread()); - // spt.arrayResponse(Schedulers.threadPoolForComputation()); + // spt.arrayResponse(Schedulers.computation()); } }); } catch (Exception e) { diff --git a/rxjava-core/src/test/java/rx/EventStream.java b/rxjava-core/src/test/java/rx/EventStream.java index 4b93ff8682..b3528b9d5c 100644 --- a/rxjava-core/src/test/java/rx/EventStream.java +++ b/rxjava-core/src/test/java/rx/EventStream.java @@ -20,9 +20,9 @@ import java.util.Map; import rx.Observable.OnSubscribeFunc; +import rx.Scheduler.Inner; import rx.schedulers.Schedulers; -import rx.subscriptions.BooleanSubscription; -import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Utility for retrieving a mock eventstream for testing. @@ -34,27 +34,31 @@ public static Observable getEventStream(final String type, final int numI @Override public Subscription onSubscribe(final Observer observer) { - final BooleanSubscription s = new BooleanSubscription(); // run on a background thread inside the OnSubscribeFunc so unsubscribe works - Schedulers.newThread().schedule(new Action0() { + return Schedulers.newThread().schedule(new Action1() { @Override - public void call() { - while (!(s.isUnsubscribed() || Thread.currentThread().isInterrupted())) { - observer.onNext(randomEvent(type, numInstances)); - try { - // slow it down somewhat - Thread.sleep(50); - } catch (InterruptedException e) { - observer.onError(e); + public void call(Inner inner) { + inner.schedule(new Action1() { + + @Override + public void call(Inner inner) { + while (!(inner.isUnsubscribed() || Thread.currentThread().isInterrupted())) { + observer.onNext(randomEvent(type, numInstances)); + try { + // slow it down somewhat + Thread.sleep(50); + } catch (InterruptedException e) { + observer.onError(e); + } + } + observer.onCompleted(); } - } - observer.onCompleted(); + + }); } }); - - return s; } }); } diff --git a/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java b/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java index 4a1fbefb01..edd31e2e04 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java @@ -28,10 +28,11 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.TestScheduler; import rx.subscriptions.CompositeSubscription; -import rx.util.functions.Action0; +import rx.util.functions.Action1; public class OperationAmbTest { @@ -47,23 +48,21 @@ private Observable createObservable(final String[] values, return Observable.create(new OnSubscribeFunc() { @Override - public Subscription onSubscribe( - final Observer observer) { + public Subscription onSubscribe(final Observer observer) { CompositeSubscription parentSubscription = new CompositeSubscription(); long delay = interval; for (final String value : values) { - parentSubscription.add(scheduler.schedule( - new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS)); + parentSubscription.add(scheduler.schedule(new Action1() { + @Override + public void call(Inner inner) { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS)); delay += interval; } - parentSubscription.add(scheduler.schedule(new Action0() { + parentSubscription.add(scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { if (e == null) { observer.onCompleted(); } else { diff --git a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java index 1864af8ac8..a29dca6447 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java @@ -33,6 +33,7 @@ import rx.Observable; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.TestScheduler; import rx.subjects.PublishSubject; @@ -347,18 +348,18 @@ private List list(String... args) { } private void push(final Observer observer, final T value, int delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(value); } }, delay, TimeUnit.MILLISECONDS); } private void complete(final Observer observer, int delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onCompleted(); } }, delay, TimeUnit.MILLISECONDS); diff --git a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java index 80b7e0fc5e..b41d082578 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java @@ -26,11 +26,13 @@ import rx.Observable; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.TestScheduler; import rx.subjects.PublishSubject; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func1; public class OperationDebounceTest { @@ -131,27 +133,27 @@ public Subscription onSubscribe(Observer observer) { } private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onCompleted(); } }, delay, TimeUnit.MILLISECONDS); } private void publishError(final Observer observer, long delay, final Exception error) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onError(error); } }, delay, TimeUnit.MILLISECONDS); } private void publishNext(final Observer observer, final long delay, final T value) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(value); } }, delay, TimeUnit.MILLISECONDS); diff --git a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java index 461add8421..ac4f217038 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java @@ -26,11 +26,13 @@ import rx.Observable; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.TestScheduler; import rx.subjects.PublishSubject; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; public class OperationSampleTest { private TestScheduler scheduler; @@ -51,21 +53,21 @@ public void testSample() { Observable source = Observable.create(new Observable.OnSubscribeFunc() { @Override public Subscription onSubscribe(final Observer observer1) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer1.onNext(1L); } }, 1, TimeUnit.SECONDS); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer1.onNext(2L); } }, 2, TimeUnit.SECONDS); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer1.onCompleted(); } }, 3, TimeUnit.SECONDS); diff --git a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java index da78c8644a..380c78eb02 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java @@ -27,8 +27,7 @@ import rx.Subscription; import rx.schedulers.Schedulers; import rx.test.OperatorTester; -import rx.util.functions.Action0; -import rx.util.functions.Func2; +import rx.util.functions.Action1; public class OperationSubscribeOnTest { @@ -42,9 +41,9 @@ public void testSubscribeOn() { Observer observer = mock(Observer.class); Subscription subscription = Observable.create(subscribeOn(w, scheduler)).subscribe(observer); - verify(scheduler, times(1)).schedule(isNull(), any(Func2.class)); + verify(scheduler, times(1)).schedule(any(Action1.class)); subscription.unsubscribe(); - verify(scheduler, times(1)).schedule(any(Action0.class)); + verify(scheduler, times(1)).schedule(any(Action1.class)); verifyNoMoreInteractions(scheduler); verify(observer, times(1)).onNext(1); diff --git a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java index 818dbbf27b..d560ca0f73 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java @@ -26,10 +26,12 @@ import rx.Observable; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.TestScheduler; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; public class OperationSwitchTest { @@ -351,27 +353,27 @@ public Subscription onSubscribe(Observer observer) { } private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onCompleted(); } }, delay, TimeUnit.MILLISECONDS); } private void publishError(final Observer observer, long delay, final Throwable error) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onError(error); } }, delay, TimeUnit.MILLISECONDS); } private void publishNext(final Observer observer, long delay, final T value) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(value); } }, delay, TimeUnit.MILLISECONDS); diff --git a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java index 28375982ab..c53b6d17d3 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java @@ -26,10 +26,11 @@ import rx.Observable; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.TestScheduler; import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; +import rx.util.functions.Action1; public class OperationThrottleFirstTest { @@ -98,27 +99,27 @@ public Subscription onSubscribe(Observer observer) { } private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onCompleted(); } }, delay, TimeUnit.MILLISECONDS); } private void publishError(final Observer observer, long delay, final Exception error) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onError(error); } }, delay, TimeUnit.MILLISECONDS); } private void publishNext(final Observer observer, long delay, final T value) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(value); } }, delay, TimeUnit.MILLISECONDS); diff --git a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java index a3f66d2e98..d403409333 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java @@ -29,6 +29,7 @@ import rx.Observable; import rx.Observer; +import rx.Scheduler.Inner; import rx.Subscription; import rx.schedulers.Schedulers; import rx.schedulers.TestScheduler; @@ -285,18 +286,18 @@ private List list(String... args) { } private void push(final Observer observer, final T value, int delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onNext(value); } }, delay, TimeUnit.MILLISECONDS); } private void complete(final Observer observer, int delay) { - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { observer.onCompleted(); } }, delay, TimeUnit.MILLISECONDS); diff --git a/rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java b/rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java index 7d30e46413..6a96a8fc6e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java @@ -453,7 +453,7 @@ public void testMaxConcurrent() { subscriptionCount, maxConcurrent); scos.add(sco); os.add(Observable.create(sco).subscribeOn( - Schedulers.threadPoolForComputation())); + Schedulers.computation())); } Iterator iter = Observable.merge(os, maxConcurrent) diff --git a/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java b/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java index 8a6e84ab6e..69446ed10b 100644 --- a/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java +++ b/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerConcurrencyTests.java @@ -28,14 +28,13 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscriber; import rx.Subscription; -import rx.operators.SafeObservableSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; import rx.util.functions.Action1; import rx.util.functions.Func1; -import rx.util.functions.Func2; /** * Base tests for schedulers that involve threads (concurrency). @@ -53,10 +52,9 @@ public abstract class AbstractSchedulerConcurrencyTests extends AbstractSchedule public final void testUnSubscribeForScheduler() throws InterruptedException { final AtomicInteger countReceived = new AtomicInteger(); final AtomicInteger countGenerated = new AtomicInteger(); - final SafeObservableSubscription s = new SafeObservableSubscription(); final CountDownLatch latch = new CountDownLatch(1); - s.wrap(Observable.interval(50, TimeUnit.MILLISECONDS) + Observable.interval(50, TimeUnit.MILLISECONDS) .map(new Func1() { @Override public Long call(Long aLong) { @@ -80,12 +78,12 @@ public void onError(Throwable e) { @Override public void onNext(Long args) { if (countReceived.incrementAndGet() == 2) { - s.unsubscribe(); + unsubscribe(); latch.countDown(); } System.out.println("==> Received " + args); } - })); + }); latch.await(1000, TimeUnit.MILLISECONDS); @@ -96,31 +94,40 @@ public void onNext(Long args) { } @Test - public void testUnsubscribeRecursiveScheduleWithStateAndFunc2() throws InterruptedException { + public void testUnsubscribeRecursiveScheduleFromOutside() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch unsubscribeLatch = new CountDownLatch(1); final AtomicInteger counter = new AtomicInteger(); - Subscription s = getScheduler().schedule(1L, new Func2() { + Subscription s = getScheduler().schedule(new Action1() { @Override - public Subscription call(Scheduler innerScheduler, Long i) { - System.out.println("Run: " + i); - if (i == 10) { - latch.countDown(); - try { - // wait for unsubscribe to finish so we are not racing it - unsubscribeLatch.await(); - } catch (InterruptedException e) { - // we expect the countDown if unsubscribe is not working - // or to be interrupted if unsubscribe is successful since - // the unsubscribe will interrupt it as it is calling Future.cancel(true) - // so we will ignore the stacktrace - } - } + public void call(final Inner inner) { + inner.schedule(new Action1() { + + int i = 0; + + @Override + public void call(Inner s) { + System.out.println("Run: " + i++); + if (i == 10) { + latch.countDown(); + try { + // wait for unsubscribe to finish so we are not racing it + unsubscribeLatch.await(); + } catch (InterruptedException e) { + // we expect the countDown if unsubscribe is not working + // or to be interrupted if unsubscribe is successful since + // the unsubscribe will interrupt it as it is calling Future.cancel(true) + // so we will ignore the stacktrace + } + } - counter.incrementAndGet(); - return innerScheduler.schedule(i + 1, this); + counter.incrementAndGet(); + inner.schedule(this); + } + }); } + }); latch.await(); @@ -131,31 +138,71 @@ public Subscription call(Scheduler innerScheduler, Long i) { } @Test - public void testUnsubscribeRecursiveScheduleWithStateAndFunc2AndDelay() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); + public void testUnsubscribeRecursiveScheduleFromInside() throws InterruptedException { final CountDownLatch unsubscribeLatch = new CountDownLatch(1); final AtomicInteger counter = new AtomicInteger(); - Subscription s = getScheduler().schedule(1L, new Func2() { + getScheduler().schedule(new Action1() { @Override - public Subscription call(Scheduler innerScheduler, Long i) { - if (i == 10) { - latch.countDown(); - try { - // wait for unsubscribe to finish so we are not racing it - unsubscribeLatch.await(); - } catch (InterruptedException e) { - // we expect the countDown if unsubscribe is not working - // or to be interrupted if unsubscribe is successful since - // the unsubscribe will interrupt it as it is calling Future.cancel(true) - // so we will ignore the stacktrace + public void call(Inner inner) { + inner.schedule(new Action1() { + + int i = 0; + + @Override + public void call(Inner inner) { + System.out.println("Run: " + i++); + if (i == 10) { + inner.unsubscribe(); + } + + counter.incrementAndGet(); + inner.schedule(this); } - } + }); + } + + }); + + unsubscribeLatch.countDown(); + Thread.sleep(200); // let time pass to see if the scheduler is still doing work + assertEquals(10, counter.get()); + } + + @Test + public void testUnsubscribeRecursiveScheduleWithDelay() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + final CountDownLatch unsubscribeLatch = new CountDownLatch(1); + final AtomicInteger counter = new AtomicInteger(); + Subscription s = getScheduler().schedule(new Action1() { + + @Override + public void call(final Inner innerScheduler) { + innerScheduler.schedule(new Action1() { + + long i = 1L; - counter.incrementAndGet(); - return innerScheduler.schedule(i + 1, this, 10, TimeUnit.MILLISECONDS); + @Override + public void call(Inner s) { + if (i++ == 10) { + latch.countDown(); + try { + // wait for unsubscribe to finish so we are not racing it + unsubscribeLatch.await(); + } catch (InterruptedException e) { + // we expect the countDown if unsubscribe is not working + // or to be interrupted if unsubscribe is successful since + // the unsubscribe will interrupt it as it is calling Future.cancel(true) + // so we will ignore the stacktrace + } + } + + counter.incrementAndGet(); + innerScheduler.schedule(this, 10, TimeUnit.MILLISECONDS); + } + }, 10, TimeUnit.MILLISECONDS); } - }, 10, TimeUnit.MILLISECONDS); + }); latch.await(); s.unsubscribe(); @@ -165,20 +212,22 @@ public Subscription call(Scheduler innerScheduler, Long i) { } @Test - public void recursionUsingFunc2() throws InterruptedException { + public void recursionFromOuterActionAndUnsubscribeInside() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - getScheduler().schedule(1L, new Func2() { + getScheduler().schedule(new Action1() { + + int i = 0; @Override - public Subscription call(Scheduler innerScheduler, Long i) { + public void call(Inner inner) { + i++; if (i % 100000 == 0) { System.out.println(i + " Total Memory: " + Runtime.getRuntime().totalMemory() + " Free: " + Runtime.getRuntime().freeMemory()); } if (i < 1000000L) { - return innerScheduler.schedule(i + 1, this); + inner.schedule(this); } else { latch.countDown(); - return Subscriptions.empty(); } } }); @@ -187,20 +236,20 @@ public Subscription call(Scheduler innerScheduler, Long i) { } @Test - public void recursionUsingAction0() throws InterruptedException { + public void testRecursion() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - getScheduler().schedule(new Action1() { + getScheduler().schedule(new Action1() { private long i = 0; @Override - public void call(Action0 self) { + public void call(Inner inner) { i++; if (i % 100000 == 0) { System.out.println(i + " Total Memory: " + Runtime.getRuntime().totalMemory() + " Free: " + Runtime.getRuntime().freeMemory()); } if (i < 1000000L) { - self.call(); + inner.schedule(this); } else { latch.countDown(); } @@ -211,7 +260,7 @@ public void call(Action0 self) { } @Test - public void testRecursiveScheduler2() throws InterruptedException { + public void testRecursionAndOuterUnsubscribe() throws InterruptedException { // use latches instead of Thread.sleep final CountDownLatch latch = new CountDownLatch(10); final CountDownLatch completionLatch = new CountDownLatch(1); @@ -220,26 +269,28 @@ public void testRecursiveScheduler2() throws InterruptedException { @Override public Subscription onSubscribe(final Observer observer) { - return getScheduler().schedule(null, new Func2() { + final Subscription s = getScheduler().schedule(new Action1() { @Override - public Subscription call(Scheduler scheduler, Void v) { + public void call(Inner inner) { observer.onNext(42); latch.countDown(); // this will recursively schedule this task for execution again - scheduler.schedule(null, this); - - return Subscriptions.create(new Action0() { + inner.schedule(this); + } + }); - @Override - public void call() { - observer.onCompleted(); - completionLatch.countDown(); - } + return Subscriptions.create(new Action0() { - }); + @Override + public void call() { + s.unsubscribe(); + observer.onCompleted(); + completionLatch.countDown(); } + }); + } }); diff --git a/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerTests.java b/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerTests.java index f744ecc5ab..bfea193310 100644 --- a/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerTests.java +++ b/rxjava-core/src/test/java/rx/schedulers/AbstractSchedulerTests.java @@ -16,6 +16,7 @@ package rx.schedulers; import static org.junit.Assert.*; +import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.util.Arrays; @@ -35,6 +36,7 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscriber; import rx.Subscription; import rx.subscriptions.BooleanSubscription; @@ -42,7 +44,6 @@ import rx.util.functions.Action0; import rx.util.functions.Action1; import rx.util.functions.Func1; -import rx.util.functions.Func2; /** * Base tests for all schedulers including Immediate/Current. @@ -106,7 +107,7 @@ public void call(Integer i) { } }).take(10).toBlockingObservable().last(); - if (getScheduler() instanceof CurrentThreadScheduler || getScheduler() instanceof ImmediateScheduler) { + if (getScheduler() instanceof TrampolineScheduler || getScheduler() instanceof ImmediateScheduler) { // since there is no concurrency it will block and only emit as many as it can process assertEquals(10, countEmitted.get()); } else { @@ -123,7 +124,7 @@ public void call(Integer i) { @Test public void testNestedActions() throws InterruptedException { - final Scheduler scheduler = getScheduler(); + Scheduler scheduler = getScheduler(); final CountDownLatch latch = new CountDownLatch(1); final Action0 firstStepStart = mock(Action0.class); @@ -135,28 +136,28 @@ public void testNestedActions() throws InterruptedException { final Action0 thirdStepStart = mock(Action0.class); final Action0 thirdStepEnd = mock(Action0.class); - final Action0 firstAction = new Action0() { + final Action1 firstAction = new Action1() { @Override - public void call() { + public void call(Inner inner) { firstStepStart.call(); firstStepEnd.call(); latch.countDown(); } }; - final Action0 secondAction = new Action0() { + final Action1 secondAction = new Action1() { @Override - public void call() { + public void call(Inner inner) { secondStepStart.call(); - scheduler.schedule(firstAction); + inner.schedule(firstAction); secondStepEnd.call(); } }; - final Action0 thirdAction = new Action0() { + final Action1 thirdAction = new Action1() { @Override - public void call() { + public void call(Inner inner) { thirdStepStart.call(); - scheduler.schedule(secondAction); + inner.schedule(secondAction); thirdStepEnd.call(); } }; @@ -217,8 +218,8 @@ public final void testSequenceOfActions() throws InterruptedException { final Scheduler scheduler = getScheduler(); final CountDownLatch latch = new CountDownLatch(2); - final Action0 first = mock(Action0.class); - final Action0 second = mock(Action0.class); + final Action1 first = mock(Action1.class); + final Action1 second = mock(Action1.class); // make it wait until both the first and second are called doAnswer(new Answer() { @@ -231,7 +232,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { latch.countDown(); } } - }).when(first).call(); + }).when(first).call(any(Inner.class)); doAnswer(new Answer() { @Override @@ -242,35 +243,35 @@ public Object answer(InvocationOnMock invocation) throws Throwable { latch.countDown(); } } - }).when(second).call(); + }).when(second).call(any(Inner.class)); scheduler.schedule(first); scheduler.schedule(second); latch.await(); - verify(first, times(1)).call(); - verify(second, times(1)).call(); + verify(first, times(1)).call(any(Inner.class)); + verify(second, times(1)).call(any(Inner.class)); } @Test public void testSequenceOfDelayedActions() throws InterruptedException { - final Scheduler scheduler = getScheduler(); + Scheduler scheduler = getScheduler(); final CountDownLatch latch = new CountDownLatch(1); - final Action0 first = mock(Action0.class); - final Action0 second = mock(Action0.class); + final Action1 first = mock(Action1.class); + final Action1 second = mock(Action1.class); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { - scheduler.schedule(first, 30, TimeUnit.MILLISECONDS); - scheduler.schedule(second, 10, TimeUnit.MILLISECONDS); - scheduler.schedule(new Action0() { + public void call(Inner inner) { + inner.schedule(first, 30, TimeUnit.MILLISECONDS); + inner.schedule(second, 10, TimeUnit.MILLISECONDS); + inner.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { latch.countDown(); } }, 40, TimeUnit.MILLISECONDS); @@ -280,32 +281,32 @@ public void call() { latch.await(); InOrder inOrder = inOrder(first, second); - inOrder.verify(second, times(1)).call(); - inOrder.verify(first, times(1)).call(); + inOrder.verify(second, times(1)).call(any(Inner.class)); + inOrder.verify(first, times(1)).call(any(Inner.class)); } @Test public void testMixOfDelayedAndNonDelayedActions() throws InterruptedException { - final Scheduler scheduler = getScheduler(); + Scheduler scheduler = getScheduler(); final CountDownLatch latch = new CountDownLatch(1); - final Action0 first = mock(Action0.class); - final Action0 second = mock(Action0.class); - final Action0 third = mock(Action0.class); - final Action0 fourth = mock(Action0.class); + final Action1 first = mock(Action1.class); + final Action1 second = mock(Action1.class); + final Action1 third = mock(Action1.class); + final Action1 fourth = mock(Action1.class); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { - scheduler.schedule(first); - scheduler.schedule(second, 300, TimeUnit.MILLISECONDS); - scheduler.schedule(third, 100, TimeUnit.MILLISECONDS); - scheduler.schedule(fourth); - scheduler.schedule(new Action0() { + public void call(Inner inner) { + inner.schedule(first); + inner.schedule(second, 300, TimeUnit.MILLISECONDS); + inner.schedule(third, 100, TimeUnit.MILLISECONDS); + inner.schedule(fourth); + inner.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { latch.countDown(); } }, 400, TimeUnit.MILLISECONDS); @@ -315,23 +316,23 @@ public void call() { latch.await(); InOrder inOrder = inOrder(first, second, third, fourth); - inOrder.verify(first, times(1)).call(); - inOrder.verify(fourth, times(1)).call(); - inOrder.verify(third, times(1)).call(); - inOrder.verify(second, times(1)).call(); + inOrder.verify(first, times(1)).call(any(Inner.class)); + inOrder.verify(fourth, times(1)).call(any(Inner.class)); + inOrder.verify(third, times(1)).call(any(Inner.class)); + inOrder.verify(second, times(1)).call(any(Inner.class)); } @Test - public final void testRecursiveExecutionWithAction0() throws InterruptedException { + public final void testRecursiveExecution() throws InterruptedException { final Scheduler scheduler = getScheduler(); final AtomicInteger i = new AtomicInteger(); final CountDownLatch latch = new CountDownLatch(1); - scheduler.schedule(new Action1() { + scheduler.schedule(new Action1() { @Override - public void call(Action0 self) { + public void call(Inner inner) { if (i.incrementAndGet() < 100) { - self.call(); + inner.schedule(this); } else { latch.countDown(); } @@ -343,73 +344,49 @@ public void call(Action0 self) { } @Test - public final void testRecursiveExecutionWithFunc2() throws InterruptedException { - final Scheduler scheduler = getScheduler(); + public final void testRecursiveExecutionWithDelayTime() throws InterruptedException { + Scheduler scheduler = getScheduler(); final AtomicInteger i = new AtomicInteger(); final CountDownLatch latch = new CountDownLatch(1); - scheduler.schedule(0, new Func2() { + scheduler.schedule(new Action1() { - @Override - public Subscription call(Scheduler innerScheduler, Integer state) { - i.set(state); - if (state < 100) { - return innerScheduler.schedule(state + 1, this); - } else { - latch.countDown(); - return Subscriptions.empty(); - } - } - - }); - - latch.await(); - assertEquals(100, i.get()); - } - - @Test - public final void testRecursiveExecutionWithFunc2AndDelayTime() throws InterruptedException { - final Scheduler scheduler = getScheduler(); - final AtomicInteger i = new AtomicInteger(); - final CountDownLatch latch = new CountDownLatch(1); - - scheduler.schedule(0, new Func2() { + int state = 0; @Override - public Subscription call(Scheduler innerScheduler, Integer state) { + public void call(Inner inner) { i.set(state); - if (state < 100) { - return innerScheduler.schedule(state + 1, this, 5, TimeUnit.MILLISECONDS); + if (state++ < 100) { + inner.schedule(this, 1, TimeUnit.MILLISECONDS); } else { latch.countDown(); - return Subscriptions.empty(); } } - }, 50, TimeUnit.MILLISECONDS); + }); latch.await(); assertEquals(100, i.get()); } @Test - public final void testRecursiveSchedulerSimple() { - final Scheduler scheduler = getScheduler(); - + public final void testRecursiveSchedulerInObservable() { Observable obs = Observable.create(new OnSubscribeFunc() { @Override public Subscription onSubscribe(final Observer observer) { - return scheduler.schedule(0, new Func2() { + return getScheduler().schedule(new Action1() { + int i = 0; + @Override - public Subscription call(Scheduler scheduler, Integer i) { + public void call(Inner inner) { if (i > 42) { observer.onCompleted(); - return Subscriptions.empty(); + return; } - observer.onNext(i); + observer.onNext(i++); - return scheduler.schedule(i + 1, this); + inner.schedule(this); } }); } @@ -428,43 +405,6 @@ public void call(Integer v) { assertEquals(42, lastValue.get()); } - @Test - public final void testSchedulingWithDueTime() throws InterruptedException { - final Scheduler scheduler = getScheduler(); - - final CountDownLatch latch = new CountDownLatch(5); - final AtomicInteger counter = new AtomicInteger(); - - long start = System.currentTimeMillis(); - - scheduler.schedule(null, new Func2() { - - @Override - public Subscription call(Scheduler scheduler, String state) { - System.out.println("doing work"); - counter.incrementAndGet(); - latch.countDown(); - if (latch.getCount() == 0) { - return Subscriptions.empty(); - } else { - return scheduler.schedule(state, this, new Date(scheduler.now() + 50)); - } - } - }, new Date(scheduler.now() + 100)); - - if (!latch.await(3000, TimeUnit.MILLISECONDS)) { - fail("didn't execute ... timed out"); - } - - long end = System.currentTimeMillis(); - - assertEquals(5, counter.get()); - System.out.println("Time taken: " + (end - start)); - if ((end - start) < 250) { - fail("it should have taken over 250ms since each step was scheduled 50ms in the future"); - } - } - @Test public final void testConcurrentOnNextFailsValidation() throws InterruptedException { final int count = 10; diff --git a/rxjava-core/src/test/java/rx/schedulers/ExecutorSchedulerTests.java b/rxjava-core/src/test/java/rx/schedulers/ExecutorSchedulerTests.java index 69564e2f35..e7d3fa37e6 100644 --- a/rxjava-core/src/test/java/rx/schedulers/ExecutorSchedulerTests.java +++ b/rxjava-core/src/test/java/rx/schedulers/ExecutorSchedulerTests.java @@ -24,18 +24,16 @@ import rx.Observable; import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Action0; +import rx.Scheduler.Inner; import rx.util.functions.Action1; import rx.util.functions.Func1; -import rx.util.functions.Func2; public class ExecutorSchedulerTests extends AbstractSchedulerConcurrencyTests { @Override protected Scheduler getScheduler() { // this is an implementation of ExecutorScheduler - return Schedulers.threadPoolForComputation(); + return Schedulers.computation(); } @Test @@ -43,39 +41,34 @@ public void testThreadSafetyWhenSchedulerIsHoppingBetweenThreads() { final int NUM = 1000000; final CountDownLatch latch = new CountDownLatch(1); - HashMap statefulMap = new HashMap(); - Schedulers.threadPoolForComputation().schedule(statefulMap, - new Func2, Subscription>() { - - @Override - public Subscription call(Scheduler innerScheduler, final HashMap statefulMap) { - return innerScheduler.schedule(new Action1() { - - int nonThreadSafeCounter = 0; - - @Override - public void call(Action0 self) { - Integer i = statefulMap.get("a"); - if (i == null) { - i = 1; - statefulMap.put("a", i); - statefulMap.put("b", i); - } else { - i++; - statefulMap.put("a", i); - statefulMap.put("b", i); - } - nonThreadSafeCounter++; - statefulMap.put("nonThreadSafeCounter", nonThreadSafeCounter); - if (i < NUM) { - self.call(); - } else { - latch.countDown(); - } - } - }); - } - }); + final HashMap map = new HashMap(); + + Schedulers.computation().schedule(new Action1() { + + private HashMap statefulMap = map; + int nonThreadSafeCounter = 0; + + @Override + public void call(Inner inner) { + Integer i = statefulMap.get("a"); + if (i == null) { + i = 1; + statefulMap.put("a", i); + statefulMap.put("b", i); + } else { + i++; + statefulMap.put("a", i); + statefulMap.put("b", i); + } + nonThreadSafeCounter++; + statefulMap.put("nonThreadSafeCounter", nonThreadSafeCounter); + if (i < NUM) { + inner.schedule(this); + } else { + latch.countDown(); + } + } + }); try { latch.await(); @@ -83,13 +76,13 @@ public void call(Action0 self) { e.printStackTrace(); } - System.out.println("Count A: " + statefulMap.get("a")); - System.out.println("Count B: " + statefulMap.get("b")); - System.out.println("nonThreadSafeCounter: " + statefulMap.get("nonThreadSafeCounter")); + System.out.println("Count A: " + map.get("a")); + System.out.println("Count B: " + map.get("b")); + System.out.println("nonThreadSafeCounter: " + map.get("nonThreadSafeCounter")); - assertEquals(NUM, statefulMap.get("a").intValue()); - assertEquals(NUM, statefulMap.get("b").intValue()); - assertEquals(NUM, statefulMap.get("nonThreadSafeCounter").intValue()); + assertEquals(NUM, map.get("a").intValue()); + assertEquals(NUM, map.get("b").intValue()); + assertEquals(NUM, map.get("nonThreadSafeCounter").intValue()); } @Test @@ -107,7 +100,7 @@ public String call(Integer t) { } }); - o.subscribeOn(Schedulers.threadPoolForComputation()).toBlockingObservable().forEach(new Action1() { + o.subscribeOn(Schedulers.computation()).toBlockingObservable().forEach(new Action1() { @Override public void call(String t) { @@ -146,7 +139,7 @@ public final void testMergeWithExecutorScheduler() { Observable o1 = Observable. from(1, 2, 3, 4, 5); Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.threadPoolForComputation()).map(new Func1() { + Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.computation()).map(new Func1() { @Override public String call(Integer t) { diff --git a/rxjava-core/src/test/java/rx/schedulers/TestSchedulerTest.java b/rxjava-core/src/test/java/rx/schedulers/TestSchedulerTest.java index 505b8134db..af64b87468 100644 --- a/rxjava-core/src/test/java/rx/schedulers/TestSchedulerTest.java +++ b/rxjava-core/src/test/java/rx/schedulers/TestSchedulerTest.java @@ -26,8 +26,8 @@ import org.mockito.InOrder; import org.mockito.Mockito; +import rx.Scheduler.Inner; import rx.Subscription; -import rx.util.functions.Action0; import rx.util.functions.Action1; import rx.util.functions.Func1; @@ -40,9 +40,9 @@ public final void testPeriodicScheduling() { final Func1 calledOp = mock(Func1.class); final TestScheduler scheduler = new TestScheduler(); - Subscription subscription = scheduler.schedulePeriodically(new Action0() { + Subscription subscription = scheduler.schedulePeriodically(new Action1() { @Override - public void call() { + public void call(Inner inner) { System.out.println(scheduler.now()); calledOp.call(scheduler.now()); } @@ -74,18 +74,18 @@ public void call() { } @Test - public final void testRecursion() { + public final void testImmediateUnsubscribes() { TestScheduler s = new TestScheduler(); final AtomicInteger counter = new AtomicInteger(0); - Subscription subscription = s.schedule(new Action1() { + Subscription subscription = s.schedule(new Action1() { @Override - public void call(Action0 self) { + public void call(Inner inner) { counter.incrementAndGet(); System.out.println("counter: " + counter.get()); - self.call(); + inner.schedule(this); } }); diff --git a/rxjava-core/src/test/java/rx/schedulers/CurrentThreadSchedulerTest.java b/rxjava-core/src/test/java/rx/schedulers/TrampolineSchedulerTest.java similarity index 93% rename from rxjava-core/src/test/java/rx/schedulers/CurrentThreadSchedulerTest.java rename to rxjava-core/src/test/java/rx/schedulers/TrampolineSchedulerTest.java index b69526760e..ae927567a5 100644 --- a/rxjava-core/src/test/java/rx/schedulers/CurrentThreadSchedulerTest.java +++ b/rxjava-core/src/test/java/rx/schedulers/TrampolineSchedulerTest.java @@ -24,11 +24,11 @@ import rx.util.functions.Action1; import rx.util.functions.Func1; -public class CurrentThreadSchedulerTest extends AbstractSchedulerTests { +public class TrampolineSchedulerTest extends AbstractSchedulerTests { @Override protected Scheduler getScheduler() { - return CurrentThreadScheduler.getInstance(); + return TrampolineScheduler.getInstance(); } @Test diff --git a/rxjava-core/src/test/java/rx/test/OperatorTester.java b/rxjava-core/src/test/java/rx/test/OperatorTester.java index e80c48b1a7..418b67965d 100644 --- a/rxjava-core/src/test/java/rx/test/OperatorTester.java +++ b/rxjava-core/src/test/java/rx/test/OperatorTester.java @@ -19,8 +19,7 @@ import rx.Scheduler; import rx.Subscription; -import rx.util.functions.Action0; -import rx.util.functions.Func2; +import rx.util.functions.Action1; /** * Common utility functions for testing operator implementations. @@ -57,38 +56,14 @@ public ForwardingScheduler(Scheduler underlying) { } @Override - public Subscription schedule(Action0 action) { + public Subscription schedule(Action1 action) { return underlying.schedule(action); } @Override - public Subscription schedule(T state, Func2 action) { - return underlying.schedule(state, action); + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + return underlying.schedule(action, delayTime, unit); } - @Override - public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) { - return underlying.schedule(action, dueTime, unit); - } - - @Override - public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { - return underlying.schedule(state, action, dueTime, unit); - } - - @Override - public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(action, initialDelay, period, unit); - } - - @Override - public Subscription schedulePeriodically(T state, Func2 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(state, action, initialDelay, period, unit); - } - - @Override - public long now() { - return underlying.now(); - } } } \ No newline at end of file From 4bdf08d2fbf7212af53f114ea39e63dcc0e118a7 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 3 Feb 2014 17:12:51 -0800 Subject: [PATCH 2/2] Update Modules to New Scheduler --- .../schedulers/HandlerThreadScheduler.java | 93 ++++-- .../HandlerThreadSchedulerTest.java | 16 +- .../src/main/java/rx/util/async/Async.java | 45 +-- .../rx/util/async/operators/Functionals.java | 9 +- .../java/rx/concurrency/SwingScheduler.java | 58 ---- .../java/rx/schedulers/SwingScheduler.java | 277 ++++++------------ .../rx/schedulers/SwingSchedulerTest.java | 156 ++++++++++ .../java/rx/ObservableCreatePerformance.java | 2 +- .../perf/java/rx/perf/IntegerSumObserver.java | 4 +- .../perf/java/rx/perf/LongSumObserver.java | 4 +- .../java/rx/performance/PerformanceTest.java | 6 +- .../schedulers/SchedulerPerformanceTests.java | 4 +- .../schedulers/TestRecursionMemoryUsage.java | 21 +- .../rx/subjects/SubjectPerformanceTests.java | 6 +- 14 files changed, 371 insertions(+), 330 deletions(-) delete mode 100644 rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java create mode 100644 rxjava-contrib/rxjava-swing/src/test/java/rx/schedulers/SwingSchedulerTest.java diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java index a1b6b39055..6f7cab027b 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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 - * + * + * 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. @@ -19,8 +19,8 @@ import rx.Scheduler; import rx.Subscription; -import rx.operators.SafeObservableSubscription; -import rx.util.functions.Func2; +import rx.subscriptions.BooleanSubscription; +import rx.util.functions.Action1; import android.os.Handler; /** @@ -32,25 +32,29 @@ public class HandlerThreadScheduler extends Scheduler { /** * Constructs a {@link HandlerThreadScheduler} using the given {@link Handler} - * @param handler {@link Handler} to use when scheduling actions + * + * @param handler + * {@link Handler} to use when scheduling actions */ public HandlerThreadScheduler(Handler handler) { this.handler = handler; } /** - * Calls {@link HandlerThreadScheduler#schedule(Object, rx.util.functions.Func2, long, java.util.concurrent.TimeUnit)} - * with a delay of zero milliseconds. - * + * Calls {@link HandlerThreadScheduler#schedule(Object, rx.util.functions.Func2, long, java.util.concurrent.TimeUnit)} with a delay of zero milliseconds. + * * See {@link #schedule(Object, rx.util.functions.Func2, long, java.util.concurrent.TimeUnit)} */ @Override - public Subscription schedule(final T state, final Func2 action) { - return schedule(state, action, 0L, TimeUnit.MILLISECONDS); + public Subscription schedule(Action1 action) { + InnerHandlerThreadScheduler inner = new InnerHandlerThreadScheduler(handler); + inner.schedule(action); + return inner; } /** * Calls {@link Handler#postDelayed(Runnable, long)} with a runnable that executes the given action. + * * @param state * State to pass into the action. * @param action @@ -62,17 +66,60 @@ public Subscription schedule(final T state, final Func2 Subscription schedule(final T state, final Func2 action, long delayTime, TimeUnit unit) { - final SafeObservableSubscription subscription = new SafeObservableSubscription(); - final Scheduler _scheduler = this; - handler.postDelayed(new Runnable() { - @Override - public void run() { - subscription.wrap(action.call(_scheduler, state)); - } - }, unit.toMillis(delayTime)); - return subscription; + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + InnerHandlerThreadScheduler inner = new InnerHandlerThreadScheduler(handler); + inner.schedule(action, delayTime, unit); + return inner; } -} + private static class InnerHandlerThreadScheduler extends Inner { + + private final Handler handler; + private BooleanSubscription innerSubscription = new BooleanSubscription(); + private Inner _inner = this; + + public InnerHandlerThreadScheduler(Handler handler) { + this.handler = handler; + } + + @Override + public void unsubscribe() { + innerSubscription.unsubscribe(); + } + + @Override + public boolean isUnsubscribed() { + return innerSubscription.isUnsubscribed(); + } + + @Override + public void schedule(final Action1 action, long delayTime, TimeUnit unit) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (_inner.isUnsubscribed()) { + return; + } + action.call(_inner); + } + }, unit.toMillis(delayTime)); + } + @Override + public void schedule(final Action1 action) { + handler.postDelayed(new Runnable() { + + @Override + public void run() { + if (_inner.isUnsubscribed()) { + return; + } + action.call(_inner); + } + + }, 0L); + } + + } + +} diff --git a/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java b/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java index 131a1cced4..af1ac21222 100644 --- a/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java +++ b/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java @@ -27,7 +27,9 @@ import org.robolectric.annotation.Config; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; +import rx.util.functions.Action1; import rx.util.functions.Func2; import android.os.Handler; @@ -38,12 +40,11 @@ public class HandlerThreadSchedulerTest { @Test public void shouldScheduleImmediateActionOnHandlerThread() { final Handler handler = mock(Handler.class); - final Object state = new Object(); @SuppressWarnings("unchecked") - final Func2 action = mock(Func2.class); + final Action1 action = mock(Action1.class); Scheduler scheduler = new HandlerThreadScheduler(handler); - scheduler.schedule(state, action); + scheduler.schedule(action); // verify that we post to the given Handler ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); @@ -51,18 +52,17 @@ public void shouldScheduleImmediateActionOnHandlerThread() { // verify that the given handler delegates to our action runnable.getValue().run(); - verify(action).call(scheduler, state); + verify(action).call(any(Inner.class)); } @Test public void shouldScheduleDelayedActionOnHandlerThread() { final Handler handler = mock(Handler.class); - final Object state = new Object(); @SuppressWarnings("unchecked") - final Func2 action = mock(Func2.class); + final Action1 action = mock(Action1.class); Scheduler scheduler = new HandlerThreadScheduler(handler); - scheduler.schedule(state, action, 1L, TimeUnit.SECONDS); + scheduler.schedule(action, 1L, TimeUnit.SECONDS); // verify that we post to the given Handler ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); @@ -70,6 +70,6 @@ public void shouldScheduleDelayedActionOnHandlerThread() { // verify that the given handler delegates to our action runnable.getValue().run(); - verify(action).call(scheduler, state); + verify(action).call(any(Inner.class)); } } diff --git a/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/Async.java b/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/Async.java index 4ac9874e96..e091e78a9c 100644 --- a/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/Async.java +++ b/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/Async.java @@ -21,6 +21,7 @@ import rx.Observable; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.schedulers.Schedulers; import rx.subjects.AsyncSubject; import rx.util.async.operators.Functionals; @@ -592,9 +593,9 @@ public static Func0> toAsync(final Func0 func, fi @Override public Observable call() { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(); @@ -649,9 +650,9 @@ public static Func1> toAsync(final Func1 call(final T1 t1) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1); @@ -708,9 +709,9 @@ public static Func2> toAsync(final Func2 call(final T1 t1, final T2 t2) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2); @@ -769,9 +770,9 @@ public static Func3> toAsync(final Fun @Override public Observable call(final T1 t1, final T2 t2, final T3 t3) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3); @@ -832,9 +833,9 @@ public static Func4> toAsync(f @Override public Observable call(final T1 t1, final T2 t2, final T3 t3, final T4 t4) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3, t4); @@ -897,9 +898,9 @@ public static Func5> t @Override public Observable call(final T1 t1, final T2 t2, final T3 t3, final T4 t4, final T5 t5) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3, t4, t5); @@ -964,9 +965,9 @@ public static Func6 call(final T1 t1, final T2 t2, final T3 t3, final T4 t4, final T5 t5, final T6 t6) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3, t4, t5, t6); @@ -1033,9 +1034,9 @@ public static Func7 call(final T1 t1, final T2 t2, final T3 t3, final T4 t4, final T5 t5, final T6 t6, final T7 t7) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3, t4, t5, t6, t7); @@ -1104,9 +1105,9 @@ public static Func8 call(final T1 t1, final T2 t2, final T3 t3, final T4 t4, final T5 t5, final T6 t6, final T7 t7, final T8 t8) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3, t4, t5, t6, t7, t8); @@ -1177,9 +1178,9 @@ public static Func9 call(final T1 t1, final T2 t2, final T3 t3, final T4 t4, final T5 t5, final T6 t6, final T7 t7, final T8 t8, final T9 t9) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(t1, t2, t3, t4, t5, t6, t7, t8, t9); @@ -1230,9 +1231,9 @@ public static FuncN> toAsync(final FuncN func, fi @Override public Observable call(final Object... args) { final AsyncSubject subject = AsyncSubject.create(); - scheduler.schedule(new Action0() { + scheduler.schedule(new Action1() { @Override - public void call() { + public void call(Inner inner) { R result; try { result = func.call(args); diff --git a/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/operators/Functionals.java b/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/operators/Functionals.java index 1117fef941..fa06825c03 100644 --- a/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/operators/Functionals.java +++ b/rxjava-contrib/rxjava-async-util/src/main/java/rx/util/async/operators/Functionals.java @@ -16,6 +16,7 @@ package rx.util.async.operators; +import rx.Scheduler.Inner; import rx.util.functions.Action0; import rx.util.functions.Action1; @@ -65,14 +66,14 @@ public void call() { * @param run the Runnable to run when the Action0 is called * @return the Action0 wrapping the Runnable */ - public static Action0 fromRunnable(Runnable run) { + public static Action1 fromRunnable(Runnable run) { if (run == null) { throw new NullPointerException("run"); } return new ActionWrappingRunnable(run); } - /** An Action0 which wraps and calls a Runnable. */ - private static final class ActionWrappingRunnable implements Action0 { + /** An Action1 which wraps and calls a Runnable. */ + private static final class ActionWrappingRunnable implements Action1 { final Runnable run; public ActionWrappingRunnable(Runnable run) { @@ -80,7 +81,7 @@ public ActionWrappingRunnable(Runnable run) { } @Override - public void call() { + public void call(Inner inner) { run.run(); } diff --git a/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java b/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java deleted file mode 100644 index c6be1c55ec..0000000000 --- a/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2013 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.concurrency; - -import java.util.concurrent.TimeUnit; - -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Func2; - -/** - * Deprecated. Package changed from rx.concurrency to rx.schedulers. - * - * @deprecated Use {@link rx.schedulers.SwingScheduler} instead. This will be removed before 1.0 release. - */ -@Deprecated -public class SwingScheduler extends Scheduler { - - private final static SwingScheduler INSTANCE = new SwingScheduler(); - - public static SwingScheduler getInstance() { - return INSTANCE; - } - - private final rx.schedulers.SwingScheduler actual; - - private SwingScheduler() { - actual = rx.schedulers.SwingScheduler.getInstance(); - } - - @Override - public Subscription schedule(T state, Func2 action) { - return actual.schedule(state, action); - } - - @Override - public Subscription schedule(T state, Func2 action, long delayTime, TimeUnit unit) { - return actual.schedule(state, action, delayTime, unit); - } - - public Subscription schedulePeriodically(T state, final Func2 action, long initialDelay, long period, TimeUnit unit) { - return actual.schedulePeriodically(state, action, initialDelay, period, unit); - } - -} diff --git a/rxjava-contrib/rxjava-swing/src/main/java/rx/schedulers/SwingScheduler.java b/rxjava-contrib/rxjava-swing/src/main/java/rx/schedulers/SwingScheduler.java index 7ce2c4e2d1..abdb34be65 100644 --- a/rxjava-contrib/rxjava-swing/src/main/java/rx/schedulers/SwingScheduler.java +++ b/rxjava-contrib/rxjava-swing/src/main/java/rx/schedulers/SwingScheduler.java @@ -15,30 +15,20 @@ */ package rx.schedulers; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import javax.swing.SwingUtilities; import javax.swing.Timer; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.InOrder; - import rx.Scheduler; import rx.Subscription; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import rx.util.functions.Func2; +import rx.util.functions.Action1; /** * Executes work on the Swing UI thread. @@ -51,224 +41,127 @@ public static SwingScheduler getInstance() { return INSTANCE; } - private SwingScheduler() { + /* package for unit test */SwingScheduler() { } @Override - public Subscription schedule(final T state, final Func2 action) { - final AtomicReference sub = new AtomicReference(); - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - sub.set(action.call(SwingScheduler.this, state)); - } - }); - return Subscriptions.create(new Action0() { - @Override - public void call() { - Subscription subscription = sub.get(); - if (subscription != null) { - subscription.unsubscribe(); - } - } - }); + public Subscription schedule(Action1 action) { + InnerSwingScheduler inner = new InnerSwingScheduler(); + inner.schedule(action); + return inner; } @Override - public Subscription schedule(final T state, final Func2 action, long dueTime, TimeUnit unit) { - final AtomicReference sub = new AtomicReference(); - long delay = unit.toMillis(dueTime); + public Subscription schedule(Action1 action, long delayTime, TimeUnit unit) { + long delay = unit.toMillis(delayTime); assertThatTheDelayIsValidForTheSwingTimer(delay); + InnerSwingScheduler inner = new InnerSwingScheduler(); + inner.schedule(action, delayTime, unit); + return inner; + } - class ExecuteOnceAction implements ActionListener { - private Timer timer; + private static class InnerSwingScheduler extends Inner { - private void setTimer(Timer timer) { - this.timer = timer; - } + private final Inner _inner = this; + private final CompositeSubscription innerSubscription = new CompositeSubscription(); - @Override - public void actionPerformed(ActionEvent e) { - timer.stop(); - sub.set(action.call(SwingScheduler.this, state)); - } + @Override + public void unsubscribe() { + innerSubscription.unsubscribe(); } - ExecuteOnceAction executeOnce = new ExecuteOnceAction(); - final Timer timer = new Timer((int) delay, executeOnce); - executeOnce.setTimer(timer); - timer.start(); - - return Subscriptions.create(new Action0() { - @Override - public void call() { - timer.stop(); + @Override + public boolean isUnsubscribed() { + return innerSubscription.isUnsubscribed(); + } - Subscription subscription = sub.get(); - if (subscription != null) { - subscription.unsubscribe(); - } - } - }); - } + @Override + public void schedule(final Action1 action, long delayTime, TimeUnit unit) { + final AtomicReference sub = new AtomicReference(); + long delay = unit.toMillis(delayTime); + assertThatTheDelayIsValidForTheSwingTimer(delay); - @Override - public Subscription schedulePeriodically(T state, final Func2 action, long initialDelay, long period, TimeUnit unit) { - final AtomicReference timer = new AtomicReference(); + final AtomicReference sf = new AtomicReference(); + class ExecuteOnceAction implements ActionListener { + private Timer timer; - final long delay = unit.toMillis(period); - assertThatTheDelayIsValidForTheSwingTimer(delay); + private void setTimer(Timer timer) { + this.timer = timer; + } - final CompositeSubscription subscriptions = new CompositeSubscription(); - final Func2 initialAction = new Func2() { - @Override - public Subscription call(final Scheduler scheduler, final T state0) { - // start timer for periodic execution, collect subscriptions - timer.set(new Timer((int) delay, new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - subscriptions.add(action.call(scheduler, state0)); + @Override + public void actionPerformed(ActionEvent e) { + timer.stop(); + if (innerSubscription.isUnsubscribed()) { + return; + } + action.call(_inner); + Subscription s = sf.get(); + if (s != null) { + innerSubscription.remove(s); } - })); - timer.get().start(); - - return action.call(scheduler, state0); - } - }; - subscriptions.add(schedule(state, initialAction, initialDelay, unit)); - - subscriptions.add(Subscriptions.create(new Action0() { - @Override - public void call() { - // in addition to all the individual unsubscriptions, stop the timer on unsubscribing - Timer maybeTimer = timer.get(); - if (maybeTimer != null) { - maybeTimer.stop(); } } - })); - return subscriptions; - } + ExecuteOnceAction executeOnce = new ExecuteOnceAction(); + final Timer timer = new Timer((int) delay, executeOnce); + executeOnce.setTimer(timer); + timer.start(); - private static void assertThatTheDelayIsValidForTheSwingTimer(long delay) { - if (delay < 0 || delay > Integer.MAX_VALUE) { - throw new IllegalArgumentException(String.format("The swing timer only accepts non-negative delays up to %d milliseconds.", Integer.MAX_VALUE)); - } - } - - public static class UnitTest { - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Test - public void testInvalidDelayValues() { - final SwingScheduler scheduler = new SwingScheduler(); - final Action0 action = mock(Action0.class); - - exception.expect(IllegalArgumentException.class); - scheduler.schedulePeriodically(action, -1L, 100L, TimeUnit.SECONDS); - - exception.expect(IllegalArgumentException.class); - scheduler.schedulePeriodically(action, 100L, -1L, TimeUnit.SECONDS); - - exception.expect(IllegalArgumentException.class); - scheduler.schedulePeriodically(action, 1L + Integer.MAX_VALUE, 100L, TimeUnit.MILLISECONDS); - - exception.expect(IllegalArgumentException.class); - scheduler.schedulePeriodically(action, 100L, 1L + Integer.MAX_VALUE / 1000, TimeUnit.SECONDS); - } - - @Test - public void testPeriodicScheduling() throws Exception { - final SwingScheduler scheduler = new SwingScheduler(); - - final CountDownLatch latch = new CountDownLatch(4); - - final Action0 innerAction = mock(Action0.class); - final Action0 action = new Action0() { + Subscription s = Subscriptions.create(new Action0() { @Override public void call() { - try { - innerAction.call(); - assertTrue(SwingUtilities.isEventDispatchThread()); - } finally { - latch.countDown(); + timer.stop(); + + Subscription subscription = sub.get(); + if (subscription != null) { + subscription.unsubscribe(); } } - }; - - Subscription sub = scheduler.schedulePeriodically(action, 50, 200, TimeUnit.MILLISECONDS); - - if (!latch.await(5000, TimeUnit.MILLISECONDS)) { - fail("timed out waiting for tasks to execute"); - } + }); - sub.unsubscribe(); - waitForEmptyEventQueue(); - verify(innerAction, times(4)).call(); + sf.set(s); + innerSubscription.add(s); } - @Test - public void testNestedActions() throws Exception { - final SwingScheduler scheduler = new SwingScheduler(); - - final Action0 firstStepStart = mock(Action0.class); - final Action0 firstStepEnd = mock(Action0.class); + @Override + public void schedule(final Action1 action) { + final AtomicReference sub = new AtomicReference(); - final Action0 secondStepStart = mock(Action0.class); - final Action0 secondStepEnd = mock(Action0.class); - - final Action0 thirdStepStart = mock(Action0.class); - final Action0 thirdStepEnd = mock(Action0.class); - - final Action0 firstAction = new Action0() { - @Override - public void call() { - assertTrue(SwingUtilities.isEventDispatchThread()); - firstStepStart.call(); - firstStepEnd.call(); - } - }; - final Action0 secondAction = new Action0() { + final AtomicReference sf = new AtomicReference(); + EventQueue.invokeLater(new Runnable() { @Override - public void call() { - assertTrue(SwingUtilities.isEventDispatchThread()); - secondStepStart.call(); - scheduler.schedule(firstAction); - secondStepEnd.call(); + public void run() { + if (innerSubscription.isUnsubscribed()) { + return; + } + action.call(_inner); + Subscription s = sf.get(); + if (s != null) { + innerSubscription.remove(s); + } } - }; - final Action0 thirdAction = new Action0() { + }); + + Subscription s = Subscriptions.create(new Action0() { @Override public void call() { - assertTrue(SwingUtilities.isEventDispatchThread()); - thirdStepStart.call(); - scheduler.schedule(secondAction); - thirdStepEnd.call(); + Subscription subscription = sub.get(); + if (subscription != null) { + subscription.unsubscribe(); + } } - }; - - InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd); - - scheduler.schedule(thirdAction); - waitForEmptyEventQueue(); + }); - inOrder.verify(thirdStepStart, times(1)).call(); - inOrder.verify(thirdStepEnd, times(1)).call(); - inOrder.verify(secondStepStart, times(1)).call(); - inOrder.verify(secondStepEnd, times(1)).call(); - inOrder.verify(firstStepStart, times(1)).call(); - inOrder.verify(firstStepEnd, times(1)).call(); + sf.set(s); + innerSubscription.add(s); } - private static void waitForEmptyEventQueue() throws Exception { - EventQueue.invokeAndWait(new Runnable() { - @Override - public void run() { - // nothing to do, we're just waiting here for the event queue to be emptied - } - }); + } + + private static void assertThatTheDelayIsValidForTheSwingTimer(long delay) { + if (delay < 0 || delay > Integer.MAX_VALUE) { + throw new IllegalArgumentException(String.format("The swing timer only accepts non-negative delays up to %d milliseconds.", Integer.MAX_VALUE)); } } } diff --git a/rxjava-contrib/rxjava-swing/src/test/java/rx/schedulers/SwingSchedulerTest.java b/rxjava-contrib/rxjava-swing/src/test/java/rx/schedulers/SwingSchedulerTest.java new file mode 100644 index 0000000000..70008fab34 --- /dev/null +++ b/rxjava-contrib/rxjava-swing/src/test/java/rx/schedulers/SwingSchedulerTest.java @@ -0,0 +1,156 @@ +/** + * Copyright 2013 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.schedulers; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.awt.EventQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.swing.SwingUtilities; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.InOrder; + +import rx.Scheduler.Inner; +import rx.Subscription; +import rx.util.functions.Action1; + +/** + * Executes work on the Swing UI thread. + * This scheduler should only be used with actions that execute quickly. + */ +public final class SwingSchedulerTest { + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void testInvalidDelayValues() { + final SwingScheduler scheduler = new SwingScheduler(); + final Action1 action = mock(Action1.class); + + exception.expect(IllegalArgumentException.class); + scheduler.schedulePeriodically(action, -1L, 100L, TimeUnit.SECONDS); + + exception.expect(IllegalArgumentException.class); + scheduler.schedulePeriodically(action, 100L, -1L, TimeUnit.SECONDS); + + exception.expect(IllegalArgumentException.class); + scheduler.schedulePeriodically(action, 1L + Integer.MAX_VALUE, 100L, TimeUnit.MILLISECONDS); + + exception.expect(IllegalArgumentException.class); + scheduler.schedulePeriodically(action, 100L, 1L + Integer.MAX_VALUE / 1000, TimeUnit.SECONDS); + } + + @Test + public void testPeriodicScheduling() throws Exception { + final SwingScheduler scheduler = new SwingScheduler(); + + final CountDownLatch latch = new CountDownLatch(4); + + final Action1 innerAction = mock(Action1.class); + final Action1 action = new Action1() { + @Override + public void call(Inner inner) { + try { + innerAction.call(inner); + assertTrue(SwingUtilities.isEventDispatchThread()); + } finally { + latch.countDown(); + } + } + }; + + Subscription sub = scheduler.schedulePeriodically(action, 50, 200, TimeUnit.MILLISECONDS); + + if (!latch.await(5000, TimeUnit.MILLISECONDS)) { + fail("timed out waiting for tasks to execute"); + } + + sub.unsubscribe(); + waitForEmptyEventQueue(); + verify(innerAction, times(4)).call(any(Inner.class)); + } + + @Test + public void testNestedActions() throws Exception { + final SwingScheduler scheduler = new SwingScheduler(); + + final Action1 firstStepStart = mock(Action1.class); + final Action1 firstStepEnd = mock(Action1.class); + + final Action1 secondStepStart = mock(Action1.class); + final Action1 secondStepEnd = mock(Action1.class); + + final Action1 thirdStepStart = mock(Action1.class); + final Action1 thirdStepEnd = mock(Action1.class); + + final Action1 firstAction = new Action1() { + @Override + public void call(Inner inner) { + assertTrue(SwingUtilities.isEventDispatchThread()); + firstStepStart.call(inner); + firstStepEnd.call(inner); + } + }; + final Action1 secondAction = new Action1() { + @Override + public void call(Inner inner) { + assertTrue(SwingUtilities.isEventDispatchThread()); + secondStepStart.call(inner); + scheduler.schedule(firstAction); + secondStepEnd.call(inner); + } + }; + final Action1 thirdAction = new Action1() { + @Override + public void call(Inner inner) { + assertTrue(SwingUtilities.isEventDispatchThread()); + thirdStepStart.call(inner); + scheduler.schedule(secondAction); + thirdStepEnd.call(inner); + } + }; + + InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd); + + scheduler.schedule(thirdAction); + waitForEmptyEventQueue(); + + inOrder.verify(thirdStepStart, times(1)).call(any(Inner.class)); + inOrder.verify(thirdStepEnd, times(1)).call(any(Inner.class)); + inOrder.verify(secondStepStart, times(1)).call(any(Inner.class)); + inOrder.verify(secondStepEnd, times(1)).call(any(Inner.class)); + inOrder.verify(firstStepStart, times(1)).call(any(Inner.class)); + inOrder.verify(firstStepEnd, times(1)).call(any(Inner.class)); + } + + private static void waitForEmptyEventQueue() throws Exception { + EventQueue.invokeAndWait(new Runnable() { + @Override + public void run() { + // nothing to do, we're just waiting here for the event queue to be emptied + } + }); + } + +} diff --git a/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java b/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java index 64e7727f94..f55dd998bd 100644 --- a/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java +++ b/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java @@ -60,7 +60,7 @@ public long timeCreateAndSubscribe() { Observable s = Observable.create(new OnSubscribe() { @Override - public void call(Observer o) { + public void call(Subscriber o) { o.onNext(1L); o.onCompleted(); } diff --git a/rxjava-core/src/perf/java/rx/perf/IntegerSumObserver.java b/rxjava-core/src/perf/java/rx/perf/IntegerSumObserver.java index 2ef1a13305..31d10f0cab 100644 --- a/rxjava-core/src/perf/java/rx/perf/IntegerSumObserver.java +++ b/rxjava-core/src/perf/java/rx/perf/IntegerSumObserver.java @@ -1,8 +1,8 @@ package rx.perf; -import rx.Observer; +import rx.Subscriber; -public class IntegerSumObserver extends Observer { +public class IntegerSumObserver extends Subscriber { public int sum = 0; diff --git a/rxjava-core/src/perf/java/rx/perf/LongSumObserver.java b/rxjava-core/src/perf/java/rx/perf/LongSumObserver.java index ae4a939edc..5465b326f0 100644 --- a/rxjava-core/src/perf/java/rx/perf/LongSumObserver.java +++ b/rxjava-core/src/perf/java/rx/perf/LongSumObserver.java @@ -1,8 +1,8 @@ package rx.perf; -import rx.Observer; +import rx.Subscriber; -public class LongSumObserver extends Observer { +public class LongSumObserver extends Subscriber { public long sum = 0; diff --git a/rxjava-core/src/perf/java/rx/performance/PerformanceTest.java b/rxjava-core/src/perf/java/rx/performance/PerformanceTest.java index e6cd6ba058..6f0cbaef38 100644 --- a/rxjava-core/src/perf/java/rx/performance/PerformanceTest.java +++ b/rxjava-core/src/perf/java/rx/performance/PerformanceTest.java @@ -19,7 +19,7 @@ import java.util.concurrent.atomic.AtomicLong; import rx.Observable; -import rx.Observer; +import rx.Subscriber; import rx.util.functions.Func1; public class PerformanceTest { @@ -226,7 +226,7 @@ public Integer call(Integer t1) { } - private static final class TestObserver extends Observer { + private static final class TestObserver extends Subscriber { private final AtomicInteger onNextSum; private final AtomicLong aggregateTime; private final long start; @@ -256,7 +256,7 @@ public void onCompleted() { } } - private static final class TestStringObserver extends Observer { + private static final class TestStringObserver extends Subscriber { private final AtomicInteger onNextSum; private final long start; diff --git a/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java b/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java index 231199faee..c412130e07 100644 --- a/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java +++ b/rxjava-core/src/perf/java/rx/schedulers/SchedulerPerformanceTests.java @@ -18,8 +18,8 @@ import java.util.Arrays; import rx.Observable; -import rx.Observer; import rx.Scheduler; +import rx.Subscriber; import rx.util.functions.Action0; public class SchedulerPerformanceTests { @@ -152,7 +152,7 @@ public long arrayResponse(Scheduler scheduler) { return o.sum; } - private static class LongObserver extends Observer { + private static class LongObserver extends Subscriber { long sum = 0; diff --git a/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java b/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java index cfa723006d..9b252ba179 100644 --- a/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java +++ b/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java @@ -19,11 +19,9 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; +import rx.Scheduler.Inner; import rx.Subscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; import rx.util.functions.Action1; -import rx.util.functions.Func2; /** * Used for manual testing of memory leaks with recursive schedulers. @@ -40,6 +38,8 @@ public static void main(String args[]) { usingFunc2(Schedulers.computation()); usingAction0(Schedulers.computation()); + + System.exit(0); } protected static void usingFunc2(final Scheduler scheduler) { @@ -48,10 +48,11 @@ protected static void usingFunc2(final Scheduler scheduler) { @Override public Subscription onSubscribe(final Observer o) { - return scheduler.schedule(0L, new Func2() { + return scheduler.schedule(new Action1() { + long i = 0; @Override - public Subscription call(Scheduler innerScheduler, Long i) { + public void call(Inner inner) { i++; if (i % 500000 == 0) { System.out.println(i + " Total Memory: " + Runtime.getRuntime().totalMemory() + " Free: " + Runtime.getRuntime().freeMemory()); @@ -59,10 +60,10 @@ public Subscription call(Scheduler innerScheduler, Long i) { } if (i == 100000000L) { o.onCompleted(); - return Subscriptions.empty(); + return; } - return innerScheduler.schedule(i, this); + inner.schedule(this); } }); } @@ -75,12 +76,12 @@ protected static void usingAction0(final Scheduler scheduler) { @Override public Subscription onSubscribe(final Observer o) { - return scheduler.schedule(new Action1() { + return scheduler.schedule(new Action1() { private long i = 0; @Override - public void call(Action0 self) { + public void call(Inner inner) { i++; if (i % 500000 == 0) { System.out.println(i + " Total Memory: " + Runtime.getRuntime().totalMemory() + " Free: " + Runtime.getRuntime().freeMemory()); @@ -90,7 +91,7 @@ public void call(Action0 self) { o.onCompleted(); return; } - self.call(); + inner.schedule(this); } }); } diff --git a/rxjava-core/src/perf/java/rx/subjects/SubjectPerformanceTests.java b/rxjava-core/src/perf/java/rx/subjects/SubjectPerformanceTests.java index 59a86f712a..f6836f975f 100644 --- a/rxjava-core/src/perf/java/rx/subjects/SubjectPerformanceTests.java +++ b/rxjava-core/src/perf/java/rx/subjects/SubjectPerformanceTests.java @@ -15,7 +15,7 @@ */ package rx.subjects; -import rx.Observer; +import rx.Subscriber; import rx.util.functions.Action0; public class SubjectPerformanceTests { @@ -127,7 +127,7 @@ public long baseline() { public long unboundedReplaySubject() { ReplaySubject s = ReplaySubject.create(); LongObserver o = new LongObserver(); - s.toObservable().subscribe(o); + s.subscribe(o); for (long l = 0; l < REPETITIONS; l++) { s.onNext(l); } @@ -135,7 +135,7 @@ public long unboundedReplaySubject() { return o.sum; } - private static class LongObserver extends Observer { + private static class LongObserver extends Subscriber { long sum = 0;