/* * Copyright 2015 Terracotta, Inc., a Software AG company. * * 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. */ package org.terracotta.offheapstore; import java.nio.ByteBuffer; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import org.terracotta.offheapstore.jdk8.BiFunction; import org.terracotta.offheapstore.jdk8.Function; import org.terracotta.offheapstore.paging.PageSource; import org.terracotta.offheapstore.storage.StorageEngine; /** * An abstract locked off-heap map. * <p> * Subclasses must implement the {@code readLock()} and {@code writeLock()} * methods such that they return the correct locks under which read and write * operations must occur. * * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values * * @author Chris Dennis */ public abstract class AbstractLockedOffHeapHashMap<K, V> extends OffHeapHashMap<K, V> implements Segment<K, V> { private Set<Entry<K, V>> entrySet; private Set<K> keySet; private Collection<V> values; public AbstractLockedOffHeapHashMap(PageSource source, StorageEngine<? super K, ? super V> storageEngine) { super(source, storageEngine); } public AbstractLockedOffHeapHashMap(PageSource source, boolean tableAllocationsSteal, StorageEngine<? super K, ? super V> storageEngine) { super(source, tableAllocationsSteal, storageEngine); } public AbstractLockedOffHeapHashMap(PageSource source, StorageEngine<? super K, ? super V> storageEngine, boolean bootstrap) { super(source, storageEngine, bootstrap); } public AbstractLockedOffHeapHashMap(PageSource source, StorageEngine<? super K, ? super V> storageEngine, int tableSize) { super(source, storageEngine, tableSize); } public AbstractLockedOffHeapHashMap(PageSource source, boolean tableAllocationsSteal, StorageEngine<? super K, ? super V> storageEngine, int tableSize) { super(source, tableAllocationsSteal, storageEngine, tableSize); } public AbstractLockedOffHeapHashMap(PageSource source, StorageEngine<? super K, ? super V> storageEngine, int tableSize, boolean bootstrap) { super(source, false, storageEngine, tableSize, bootstrap); } @Override public int size() { Lock l = readLock(); l.lock(); try { return super.size(); } finally { l.unlock(); } } @Override public boolean containsKey(Object key) { Lock l = readLock(); l.lock(); try { return super.containsKey(key); } finally { l.unlock(); } } @Override public V get(Object key) { Lock l = readLock(); l.lock(); try { return super.get(key); } finally { l.unlock(); } } @Override public Long getEncodingForHashAndBinary(int hash, ByteBuffer binaryKey) { Lock l = readLock(); l.lock(); try { return super.getEncodingForHashAndBinary(hash, binaryKey); } finally { l.unlock(); } } @Override public long installMappingForHashAndEncoding(int pojoHash, ByteBuffer offheapBinaryKey, ByteBuffer offheapBinaryValue, int metadata) { Lock l = writeLock(); l.lock(); try { return super.installMappingForHashAndEncoding(pojoHash, offheapBinaryKey, offheapBinaryValue, metadata); } finally { l.unlock(); } } @Override public V put(K key, V value) { Lock l = writeLock(); l.lock(); try { return super.put(key, value); } finally { l.unlock(); } } @Override public V put(K key, V value, int metadata) { Lock l = writeLock(); l.lock(); try { return super.put(key, value, metadata); } finally { l.unlock(); } } @Override public V fill(K key, V value) { Lock l = writeLock(); l.lock(); try { return super.fill(key, value); } finally { l.unlock(); } } @Override public V fill(K key, V value, int metadata) { Lock l = writeLock(); l.lock(); try { return super.fill(key, value, metadata); } finally { l.unlock(); } } @Override public V remove(Object key) { Lock l = writeLock(); l.lock(); try { return super.remove(key); } finally { l.unlock(); } } @Override public boolean removeNoReturn(Object key) { Lock l = writeLock(); l.lock(); try { return super.removeNoReturn(key); } finally { l.unlock(); } } @Override public void clear() { Lock l = writeLock(); l.lock(); try { super.clear(); } finally { l.unlock(); } } @Override public V putIfAbsent(K key, V value) { Lock l = writeLock(); l.lock(); try { if (key == null || value == null) { throw new NullPointerException(); } V existing = get(key); if (existing == null) { put(key, value); } return existing; } finally { l.unlock(); } } @Override public boolean remove(Object key, Object value) { Lock l = writeLock(); l.lock(); try { if (key == null) { throw new NullPointerException(); } if (value == null) { return false; } V existing = get(key); if (value.equals(existing)) { remove(key); return true; } else { return false; } } finally { l.unlock(); } } @Override public boolean replace(K key, V oldValue, V newValue) { Lock l = writeLock(); l.lock(); try { V existing = get(key); if (oldValue.equals(existing)) { put(key, newValue); return true; } else { return false; } } finally { l.unlock(); } } @Override public V replace(K key, V value) { Lock l = writeLock(); l.lock(); try { if (value == null || key == null) { throw new NullPointerException(); } V existing = get(key); if (existing != null) { put(key, value); } return existing; } finally { l.unlock(); } } @Override public Integer getMetadata(Object key, int mask) { Lock l = readLock(); l.lock(); try { return super.getMetadata(key, mask); } finally { l.unlock(); } } @Override public Integer getAndSetMetadata(Object key, int mask, int values) { Lock l = writeLock(); l.lock(); try { return super.getAndSetMetadata(key, mask, values); } finally { l.unlock(); } } @Override public V getValueAndSetMetadata(Object key, int mask, int values) { Lock l = writeLock(); l.lock(); try { return super.getValueAndSetMetadata(key, mask, values); } finally { l.unlock(); } } /* * remove used by EntrySet */ @Override protected boolean removeMapping(Object o) { Lock l = writeLock(); l.lock(); try { return super.removeMapping(o); } finally { l.unlock(); } } @Override public boolean evict(int index, boolean shrink) { Lock l = writeLock(); l.lock(); try { return super.evict(index, shrink); } finally { l.unlock(); } } @Override public Set<Entry<K, V>> entrySet() { Set<Entry<K, V>> es = entrySet; return es == null ? (entrySet = new LockedEntrySet()) : es; } class LockedEntrySet extends AbstractSet<Entry<K, V>> { @Override public Iterator<Entry<K, V>> iterator() { Lock l = readLock(); l.lock(); try { return new LockedEntryIterator(); } finally { l.unlock(); } } @Override public boolean contains(Object o) { if (!(o instanceof Entry<?, ?>)) { return false; } @SuppressWarnings("unchecked") Entry<K, V> e = (Entry<K, V>) o; Lock l = readLock(); l.lock(); try { V value = AbstractLockedOffHeapHashMap.this.get(e.getKey()); return value != null && value.equals(e.getValue()); } finally { l.unlock(); } } @Override public boolean remove(Object o) { return AbstractLockedOffHeapHashMap.this.removeMapping(o); } @Override public int size() { return AbstractLockedOffHeapHashMap.this.size(); } @Override public void clear() { AbstractLockedOffHeapHashMap.this.clear(); } } class LockedEntryIterator extends EntryIterator { @Override public Entry<K, V> next() { Lock l = readLock(); l.lock(); try { return super.next(); } finally { l.unlock(); } } @Override public void remove() { Lock l = writeLock(); l.lock(); try { super.remove(); } finally { l.unlock(); } } @Override protected void checkForConcurrentModification() { //no-op } } @Override public Set<K> keySet() { if (keySet == null) { keySet = new LockedKeySet(); } return keySet; } class LockedKeySet extends AbstractSet<K> { @Override public Iterator<K> iterator() { Lock l = readLock(); l.lock(); try { return new LockedKeyIterator(); } finally { l.unlock(); } } @Override public boolean contains(Object o) { return AbstractLockedOffHeapHashMap.this.containsKey(o); } @Override public boolean remove(Object o) { return AbstractLockedOffHeapHashMap.this.remove(o) != null; } @Override public int size() { return AbstractLockedOffHeapHashMap.this.size(); } @Override public void clear() { AbstractLockedOffHeapHashMap.this.clear(); } } class LockedKeyIterator extends KeyIterator { @Override public K next() { Lock l = readLock(); l.lock(); try { return super.next(); } finally { l.unlock(); } } @Override public void remove() { Lock l = writeLock(); l.lock(); try { super.remove(); } finally { l.unlock(); } } @Override protected void checkForConcurrentModification() { //no-op } } @Override public Collection<V> values() { if (values == null) { values = new AbstractCollection<V>() { @Override public Iterator<V> iterator() { return new Iterator<V>() { private final Iterator<Entry<K, V>> i = entrySet().iterator(); @Override public boolean hasNext() { return i.hasNext(); } @Override public V next() { return i.next().getValue(); } @Override public void remove() { i.remove(); } }; } @Override public int size() { return AbstractLockedOffHeapHashMap.this.size(); } @Override public boolean isEmpty() { return AbstractLockedOffHeapHashMap.this.isEmpty(); } @Override public void clear() { AbstractLockedOffHeapHashMap.this.clear(); } @Override public boolean contains(Object v) { return AbstractLockedOffHeapHashMap.this.containsValue(v); } }; } return values; } @Override public void destroy() { Lock l = writeLock(); l.lock(); try { super.destroy(); } finally { l.unlock(); } } @Override public boolean shrink() { Lock l = writeLock(); l.lock(); try { return storageEngine.shrink(); } finally { l.unlock(); } } @Override public abstract Lock readLock(); @Override public abstract Lock writeLock(); /* * JDK-8-alike metadata methods */ @Override public MetadataTuple<V> computeWithMetadata(K key, BiFunction<? super K, ? super MetadataTuple<V>, ? extends MetadataTuple<V>> remappingFunction) { Lock l = writeLock(); l.lock(); try { return super.computeWithMetadata(key, remappingFunction); } finally { l.unlock(); } } @Override public MetadataTuple<V> computeIfAbsentWithMetadata(K key, Function<? super K,? extends MetadataTuple<V>> mappingFunction) { Lock l = writeLock(); l.lock(); try { return super.computeIfAbsentWithMetadata(key, mappingFunction); } finally { l.unlock(); } } @Override public MetadataTuple<V> computeIfPresentWithMetadata(K key, BiFunction<? super K,? super MetadataTuple<V>,? extends MetadataTuple<V>> remappingFunction) { Lock l = writeLock(); l.lock(); try { return super.computeIfPresentWithMetadata(key, remappingFunction); } finally { l.unlock(); } } @Override public Map<K, V> removeAllWithHash(int hash) { Lock l = writeLock(); l.lock(); try { return super.removeAllWithHash(hash); } finally { l.unlock(); } } }