View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
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   * A completion service for the RpcRetryingCallerFactory.
32   * Keeps the list of the futures, and allows to cancel them all.
33   * This means as well that it can be used for a small set of tasks only.
34   * <br>Implementation is not Thread safe.
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; // all the 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 }