1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
157 System.arraycopy(objects, head, newObjects, 0, tail);
158 tail -= head;
159 newObjects[tail++] = elem;
160 } else if (compareTimeouts(objects[head], elem) > 0) {
161
162 System.arraycopy(objects, head, newObjects, 1, tail);
163 newObjects[0] = elem;
164 tail -= (head - 1);
165 } else {
166
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
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
186 objects[tail++] = elem;
187 } else if (head > 0 && compareTimeouts(objects[head], elem) > 0) {
188
189 objects[--head] = elem;
190 } else {
191
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 }