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

org.infinispan.atomic.impl.AtomicHashMapProxy Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version
package org.infinispan.atomic.impl;

import org.infinispan.AdvancedCache;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.atomic.AtomicMapLookup;
import org.infinispan.batch.AutoBatchSupport;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.context.Flag;
import org.infinispan.marshall.core.MarshalledValue;
import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.TransactionMode;
import org.infinispan.transaction.impl.LocalTransaction;
import org.infinispan.transaction.impl.TransactionTable;

import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;


/**
 * A layer of indirection around an {@link AtomicHashMap} to provide consistency and isolation for concurrent readers
 * while writes may also be going on.  The techniques used in this implementation are very similar to the lock-free
 * reader MVCC model used in the {@link org.infinispan.container.entries.MVCCEntry} implementations for the core data
 * container, which closely follow software transactional memory approaches to dealing with concurrency.
 * 

* Implementations of this class are rarely created on their own; {@link AtomicHashMap#getProxy(AdvancedCache, Object)} * should be used to retrieve an instance of this proxy. *

* Typically proxies are only created by the {@link AtomicMapLookup} helper, and would not be created by end-user code * directly. * * @param the type of keys maintained by this map * @param the type of mapped values * @author Manik Surtani * @see AtomicHashMap * @since 4.0 */ public class AtomicHashMapProxy extends AutoBatchSupport implements AtomicMap { protected final Object deltaMapKey; protected final AdvancedCache> cache; protected final AdvancedCache> cacheForWriting; protected volatile boolean startedReadingMap = false; protected TransactionTable transactionTable; protected TransactionManager transactionManager; AtomicHashMapProxy(AdvancedCache cache, Object deltaMapKey) { Configuration configuration = cache.getCacheConfiguration(); if (configuration.transaction().transactionMode() == TransactionMode.NON_TRANSACTIONAL) { throw new IllegalStateException("AtomicMap needs a transactional cache."); } this.cache = (AdvancedCache>) cache; this.cacheForWriting = (AdvancedCache>) cache.getAdvancedCache().withFlags(Flag.DELTA_WRITE); this.deltaMapKey = deltaMapKey; this.batchContainer = cache.getBatchContainer(); transactionTable = cache.getComponentRegistry().getComponent(TransactionTable.class); transactionManager = cache.getTransactionManager(); } // internal helper, reduces lots of casts. @SuppressWarnings("unchecked") protected AtomicHashMap toMap(Object object) { Object map = (object instanceof MarshalledValue) ? ((MarshalledValue) object).get() : object; return (AtomicHashMap) map; } protected AtomicHashMap getDeltaMapForRead() { AtomicHashMap ahm = toMap(cache.get(deltaMapKey)); if (ahm != null && !startedReadingMap) startedReadingMap = true; assertValid(ahm); return ahm; } /** * Looks up the CacheEntry stored in transactional context corresponding to this AtomicMap. If this AtomicMap * has yet to be touched by the current transaction, this method will return a null. * * @return */ protected CacheEntry lookupEntryFromCurrentTransaction() { // Prior to 5.1, this used to happen by grabbing any InvocationContext in ThreadLocal. Since ThreadLocals // can no longer be relied upon in 5.1, we need to grab the TransactionTable and check if an ongoing // transaction exists, peeking into transactional state instead. try { Transaction tx = transactionManager.getTransaction(); LocalTransaction localTransaction = tx == null ? null : transactionTable.getLocalTransaction(tx); // The stored localTransaction could be null, if this is the first call in a transaction. In which case // we know that there is no transactional state to refer to - i.e., no entries have been looked up as yet. return localTransaction == null ? null : localTransaction.lookupEntry(deltaMapKey); } catch (SystemException e) { return null; } } @SuppressWarnings("unchecked") protected AtomicHashMap getDeltaMapForWrite() { CacheEntry lookedUpEntry = lookupEntryFromCurrentTransaction(); boolean lockedAndCopied = lookedUpEntry != null && lookedUpEntry.isChanged() && toMap(lookedUpEntry.getValue()).copied; if (lockedAndCopied) { return getDeltaMapForRead(); } else { AdvancedCache> cacheForRead = cache; if (cache.getCacheConfiguration().transaction().lockingMode() == LockingMode.PESSIMISTIC) { cacheForRead = cache.withFlags(Flag.FORCE_WRITE_LOCK); } // acquire WL AtomicHashMap map = toMap(cacheForRead.get(deltaMapKey)); if (map != null && !startedReadingMap) startedReadingMap = true; assertValid(map); // copy for write AtomicHashMap copy = map == null ? new AtomicHashMap<>(true, AtomicHashMap.ProxyMode.COARSE) : map.copy(); copy.initForWriting(); cacheForWriting.put(deltaMapKey, copy); return copy; } } // readers protected void assertValid(AtomicHashMap map) { if (startedReadingMap && (map == null || map.removed)) throw new IllegalStateException("AtomicMap stored under key " + deltaMapKey + " has been concurrently removed!"); } @Override public Set keySet() { AtomicHashMap map = getDeltaMapForRead(); return map == null ? Collections.emptySet() : map.keySet(); } @Override public Collection values() { AtomicHashMap map = getDeltaMapForRead(); return map == null ? Collections.emptySet() : map.values(); } @Override public Set> entrySet() { AtomicHashMap map = getDeltaMapForRead(); return map == null ? Collections.>emptySet() : map.entrySet(); } @Override public int size() { AtomicHashMap map = getDeltaMapForRead(); return map == null ? 0 : map.size(); } @Override public boolean isEmpty() { AtomicHashMap map = getDeltaMapForRead(); return map == null || map.isEmpty(); } @Override public boolean containsKey(Object key) { AtomicHashMap map = getDeltaMapForRead(); return map != null && map.containsKey(key); } @Override public boolean containsValue(Object value) { AtomicHashMap map = getDeltaMapForRead(); return map != null && map.containsValue(value); } @Override public V get(Object key) { AtomicHashMap map = getDeltaMapForRead(); return map == null ? null : map.get(key); } //writers @Override public V put(K key, V value) { AtomicHashMap deltaMapForWrite; try { startAtomic(); deltaMapForWrite = getDeltaMapForWrite(); return deltaMapForWrite.put(key, value); } finally { endAtomic(); } } @Override public V remove(Object key) { AtomicHashMap deltaMapForWrite; try { startAtomic(); deltaMapForWrite = getDeltaMapForWrite(); return deltaMapForWrite.remove(key); } finally { endAtomic(); } } @Override public void putAll(Map m) { AtomicHashMap deltaMapForWrite; try { startAtomic(); deltaMapForWrite = getDeltaMapForWrite(); deltaMapForWrite.putAll(m); } finally { endAtomic(); } } @Override public void clear() { AtomicHashMap deltaMapForWrite; try { startAtomic(); deltaMapForWrite = getDeltaMapForWrite(); deltaMapForWrite.clear(); } finally { endAtomic(); } } @Override public String toString() { StringBuilder sb = new StringBuilder("AtomicHashMapProxy{deltaMapKey="); sb.append(deltaMapKey); sb.append("}"); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy