Advertisement
Guest User

Untitled

a guest
Oct 6th, 2015
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.09 KB | None | 0 0
  1. class StripedWriteMap<K,V> implements OurMap<K,V>
  2. {
  3.   // Synchronization policy: writing to
  4.   //   buckets[hash] is guarded by locks[hash % lockCount]
  5.   //   sizes[stripe] is guarded by locks[stripe]
  6.   // Visibility of writes to reads is ensured by writes writing to
  7.   // the stripe's size component (even if size does not change) and
  8.   // reads reading from the stripe's size component.
  9.   private volatile ItemNode<K,V>[] buckets;
  10.   private final int lockCount;
  11.   private final Object[] locks;
  12.   private final AtomicIntegerArray sizes;  
  13.  
  14.   public StripedWriteMap(int bucketCount, int lockCount) {
  15.     if (bucketCount % lockCount != 0)
  16.       throw new RuntimeException("bucket count must be a multiple of stripe count");
  17.     this.lockCount = lockCount;
  18.     this.buckets = makeBuckets(bucketCount);
  19.     this.locks = new Object[lockCount];
  20.     this.sizes = new AtomicIntegerArray(lockCount);
  21.     for (int stripe=0; stripe<lockCount; stripe++)
  22.       this.locks[stripe] = new Object();
  23.   }
  24.  
  25.   @SuppressWarnings("unchecked")
  26.   private static <K,V> ItemNode<K,V>[] makeBuckets(int size) {
  27.     // Java's @$#@?!! type system requires "unsafe" cast here:
  28.     return (ItemNode<K,V>[])new ItemNode[size];
  29.   }
  30.  
  31.   // Protect against poor hash functions and make non-negative
  32.   private static <K> int getHash(K k) {
  33.     final int kh = k.hashCode();
  34.     return (kh ^ (kh >>> 16)) & 0x7FFFFFFF;  
  35.   }
  36.  
  37.   // Return true if key k is in map, else false
  38.   public boolean containsKey(K k) {
  39.     final ItemNode<K,V>[] bs = buckets;
  40.     final int h = getHash(k), stripe = h % lockCount, hash = h % bs.length;
  41.     // The sizes access is necessary for visibility of bs elements
  42.     return sizes.get(stripe) != 0 && ItemNode.search(bs[hash], k, null);
  43.   }
  44.  
  45.   // Return value v associated with key k, or null
  46.   public V get(K k) {    
  47.     final int h = getHash(k), hash = h % buckets.length;
  48.     final Holder<V> old = new Holder<V>();
  49.     ItemNode<K,V>[] bs;
  50.     int afterSize;
  51.     bs = buckets;
  52.     final ItemNode<K,V> node = bs[hash];
  53.     boolean found = ItemNode.search(node, k,old);
  54.    
  55.     if (found)
  56.       return old.get();
  57.     else
  58.       return null;
  59.   }
  60.  
  61.   public int size() {
  62.     int rtnSize = 0;
  63.  
  64.     for (int stripe=0; stripe<lockCount; stripe++)
  65.       rtnSize += sizes.get(stripe);
  66.  
  67.     return rtnSize;
  68.   }
  69.  
  70.   // Put v at key k, or update if already present.  The logic here has
  71.   // become more contorted because we must not hold the stripe lock
  72.   // when calling reallocateBuckets, otherwise there will be deadlock
  73.   // when two threads working on different stripes try to reallocate
  74.   // at the same time.
  75.   public V put(K k, V v) {
  76.     final int h = getHash(k), stripe = h % lockCount;
  77.     final Holder<V> old = new Holder<V>();
  78.     ItemNode<K,V>[] bs;
  79.     int afterSize;
  80.     synchronized (locks[stripe]) {
  81.       bs = buckets;
  82.       final int hash = h % bs.length;
  83.       final ItemNode<K,V> node = bs[hash],
  84.         newNode = ItemNode.delete(node, k, old);
  85.       bs[hash] = new ItemNode<K,V>(k, v, newNode);
  86.       // Write for visibility; increment if k was not already in map
  87.       afterSize = sizes.addAndGet(stripe, newNode == node ? 1 : 0);
  88.     }
  89.     if (afterSize * lockCount > bs.length)
  90.       reallocateBuckets(bs);
  91.     return old.get();
  92.   }
  93.  
  94.   // Put v at key k only if absent.  
  95.   public V putIfAbsent(K k, V v) {
  96.     final int h = getHash(k), stripe = h % lockCount;
  97.     final Holder<V> old = new Holder<V>();
  98.     ItemNode<K,V>[] bs;
  99.     int afterSize;
  100.     bs = buckets;
  101.     final int hash = h % bs.length;
  102.                 synchronized (locks[stripe]) {
  103.     final ItemNode<K,V> node = bs[hash];
  104.     boolean found = ItemNode.search(node, k,old);
  105.    
  106.     if (found)
  107.       return node.v;
  108.     else {
  109.  
  110.       buckets[hash] = new ItemNode<K,V>(k, v, buckets[hash]);
  111.             sizes.addAndGet(stripe, 1);
  112.             }
  113.         }
  114.       reallocateBuckets(bs);
  115.       return old.get();
  116.   }
  117.  
  118.   // Remove and return the value at key k if any, else return null
  119.   public V remove(K k) {
  120. final int h = getHash(k), stripe = h % lockCount;
  121.     final Holder<V> old = new Holder<V>();
  122.     ItemNode<K,V>[] bs;
  123.     int afterSize;
  124.     bs = buckets;
  125.             synchronized (locks[stripe]) {
  126.     final int hash = h % bs.length;
  127.     final ItemNode<K,V> node = bs[hash];
  128.  
  129.             bs[hash] = ItemNode.delete(node, k,old);
  130.             sizes.addAndGet(stripe, -1);
  131.         }
  132.  
  133.         reallocateBuckets(bs);
  134.         return old.get();
  135.   }
  136.  
  137.   // Iterate over the hashmap's entries one stripe at a time.  
  138.   public void forEach(Consumer<K,V> consumer) {
  139.     for (int hash=0; hash<buckets.length; hash++) {
  140.       ItemNode<K,V> node = buckets[hash];
  141.       while (node != null) {
  142.         consumer.accept(node.k, node.v);
  143.         node = node.next;
  144.       }
  145.     }
  146.   }
  147.  
  148.   // Now that reallocation happens internally, do not do it externally
  149.   public void reallocateBuckets() { }
  150.  
  151.   // First lock all stripes.  Then double bucket table size, rehash,
  152.   // and redistribute entries.  Since the number of stripes does not
  153.   // change, and since buckets.length is a multiple of lockCount, a
  154.   // key that belongs to stripe s because (getHash(k) % N) %
  155.   // lockCount == s will continue to belong to stripe s.  Hence the
  156.   // sizes array need not be recomputed.  
  157.  
  158.   // In any case, do not reallocate if the buckets field was updated
  159.   // since the need for reallocation was discovered; this means that
  160.   // another thread has already reallocated.  This happens very often
  161.   // with 16 threads and a largish buckets table, size > 10,000.
  162.  
  163.   public void reallocateBuckets(final ItemNode<K,V>[] oldBuckets) {
  164.     lockAllAndThen(() -> {
  165.         final ItemNode<K,V>[] bs = buckets;
  166.         if (oldBuckets == bs) {
  167.           // System.out.printf("Reallocating from %d buckets%n", bs.length);
  168.           final ItemNode<K,V>[] newBuckets = makeBuckets(2 * bs.length);
  169.           for (int hash=0; hash<bs.length; hash++) {
  170.             ItemNode<K,V> node = bs[hash];
  171.             while (node != null) {
  172.               final int newHash = getHash(node.k) % newBuckets.length;
  173.               newBuckets[newHash]
  174.                 = new ItemNode<K,V>(node.k, node.v, newBuckets[newHash]);
  175.               node = node.next;
  176.             }
  177.           }
  178.           buckets = newBuckets; // Visibility: buckets field is volatile
  179.         }
  180.       });
  181.   }
  182.  
  183.   // Lock all stripes, perform action, then unlock all stripes
  184.   private void lockAllAndThen(Runnable action) {
  185.     lockAllAndThen(0, action);
  186.   }
  187.  
  188.   private void lockAllAndThen(int nextStripe, Runnable action) {
  189.     if (nextStripe >= lockCount)
  190.       action.run();
  191.     else
  192.       synchronized (locks[nextStripe]) {
  193.         lockAllAndThen(nextStripe + 1, action);
  194.       }
  195.   }
  196.  
  197.   static class ItemNode<K,V> {
  198.     private final K k;
  199.     private final V v;
  200.     private final ItemNode<K,V> next;
  201.    
  202.     public ItemNode(K k, V v, ItemNode<K,V> next) {
  203.       this.k = k;
  204.       this.v = v;
  205.       this.next = next;
  206.     }
  207.  
  208.     // These work on immutable data only, no synchronization needed.
  209.  
  210.     public static <K,V> boolean search(ItemNode<K,V> node, K k, Holder<V> old) {
  211.       while (node != null)
  212.         if (k.equals(node.k)) {
  213.           if (old != null)
  214.             old.set(node.v);
  215.           return true;
  216.         } else
  217.           node = node.next;
  218.       return false;
  219.     }
  220.    
  221.     public static <K,V> ItemNode<K,V> delete(ItemNode<K,V> node, K k, Holder<V> old) {
  222.       if (node == null)
  223.         return null;
  224.       else if (k.equals(node.k)) {
  225.         old.set(node.v);
  226.         return node.next;
  227.       } else {
  228.         final ItemNode<K,V> newNode = delete(node.next, k, old);
  229.         if (newNode == node.next)
  230.           return node;
  231.         else
  232.           return new ItemNode<K,V>(node.k, node.v, newNode);
  233.       }
  234.     }
  235.   }
  236.  
  237.   // Object to hold a "by reference" parameter.  For use only on a
  238.   // single thread, so no need for "volatile" or synchronization.
  239.  
  240.   static class Holder<V> {
  241.     private V value;
  242.     public V get() {
  243.       return value;
  244.     }
  245.     public void set(V value) {
  246.       this.value = value;
  247.     }
  248.   }
  249. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement