View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.procedure2.util;
20  
21  import java.util.concurrent.locks.Condition;
22  import java.util.concurrent.locks.ReentrantLock;
23  import java.util.concurrent.TimeUnit;
24  
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.classification.InterfaceStability;
27  
28  @InterfaceAudience.Private
29  @InterfaceStability.Evolving
30  public class TimeoutBlockingQueue<E> {
31    public static interface TimeoutRetriever<T> {
32      long getTimeout(T object);
33      TimeUnit getTimeUnit(T object);
34    }
35  
36    private final ReentrantLock lock = new ReentrantLock();
37    private final Condition waitCond = lock.newCondition();
38    private final TimeoutRetriever<? super E> timeoutRetriever;
39  
40    private E[] objects;
41    private int head = 0;
42    private int tail = 0;
43  
44    public TimeoutBlockingQueue(TimeoutRetriever<? super E> timeoutRetriever) {
45      this(32, timeoutRetriever);
46    }
47  
48    @SuppressWarnings("unchecked")
49    public TimeoutBlockingQueue(int capacity, TimeoutRetriever<? super E> timeoutRetriever) {
50      this.objects = (E[])new Object[capacity];
51      this.timeoutRetriever = timeoutRetriever;
52    }
53  
54    public void dump() {
55      for (int i = 0; i < objects.length; ++i) {
56        if (i == head) {
57          System.out.print("[" + objects[i] + "] ");
58        } else if (i == tail) {
59          System.out.print("]" + objects[i] + "[ ");
60        } else {
61          System.out.print(objects[i] + " ");
62        }
63      }
64      System.out.println();
65    }
66  
67    public void clear() {
68      lock.lock();
69      try {
70        if (head != tail) {
71          for (int i = head; i < tail; ++i) {
72            objects[i] = null;
73          }
74          head = 0;
75          tail = 0;
76          waitCond.signal();
77        }
78      } finally {
79        lock.unlock();
80      }
81    }
82  
83    public void add(E e) {
84      if (e == null) throw new NullPointerException();
85  
86      lock.lock();
87      try {
88        addElement(e);
89        waitCond.signal();
90      } finally {
91        lock.unlock();
92      }
93    }
94  
95    public boolean remove(E e) {
96      if (e == null) return false;
97      lock.lock();
98      try {
99        for (int i = 0; i < objects.length; ++i) {
100         if (e.equals(objects[i])) {
101           objects[i] = null;
102           return true;
103         }
104       }
105       return false;
106     } finally {
107       lock.unlock();
108     }
109   }
110 
111   @edu.umd.cs.findbugs.annotations.SuppressWarnings("WA_AWAIT_NOT_IN_LOOP")
112   public E poll() {
113     lock.lock();
114     try {
115       if (isEmpty()) {
116         waitCond.await();
117         return null;
118       }
119 
120       E elem = objects[head];
121       long nanos = getNanosTimeout(elem);
122       nanos = waitCond.awaitNanos(nanos);
123       return nanos > 0 ? null : removeFirst();
124     } catch (InterruptedException e) {
125       Thread.currentThread().interrupt();
126       return null;
127     } finally {
128       lock.unlock();
129     }
130   }
131 
132   public int size() {
133     return tail - head;
134   }
135 
136   public boolean isEmpty() {
137     return (tail - head) == 0;
138   }
139 
140   public void signalAll() {
141     lock.lock();
142     try {
143       waitCond.signalAll();
144     } finally {
145       lock.unlock();
146     }
147   }
148 
149   private void addElement(E elem) {
150     int size = (tail - head);
151     if ((objects.length - size) == 0) {
152       int capacity = size + ((size < 64) ? (size + 2) : (size >> 1));
153       E[] newObjects = (E[])new Object[capacity];
154 
155       if (compareTimeouts(objects[tail - 1], elem) <= 0) {
156         // Append
157         System.arraycopy(objects, head, newObjects, 0, tail);
158         tail -= head;
159         newObjects[tail++] = elem;
160       } else if (compareTimeouts(objects[head], elem) > 0) {
161         // Prepend
162         System.arraycopy(objects, head, newObjects, 1, tail);
163         newObjects[0] = elem;
164         tail -= (head - 1);
165       } else {
166         // Insert in the middle
167         int index = upperBound(head, tail - 1, elem);
168         int newIndex = (index - head);
169         System.arraycopy(objects, head, newObjects, 0, newIndex);
170         newObjects[newIndex] = elem;
171         System.arraycopy(objects, index, newObjects, newIndex + 1, tail - index);
172         tail -= (head - 1);
173       }
174       head = 0;
175       objects = newObjects;
176     } else {
177       if (tail == objects.length) {
178         // shift down |-----AAAAAAA|
179         tail -= head;
180         System.arraycopy(objects, head, objects, 0, tail);
181         head = 0;
182       }
183 
184       if (tail == head || compareTimeouts(objects[tail - 1], elem) <= 0) {
185         // Append
186         objects[tail++] = elem;
187       } else if (head > 0 && compareTimeouts(objects[head], elem) > 0) {
188         // Prepend
189         objects[--head] = elem;
190       } else {
191         // Insert in the middle
192         int index = upperBound(head, tail - 1, elem);
193         System.arraycopy(objects, index, objects, index + 1, tail - index);
194         objects[index] = elem;
195         tail++;
196       }
197     }
198   }
199 
200   private E removeFirst() {
201     E elem = objects[head];
202     objects[head] = null;
203     head = (head + 1) % objects.length;
204     if (head == 0) tail = 0;
205     return elem;
206   }
207 
208   private int upperBound(int start, int end, E key) {
209     while (start < end) {
210       int mid = (start + end) >>> 1;
211       E mitem = objects[mid];
212       int cmp = compareTimeouts(mitem, key);
213       if (cmp > 0) {
214         end = mid;
215       } else {
216         start = mid + 1;
217       }
218     }
219     return start;
220   }
221 
222   private int compareTimeouts(final E a, final E b) {
223     long t1 = getNanosTimeout(a);
224     long t2 = getNanosTimeout(b);
225     return (t1 < t2) ? -1 : (t1 > t2) ? 1 : 0;
226   }
227 
228   private long getNanosTimeout(final E obj) {
229     if (obj == null) return 0;
230     TimeUnit unit = timeoutRetriever.getTimeUnit(obj);
231     long timeout = timeoutRetriever.getTimeout(obj);
232     return unit.toNanos(timeout);
233   }
234 }