/**
 * 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.PriorityQueue;
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.subscriptions.BooleanSubscription;
import rx.subscriptions.Subscriptions;

/**
 * Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed
 * after the current unit of work is completed.
 */
public final class TrampolineScheduler extends Scheduler {
    private static final TrampolineScheduler INSTANCE = new TrampolineScheduler();

    /* package */static TrampolineScheduler instance() {
        return INSTANCE;
    }

    @Override
    public Worker createWorker() {
        return new InnerCurrentThreadScheduler();
    }

    /* package accessible for unit tests */TrampolineScheduler() {
    }

    volatile int counter;
    static final AtomicIntegerFieldUpdater<TrampolineScheduler> COUNTER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(TrampolineScheduler.class, "counter");

    private class InnerCurrentThreadScheduler extends Scheduler.Worker implements Subscription {

        final PriorityQueue<TimedAction> queue = new PriorityQueue<TimedAction>();
        private final BooleanSubscription innerSubscription = new BooleanSubscription();
        private final AtomicInteger wip = new AtomicInteger();

        @Override
        public Subscription schedule(Action0 action) {
            return enqueue(action, now());
        }

        @Override
        public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
            long execTime = now() + unit.toMillis(delayTime);

            return enqueue(new SleepingAction(action, this, execTime), execTime);
        }

        private Subscription enqueue(Action0 action, long execTime) {
            if (innerSubscription.isUnsubscribed()) {
                return Subscriptions.unsubscribed();
            }
            final TimedAction timedAction = new TimedAction(action, execTime, COUNTER_UPDATER.incrementAndGet(TrampolineScheduler.this));
            synchronized (queue) {
                queue.add(timedAction);
            }

            if (wip.getAndIncrement() == 0) {
                do {
                    TimedAction polled;
                    synchronized (queue) {
                        polled = queue.poll();
                    }
                    polled.action.call();
                } while (wip.decrementAndGet() > 0);
                return Subscriptions.unsubscribed();
            } 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() {
                        synchronized (queue) {
                            queue.remove(timedAction);
                        }
                    }

                });
            }
        }

        @Override
        public void unsubscribe() {
            innerSubscription.unsubscribe();
        }

        @Override
        public boolean isUnsubscribed() {
            return innerSubscription.isUnsubscribed();
        }

    }

    private static final class TimedAction implements Comparable<TimedAction> {
        final Action0 action;
        final Long execTime;
        final int count; // In case if time between enqueueing took less than 1ms

        private TimedAction(Action0 action, Long execTime, int count) {
            this.action = action;
            this.execTime = execTime;
            this.count = count;
        }

        @Override
        public int compareTo(TimedAction that) {
            int result = execTime.compareTo(that.execTime);
            if (result == 0) {
                return compare(count, that.count);
            }
            return result;
        }
    }

    // because I can't use Integer.compare from Java 7
    private static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

}

@@ -628,24 +628,43 @@
 va.util.
-Priority
+concurrent.PriorityBlocking
 Queue;%0Ai
@@ -1554,193 +1554,8 @@
  %7D%0A%0A
-    volatile int counter;%0A    static final AtomicIntegerFieldUpdater%3CTrampolineScheduler%3E COUNTER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(TrampolineScheduler.class, %22counter%22);%0A%0A
     
@@ -1652,24 +1652,32 @@
  %7B%0A%0A        
+private 
 final Priori
@@ -1670,32 +1670,40 @@
 e final Priority
+Blocking
 Queue%3CTimedActio
@@ -1725,16 +1725,24 @@
 Priority
+Blocking
 Queue%3CTi
@@ -1902,24 +1902,91 @@
 icInteger();
+%0A        private final AtomicInteger counter = new AtomicInteger();
 %0A%0A        @O
@@ -2635,23 +2635,15 @@
 me, 
-COUNTER_UPDATER
+counter
 .inc
@@ -2659,75 +2659,12 @@
 Get(
-TrampolineScheduler.this));%0A            synchronized (queue) %7B%0A    
+));%0A
     
@@ -2694,30 +2694,16 @@
 Action);
-%0A            %7D
 %0A%0A      
@@ -2783,16 +2783,22 @@
         
+final 
 TimedAct
@@ -2807,16 +2807,31 @@
 n polled
+ = queue.poll()
 ;%0A      
@@ -2848,33 +2848,30 @@
     
-synchronized (queue
+if (polled != null
 ) %7B%0A
-  
     
@@ -2890,35 +2890,34 @@
       polled
- = queue.po
+.action.ca
 ll();%0A      
@@ -2936,50 +2936,8 @@
   %7D%0A
-                    polled.action.call();%0A
     
@@ -3328,111 +3328,34 @@
     
-synchronized (queue) %7B%0A                            queue.remove(timedAction);%0A                        %7D
+queue.remove(timedAction);
 %0A   

Use a PBQ instead of a PQ on the trampoline. Moves counter inside to worker class and ditches the separate field updater.