/xbird-db/main/test/java/xbird/util/concurrent/collections/set/PhasedCuckooHashSet.java

http://xbird.googlecode.com/ · Java · 313 lines · 214 code · 17 blank · 82 comment · 53 complexity · 901bdae7486c7ce64e50e86a30a5e9de MD5 · raw file

  1. /*
  2. * PhasedCuckooHashSet.java
  3. *
  4. * Created on November 1, 2006, 3:22 PM
  5. *
  6. * From "The Art of Multiprocessor Programming",
  7. * by Maurice Herlihy and Nir Shavit.
  8. *
  9. * This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.
  10. * http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png
  11. */
  12. package xbird.util.concurrent.collections.set;
  13. import java.util.ArrayList;
  14. import java.util.List;
  15. /**
  16. * Phased concurrent cuckoo hash set.
  17. * * @param <T> type
  18. * @author Maurice Herlihy
  19. */
  20. public abstract class PhasedCuckooHashSet<T> {
  21. /**
  22. * Number of entries in the table. Should be about twice the number of items.
  23. */
  24. volatile int capacity;
  25. volatile List<T>[][] table;
  26. // resize when overflow reaches this size
  27. static final int PROBE_SIZE = 8;
  28. static final int THRESHOLD = PROBE_SIZE / 2;
  29. // resize when chain of phases exceeds this
  30. static final int LIMIT = 8;
  31. private final static long multiplier = 0x5DEECE66DL;
  32. private final static long addend = 0xBL;
  33. private final static long mask = (1L << 31) - 1;
  34. /**
  35. * Create new set holding at least this many entries.
  36. * @param size number of entries to expect
  37. */
  38. public PhasedCuckooHashSet(int size) {
  39. capacity = size;
  40. table = (List<T>[][]) new java.util.ArrayList[2][capacity];
  41. for(int i = 0; i < 2; i++) {
  42. for(int j = 0; j < capacity; j++) {
  43. table[i][j] = new ArrayList<T>(PROBE_SIZE);
  44. }
  45. }
  46. }
  47. /**
  48. * First hash function
  49. * @param x Item to hash.
  50. * @return non-negative hash value
  51. */
  52. final public int hash0(T x) {
  53. return (x.hashCode() & 0xffffff) % capacity;
  54. }
  55. /**
  56. * Second hash function
  57. * @param x item to hash
  58. * @return non-negative hash value
  59. */
  60. final public synchronized int hash1(T x) {
  61. return (int) Math.abs((x.hashCode() * multiplier + addend) & mask);
  62. }
  63. /**
  64. * Is item in the set?
  65. * @param x search for this item
  66. * @return true iff item is found
  67. */
  68. public boolean contains(T x) {
  69. acquire(x);
  70. try {
  71. int h0 = hash0(x) % capacity;
  72. if(table[0][h0].contains(x)) {
  73. return true;
  74. } else {
  75. int h1 = hash1(x) % capacity;
  76. if(table[1][h1].contains(x)) {
  77. return true;
  78. }
  79. }
  80. return false;
  81. } finally {
  82. release(x);
  83. }
  84. }
  85. /**
  86. * remove this entry from the set
  87. * @param x entry to remove
  88. * @return true iff entry found in set
  89. */
  90. public boolean remove(T x) {
  91. acquire(x);
  92. try {
  93. List<T> set0 = table[0][hash0(x) % capacity];
  94. if(set0.contains(x)) {
  95. set0.remove(x);
  96. return true;
  97. } else {
  98. List<T> set1 = table[1][hash1(x) % capacity];
  99. if(set1.contains(x)) {
  100. set1.remove(x);
  101. return true;
  102. }
  103. }
  104. return false;
  105. } finally {
  106. release(x);
  107. }
  108. }
  109. /**
  110. * Add an item to the set.
  111. * @return true iff entry was not preset
  112. * @param x entry to add
  113. */
  114. public boolean add(T x) {
  115. T y = null;
  116. acquire(x);
  117. int h0 = hash0(x) % capacity;
  118. int h1 = hash1(x) % capacity;
  119. int i = -1, h = -1;
  120. boolean mustResize = false;
  121. try {
  122. if(present(x)) {
  123. return false;
  124. }
  125. List<T> set0 = table[0][h0];
  126. List<T> set1 = table[1][h1];
  127. if(set0.size() < THRESHOLD) {
  128. set0.add(x);
  129. return true;
  130. } else if(set1.size() < THRESHOLD) {
  131. set1.add(x);
  132. return true;
  133. } else if(set0.size() < PROBE_SIZE) {
  134. set0.add(x);
  135. i = 0;
  136. h = h0;
  137. } else if(set1.size() < PROBE_SIZE) {
  138. set1.add(x);
  139. i = 1;
  140. h = h1;
  141. } else {
  142. mustResize = true;
  143. }
  144. } finally {
  145. release(x);
  146. }
  147. if(mustResize) {
  148. resize();
  149. add(x);
  150. } else if(!relocate(i, h)) {
  151. resize();
  152. }
  153. return true; // x must have been present
  154. }
  155. /**
  156. * Synchronize before adding, removing, or testing for item
  157. * @param x item involved
  158. */
  159. public abstract void acquire(T x);
  160. /**
  161. * synchronize after adding, removing, or testing for item
  162. * @param x item involved
  163. */
  164. public abstract void release(T x);
  165. /**
  166. * double the set size
  167. */
  168. public abstract void resize();
  169. /**
  170. * Repeatedly moves excess items to other probe sets.
  171. * @param i Probe set column (0 or 1)
  172. * @param hi Probe set row (hash0 or hash1)
  173. * @return <CODE>true</CODE> if all probe sets less than <CODE>THRESHOLD</CODE>,
  174. * <CODE>false</CODE> to trigger global resizing.
  175. */
  176. protected boolean relocate(int i, int hi) {
  177. int hj = 0;
  178. int j = 1 - i;
  179. for(int round = 0; round < LIMIT; round++) {
  180. List<T> iSet = table[i][hi];
  181. T y = iSet.get(0);
  182. switch(i) {
  183. case 0:
  184. hj = hash1(y) % capacity;
  185. break;
  186. case 1:
  187. hj = hash0(y) % capacity;
  188. break;
  189. }
  190. acquire(y);
  191. List<T> jSet = table[j][hj];
  192. try {
  193. if(iSet.remove(y)) {
  194. if(jSet.size() < THRESHOLD) {
  195. jSet.add(y);
  196. return true;
  197. } else if(jSet.size() < PROBE_SIZE) {
  198. jSet.add(y);
  199. i = 1 - i;
  200. hi = hj;
  201. j = 1 - j;
  202. } else {
  203. iSet.add(y);
  204. return false;
  205. }
  206. } else if(iSet.size() >= THRESHOLD) {
  207. continue;
  208. } else {
  209. return true;
  210. }
  211. } finally {
  212. release(y);
  213. }
  214. }
  215. return false;
  216. }
  217. /**
  218. * Simple sanity check for debugging
  219. * @return <CODE>true</CODE> iff structure passes tests
  220. */
  221. public boolean check() {
  222. for(int i = 0; i < capacity; i++) {
  223. List<T> set = table[0][i];
  224. for(T x : set) {
  225. if((hash0(x) % capacity) != i) {
  226. System.out.printf("Unexpected value %d at table[0][%d] hash %d\n", x, i, hash0(x)
  227. % capacity);
  228. return false;
  229. }
  230. }
  231. }
  232. for(int i = 0; i < capacity; i++) {
  233. List<T> set = table[1][i];
  234. for(T x : set) {
  235. if((hash1(x) % capacity) != i) {
  236. System.out.printf("Unexpected value %d at table[0][%d] hash %d\n", x, i, hash1(x)
  237. % capacity);
  238. return false;
  239. }
  240. }
  241. }
  242. return true;
  243. }
  244. /**
  245. * Unsynchronized version of contains()
  246. * @param x search for this item
  247. * @return true iff item is found
  248. */
  249. private boolean present(T x) {
  250. int h0 = hash0(x) % capacity;
  251. if(table[0][h0].contains(x)) {
  252. return true;
  253. } else {
  254. int h1 = hash1(x) % capacity;
  255. if(table[1][h1].contains(x)) {
  256. return true;
  257. }
  258. }
  259. return false;
  260. }
  261. /**
  262. * Simple sanity check for debugging
  263. * @param expectedSize expected size
  264. * @return <CODE>true</CODE> iff structure passes tests
  265. */
  266. public boolean check(int expectedSize) {
  267. int size = 0;
  268. for(int i = 0; i < capacity; i++) {
  269. for(T x : table[0][i]) {
  270. if(x != null) {
  271. size++;
  272. if((hash0(x) % capacity) != i) {
  273. System.out.printf("Unexpected value %d at table[0][%d] hash %d\n", x, i, hash0(x)
  274. % capacity);
  275. return false;
  276. }
  277. }
  278. }
  279. for(T x : table[1][i]) {
  280. if(x != null) {
  281. size++;
  282. if((hash1(x) % capacity) != i) {
  283. System.out.printf("Unexpected value %d at table[0][%d] hash %d\n", x, i, hash1(x)
  284. % capacity);
  285. return false;
  286. }
  287. }
  288. }
  289. }
  290. if(size != expectedSize) {
  291. System.out.printf("Bad size: found %d, expected %d\n", size, expectedSize);
  292. return false;
  293. }
  294. return true;
  295. }
  296. }