package com.lambdaworks.redis.resource; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import rx.Scheduler; import rx.Subscription; import rx.functions.Action0; import rx.internal.schedulers.ScheduledAction; import rx.internal.util.SubscriptionList; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutorGroup; /** * A scheduler that uses a provided {@link EventExecutorGroup} instance to schedule tasks. This should typically be used as a * computation scheduler or any other scheduler that do not schedule blocking tasks. See also * https://github.com/ReactiveX/RxNetty * /blob/0.5.x/rxnetty-common/src/main/java/io/reactivex/netty/threads/RxJavaEventloopScheduler.java */ public class RxJavaEventExecutorGroupScheduler extends Scheduler { private final EventExecutorGroup eventLoopGroup; public RxJavaEventExecutorGroupScheduler(EventExecutorGroup eventLoopGroup) { this.eventLoopGroup = eventLoopGroup; } @Override public Worker createWorker() { final EventExecutor eventLoop = eventLoopGroup.next(); return new ScheduledExecutorServiceWorker(eventLoop); } /** * This code is more or less copied from rx-netty's EventloopWorker worker code. **/ private static class ScheduledExecutorServiceWorker extends Worker { /** * Why are there two subscription holders? * * The serial subscriptions are used for non-delayed schedules which are always executed (and hence removed) in order. * Since SubscriptionList holds the subs as a linked list, removals are optimal for serial removes. OTOH, delayed * schedules are executed (and hence removed) out of order and hence a CompositeSubscription, that stores the subs in a * hash structure is more optimal for removals. */ private final SubscriptionList serial; private final CompositeSubscription timed; private final SubscriptionList both; private final ScheduledExecutorService scheduledExecutor; public ScheduledExecutorServiceWorker(EventExecutor scheduledExecutor) { this.scheduledExecutor = scheduledExecutor; serial = new SubscriptionList(); timed = new CompositeSubscription(); both = new SubscriptionList(serial, timed); } @Override public Subscription schedule(final Action0 action) { return schedule(action, 0, TimeUnit.DAYS); } @Override public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) { if (isUnsubscribed()) { return Subscriptions.unsubscribed(); } final ScheduledAction sa; if (delayTime <= 0) { sa = new ScheduledAction(action, serial); serial.add(sa); } else { sa = new ScheduledAction(action, timed); timed.add(sa); } final Future<?> result = scheduledExecutor.schedule(sa, delayTime, unit); Subscription cancelFuture = Subscriptions.create(new Action0() { @Override public void call() { result.cancel(false); } }); sa.add(cancelFuture); /* An unsubscribe of the returned sub should cancel the future */ return sa; } @Override public void unsubscribe() { both.unsubscribe(); } @Override public boolean isUnsubscribed() { return both.isUnsubscribed(); } } }