- 
                Notifications
    You must be signed in to change notification settings 
- Fork 7.6k
Schedulers shutdown capability. #1769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -15,44 +15,37 @@ | |
| */ | ||
| package rx.schedulers; | ||
|  | ||
| import java.util.concurrent.Executor; | ||
|  | ||
| import rx.Scheduler; | ||
| import rx.plugins.RxJavaPlugins; | ||
|  | ||
| import java.util.concurrent.Executor; | ||
|  | ||
| /** | ||
| * Static factory methods for creating Schedulers. | ||
| */ | ||
| public final class Schedulers { | ||
|  | ||
| private final Scheduler computationScheduler; | ||
| private final Scheduler ioScheduler; | ||
| private final Scheduler newThreadScheduler; | ||
|  | ||
| private static final Schedulers INSTANCE = new Schedulers(); | ||
|  | ||
| private Schedulers() { | ||
| Scheduler c = RxJavaPlugins.getInstance().getSchedulersHook().getComputationScheduler(); | ||
| if (c != null) { | ||
| computationScheduler = c; | ||
| } else { | ||
| computationScheduler = new EventLoopsScheduler(); | ||
| } | ||
|  | ||
| Scheduler io = RxJavaPlugins.getInstance().getSchedulersHook().getIOScheduler(); | ||
| if (io != null) { | ||
| ioScheduler = io; | ||
| } else { | ||
| ioScheduler = new CachedThreadScheduler(); | ||
| } | ||
|  | ||
| private static final Object computationGuard = new Object(); | ||
| /** The computation scheduler instance, guarded by computationGuard. */ | ||
| private static volatile Scheduler computationScheduler; | ||
| private static final Object ioGuard = new Object(); | ||
| /** The io scheduler instance, guarded by ioGuard. */ | ||
| private static volatile Scheduler ioScheduler; | ||
| /** The new thread scheduler, fixed because it doesn't need to support shutdown. */ | ||
| private static final Scheduler newThreadScheduler; | ||
|  | ||
| static { | ||
| Scheduler nt = RxJavaPlugins.getInstance().getSchedulersHook().getNewThreadScheduler(); | ||
| if (nt != null) { | ||
| newThreadScheduler = nt; | ||
| } else { | ||
| newThreadScheduler = NewThreadScheduler.instance(); | ||
| } | ||
| } | ||
|  | ||
| private Schedulers() { | ||
| throw new IllegalStateException("No instances!"); | ||
| } | ||
|  | ||
| /** | ||
| * Creates and returns a {@link Scheduler} that executes work immediately on the current thread. | ||
|  | @@ -81,7 +74,7 @@ public static Scheduler trampoline() { | |
| * @return a {@link NewThreadScheduler} instance | ||
| */ | ||
| public static Scheduler newThread() { | ||
| return INSTANCE.newThreadScheduler; | ||
| return newThreadScheduler; | ||
| } | ||
|  | ||
| /** | ||
|  | @@ -96,7 +89,21 @@ public static Scheduler newThread() { | |
| * @return a {@link Scheduler} meant for computation-bound work | ||
| */ | ||
| public static Scheduler computation() { | ||
| return INSTANCE.computationScheduler; | ||
| Scheduler s = computationScheduler; | ||
| if (s != null) { | ||
| return s; | ||
| } | ||
| synchronized (computationGuard) { | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like putting a synchronized block in the line of fire on something like this that doesn't need to be. This is a global lock and exactly the type of stuff I find in our production environments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course we can do shutdown without this, but then the schedulers are dead and without app restrart, they won't come back. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not to mention, they can't be tested because they shutdown the global threads for good. | ||
| if (computationScheduler == null) { | ||
| Scheduler c = RxJavaPlugins.getInstance().getSchedulersHook().getComputationScheduler(); | ||
| if (c != null) { | ||
| computationScheduler = c; | ||
| } else { | ||
| computationScheduler = new EventLoopsScheduler(); | ||
| } | ||
| } | ||
| return computationScheduler; | ||
| } | ||
| } | ||
|  | ||
| /** | ||
|  | @@ -113,7 +120,21 @@ public static Scheduler computation() { | |
| * @return a {@link Scheduler} meant for IO-bound work | ||
| */ | ||
| public static Scheduler io() { | ||
| return INSTANCE.ioScheduler; | ||
| Scheduler s = ioScheduler; | ||
| if (s != null) { | ||
| return s; | ||
| } | ||
| synchronized (ioGuard) { | ||
| if (ioScheduler == null) { | ||
| Scheduler io = RxJavaPlugins.getInstance().getSchedulersHook().getIOScheduler(); | ||
| if (io != null) { | ||
| ioScheduler = io; | ||
| } else { | ||
| ioScheduler = new CachedThreadScheduler(); | ||
| } | ||
| } | ||
| return ioScheduler; | ||
| } | ||
| } | ||
|  | ||
| /** | ||
|  | @@ -136,4 +157,37 @@ public static TestScheduler test() { | |
| public static Scheduler from(Executor executor) { | ||
| return new ExecutorScheduler(executor); | ||
| } | ||
| /** | ||
| * Shuts down the threads of the Computation and IO schedulers. | ||
| * The newThread() scheduler doesn't need to be shut down as its workers shut themselves | ||
| * down once they complete. | ||
| */ | ||
| public static void shutdown() { | ||
| synchronized (computationGuard) { | ||
| if (computationScheduler != null) { | ||
| computationScheduler.shutdown(); | ||
| } | ||
| computationScheduler = null; | ||
| } | ||
| synchronized (ioGuard) { | ||
| if (ioScheduler != null) { | ||
| ioScheduler.shutdown(); | ||
| } | ||
| ioScheduler = null; | ||
| } | ||
| } | ||
| /** | ||
| * Test support. | ||
| * @return returns true if there is a computation scheduler instance available. | ||
| */ | ||
| static boolean hasComputationScheduler() { | ||
| return computationScheduler != null; | ||
| } | ||
| /** | ||
| * Test support. | ||
| * @return returns true if there is an io scheduler instance available. | ||
| */ | ||
| static boolean hasIOScheduler() { | ||
| return ioScheduler != null; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have some javadoc on this method about if this method is synchronous or asynchronous? If it's not synchronous then how will I know when the
Schedulerhas shutdown, that is when the running threads currently associated with theSchedulerhave completed?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not detailed in the requirements, but the implementation basically calls unsubscribe() and shutdownNow() which generally do best effort to terminate outstanding work. So it is quite possible an unresponsive/ignorant work may keep a worker thread operational and make Tomcat still print out the leak notification.
Is the underlying issue still relevant (almost a month passed with no feedback)?