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

rx.schedulers.JavaFxScheduler Maven / Gradle / Ivy

/**
 * Copyright 2016 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 javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.util.Duration; import rx.Scheduler; import rx.Subscription; import rx.functions.Action0; import rx.subscriptions.Subscriptions; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * Executes work on the JavaFx UI thread. * This scheduler should only be used with actions that execute quickly. */ public final class JavaFxScheduler extends Scheduler { private static final JavaFxScheduler INSTANCE = new JavaFxScheduler(); /* package for unit test */JavaFxScheduler() { } public static JavaFxScheduler getInstance() { return INSTANCE; } public static JavaFxScheduler platform() { return INSTANCE; } private static void assertThatTheDelayIsValidForTheJavaFxTimer(long delay) { if (delay < 0 || delay > Integer.MAX_VALUE) { throw new IllegalArgumentException(String.format("The JavaFx timer only accepts non-negative delays up to %d milliseconds.", Integer.MAX_VALUE)); } } @Override public Worker createWorker() { return new JavaFxWorker(); } /** * A Worker implementation which manages a queue of QueuedRunnable for execution on the Java FX Application thread * For a simpler implementation the queue always contains at least one element. * {@link #head} is the element, which is in execution or was last executed * {@link #tail} is an atomic reference to the last element in the queue, or null when the worker was disposed * Recursive actions are not preferred and inserted at the tail of the queue as any other action would be * The Worker will only schedule a single job with {@link Platform#runLater(Runnable)} for when the queue was previously empty */ private static class JavaFxWorker extends Worker implements Runnable { private volatile QueuedRunnable head = new QueuedRunnable(null); /// only advanced in run(), initialised with a starter element private final AtomicReference tail = new AtomicReference<>(head); /// points to the last element, null when disposed private static class QueuedRunnable extends AtomicReference implements Subscription, Action0 { private volatile Action0 action; private QueuedRunnable(Action0 action) { this.action = action; } @Override public void unsubscribe() { action = null; } @Override public boolean isUnsubscribed() { return action == null; } @Override public void call() { Action0 action = this.action; if (action != null) { action.call(); } this.action = null; } } @Override public void unsubscribe() { tail.set(null); QueuedRunnable qr = this.head; while (qr != null) { qr.unsubscribe(); qr = qr.getAndSet(null); } } @Override public boolean isUnsubscribed() { return tail.get() == null; } @Override public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) { long delay = Math.max(0, unit.toMillis(delayTime)); assertThatTheDelayIsValidForTheJavaFxTimer(delay); final QueuedRunnable queuedRunnable = new QueuedRunnable(action); if (delay == 0) { // delay is too small for the java fx timer, schedule it without delay return schedule(queuedRunnable); } final Timeline timer = new Timeline(new KeyFrame(Duration.millis(delay), event -> schedule(queuedRunnable))); timer.play(); return Subscriptions.create(() -> { queuedRunnable.unsubscribe(); timer.stop(); }); } @Override public Subscription schedule(final Action0 action) { if (isUnsubscribed()) { return Subscriptions.unsubscribed(); } final QueuedRunnable queuedRunnable = action instanceof QueuedRunnable ? (QueuedRunnable) action : new QueuedRunnable(action); QueuedRunnable tailPivot; do { tailPivot = tail.get(); } while (tailPivot != null && !tailPivot.compareAndSet(null, queuedRunnable)); if (tailPivot == null) { queuedRunnable.unsubscribe(); } else { tail.compareAndSet(tailPivot, queuedRunnable); // can only fail with a concurrent dispose and we don't want to override the disposed value if (tailPivot == head) { if (Platform.isFxApplicationThread()) { run(); } else { Platform.runLater(this); } } } return queuedRunnable; } @Override public void run() { for (QueuedRunnable qr = head.get(); qr != null; qr = qr.get()) { qr.call(); head = qr; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy