1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import java.util.LinkedList;
22
23 import org.apache.hadoop.classification.InterfaceAudience;
24 import org.apache.hadoop.hbase.util.Bytes;
25 import org.apache.hadoop.hbase.util.ClassSize;
26
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.commons.logging.Log;
29
30
31
32
33
34
35
36 @InterfaceAudience.Private
37 public class MultiVersionConsistencyControl {
38 private volatile long memstoreRead = 0;
39 private volatile long memstoreWrite = 0;
40
41 private final Object readWaiters = new Object();
42
43
44 private final LinkedList<WriteEntry> writeQueue =
45 new LinkedList<WriteEntry>();
46
47 private static final ThreadLocal<Long> perThreadReadPoint =
48 new ThreadLocal<Long>() {
49 @Override
50 protected
51 Long initialValue() {
52 return Long.MAX_VALUE;
53 }
54 };
55
56
57
58
59 public MultiVersionConsistencyControl() {
60 this.memstoreRead = this.memstoreWrite = 0;
61 }
62
63
64
65
66
67 public void initialize(long startPoint) {
68 synchronized (writeQueue) {
69 if (this.memstoreWrite != this.memstoreRead) {
70 throw new RuntimeException("Already used this mvcc. Too late to initialize");
71 }
72
73 this.memstoreRead = this.memstoreWrite = startPoint;
74 }
75 }
76
77
78
79
80
81
82 public static long getThreadReadPoint() {
83 return perThreadReadPoint.get();
84 }
85
86
87
88
89
90
91 public static void setThreadReadPoint(long readPoint) {
92 perThreadReadPoint.set(readPoint);
93 }
94
95
96
97
98
99 public static long resetThreadReadPoint(MultiVersionConsistencyControl mvcc) {
100 perThreadReadPoint.set(mvcc.memstoreReadPoint());
101 return getThreadReadPoint();
102 }
103
104
105
106
107 public static void resetThreadReadPoint() {
108 perThreadReadPoint.set(0L);
109 }
110
111
112
113
114
115
116 public WriteEntry beginMemstoreInsert() {
117 synchronized (writeQueue) {
118 long nextWriteNumber = ++memstoreWrite;
119 WriteEntry e = new WriteEntry(nextWriteNumber);
120 writeQueue.add(e);
121 return e;
122 }
123 }
124
125
126
127
128
129
130
131 public void completeMemstoreInsert(WriteEntry e) {
132 advanceMemstore(e);
133 waitForRead(e);
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147 boolean advanceMemstore(WriteEntry e) {
148 synchronized (writeQueue) {
149 e.markCompleted();
150
151 long nextReadValue = -1;
152 boolean ranOnce=false;
153 while (!writeQueue.isEmpty()) {
154 ranOnce=true;
155 WriteEntry queueFirst = writeQueue.getFirst();
156
157 if (nextReadValue > 0) {
158 if (nextReadValue+1 != queueFirst.getWriteNumber()) {
159 throw new RuntimeException("invariant in completeMemstoreInsert violated, prev: "
160 + nextReadValue + " next: " + queueFirst.getWriteNumber());
161 }
162 }
163
164 if (queueFirst.isCompleted()) {
165 nextReadValue = queueFirst.getWriteNumber();
166 writeQueue.removeFirst();
167 } else {
168 break;
169 }
170 }
171
172 if (!ranOnce) {
173 throw new RuntimeException("never was a first");
174 }
175
176 if (nextReadValue > 0) {
177 synchronized (readWaiters) {
178 memstoreRead = nextReadValue;
179 readWaiters.notifyAll();
180 }
181 }
182 if (memstoreRead >= e.getWriteNumber()) {
183 return true;
184 }
185 return false;
186 }
187 }
188
189
190
191
192
193 public void waitForRead(WriteEntry e) {
194 boolean interrupted = false;
195 synchronized (readWaiters) {
196 while (memstoreRead < e.getWriteNumber()) {
197 try {
198 readWaiters.wait(0);
199 } catch (InterruptedException ie) {
200
201
202 interrupted = true;
203 }
204 }
205 }
206 if (interrupted) Thread.currentThread().interrupt();
207 }
208
209 public long memstoreReadPoint() {
210 return memstoreRead;
211 }
212
213
214 public static class WriteEntry {
215 private long writeNumber;
216 private boolean completed = false;
217 WriteEntry(long writeNumber) {
218 this.writeNumber = writeNumber;
219 }
220 void markCompleted() {
221 this.completed = true;
222 }
223 boolean isCompleted() {
224 return this.completed;
225 }
226 long getWriteNumber() {
227 return this.writeNumber;
228 }
229 }
230
231 public static final long FIXED_SIZE = ClassSize.align(
232 ClassSize.OBJECT +
233 2 * Bytes.SIZEOF_LONG +
234 2 * ClassSize.REFERENCE);
235
236 }