/* * Copyright Terracotta, Inc. * * 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.ehcache.clustered.server.store.impl; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.common.internal.store.ServerStore; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Implements {@link ServerStore} */ public class ReferenceStoreImpl implements ServerStore { private final Map<Long, Chain> map = new HashMap<Long, Chain>(); private final List<ReadWriteLock> locks = new ArrayList<ReadWriteLock>(); private final AtomicLong sequenceGenerator = new AtomicLong(); private final int LOCK_COUNT = 16; public ReferenceStoreImpl() { for (int i = 0; i < LOCK_COUNT; i++) { locks.add(new ReentrantReadWriteLock()); } } private ReadWriteLock getLock(long key) { return locks.get((int)Math.abs(key)%LOCK_COUNT); } @Override public Chain get(long key) { Lock lock = getLock(key).readLock(); lock.lock(); try { Chain chain = map.get(key); if (chain != null) { return chain; } else { return new HeapChainImpl(); } } finally { lock.unlock(); } } @Override public void append(long key, ByteBuffer payLoad) { Lock lock = getLock(key).writeLock(); lock.lock(); try { Chain mapping = map.get(key); if (mapping == null) { map.put(key, new HeapChainImpl(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad))); return; } Chain newMapping = cast(mapping).append(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad)); map.put(key, newMapping); } finally { lock.unlock(); } } @Override public Chain getAndAppend(long key, ByteBuffer payLoad) { Lock lock = getLock(key).writeLock(); lock.lock(); try { Chain mapping = map.get(key); if (mapping != null) { Chain newMapping = cast(mapping).append(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad)); map.put(key, newMapping); return mapping; } else { map.put(key, new HeapChainImpl(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad))); return new HeapChainImpl(); } } finally { lock.unlock(); } } @Override public void replaceAtHead(long key, Chain expect, Chain update) { Lock lock = getLock(key).writeLock(); lock.lock(); try { Chain mapping = map.get(key); if (mapping == null) { return; } boolean replaceable = true; List<Element> elements = new LinkedList<Element>(); Iterator<Element> current = mapping.iterator(); Iterator<Element> expected = expect.iterator(); while (expected.hasNext()) { if (current.hasNext()) { HeapElementImpl expectedLink = (HeapElementImpl)expected.next(); if (expectedLink.getSequenceNumber() != ((HeapElementImpl)current.next()).getSequenceNumber()) { replaceable = false; break; } } else { replaceable = false; break; } } if (replaceable) { for (Element element : update) { elements.add(element); } while(current.hasNext()) { elements.add(current.next()); } map.put(key, new HeapChainImpl(elements.toArray(new Element[elements.size()]))); } } finally { lock.unlock(); } } private void writeLockAll() { for (ReadWriteLock lock : locks) { lock.writeLock().lock(); } } private void writeUnlockAll() { for (ReadWriteLock lock : locks) { lock.writeLock().unlock(); } } @Override public void clear() { writeLockAll(); try { map.clear(); } finally { writeUnlockAll(); } } private HeapChainImpl cast(Chain chain) { return (HeapChainImpl)chain; } }