Skip to content

Commit f53e029

Browse files
ZacSweersakarnokd
authored andcommitted
2.x: Add scheduler creation factories (#5002)
* Add scheduler creation factories Resolves #4993 This is a pretty vanilla copy from RxJava 1's implementation. Note that I had to tune NewThread scheduler to not be a singleton to support this. We had talked about borrowing from project reactor's APIs for different overloads, let me know if you think we should add more fine-grained controls through these. * Add `@since` info * Change failure string to "is null" * Move to RxJavaPlugins * Remove no-arg overloads * Rename to make it clearer about creation Added scheduler because we're not in Scheduler anymore. Changed to "create" because "newNewThread" was weird * Add tests (WIP) * Remove unnecessary nullcheck * Remove double try * Fix tests, make them more robust with integration flow * Shut down custom schedulers when done
1 parent 0ce3c59 commit f53e029

File tree

7 files changed

+277
-53
lines changed

7 files changed

+277
-53
lines changed

src/main/java/io/reactivex/internal/schedulers/ComputationScheduler.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@
1515
*/
1616
package io.reactivex.internal.schedulers;
1717

18-
import java.util.concurrent.*;
19-
import java.util.concurrent.atomic.AtomicReference;
20-
2118
import io.reactivex.Scheduler;
2219
import io.reactivex.disposables.*;
2320
import io.reactivex.internal.disposables.*;
2421

22+
import java.util.concurrent.*;
23+
import java.util.concurrent.atomic.AtomicReference;
24+
2525
/**
2626
* Holds a fixed pool of worker threads and assigns them
2727
* to requested Scheduler.Workers in a round-robin fashion.
2828
*/
2929
public final class ComputationScheduler extends Scheduler {
3030
/** This will indicate no pool is active. */
31-
static final FixedSchedulerPool NONE = new FixedSchedulerPool(0);
31+
static final FixedSchedulerPool NONE;
3232
/** Manages a fixed number of workers. */
3333
private static final String THREAD_NAME_PREFIX = "RxComputationThreadPool";
3434
static final RxThreadFactory THREAD_FACTORY;
@@ -42,6 +42,7 @@ public final class ComputationScheduler extends Scheduler {
4242

4343
static final PoolWorker SHUTDOWN_WORKER;
4444

45+
final ThreadFactory threadFactory;
4546
final AtomicReference<FixedSchedulerPool> pool;
4647
/** The name of the system property for setting the thread priority for this Scheduler. */
4748
private static final String KEY_COMPUTATION_PRIORITY = "rx2.computation-priority";
@@ -56,6 +57,9 @@ public final class ComputationScheduler extends Scheduler {
5657
Integer.getInteger(KEY_COMPUTATION_PRIORITY, Thread.NORM_PRIORITY)));
5758

5859
THREAD_FACTORY = new RxThreadFactory(THREAD_NAME_PREFIX, priority);
60+
61+
NONE = new FixedSchedulerPool(0, THREAD_FACTORY);
62+
NONE.shutdown();
5963
}
6064

6165
static int cap(int cpuCount, int paramThreads) {
@@ -68,12 +72,12 @@ static final class FixedSchedulerPool {
6872
final PoolWorker[] eventLoops;
6973
long n;
7074

71-
FixedSchedulerPool(int maxThreads) {
75+
FixedSchedulerPool(int maxThreads, ThreadFactory threadFactory) {
7276
// initialize event loops
7377
this.cores = maxThreads;
7478
this.eventLoops = new PoolWorker[maxThreads];
7579
for (int i = 0; i < maxThreads; i++) {
76-
this.eventLoops[i] = new PoolWorker(THREAD_FACTORY);
80+
this.eventLoops[i] = new PoolWorker(threadFactory);
7781
}
7882
}
7983

@@ -98,6 +102,18 @@ public void shutdown() {
98102
* count and using least-recent worker selection policy.
99103
*/
100104
public ComputationScheduler() {
105+
this(THREAD_FACTORY);
106+
}
107+
108+
/**
109+
* Create a scheduler with pool size equal to the available processor
110+
* count and using least-recent worker selection policy.
111+
*
112+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
113+
* system properties for configuring new thread creation. Cannot be null.
114+
*/
115+
public ComputationScheduler(ThreadFactory threadFactory) {
116+
this.threadFactory = threadFactory;
101117
this.pool = new AtomicReference<FixedSchedulerPool>(NONE);
102118
start();
103119
}
@@ -121,7 +137,7 @@ public Disposable schedulePeriodicallyDirect(Runnable run, long initialDelay, lo
121137

122138
@Override
123139
public void start() {
124-
FixedSchedulerPool update = new FixedSchedulerPool(MAX_THREADS);
140+
FixedSchedulerPool update = new FixedSchedulerPool(MAX_THREADS, threadFactory);
125141
if (!pool.compareAndSet(NONE, update)) {
126142
update.shutdown();
127143
}

src/main/java/io/reactivex/internal/schedulers/IoScheduler.java

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616

1717
package io.reactivex.internal.schedulers;
1818

19-
import java.util.concurrent.*;
20-
import java.util.concurrent.atomic.*;
21-
2219
import io.reactivex.Scheduler;
2320
import io.reactivex.disposables.*;
2421
import io.reactivex.internal.disposables.EmptyDisposable;
2522

23+
import java.util.concurrent.*;
24+
import java.util.concurrent.atomic.*;
25+
2626
/**
2727
* Scheduler that creates and caches a set of thread pools and reuses them if possible.
2828
*/
@@ -37,16 +37,14 @@ public final class IoScheduler extends Scheduler {
3737
private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.SECONDS;
3838

3939
static final ThreadWorker SHUTDOWN_THREAD_WORKER;
40+
final ThreadFactory threadFactory;
4041
final AtomicReference<CachedWorkerPool> pool;
4142

4243
/** The name of the system property for setting the thread priority for this Scheduler. */
4344
private static final String KEY_IO_PRIORITY = "rx2.io-priority";
4445

4546
static final CachedWorkerPool NONE;
4647
static {
47-
NONE = new CachedWorkerPool(0, null);
48-
NONE.shutdown();
49-
5048
SHUTDOWN_THREAD_WORKER = new ThreadWorker(new RxThreadFactory("RxCachedThreadSchedulerShutdown"));
5149
SHUTDOWN_THREAD_WORKER.dispose();
5250

@@ -56,6 +54,9 @@ public final class IoScheduler extends Scheduler {
5654
WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);
5755

5856
EVICTOR_THREAD_FACTORY = new RxThreadFactory(EVICTOR_THREAD_NAME_PREFIX, priority);
57+
58+
NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
59+
NONE.shutdown();
5960
}
6061

6162
static final class CachedWorkerPool implements Runnable {
@@ -64,11 +65,13 @@ static final class CachedWorkerPool implements Runnable {
6465
final CompositeDisposable allWorkers;
6566
private final ScheduledExecutorService evictorService;
6667
private final Future<?> evictorTask;
68+
private final ThreadFactory threadFactory;
6769

68-
CachedWorkerPool(long keepAliveTime, TimeUnit unit) {
70+
CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
6971
this.keepAliveTime = unit != null ? unit.toNanos(keepAliveTime) : 0L;
7072
this.expiringWorkerQueue = new ConcurrentLinkedQueue<ThreadWorker>();
7173
this.allWorkers = new CompositeDisposable();
74+
this.threadFactory = threadFactory;
7275

7376
ScheduledExecutorService evictor = null;
7477
Future<?> task = null;
@@ -97,7 +100,7 @@ ThreadWorker get() {
97100
}
98101

99102
// No cached worker found, so create a new one.
100-
ThreadWorker w = new ThreadWorker(WORKER_THREAD_FACTORY);
103+
ThreadWorker w = new ThreadWorker(threadFactory);
101104
allWorkers.add(w);
102105
return w;
103106
}
@@ -143,13 +146,22 @@ void shutdown() {
143146
}
144147

145148
public IoScheduler() {
149+
this(WORKER_THREAD_FACTORY);
150+
}
151+
152+
/**
153+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
154+
* system properties for configuring new thread creation. Cannot be null.
155+
*/
156+
public IoScheduler(ThreadFactory threadFactory) {
157+
this.threadFactory = threadFactory;
146158
this.pool = new AtomicReference<CachedWorkerPool>(NONE);
147159
start();
148160
}
149161

150162
@Override
151163
public void start() {
152-
CachedWorkerPool update = new CachedWorkerPool(KEEP_ALIVE_TIME, KEEP_ALIVE_UNIT);
164+
CachedWorkerPool update = new CachedWorkerPool(KEEP_ALIVE_TIME, KEEP_ALIVE_UNIT, threadFactory);
153165
if (!pool.compareAndSet(NONE, update)) {
154166
update.shutdown();
155167
}

src/main/java/io/reactivex/internal/schedulers/NewThreadScheduler.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@
1818

1919
import io.reactivex.Scheduler;
2020

21+
import java.util.concurrent.ThreadFactory;
22+
2123
/**
2224
* Schedules work on a new thread.
2325
*/
2426
public final class NewThreadScheduler extends Scheduler {
2527

28+
final ThreadFactory threadFactory;
29+
2630
private static final String THREAD_NAME_PREFIX = "RxNewThreadScheduler";
2731
private static final RxThreadFactory THREAD_FACTORY;
2832

29-
private static final NewThreadScheduler INSTANCE = new NewThreadScheduler();
30-
3133
/** The name of the system property for setting the thread priority for this Scheduler. */
3234
private static final String KEY_NEWTHREAD_PRIORITY = "rx2.newthread-priority";
3335

@@ -38,16 +40,16 @@ public final class NewThreadScheduler extends Scheduler {
3840
THREAD_FACTORY = new RxThreadFactory(THREAD_NAME_PREFIX, priority);
3941
}
4042

41-
public static NewThreadScheduler instance() {
42-
return INSTANCE;
43+
public NewThreadScheduler() {
44+
this(THREAD_FACTORY);
4345
}
4446

45-
private NewThreadScheduler() {
46-
47+
public NewThreadScheduler(ThreadFactory threadFactory) {
48+
this.threadFactory = threadFactory;
4749
}
4850

4951
@Override
5052
public Worker createWorker() {
51-
return new NewThreadWorker(THREAD_FACTORY);
53+
return new NewThreadWorker(threadFactory);
5254
}
5355
}

src/main/java/io/reactivex/internal/schedulers/SingleScheduler.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@
1212
*/
1313
package io.reactivex.internal.schedulers;
1414

15-
import java.util.concurrent.*;
16-
import java.util.concurrent.atomic.AtomicReference;
17-
1815
import io.reactivex.Scheduler;
1916
import io.reactivex.disposables.*;
2017
import io.reactivex.internal.disposables.EmptyDisposable;
2118
import io.reactivex.plugins.RxJavaPlugins;
2219

20+
import java.util.concurrent.*;
21+
import java.util.concurrent.atomic.AtomicReference;
22+
2323
/**
2424
* A scheduler with a shared, single threaded underlying ScheduledExecutorService.
2525
* @since 2.0
2626
*/
2727
public final class SingleScheduler extends Scheduler {
2828

29+
final ThreadFactory threadFactory;
2930
final AtomicReference<ScheduledExecutorService> executor = new AtomicReference<ScheduledExecutorService>();
3031

3132
/** The name of the system property for setting the thread priority for this Scheduler. */
@@ -47,11 +48,20 @@ public final class SingleScheduler extends Scheduler {
4748
}
4849

4950
public SingleScheduler() {
50-
executor.lazySet(createExecutor());
51+
this(SINGLE_THREAD_FACTORY);
52+
}
53+
54+
/**
55+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
56+
* system properties for configuring new thread creation. Cannot be null.
57+
*/
58+
public SingleScheduler(ThreadFactory threadFactory) {
59+
this.threadFactory = threadFactory;
60+
executor.lazySet(createExecutor(threadFactory));
5161
}
5262

53-
static ScheduledExecutorService createExecutor() {
54-
return SchedulerPoolFactory.create(SINGLE_THREAD_FACTORY);
63+
static ScheduledExecutorService createExecutor(ThreadFactory threadFactory) {
64+
return SchedulerPoolFactory.create(threadFactory);
5565
}
5666

5767
@Override
@@ -66,7 +76,7 @@ public void start() {
6676
return;
6777
}
6878
if (next == null) {
69-
next = createExecutor();
79+
next = createExecutor(threadFactory);
7080
}
7181
if (executor.compareAndSet(current, next)) {
7282
return;

src/main/java/io/reactivex/plugins/RxJavaPlugins.java

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@
1212
*/
1313
package io.reactivex.plugins;
1414

15-
import java.lang.Thread.UncaughtExceptionHandler;
16-
import java.util.concurrent.Callable;
17-
18-
import io.reactivex.internal.functions.ObjectHelper;
19-
import org.reactivestreams.Subscriber;
20-
2115
import io.reactivex.*;
16+
import io.reactivex.annotations.Experimental;
2217
import io.reactivex.flowables.ConnectableFlowable;
2318
import io.reactivex.functions.*;
19+
import io.reactivex.internal.functions.ObjectHelper;
20+
import io.reactivex.internal.schedulers.*;
2421
import io.reactivex.internal.util.ExceptionHelper;
2522
import io.reactivex.observables.ConnectableObservable;
23+
import io.reactivex.schedulers.Schedulers;
24+
import org.reactivestreams.Subscriber;
25+
26+
import java.lang.Thread.UncaughtExceptionHandler;
27+
import java.util.concurrent.*;
2628

2729
/**
2830
* Utility class to inject handlers to certain standard RxJava operations.
@@ -926,6 +928,58 @@ public static Completable onAssembly(Completable source) {
926928
return source;
927929
}
928930

931+
/**
932+
* Create an instance of the default {@link Scheduler} used for {@link Schedulers#computation()}
933+
* except using {@code threadFactory} for thread creation.
934+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
935+
* system properties for configuring new thread creation. Cannot be null.
936+
* @return the created Scheduler instance
937+
* @since 2.0.5 - experimental
938+
*/
939+
@Experimental
940+
public static Scheduler createComputationScheduler(ThreadFactory threadFactory) {
941+
return new ComputationScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null"));
942+
}
943+
944+
/**
945+
* Create an instance of the default {@link Scheduler} used for {@link Schedulers#io()}
946+
* except using {@code threadFactory} for thread creation.
947+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
948+
* system properties for configuring new thread creation. Cannot be null.
949+
* @return the created Scheduler instance
950+
* @since 2.0.5 - experimental
951+
*/
952+
@Experimental
953+
public static Scheduler createIoScheduler(ThreadFactory threadFactory) {
954+
return new IoScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null"));
955+
}
956+
957+
/**
958+
* Create an instance of the default {@link Scheduler} used for {@link Schedulers#newThread()}
959+
* except using {@code threadFactory} for thread creation.
960+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
961+
* system properties for configuring new thread creation. Cannot be null.
962+
* @return the created Scheduler instance
963+
* @since 2.0.5 - experimental
964+
*/
965+
@Experimental
966+
public static Scheduler createNewThreadScheduler(ThreadFactory threadFactory) {
967+
return new NewThreadScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null"));
968+
}
969+
970+
/**
971+
* Create an instance of the default {@link Scheduler} used for {@link Schedulers#single()}
972+
* except using {@code threadFactory} for thread creation.
973+
* @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
974+
* system properties for configuring new thread creation. Cannot be null.
975+
* @return the created Scheduler instance
976+
* @since 2.0.5 - experimental
977+
*/
978+
@Experimental
979+
public static Scheduler createSingleScheduler(ThreadFactory threadFactory) {
980+
return new SingleScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null"));
981+
}
982+
929983
/**
930984
* Wraps the call to the function in try-catch and propagates thrown
931985
* checked exceptions as RuntimeException.

src/main/java/io/reactivex/schedulers/Schedulers.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@
1313

1414
package io.reactivex.schedulers;
1515

16-
import java.util.concurrent.Callable;
17-
import java.util.concurrent.Executor;
18-
1916
import io.reactivex.Scheduler;
2017
import io.reactivex.internal.schedulers.*;
2118
import io.reactivex.plugins.RxJavaPlugins;
2219

20+
import java.util.concurrent.*;
21+
2322
/**
2423
* Static factory methods for returning standard Scheduler instances.
2524
* <p>
@@ -58,7 +57,7 @@ static final class IoHolder {
5857
}
5958

6059
static final class NewThreadHolder {
61-
static final Scheduler DEFAULT = NewThreadScheduler.instance();
60+
static final Scheduler DEFAULT = new NewThreadScheduler();
6261
}
6362

6463
static {

0 commit comments

Comments
 (0)