/** * Create a new {@link VirtualTimeScheduler} without enabling it. Call * {@link #getOrSet(VirtualTimeScheduler)} to enable it on * {@link reactor.core.scheduler.Schedulers.Factory} factories. * * @return a new {@link VirtualTimeScheduler} intended for timed-only * {@link Schedulers} factories. */ public static VirtualTimeScheduler create() { return new VirtualTimeScheduler(); }
/** * Assign a single newly created {@link VirtualTimeScheduler} to all {@link reactor.core.scheduler.Schedulers.Factory} * factories. While the method is thread safe, its usually advised to execute such * wide-impact BEFORE all tested code runs (setup etc). The created scheduler is returned. * * @return the VirtualTimeScheduler that was created and set through the factory */ public static VirtualTimeScheduler getOrSet() { return enable(VirtualTimeScheduler::new, false); }
@Override public Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { final long periodInNanoseconds = unit.toNanos(period); final long firstNowNanoseconds = nanoTime; final long firstStartInNanoseconds = firstNowNanoseconds + unit.toNanos(initialDelay); PeriodicTask periodicTask = new PeriodicTask(firstStartInNanoseconds, task, firstNowNanoseconds, periodInNanoseconds); replace(periodicTask, schedule(periodicTask, initialDelay, unit)); return periodicTask; }
@After public void teardownVirtualTime(){ VirtualTimeScheduler.reset(); } }
/** * Triggers any tasks that have not yet been executed and that are scheduled to be * executed at or before this {@link VirtualTimeScheduler}'s present time. */ public void advanceTime() { advanceTimeBy(Duration.ZERO); }
@Test public void enableProvidedAllSchedulerIdempotent() { VirtualTimeScheduler vts = VirtualTimeScheduler.create(); VirtualTimeScheduler.getOrSet(vts); Assert.assertSame(vts, uncache(Schedulers.single())); Assert.assertFalse(vts.shutdown); VirtualTimeScheduler.getOrSet(vts); Assert.assertSame(vts, uncache(Schedulers.single())); Assert.assertFalse(vts.shutdown); }
@Override public Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { if (shutdown) { throw Exceptions.failWithRejected(); } PeriodicDirectTask periodicTask = new PeriodicDirectTask(task); directWorker.schedulePeriodically(periodicTask, initialDelay, period, unit); return periodicTask; }
protected VirtualTimeScheduler() { directWorker = createWorker(); }
@Override public Disposable schedule(Runnable task, long delay, TimeUnit unit) { if (shutdown) { throw Exceptions.failWithRejected(); } return directWorker.schedule(task, delay, unit); }
PeriodicTask(long firstStartInNanoseconds, Runnable decoratedRun, long firstNowNanoseconds, long periodInNanoseconds) { this.decoratedRun = decoratedRun; this.periodInNanoseconds = periodInNanoseconds; lastNowNanoseconds = firstNowNanoseconds; startInNanoseconds = firstStartInNanoseconds; lazySet(EMPTY); }
/** * Moves the {@link VirtualTimeScheduler}'s clock forward by a specified amount of time. * * @param delayTime the amount of time to move the {@link VirtualTimeScheduler}'s clock forward */ public void advanceTimeBy(Duration delayTime) { advanceTime(delayTime.toNanos()); }
@Override public VirtualTimeWorker createWorker() { if (shutdown) { throw new IllegalStateException("VirtualTimeScheduler is shutdown"); } return new VirtualTimeWorker(); }
@Override public void dispose() { getAndSet(CANCELLED).dispose(); } }
@Before public void vtsStart() { //delayElements (notably) now uses parallel() so VTS must be enabled everywhere vts = VirtualTimeScheduler.getOrSet(); }
@After public void vtsStop() { vts = null; VirtualTimeScheduler.reset(); }
/** * Common method to enable a {@link VirtualTimeScheduler} in {@link Schedulers} * factories. The supplier is lazily called. Enabling the same scheduler twice is * also idempotent. * * @param schedulerSupplier the supplier executed to obtain a fresh {@link VirtualTimeScheduler} * @return the scheduler that is actually used after the operation. */ static VirtualTimeScheduler enable(Supplier<VirtualTimeScheduler> schedulerSupplier) { return enable(schedulerSupplier, false); }
@Override public Disposable schedule(Runnable task) { if (shutdown) { throw Exceptions.failWithRejected(); } return directWorker.schedule(task); }
/** * Assign an externally created {@link VirtualTimeScheduler} to the relevant * {@link reactor.core.scheduler.Schedulers.Factory} factories, depending on how it was created (see * {@link #create()} and {@link #create()}). Note that the returned scheduler * should always be captured and used going forward, as the provided scheduler can be * superseded by a matching scheduler that has already been enabled. * <p> * While the method is thread safe, it's usually advised to execute such wide-impact * BEFORE all tested code runs (setup etc). The actual enabled Scheduler is returned. * * @param scheduler the {@link VirtualTimeScheduler} to use in factories. * @return the enabled VirtualTimeScheduler (can be different from the provided one) */ public static VirtualTimeScheduler getOrSet(VirtualTimeScheduler scheduler) { return enable(() -> scheduler, false); }
/** * Assign an externally created {@link VirtualTimeScheduler} to the relevant * {@link reactor.core.scheduler.Schedulers.Factory} factories, depending on how it was created (see * {@link #create()} and {@link #create()}). Contrary to * {@link #getOrSet(VirtualTimeScheduler)}, the provided scheduler is always used, even * if a matching scheduler is currently enabled. * <p> * While the method is thread safe, it's usually advised to execute such wide-impact * BEFORE all tested code runs (setup etc). * * @param scheduler the {@link VirtualTimeScheduler} to use in factories. * @return the enabled VirtualTimeScheduler (same as provided), for chaining */ public static VirtualTimeScheduler set(VirtualTimeScheduler scheduler) { return enable(() -> scheduler, true); }