Description
There is an issue in TrampolineScheduler on 0.20.4 where it can throw a NullPointerException in InnerCurrentThreadScheduler enqueue(Action0 action, long execTime)
private Subscription enqueue(Action0 action, long execTime) {
if (innerSubscription.isUnsubscribed()) {
return Subscriptions.empty();
}
PriorityQueue<TimedAction> queue = QUEUE.get();
final TimedAction timedAction = new TimedAction(action, execTime, COUNTER_UPDATER.incrementAndGet(TrampolineScheduler.this));
queue.add(timedAction);
if (wip.getAndIncrement() == 0) {
do {
queue.poll().action.call();
} while (wip.decrementAndGet() > 0);
return Subscriptions.empty();
} else {
// queue wasn't empty, a parent is already processing so we just add to the end of the queue
return Subscriptions.create(new Action0() {
@Override
public void call() {
PriorityQueue<TimedAction> _q = QUEUE.get();
if (_q != null) {
_q.remove(timedAction);
}
}
});
}
}
The Exception happens on queue.poll().action.call();
.
From what I can tell, the queue is empty, and poll is returning null.
This is happening inside a nested observable.redo().timeout().redo()
chain (the redo().timeout()
happens earlier in the application, it isn't just one after the other like that), if that helps. I'm not in a position to create a simple replication example however.
My current theory is: One of the redos could be unsubscribing, calling _q.remove
, while the wip
loop is running (perhaps unsubscribe is happening on another thread?), causing the queue to become empty, and the next loop of the wip
loop returns null
. I think the queue.poll()
call should check for null.