All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.gemstone.gemfire.internal.cache.UnsharedImageState Maven / Gradle / Ivy
/*
* 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.cache;
import com.gemstone.gemfire.CancelCriterion;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.util.concurrent.StoppableNonReentrantLock;
import com.gemstone.gemfire.internal.util.concurrent.StoppableReentrantReadWriteLock;
import com.gemstone.gemfire.internal.cache.locks.NonReentrantReadWriteLock;
import com.gemstone.gemfire.internal.cache.versions.RegionVersionVector;
import com.gemstone.gemfire.internal.cache.versions.VersionSource;
import com.gemstone.gemfire.internal.cache.versions.VersionTag;
import com.gemstone.gemfire.internal.concurrent.ConcurrentTHashSet;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gnu.trove.TObjectIntHashMap;
import com.gemstone.org.jgroups.util.StringId;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Used on distributed replicated regions to track GII and various state.
* Also used on pool regions to track register interest.
* Note that currently a region will never have both a GII and RI in progress
* at the same time.
* @author Eric Zoerner
* @author darrel
*/
public class UnsharedImageState implements ImageState {
private final StoppableNonReentrantLock giiLock; // used for gii
private final StoppableReentrantReadWriteLock riLock; // used for ri
/**
* Using CHS as a Set of keys
*/
private volatile ConcurrentTHashSet destroyedEntryKeys;
private volatile ConcurrentTHashSet deltaEntryKeys;
private volatile boolean regionInvalidated = false;
private volatile boolean mayDoRecovery = false;
private volatile boolean inRecovery = false;
private volatile boolean clearRegionFlag = false;
private volatile RegionVersionVector clearRVV;
private volatile boolean wasRegionClearedDuringGII = false;
//private volatile DiskAccessException dae = null;
private volatile ConcurrentTHashSet versionTags;
private volatile ConcurrentTHashSet leftMembers;
/**
* set of failed events during GII; used for GemFireXD failed events due to
* constraint violations for replicated tables which should also be skipped if
* these events are received directly
*/
private ConcurrentTHashSet failedEvents;
/**
* the set of pending TXRegionStates that have pending ops coming in before
* GII is complete
*/
private volatile THashMapWithCreate pendingTXRegionStates;
private final NonReentrantReadWriteLock pendingTXRegionStatesLock;
private volatile Thread pendingTXRegionStatesLockOwner;
private final AtomicInteger pendingTXOrder;
private volatile TObjectIntHashMap finishedTXIdOrders;
UnsharedImageState(final boolean isClient,
final boolean isReplicate,
final boolean isLocal,
final boolean mayDoRecovery,
CancelCriterion stopper) {
this.riLock = isClient ? new StoppableReentrantReadWriteLock(stopper) : null;
this.giiLock = isReplicate ? new StoppableNonReentrantLock(stopper) : null;
initDestroyedKeysMap();
initDeltaKeysSet();
initVersionTagsSet();
initFailedMembersSet();
this.mayDoRecovery = mayDoRecovery;
if (mayDoRecovery) {
this.inRecovery = true; // default to true to fix 41147
}
// we don't actually require much concurrency since putter will be only
// the GII thread, but for multiple getters this is a concurrent set
this.failedEvents = new ConcurrentTHashSet(2);
this.pendingTXRegionStates = isLocal ? null : new THashMapWithCreate();
this.pendingTXRegionStatesLock = isLocal ? null
: new NonReentrantReadWriteLock(stopper);
this.pendingTXOrder = new AtomicInteger(0);
}
private void initDestroyedKeysMap() {
this.destroyedEntryKeys = new ConcurrentTHashSet(4);
}
private void initDeltaKeysSet() {
this.deltaEntryKeys = new ConcurrentTHashSet(4);
}
public boolean isReplicate() {
return this.giiLock != null;
}
public boolean isClient() {
return this.riLock != null;
}
public void init() {
if (isReplicate()) {
this.wasRegionClearedDuringGII = false;
}
}
public boolean getRegionInvalidated() {
if (isReplicate()) {
return this.regionInvalidated;
} else {
return false;
}
}
public void setRegionInvalidated(boolean b) {
if (isReplicate()) {
this.regionInvalidated = b;
}
}
public void setInRecovery(boolean b) {
if (this.mayDoRecovery) {
this.inRecovery = b;
}
}
public boolean getInRecovery() {
if (this.mayDoRecovery) {
return this.inRecovery;
} else {
return false;
}
}
public void addDestroyedEntry(Object key) {
// assert if ri then readLock held
// assert if gii then lock held
if (isReplicate() || isClient()) {
this.destroyedEntryKeys.add(key);
}
}
public void removeDestroyedEntry(Object key) {
this.destroyedEntryKeys.remove(key);
}
public boolean hasDestroyedEntry(Object key) {
return this.destroyedEntryKeys.contains(key);
}
public Iterator getDestroyedEntries() {
// assert if ri then writeLock held
// assert if gii then lock held
Iterator result = this.destroyedEntryKeys.iterator();
initDestroyedKeysMap();
return result;
}
private void initVersionTagsSet() {
this.versionTags = new ConcurrentTHashSet(4);
}
public void addVersionTag(Object key, VersionTag> tag) {
this.versionTags.add(new VersionTagEntryImpl(key, tag.getMemberID(), tag.getRegionVersion()));
}
public Iterator getVersionTags() {
Iterator result = this.versionTags.iterator();
initVersionTagsSet();
return result;
}
private void initFailedMembersSet() {
this.leftMembers = new ConcurrentTHashSet(4);
}
public void addLeftMember(VersionSource> mbr) {
this.leftMembers.add(mbr);
}
public Set getLeftMembers() {
Set result = this.leftMembers;
initFailedMembersSet();
return result;
}
public boolean hasLeftMembers() {
return this.leftMembers.size() > 0;
}
public void dumpDestroyedEntryKeys(LogWriterI18n log) {
StringId str = LocalizedStrings.DEBUG;
if (this.destroyedEntryKeys == null) {
log.info(str, "region has no destroyedEntryKeys in its image state");
} else {
log.info(str, "dump of image state destroyed entry keys of size " + this.destroyedEntryKeys.size());
for (Iterator it = this.destroyedEntryKeys.iterator(); it.hasNext(); ) {
Object key = it.next();
log.info(str, "key=" + key);
}
}
}
/**
* returns count of entries that have been destroyed by concurrent operations
* while in token mode
*/
public int getDestroyedEntriesCount() {
return this.destroyedEntryKeys.size();
}
public void addDeltaEntry(Object key) {
this.deltaEntryKeys.add(key);
}
public void removeDeltaEntry(Object key) {
this.deltaEntryKeys.remove(key);
}
public boolean hasDeltaEntry(Object key) {
return this.deltaEntryKeys.contains(key);
}
public Iterator getDeltaEntries() {
Iterator result = this.deltaEntryKeys.iterator();
// TODO: KN why is this re-initialized. Do we require
// something similar for delta entries
// initDestroyedKeysMap();
return result;
}
@Override
public void addFailedEvents(Collection events) {
this.failedEvents.addAll(events);
}
public boolean isFailedEvent(EventID eventId) {
return eventId != null && this.failedEvents.contains(eventId);
}
@Override
public void clearFailedEvents() {
this.failedEvents.clear();
this.failedEvents = new ConcurrentTHashSet(1, 1, 0.8f, null, null);
}
public void setClearRegionFlag(boolean isClearOn, RegionVersionVector rvv) {
if (isReplicate()) {
this.clearRegionFlag = isClearOn;
if (isClearOn) {
this.clearRVV = rvv; // will be used to selectively clear content
this.wasRegionClearedDuringGII = true;
}
}
}
public boolean getClearRegionFlag() {
if (isReplicate()) {
return this.clearRegionFlag;
} else {
return false;
}
}
public RegionVersionVector getClearRegionVersionVector() {
if (isReplicate()) {
return this.clearRVV;
}
return null;
}
/**
* Returns true if a region clear was received on the region during a GII.
* If true is returned the the flag is cleared.
* This method is used by unit tests.
*/
public boolean wasRegionClearedDuringGII() {
if (isReplicate()) {
boolean result = this.wasRegionClearedDuringGII;
if (result) {
this.wasRegionClearedDuringGII = false;
}
return result;
} else {
return false;
}
}
public void lockGII() {
this.giiLock.lock();
}
public void unlockGII() {
this.giiLock.unlock();
}
public void readLockRI() {
this.riLock.readLock().lock();
}
public void readUnlockRI() {
this.riLock.readLock().unlock();
}
public void writeLockRI() {
this.riLock.writeLock().lock();
}
public void writeUnlockRI() {
this.riLock.writeLock().unlock();
}
/**
* {@inheritDoc}
*/
@Override
public boolean addPendingTXRegionState(TXRegionState txrs) {
if (lockPendingTXRegionStates(true, false)) {
try {
if (this.pendingTXRegionStates != null) {
// don't add if the advisor has not been initialized yet, that is the
// initial CreateRegionMessage replies are still on the wire so all
// operations on TXRegionState are essentially ignored
final LocalRegion region = txrs.region;
if (region.isProfileExchanged()) {
Object old;
if ((old = this.pendingTXRegionStates.putIfAbsent(txrs.getTXState()
.getTransactionId(), txrs)) != null) {
Assert.fail("ImageState#addPendingTXRegionState: failed to add "
+ txrs + ", existing=" + old);
}
if (TXStateProxy.LOG_FINE) {
final LogWriterI18n logger = region.getLogWriterI18n();
logger.info(LocalizedStrings.DEBUG,
"ImageState#addPendingTXRegionState: adding " + txrs);
}
}
return true;
}
else {
return false;
}
} finally {
unlockPendingTXRegionStates(true);
}
}
else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public void removePendingTXRegionState(TXId txId) {
if (this.pendingTXRegionStates != null) {
TXRegionState txrs;
if ((txrs = (TXRegionState)this.pendingTXRegionStates.remove(
txId)) != null) {
if (TXStateProxy.LOG_FINE) {
final LogWriterI18n logger = txrs.region.getLogWriterI18n();
logger.info(LocalizedStrings.DEBUG,
"ImageState#removePendingTXRegionState: removing " + txrs);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public TXRegionState getPendingTXRegionState(TXId txId, boolean lock) {
if (this.pendingTXRegionStates == null) {
return null;
}
TXRegionState txrs = null;
if (lock) {
if (Thread.currentThread() != this.pendingTXRegionStatesLockOwner) {
this.pendingTXRegionStatesLock.attemptReadLock(-1);
}
else {
lock = false;
}
}
try {
if (this.pendingTXRegionStates != null) {
txrs = (TXRegionState)this.pendingTXRegionStates.get(txId);
if (TXStateProxy.LOG_FINE) {
if (txrs != null) {
final LogWriterI18n logger = txrs.region.getLogWriterI18n();
logger.info(LocalizedStrings.DEBUG, "ImageState#"
+ "getPendingTXRegionState: result " + txrs + " for " + txId);
}
}
}
} finally {
if (lock) {
this.pendingTXRegionStatesLock.releaseReadLock();
}
}
return txrs;
}
/**
* {@inheritDoc}
*/
@Override
public boolean lockPendingTXRegionStates(final boolean forWrite,
final boolean force) {
if (!force && this.pendingTXRegionStates == null) {
return false;
}
if (TXStateProxy.LOG_FINE) {
final LogWriterI18n logger = InternalDistributedSystem.getLoggerI18n();
if (logger != null) {
logger.info(LocalizedStrings.DEBUG,
"ImageState#lockPendingTXRegionStates: acquiring "
+ (forWrite ? "write" : "read") + " lock");
}
}
if (forWrite) {
this.pendingTXRegionStatesLock.attemptWriteLock(-1);
this.pendingTXRegionStatesLockOwner = Thread.currentThread();
}
else {
this.pendingTXRegionStatesLock.attemptReadLock(-1);
}
if (this.pendingTXRegionStates != null) {
if (TXStateProxy.LOG_FINE) {
final LogWriterI18n logger = InternalDistributedSystem.getLoggerI18n();
if (logger != null) {
logger.info(LocalizedStrings.DEBUG,
"ImageState#lockPendingTXRegionStates: lock acquired on "
+ this.pendingTXRegionStatesLock);
}
}
return true;
}
else {
if (forWrite) {
this.pendingTXRegionStatesLockOwner = null;
this.pendingTXRegionStatesLock.releaseWriteLock();
}
else {
this.pendingTXRegionStatesLock.releaseReadLock();
}
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public void unlockPendingTXRegionStates(final boolean forWrite) {
if (this.pendingTXRegionStatesLock != null) {
if (forWrite) {
this.pendingTXRegionStatesLockOwner = null;
this.pendingTXRegionStatesLock.releaseWriteLock();
}
else {
this.pendingTXRegionStatesLock.releaseReadLock();
}
if (TXStateProxy.LOG_FINE) {
final LogWriterI18n logger = InternalDistributedSystem.getLoggerI18n();
if (logger != null) {
logger.info(LocalizedStrings.DEBUG, "ImageState#"
+ "unlockPendingTXRegionStates: " + (forWrite ? "write" : "read")
+ " lock released " + this.pendingTXRegionStatesLock);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Collection getPendingTXRegionStates() {
if (this.pendingTXRegionStates != null) {
@SuppressWarnings("unchecked")
final Collection result = this.pendingTXRegionStates
.values();
if (TXStateProxy.LOG_FINE) {
final LogWriterI18n logger = InternalDistributedSystem.getLoggerI18n();
if (logger != null) {
logger.info(LocalizedStrings.DEBUG,
"ImageState#getPendingTXRegionStates: returning " + result);
}
}
return result;
}
else {
return Collections.emptySet();
}
}
/**
* {@inheritDoc}
*/
@Override
public void setTXOrderForFinish(TXRegionState txrs) {
if (this.pendingTXRegionStatesLock != null) {
TObjectIntHashMap finishedOrders;
// assume read lock on pendingTXRegionStates is already held
Assert.assertTrue(this.pendingTXRegionStatesLock.numReaders() > 0);
if ((finishedOrders = this.finishedTXIdOrders) != null) {
int order = finishedOrders.get(txrs.getTXState().getTransactionId());
if (order != 0) {
txrs.finishOrder = Math.abs(order);
return;
}
}
}
txrs.finishOrder = this.pendingTXOrder.incrementAndGet();
}
/**
* {@inheritDoc}
*/
@Override
public int getFinishedTXOrder(TXId txId) {
TObjectIntHashMap finishedOrders;
if ((finishedOrders = this.finishedTXIdOrders) != null) {
return finishedOrders.get(txId);
}
else {
return 0;
}
}
/**
* {@inheritDoc}
*/
@Override
public void mergeFinishedTXOrders(final LocalRegion region,
final Collection txIds) {
final THashMapWithCreate pendingTXRS = this.pendingTXRegionStates;
if (pendingTXRS != null) {
this.pendingTXRegionStatesLock.attemptWriteLock(-1);
try {
// first get the ordering for finished transactions from TX manager;
// this is deliberately invoked under the lock to sync against
// any concurrent getPendingTXOrder call
final TXManagerImpl txMgr = region.getCache().getTxManager();
TObjectIntHashMap txIdOrders = txMgr.finishedTXStates
.getTXCommitOrders(txIds);
// loop through existing TXRegionStates and reset the order to that
// passed in txIdOrders
// also conservatively increment the current order by the size of
// txIdOrders since the numbering in txIdOrders will be 1, 2, 3, ...
final int increment = txIdOrders.size();
if (increment == 0) {
return;
}
if (!pendingTXRS.isEmpty()) {
for (Object e : pendingTXRS.values()) {
TXRegionState txrs = (TXRegionState)e;
if (txrs.finishOrder == 0) {
continue;
}
int order = txIdOrders.get(txrs.getTXState().getTransactionId());
if (order != 0) {
txrs.finishOrder = Math.abs(order);
}
else {
txrs.finishOrder += increment;
}
}
}
this.pendingTXOrder.addAndGet(increment);
this.finishedTXIdOrders = txIdOrders;
} finally {
this.pendingTXRegionStatesLock.releaseWriteLock();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void clearPendingTXRegionStates(boolean reset) {
if (reset) {
// in this case only reset the list else null it out
final THashMapWithCreate txrs = this.pendingTXRegionStates;
if (txrs != null) {
txrs.clear();
}
final TObjectIntHashMap txIds = this.finishedTXIdOrders;
if (txIds != null) {
txIds.clear();
}
}
else {
this.pendingTXRegionStates = null;
this.finishedTXIdOrders = null;
}
}
/** tracks RVV versions applied to the region during GII */
private static final class VersionTagEntryImpl implements ImageState.VersionTagEntry {
Object key;
VersionSource member;
long regionVersion;
VersionTagEntryImpl(Object key, VersionSource> member, long regionVersion) {
this.key = key;
this.member = member;
this.regionVersion = regionVersion;
}
public Object getKey() {
return key;
}
public VersionSource getMemberID() {
return member;
}
@Override
public long getRegionVersion() {
return regionVersion;
}
@Override
public String toString() {
return "{rv"+regionVersion+"; mbr="+member+"}";
}
}
}