/*
* Copyright (C) 2012, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.hash.impl.stage.query;
import net.openhft.chronicle.hash.Data;
import net.openhft.chronicle.hash.impl.VanillaChronicleHashHolder;
import net.openhft.chronicle.hash.impl.stage.entry.HashEntryStages;
import net.openhft.chronicle.hash.impl.stage.entry.HashLookupPos;
import net.openhft.chronicle.hash.impl.stage.entry.HashLookupSearch;
import net.openhft.chronicle.hash.impl.stage.entry.SegmentStages;
import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation;
import net.openhft.chronicle.hash.serialization.DataAccess;
import net.openhft.chronicle.set.SetEntry;
import net.openhft.sg.Stage;
import net.openhft.sg.StageRef;
import net.openhft.sg.Staged;
import static net.openhft.chronicle.hash.impl.stage.query.KeySearch.SearchState.ABSENT;
@Staged
public abstract class HashQuery<K> implements SetEntry<K> {
@StageRef public VanillaChronicleHashHolder<K> hh;
@StageRef public SegmentStages s;
@StageRef public HashEntryStages<K> entry;
@StageRef public HashLookupSearch hashLookupSearch;
@StageRef public CheckOnEachPublicOperation checkOnEachPublicOperation;
@StageRef public HashLookupPos hlp;
@StageRef public KeySearch<K> ks;
final DataAccess<K> innerInputKeyDataAccess = hh.h().keyDataAccess.copy();
/**
* This stage exists for hooking {@link #innerInputKeyDataAccess} usage, to trigger {@link
* DataAccess#uninit()} on context exit
*/
@Stage("InputKeyDataAccess") private boolean inputKeyDataAccessInitialized = false;
void initInputKeyDataAccess() {
inputKeyDataAccessInitialized = true;
}
void closeInputKeyDataAccess() {
innerInputKeyDataAccess.uninit();
inputKeyDataAccessInitialized = false;
}
public DataAccess<K> inputKeyDataAccess() {
initInputKeyDataAccess();
return innerInputKeyDataAccess;
}
public void dropSearchIfNestedContextsAndPresentHashLookupSlotCheckFailed() {
if (s.locksInit()) {
if (s.nestedContextsLockedOnSameSegment &&
s.rootContextLockedOnThisSegment.latestSameThreadSegmentModCount() !=
s.contextModCount) {
if (ks.keySearchInit() && ks.searchStatePresent() &&
!hashLookupSearch.checkSlotContainsExpectedKeyAndValue(entry.pos)) {
hlp.closeHashLookupPos();
}
}
}
}
public Data<K> queriedKey() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
return ks.inputKey;
}
public enum EntryPresence {PRESENT, ABSENT}
@Stage("PresenceOfEntry") private EntryPresence entryPresence = null;
private void initPresenceOfEntry() {
if (ks.searchStatePresent() || tieredEntryPresent()) {
entryPresence = EntryPresence.PRESENT;
} else {
entryPresence = EntryPresence.ABSENT;
}
}
public void initPresenceOfEntry(EntryPresence entryPresence) {
this.entryPresence = entryPresence;
}
private boolean tieredEntryPresent() {
int firstTier = s.tier;
long firstTierBaseAddr = s.tierBaseAddr;
while (true) {
if (s.hasNextTier()) {
s.nextTier();
} else {
if (s.tier != 0)
s.initSegmentTier(); // loop to the root tier
}
if (s.tierBaseAddr == firstTierBaseAddr)
break;
if (ks.searchStatePresent())
return true;
}
// not found
if (firstTier != 0) {
// key is absent; probably are going to allocate a new entry;
// start trying from the root tier
s.initSegmentTier();
}
return false;
}
public boolean entryPresent() {
return entryPresence == EntryPresence.PRESENT;
}
@Override
public void doRemove() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerWriteLock.lock();
if (ks.searchStatePresent()) {
entry.innerRemoveEntryExceptHashLookupUpdate();
hashLookupSearch.remove();
ks.setSearchState(ABSENT);
initPresenceOfEntry(EntryPresence.ABSENT);
} else {
throw new IllegalStateException(
hh.h().toIdentityString() + ": Entry is absent when doRemove() is called");
}
}
}