1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.art.testing; 18 19 import android.annotation.NonNull; 20 import android.util.Pair; 21 22 import java.util.ArrayList; 23 import java.util.Comparator; 24 import java.util.List; 25 import java.util.PriorityQueue; 26 import java.util.concurrent.RunnableScheduledFuture; 27 import java.util.concurrent.ScheduledFuture; 28 import java.util.concurrent.ScheduledThreadPoolExecutor; 29 import java.util.concurrent.TimeUnit; 30 31 public class MockClock { 32 private long mCurrentTimeMs = 0; 33 @NonNull private List<ScheduledExecutor> mExecutors = new ArrayList<>(); 34 35 @NonNull createScheduledExecutor()36 public ScheduledExecutor createScheduledExecutor() { 37 var executor = new ScheduledExecutor(); 38 mExecutors.add(executor); 39 return executor; 40 } 41 getCurrentTimeMs()42 public long getCurrentTimeMs() { 43 return mCurrentTimeMs; 44 } 45 advanceTime(long timeMs)46 public void advanceTime(long timeMs) { 47 mCurrentTimeMs += timeMs; 48 for (ScheduledExecutor executor : mExecutors) { 49 executor.notifyUpdate(); 50 } 51 } 52 53 @NonNull getCreatedExecutors()54 public List<ScheduledExecutor> getCreatedExecutors() { 55 return mExecutors; 56 } 57 58 public class ScheduledExecutor extends ScheduledThreadPoolExecutor { 59 // The second element of the pair is the scheduled time. 60 @NonNull 61 private PriorityQueue<Pair<RunnableScheduledFuture<?>, Long>> tasks = new PriorityQueue<>( 62 1 /* initialCapacity */, Comparator.comparingLong(pair -> pair.second)); 63 ScheduledExecutor()64 public ScheduledExecutor() { 65 super(1 /* corePoolSize */); 66 } 67 68 @NonNull schedule( @onNull Runnable command, long delay, @NonNull TimeUnit unit)69 public ScheduledFuture<?> schedule( 70 @NonNull Runnable command, long delay, @NonNull TimeUnit unit) { 71 // Use `Long.MAX_VALUE` to prevent the task from being automatically run. 72 var task = (RunnableScheduledFuture<?>) super.schedule( 73 command, Long.MAX_VALUE, TimeUnit.MILLISECONDS); 74 tasks.add(Pair.create(task, getCurrentTimeMs() + unit.toMillis(delay))); 75 return task; 76 } 77 notifyUpdate()78 public void notifyUpdate() { 79 while (!tasks.isEmpty()) { 80 Pair<RunnableScheduledFuture<?>, Long> pair = tasks.peek(); 81 RunnableScheduledFuture<?> task = pair.first; 82 long scheduledTimeMs = pair.second; 83 if (getCurrentTimeMs() >= scheduledTimeMs || task.isCancelled()) { 84 if (!task.isDone() && !task.isCancelled()) { 85 task.run(); 86 } 87 tasks.poll(); 88 // Remove the task from the queue of the executor. Terminate the executor if 89 // it's shutdown and the queue is empty. 90 super.remove(task); 91 } else { 92 break; 93 } 94 } 95 } 96 } 97 } 98