1 /* 2 * Copyright (C) 2017 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 package com.android.google.gce.gceservice; 17 18 import android.util.Log; 19 import java.util.ArrayList; 20 import java.util.concurrent.CancellationException; 21 import java.util.concurrent.ExecutionException; 22 import java.util.concurrent.Future; 23 import java.util.concurrent.TimeoutException; 24 import java.util.concurrent.TimeUnit; 25 26 public class GceFuture<T> implements Future<T> { 27 private static final String LOG_TAG = "GceFuture"; 28 private boolean mDone = false; 29 private Exception mException = null; 30 private T mResult = null; 31 private final String mName; 32 33 34 public GceFuture(String name) { 35 mName = name; 36 } 37 38 39 public String getName() { 40 return mName; 41 } 42 43 44 public void set(T value) { 45 synchronized(this) { 46 if (mDone) { 47 Exception e = new Exception(); 48 Log.e(LOG_TAG, mName + ": Multiple return values from a future object.", e); 49 return; 50 } 51 52 mResult = value; 53 mDone = true; 54 notifyAll(); 55 } 56 } 57 58 59 public void set(Exception e) { 60 synchronized(this) { 61 if (mDone) { 62 Log.w(LOG_TAG, mName + ": Discarding execution exception -- job done.", e); 63 return; 64 } 65 66 Log.w(LOG_TAG, mName + ": Could not complete job: " + e.getMessage(), e); 67 mException = e; 68 mDone = true; 69 notifyAll(); 70 } 71 } 72 73 74 @Override 75 public boolean cancel(boolean canInterrupt) { 76 // We do not support interrupting jobs on purpose: 77 // this offers us little benefit (stripping maybe a second or two), at the expense 78 // of killing something that may cascade, like BroadcastReceiver. 79 synchronized(this) { 80 if (mDone) return false; 81 set(new CancellationException("cancelled")); 82 } 83 84 return true; 85 } 86 87 88 @Override 89 public boolean isCancelled() { 90 synchronized(this) { 91 return (mException != null) && (mException instanceof CancellationException); 92 } 93 } 94 95 96 @Override 97 public boolean isDone() { 98 synchronized(this) { 99 return mDone; 100 } 101 } 102 103 104 @Override 105 public T get() throws CancellationException, ExecutionException, InterruptedException { 106 try { 107 return get(-1, TimeUnit.SECONDS); 108 } catch (TimeoutException e) { 109 // This is a really interesting case to consider. 110 // Fatal error to add to the drama. 111 Log.wtf(LOG_TAG, mName + ": Unexpected condition: Infinite wait timed out."); 112 return null; 113 } 114 } 115 116 117 @Override 118 public T get(long timeout, TimeUnit units) 119 throws CancellationException, ExecutionException, InterruptedException, TimeoutException { 120 waitDone(timeout, units); 121 122 if (mException != null) { 123 if (mException instanceof CancellationException) 124 throw (CancellationException)mException; 125 throw new ExecutionException(mException); 126 } 127 128 return mResult; 129 } 130 131 132 /** Wait for final result. 133 * 134 * Result is considered available, when: 135 * - provider returned value, 136 * - this object was cancelled, 137 * - provider threw an exception. 138 */ 139 private void waitDone(long timeout, TimeUnit units) 140 throws InterruptedException, TimeoutException { 141 while (!mDone) { 142 synchronized(this) { 143 if (timeout >= 0) { 144 this.wait(units.toMillis(timeout)); 145 if (!mDone) throw new InterruptedException(); 146 } else { 147 this.wait(); 148 } 149 } 150 } 151 } 152 153 154 /** Convert list of GceFuture objects to string representation. 155 */ 156 public static String toString(ArrayList<GceFuture<?>> futures) { 157 StringBuilder b = new StringBuilder(); 158 for (GceFuture<?> dep : futures) { 159 if (b.length() > 0) b.append(", "); 160 b.append(dep.getName()); 161 } 162 163 return b.toString(); 164 } 165 } 166