All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rx.schedulers.ExecutorScheduler Maven / Gradle / Ivy

There is a newer version: 0.20.7
Show newest version
/**
 * 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.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import rx.Scheduler;
import rx.Subscription;
import rx.functions.Action0;
import rx.plugins.RxJavaPlugins;
import rx.subscriptions.CompositeSubscription;
import rx.subscriptions.MultipleAssignmentSubscription;
import rx.subscriptions.Subscriptions;

/**
 * Scheduler that wraps an Executor instance and establishes the Scheduler contract upon it.
 * 

* Note that thread-hopping is unavoidable with this kind of Scheduler as we don't know about the underlying * threading behavior of the executor. */ /* public */final class ExecutorScheduler extends Scheduler { final Executor executor; public ExecutorScheduler(Executor executor) { this.executor = executor; } /** * @warn javadoc missing * @return */ @Override public Worker createWorker() { return new ExecutorSchedulerWorker(executor); } /** Worker that schedules tasks on the executor indirectly through a trampoline mechanism. */ static final class ExecutorSchedulerWorker extends Scheduler.Worker implements Runnable { final Executor executor; // TODO: use a better performing structure for task tracking final CompositeSubscription tasks; // TODO: use MpscLinkedQueue once available final ConcurrentLinkedQueue queue; final AtomicInteger wip; public ExecutorSchedulerWorker(Executor executor) { this.executor = executor; this.queue = new ConcurrentLinkedQueue(); this.wip = new AtomicInteger(); this.tasks = new CompositeSubscription(); } @Override public Subscription schedule(Action0 action) { if (isUnsubscribed()) { return Subscriptions.empty(); } ExecutorAction ea = new ExecutorAction(action, tasks); tasks.add(ea); queue.offer(ea); if (wip.getAndIncrement() == 0) { try { executor.execute(this); } catch (RejectedExecutionException t) { // cleanup if rejected tasks.remove(ea); wip.decrementAndGet(); // report the error to the plugin RxJavaPlugins.getInstance().getErrorHandler().handleError(t); // throw it to the caller throw t; } } return ea; } @Override public void run() { do { queue.poll().run(); } while (wip.decrementAndGet() > 0); } @Override public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) { if (delayTime <= 0) { return schedule(action); } if (isUnsubscribed()) { return Subscriptions.empty(); } ScheduledExecutorService service; if (executor instanceof ScheduledExecutorService) { service = (ScheduledExecutorService)executor; } else { service = GenericScheduledExecutorService.getInstance(); } final MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription(); // tasks.add(mas); // Needs a removal without unsubscription try { Future f = service.schedule(new Runnable() { @Override public void run() { if (mas.isUnsubscribed()) { return; } mas.set(schedule(action)); // tasks.delete(mas); // Needs a removal without unsubscription } }, delayTime, unit); mas.set(Subscriptions.from(f)); } catch (RejectedExecutionException t) { // report the rejection to plugins RxJavaPlugins.getInstance().getErrorHandler().handleError(t); throw t; } return mas; } @Override public boolean isUnsubscribed() { return tasks.isUnsubscribed(); } @Override public void unsubscribe() { tasks.unsubscribe(); } } /** Runs the actual action and maintains an unsubscription state. */ static final class ExecutorAction implements Runnable, Subscription { final Action0 actual; final CompositeSubscription parent; volatile int unsubscribed; static final AtomicIntegerFieldUpdater UNSUBSCRIBED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ExecutorAction.class, "unsubscribed"); public ExecutorAction(Action0 actual, CompositeSubscription parent) { this.actual = actual; this.parent = parent; } @Override public void run() { if (isUnsubscribed()) { return; } try { actual.call(); } catch (Throwable t) { RxJavaPlugins.getInstance().getErrorHandler().handleError(t); } finally { unsubscribe(); } } @Override public boolean isUnsubscribed() { return unsubscribed != 0; } @Override public void unsubscribe() { if (UNSUBSCRIBED_UPDATER.compareAndSet(this, 0, 1)) { parent.remove(this); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy