108bd32ce48b12ae751dd5c4829ff09a6fb9894f0Philip P. Moltmann/*
208bd32ce48b12ae751dd5c4829ff09a6fb9894f0Philip P. Moltmann * Copyright (c) 2017 Mockito contributors
308bd32ce48b12ae751dd5c4829ff09a6fb9894f0Philip P. Moltmann * This program is made available under the terms of the MIT License.
408bd32ce48b12ae751dd5c4829ff09a6fb9894f0Philip P. Moltmann */
52637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinpackage org.mockitousage.verification;
62637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
72637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport static java.lang.System.currentTimeMillis;
82637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport static java.lang.Thread.MAX_PRIORITY;
92637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport static java.util.concurrent.Executors.newScheduledThreadPool;
102637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport static java.util.concurrent.TimeUnit.SECONDS;
112637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport static java.util.concurrent.locks.LockSupport.parkUntil;
122637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
132637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport java.util.concurrent.ScheduledExecutorService;
142637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport java.util.concurrent.ThreadFactory;
152637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinimport java.util.concurrent.TimeUnit;
162637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
172637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffinclass DelayedExecution {
182637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    private static final int CORE_POOL_SIZE = 3;
192637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    /**
202637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin     * Defines the number of milliseconds we expecting a Thread might need to unpark, we use this to avoid "oversleeping" while awaiting the deadline for
212637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin     */
222637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    private static final long MAX_EXPECTED_OVERSLEEP_MILLIS = 50;
232637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
242637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    private final ScheduledExecutorService executor;
252637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
262637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    public DelayedExecution() {
272637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        this.executor = newScheduledThreadPool(CORE_POOL_SIZE, maxPrioThreadFactory());
282637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    }
292637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
302637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    public void callAsync(long delay, TimeUnit timeUnit, Runnable r) {
312637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        long deadline = timeUnit.toMillis(delay) + currentTimeMillis();
322637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
332637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        executor.submit(delayedExecution(r, deadline));
342637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    }
352637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
362637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    public void close() throws InterruptedException {
372637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        executor.shutdownNow();
382637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
392637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        if (!executor.awaitTermination(5, SECONDS)) {
4008bd32ce48b12ae751dd5c4829ff09a6fb9894f0Philip P. Moltmann            throw new IllegalStateException("This delayed execution did not terminated after 5 seconds");
412637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        }
422637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    }
432637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
442637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    private static Runnable delayedExecution(final Runnable r, final long deadline) {
452637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        return new Runnable() {
462637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            @Override
472637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            public void run() {
482637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                //we park the current Thread till 50ms before we want to execute the runnable
492637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                parkUntil(deadline - MAX_EXPECTED_OVERSLEEP_MILLIS);
502637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                //now we closing to the deadline by burning CPU-time in a loop
512637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                burnRemaining(deadline);
522637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
532637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                System.out.println("[DelayedExecution] exec delay = "+(currentTimeMillis() - deadline)+"ms");
542637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
552637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                r.run();
562637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            }
572637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
582637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            /**
592637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin             * Loop in tight cycles until we reach the dead line. We do this cause sleep or park is very not precise,
602637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin             * this can causes a Thread to under- or oversleep, sometimes by +50ms.
612637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin             */
622637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            private void burnRemaining(final long deadline) {
632637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                long remaining;
642637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                do {
652637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                    remaining = deadline - currentTimeMillis();
662637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                } while (remaining > 0);
672637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            }
682637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        };
692637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    }
702637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin
712637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    private static ThreadFactory maxPrioThreadFactory() {
722637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        return new ThreadFactory() {
732637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            @Override
742637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            public Thread newThread(Runnable r) {
752637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                Thread t = new Thread(r);
762637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                t.setDaemon(true);  // allows the JVM to exit when clients forget to call DelayedExecution.close()
772637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                t.setPriority(MAX_PRIORITY);
782637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin                return t;
792637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin            }
802637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin        };
812637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin    }
822637d96c202372854a7c71466ddcc6e90fc4fc53Paul Duffin}
83