1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Executor;
23 import java.util.concurrent.RunnableFuture;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.TimeoutException;
26
27 import org.apache.hadoop.hbase.classification.InterfaceAudience;
28 import org.apache.htrace.Trace;
29
30
31
32
33
34
35
36 @InterfaceAudience.Private
37 public class ResultBoundedCompletionService<V> {
38 private final RpcRetryingCallerFactory retryingCallerFactory;
39 private final Executor executor;
40 private final QueueingFuture<V>[] tasks;
41 private volatile QueueingFuture<V> completed = null;
42
43 class QueueingFuture<T> implements RunnableFuture<T> {
44 private final RetryingCallable<T> future;
45 private T result = null;
46 private ExecutionException exeEx = null;
47 private volatile boolean cancelled;
48 private final int callTimeout;
49 private final RpcRetryingCaller<T> retryingCaller;
50 private boolean resultObtained = false;
51
52
53 public QueueingFuture(RetryingCallable<T> future, int callTimeout) {
54 this.future = future;
55 this.callTimeout = callTimeout;
56 this.retryingCaller = retryingCallerFactory.<T>newCaller();
57 }
58
59 @SuppressWarnings("unchecked")
60 @Override
61 public void run() {
62 try {
63 if (!cancelled) {
64 result =
65 this.retryingCaller.callWithRetries(future, callTimeout);
66 resultObtained = true;
67 }
68 } catch (Throwable t) {
69 exeEx = new ExecutionException(t);
70 } finally {
71 if (!cancelled && completed == null) {
72 completed = (QueueingFuture<V>) QueueingFuture.this;
73 synchronized (tasks) {
74 tasks.notify();
75 }
76 }
77 }
78 }
79 @Override
80 public boolean cancel(boolean mayInterruptIfRunning) {
81 if (resultObtained || exeEx != null) return false;
82 retryingCaller.cancel();
83 if (future instanceof Cancellable) ((Cancellable)future).cancel();
84 cancelled = true;
85 return true;
86 }
87
88 @Override
89 public boolean isCancelled() {
90 return cancelled;
91 }
92
93 @Override
94 public boolean isDone() {
95 return resultObtained || exeEx != null;
96 }
97
98 @Override
99 public T get() throws InterruptedException, ExecutionException {
100 try {
101 return get(1000, TimeUnit.DAYS);
102 } catch (TimeoutException e) {
103 throw new RuntimeException("You did wait for 1000 days here?", e);
104 }
105 }
106
107 @Override
108 public T get(long timeout, TimeUnit unit)
109 throws InterruptedException, ExecutionException, TimeoutException {
110 synchronized (tasks) {
111 if (resultObtained) {
112 return result;
113 }
114 if (exeEx != null) {
115 throw exeEx;
116 }
117 unit.timedWait(tasks, timeout);
118 }
119 if (resultObtained) {
120 return result;
121 }
122 if (exeEx != null) {
123 throw exeEx;
124 }
125
126 throw new TimeoutException("timeout=" + timeout + ", " + unit);
127 }
128 }
129
130 @SuppressWarnings("unchecked")
131 public ResultBoundedCompletionService(
132 RpcRetryingCallerFactory retryingCallerFactory, Executor executor,
133 int maxTasks) {
134 this.retryingCallerFactory = retryingCallerFactory;
135 this.executor = executor;
136 this.tasks = new QueueingFuture[maxTasks];
137 }
138
139
140 public void submit(RetryingCallable<V> task, int callTimeout, int id) {
141 QueueingFuture<V> newFuture = new QueueingFuture<V>(task, callTimeout);
142 executor.execute(Trace.wrap(newFuture));
143 tasks[id] = newFuture;
144 }
145
146 public QueueingFuture<V> take() throws InterruptedException {
147 synchronized (tasks) {
148 while (completed == null) tasks.wait();
149 }
150 return completed;
151 }
152
153 public QueueingFuture<V> poll(long timeout, TimeUnit unit) throws InterruptedException {
154 synchronized (tasks) {
155 if (completed == null) unit.timedWait(tasks, timeout);
156 }
157 return completed;
158 }
159
160 public void cancelAll() {
161 for (QueueingFuture<V> future : tasks) {
162 if (future != null) future.cancel(true);
163 }
164 }
165 }