@Override public Object call() { // Being Callable saves an allocation in ThreadPoolExecutor run(); return null; }
@Override public void run() { run.setFuture(ft); } };
@Override public void run() { lazySet(THREAD_INDEX, Thread.currentThread()); try { try { actual.run(); } catch (Throwable e) { // Exceptions.throwIfFatal(e); nowhere to go RxJavaCommonPlugins.onError(e); } } finally { lazySet(THREAD_INDEX, null); Object o = get(PARENT_INDEX); if (o != DISPOSED && o != null && compareAndSet(PARENT_INDEX, o, DONE)) { ((DisposableContainer)o).delete(this); } for (;;) { o = get(FUTURE_INDEX); if (o == DISPOSED || compareAndSet(FUTURE_INDEX, o, DONE)) { break; } } } }
public void setFuture(Future<?> f) { for (;;) { Object o = get(FUTURE_INDEX); if (o == DONE) { return; } if (o == DISPOSED) { f.cancel(get(THREAD_INDEX) != Thread.currentThread()); return; } if (compareAndSet(FUTURE_INDEX, o, f)) { return; } } }
@NonNull @Override public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { if (disposed) { return REJECTED; } Runnable decoratedRun = RxJavaCommonPlugins.onSchedule(run); ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, tasks); tasks.add(sr); try { Future<?> f; if (delay <= 0L) { f = executor.submit((Callable<Object>)sr); } else { f = executor.schedule((Callable<Object>)sr, delay, unit); } sr.setFuture(f); } catch (RejectedExecutionException ex) { dispose(); RxJavaCommonPlugins.onError(ex); return REJECTED; } return sr; }
@Test public void withFutureDisposed2() { ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, null); run.dispose(); run.setFuture(new FutureTask<Void>(Functions.EMPTY_RUNNABLE, null)); run.call(); }
@Test public void pluginCrash() { Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { throw new TestException("Second"); } }); CompositeDisposable set = new CompositeDisposable(); final ScheduledRunnable run = new ScheduledRunnable(new Runnable() { @Override public void run() { throw new TestException("First"); } }, set); set.add(run); try { run.run(); fail("Should have thrown!"); } catch (TestException ex) { assertEquals("Second", ex.getMessage()); } finally { Thread.currentThread().setUncaughtExceptionHandler(null); } assertTrue(run.isDisposed()); assertEquals(0, set.size()); }
@Test public void runFuture() { for (int i = 0; i < 500; i++) { CompositeDisposable set = new CompositeDisposable(); final ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); final FutureTask<Void> ft = new FutureTask<Void>(Functions.EMPTY_RUNNABLE, null); Runnable r1 = new Runnable() { @Override public void run() { run.call(); } }; Runnable r2 = new Runnable() { @Override public void run() { run.setFuture(ft); } }; TestCommonHelper.race(r1, r2); } } }
@Test public void withoutParentDisposed() { ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, null); run.dispose(); run.call(); }
@Test public void disposeRun() { CompositeDisposable set = new CompositeDisposable(); ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); assertFalse(run.isDisposed()); run.dispose(); run.dispose(); assertTrue(run.isDisposed()); }
@Test public void dispose() { CompositeDisposable set = new CompositeDisposable(); ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); assertFalse(run.isDisposed()); set.dispose(); assertTrue(run.isDisposed()); }
/** * Creates a ScheduledRunnable by wrapping the given action and setting * up the optional parent. * @param actual the runnable to wrap, not-null (not verified) * @param parent the parent tracking container or null if none */ public ScheduledRunnable(Runnable actual, DisposableContainer parent) { super(3); this.actual = actual; this.lazySet(0, parent); }
@Override public boolean isDisposed() { Object o = get(FUTURE_INDEX); return o == DISPOSED || o == DONE; } }
@Override public void run() { run.call(); } };
@NonNull @Override public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { if (disposed) { return REJECTED; } Runnable decoratedRun = RxJavaCommonPlugins.onSchedule(run); ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, tasks); tasks.add(sr); try { Future<?> f; if (delay <= 0L) { f = executor.submit((Callable<Object>)sr); } else { f = executor.schedule((Callable<Object>)sr, delay, unit); } sr.setFuture(f); } catch (RejectedExecutionException ex) { dispose(); RxJavaCommonPlugins.onError(ex); return REJECTED; } return sr; }
@Test public void withFutureDisposed() { ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, null); run.setFuture(new FutureTask<Void>(Functions.EMPTY_RUNNABLE, null)); run.dispose(); run.call(); }
public void setFuture(Future<?> f) { for (;;) { Object o = get(FUTURE_INDEX); if (o == DONE) { return; } if (o == DISPOSED) { f.cancel(get(THREAD_INDEX) != Thread.currentThread()); return; } if (compareAndSet(FUTURE_INDEX, o, f)) { return; } } }
@Test public void crashReported() { List<Throwable> errors = TestCommonHelper.trackPluginErrors(); try { CompositeDisposable set = new CompositeDisposable(); final ScheduledRunnable run = new ScheduledRunnable(new Runnable() { @Override public void run() { throw new TestException("First"); } }, set); set.add(run); run.run(); assertTrue(run.isDisposed()); assertEquals(0, set.size()); TestCommonHelper.assertUndeliverable(errors, 0, TestException.class, "First"); } finally { RxJavaCommonPlugins.reset(); } }
@Test public void setFutureRunRace() { for (int i = 0; i < 500; i++) { CompositeDisposable set = new CompositeDisposable(); final ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); final FutureTask<Object> ft = new FutureTask<Object>(Functions.EMPTY_RUNNABLE, 0); Runnable r1 = new Runnable() { @Override public void run() { run.setFuture(ft); } }; Runnable r2 = new Runnable() { @Override public void run() { run.run(); } }; TestCommonHelper.race(r1, r2); assertEquals(0, set.size()); } }
@Test public void withParentDisposed() { ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, new CompositeDisposable()); run.dispose(); run.call(); }