All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.concurrent.ConcurrentTLongObjectHashMap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.internal.concurrent;

import java.util.AbstractCollection;
import java.util.AbstractMap.SimpleEntry;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import com.gemstone.gemfire.internal.size.SingleObjectSizer;
import com.gemstone.gnu.trove.*;

/**
 * A concurrent version of Trove's TLongObjectHashMap.
 *
 * @author swale
 * @since gfxd 1.0
 *
 * @param 
 *          the type of mapped values
 */
@SuppressWarnings("serial")
public final class ConcurrentTLongObjectHashMap extends THashParameters
    implements Map {

  public static final int DEFAULT_CONCURRENCY = 16;

  private final ConcurrentTLongObjectHashSegment[] segments;
  private final AtomicLong totalSize;

  private transient Set keySet;
  private transient Collection values;
  private transient Set> entrySet;

  public ConcurrentTLongObjectHashMap() {
    this(DEFAULT_CONCURRENCY, THash.DEFAULT_INITIAL_CAPACITY,
        THash.DEFAULT_LOAD_FACTOR, null);
  }

  public ConcurrentTLongObjectHashMap(int concurrency) {
    this(concurrency, THash.DEFAULT_INITIAL_CAPACITY,
        THash.DEFAULT_LOAD_FACTOR, null);
  }

  public ConcurrentTLongObjectHashMap(int concurrency, int initialCapacity) {
    this(concurrency, initialCapacity, THash.DEFAULT_LOAD_FACTOR, null);
  }

  @SuppressWarnings("unchecked")
  public ConcurrentTLongObjectHashMap(int concurrency, int initialCapacity,
      float loadFactor, HashingStats stats) {
    super(loadFactor, null, stats);

    if (concurrency <= 0 || !(loadFactor > 0) || initialCapacity < 0) {
      throw new IllegalArgumentException();
    }

    if (concurrency > 1) {
      concurrency = PrimeFinder.nextPrime(concurrency);
    }
    int segSize = initialCapacity / concurrency;
    if (segSize < 2) {
      segSize = 2;
    }
    this.totalSize = new AtomicLong(0);
    this.segments = new ConcurrentTLongObjectHashSegment[concurrency];
    for (int index = 0; index < concurrency; index++) {
      this.segments[index] = new ConcurrentTLongObjectHashSegment(segSize,
          this, this.totalSize);
    }
  }

  /**
   * {@inheritDoc}
   */
  @SuppressWarnings("unchecked")
  @Override
  public T get(Object key) {
    if (key != null) {
      long k = (Long)key;
      final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(k);
      return (T)segmentFor(hash).get(k, hash);
    }
    else {
      throw new NullPointerException("null key");
    }
  }

  @SuppressWarnings("unchecked")
  public T getPrimitive(long key) {
    final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(key);
    return (T)segmentFor(hash).get(key, hash);
  }

  /**
   * {@inheritDoc}
   */
  @SuppressWarnings("unchecked")
  @Override
  public T put(Long key, T value) {
    if (key != null) {
      long k = key;
      final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(k);
      return (T)segmentFor(hash).put(k, value, hash);
    }
    else {
      throw new NullPointerException("null key");
    }
  }

  /**
   * Like {@link #put(Long, Object)} but takes a primitive long as key to avoid
   * creating a Long object.
   *
   * @see #put(Long, Object)
   */
  public Object putPrimitive(long key, T value) {
    final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(key);
    return segmentFor(hash).put(key, value, hash);
  }

  /**
   * If the specified key is not already associated with a value, associate it
   * with the given value. This is equivalent to
   *
   * 
   * if (!map.containsKey(key))
   *   return map.put(key, value);
   * else
   *   return map.get(key);
   * 
* * except that the action is performed atomically. * * @param key * long key with which the specified value is to be associated * @param value * value to be associated with the specified key * * @return the previous value associated with the specified key, or * null if there was no mapping for the key. (A null * return can also indicate that the map previously associated * null with the key) * * @throws NullPointerException * if the specified key is null */ public Object putIfAbsent(long key, T value) { final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(key); return segmentFor(hash).putIfAbsent(key, value, hash); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public T remove(Object key) { if (key != null) { long k = (Long)key; final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(k); return (T)segmentFor(hash).remove(k, hash); } else { throw new NullPointerException("null key"); } } public Object removePrimitive(long key) { final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(key); return segmentFor(hash).remove(key, hash); } /** * {@inheritDoc} */ @Override public void putAll(Map m) { if (m != null) { Long key; T value; for (Map.Entry e : m.entrySet()) { key = e.getKey(); value = e.getValue(); if (key != null) { putPrimitive(key, value); } else { throw new NullPointerException("null key"); } } } else { throw new NullPointerException("null collection"); } } public final long longSize() { return this.totalSize.get(); } /** * {@inheritDoc} */ @Override public int size() { final long size = this.totalSize.get(); return size < Integer.MAX_VALUE ? (int)size : Integer.MAX_VALUE; } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return this.totalSize.get() == 0; } /** * {@inheritDoc} */ @Override public boolean containsKey(Object key) { if (key != null) { long k = (Long)key; final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(k); return segmentFor(hash).contains(k, hash); } else { throw new NullPointerException("null key"); } } public boolean containsKeyPrimitive(long key) { final int hash = ConcurrentTLongObjectHashSegment.computeHashCode(key); return segmentFor(hash).contains(key, hash); } /** * {@inheritDoc} */ @Override public boolean containsValue(Object value) { for (ConcurrentTLongObjectHashSegment seg : this.segments) { if (seg.containsValue(value)) { return true; } } return false; } /** * {@inheritDoc} */ @Override public void clear() { for (ConcurrentTLongObjectHashSegment seg : this.segments) { seg.clear(); } } /** * {@inheritDoc} */ @Override public Set keySet() { if (this.keySet == null) { this.keySet = new AbstractSet() { @Override public Iterator iterator() { return new HashIterator() { @Override public Long next() { return nextKey(); } }; } @Override public boolean contains(Object k) { return ConcurrentTLongObjectHashMap.this.containsKey(k); } @Override public boolean remove(Object k) { return ConcurrentTLongObjectHashMap.this.remove(k) != null; } @Override public int size() { return ConcurrentTLongObjectHashMap.this.size(); } @Override public boolean isEmpty() { return ConcurrentTLongObjectHashMap.this.isEmpty(); } @Override public void clear() { ConcurrentTLongObjectHashMap.this.clear(); } }; } return this.keySet; } /** * returns the keys of the map. * * @return a Set value */ public long[] keys() { long[] resultKeys; acquireAllLocks(false); try { int size = 0; for (ConcurrentTLongObjectHashSegment seg : this.segments) { size += seg.size; } resultKeys = new long[size]; int index = 0; for (ConcurrentTLongObjectHashSegment seg : this.segments) { long[] keys = seg.keys; Object[] vals = seg.values; Object val; int sz = keys.length; for (int i = 0; i < sz; i++) { val = vals[i]; if (val != null && val != ConcurrentTLongObjectHashSegment.REMOVED) { resultKeys[index++] = keys[i]; } } } } finally { releaseAllLocks(false); } return resultKeys; } /** * {@inheritDoc} */ @Override public Collection values() { if (this.values == null) { this.values = new AbstractCollection() { @Override public Iterator iterator() { return new HashIterator() { @Override public T next() { return nextValue(); } }; } @Override public boolean contains(Object v) { return ConcurrentTLongObjectHashMap.this.containsValue(v); } @Override public int size() { return ConcurrentTLongObjectHashMap.this.size(); } @Override public boolean isEmpty() { return ConcurrentTLongObjectHashMap.this.isEmpty(); } @Override public void clear() { ConcurrentTLongObjectHashMap.this.clear(); } }; } return this.values; } /** * {@inheritDoc} */ @Override public Set> entrySet() { if (this.entrySet == null) { this.entrySet = new AbstractSet>() { @Override public Iterator> iterator() { return new HashIterator>() { @Override public Map.Entry next() { return nextEntry(); } }; } @Override public boolean contains(Object o) { if (o instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry e = (Map.Entry)o; return ConcurrentTLongObjectHashMap.this.containsKey(e.getKey()); } else { return false; } } @Override public boolean remove(Object o) { if (o instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry e = (Map.Entry)o; return ConcurrentTLongObjectHashMap.this.remove(e.getKey()) != null; } else { return false; } } @Override public int size() { return ConcurrentTLongObjectHashMap.this.size(); } @Override public void clear() { ConcurrentTLongObjectHashMap.this.clear(); } }; } return this.entrySet; } public long estimateMemoryOverhead(SingleObjectSizer sizer) { long totalOverhead = sizer.sizeof(this); for (ConcurrentTLongObjectHashSegment seg : this.segments) { totalOverhead += sizer.sizeof(seg); } return totalOverhead; } /** * Returns the segment that should be used for key with given hash * * @param hash * the hash code for the key * @return the segment */ private ConcurrentTLongObjectHashSegment segmentFor(final int hash) { return this.segments[hash % this.segments.length]; } /** * Apply the given procedure for each key in the map. Do not invoke any * operation on this map directly from inside the body of procedure itself * else it will result in a deadlock. */ public boolean forEachKey(TLongProcedure proc) { acquireAllLocks(false); try { for (ConcurrentTLongObjectHashSegment seg : this.segments) { int size = seg.size; long key; Object value; for (int index = 0; index < size; index++) { key = seg.keys[index]; value = seg.values[index]; if (value != null && value != ConcurrentTLongObjectHashSegment.REMOVED) { if (!proc.execute(key)) { return false; } } } } } finally { releaseAllLocks(false); } return true; } /** * Apply the given procedure for each value in the map. Do not invoke any * operation on this map directly from inside the body of procedure itself * else it will result in a deadlock. */ public boolean forEachValue(TObjectProcedure proc) { acquireAllLocks(false); try { for (ConcurrentTLongObjectHashSegment seg : this.segments) { for (Object o : seg.values) { if (o != null && o != ConcurrentTLongObjectHashSegment.REMOVED) { if (!proc.execute(o)) { return false; } } } } } finally { releaseAllLocks(false); } return true; } /** * Apply the given procedure for each key, value pair in the map. Do not * invoke any operation on this map directly from inside the body of procedure * itself else it will result in a deadlock. */ public boolean forEachEntry(TLongObjectProcedure proc) { acquireAllLocks(false); try { for (ConcurrentTLongObjectHashSegment seg : this.segments) { int size = seg.size; long key; Object value; for (int index = 0; index < size; index++) { key = seg.keys[index]; value = seg.values[index]; if (value != null && value != ConcurrentTLongObjectHashSegment.REMOVED) { if (!proc.execute(key, value)) { return false; } } } } } finally { releaseAllLocks(false); } return true; } private void acquireAllLocks(boolean forWrite) { if (forWrite) { for (ConcurrentTLongObjectHashSegment seg : this.segments) { seg.writeLock().lock(); } } else { for (ConcurrentTLongObjectHashSegment seg : this.segments) { seg.readLock().lock(); } } } private void releaseAllLocks(boolean forWrite) { if (forWrite) { for (ConcurrentTLongObjectHashSegment seg : this.segments) { seg.writeLock().unlock(); } } else { for (ConcurrentTLongObjectHashSegment seg : this.segments) { seg.readLock().unlock(); } } } abstract class HashIterator implements Iterator { private ConcurrentTLongObjectHashSegment seg; private int segIndex; private long[] keys; private Object[] values; private long currentKey; private Object currentObj; private long nextKey; private Object nextObj; private int nextIndex; HashIterator() { setMap(segments[0]); } final void setMap(ConcurrentTLongObjectHashSegment segment) { this.seg = segment; // acquire read lock since we need snap of both keys and values segment.readLock().lock(); this.keys = segment.keys; this.values = segment.values; segment.readLock().unlock(); this.currentKey = 0; this.currentObj = null; moveNext(); } @Override public final boolean hasNext() { return this.nextObj != null; } final long nextKey() { final Object o = this.currentObj = this.nextObj; if (o != null) { final long k = this.currentKey = this.nextKey; moveNext(); return k; } else { throw new NoSuchElementException(); } } @SuppressWarnings("unchecked") final T nextValue() { final Object o = this.currentObj = this.nextObj; this.currentKey = this.nextKey; if (o != null) { moveNext(); return (T)o; } else { throw new NoSuchElementException(); } } @SuppressWarnings("unchecked") final Map.Entry nextEntry() { final Object o = this.currentObj = this.nextObj; if (o != null) { final long k = this.currentKey = this.nextKey; moveNext(); return new SimpleEntry(k, (T)o); } else { throw new NoSuchElementException(); } } @Override public final void remove() { int hash; if (this.currentObj != null) { final long key = this.currentKey; hash = ConcurrentTLongObjectHashSegment.computeHashCode(key); this.seg.remove(key, hash); } else { throw new NoSuchElementException(); } } final void moveNext() { Object o; final int size = this.values.length; this.nextObj = null; for (int i = this.nextIndex; i < size; i++) { o = this.values[i]; if (o != null && o != ConcurrentTLongObjectHashSegment.REMOVED) { this.nextIndex = i + 1; this.nextKey = this.keys[i]; this.nextObj = o; break; } } if (this.nextObj == null) { // move to next segment if (++this.segIndex < segments.length) { setMap(segments[this.segIndex]); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy