/*
* 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.entry;
import net.openhft.chronicle.hash.impl.CompactOffHeapLinearHashTable;
import net.openhft.chronicle.hash.impl.VanillaChronicleHashHolder;
import net.openhft.chronicle.hash.impl.stage.query.KeySearch;
import net.openhft.chronicle.map.impl.stage.entry.MapEntryStages;
import net.openhft.sg.Stage;
import net.openhft.sg.StageRef;
import net.openhft.sg.Staged;
import static net.openhft.chronicle.hash.impl.CompactOffHeapLinearHashTable.UNSET_KEY;
@Staged
public abstract class HashLookupSearch {
@StageRef SegmentStages s;
@StageRef public VanillaChronicleHashHolder<?> hh;
@StageRef HashLookupPos hlp;
@StageRef KeySearch<?> ks;
@StageRef MapEntryStages<?, ?> e;
@Stage("SearchKey") long searchKey = UNSET_KEY;
@Stage("SearchKey") public long searchStartPos;
public CompactOffHeapLinearHashTable hl() {
return hh.h().hashLookup;
}
public void initSearchKey(long searchKey) {
this.searchKey = searchKey;
searchStartPos = hl().hlPos(searchKey);
}
private long addr() {
return s.tierBaseAddr;
}
public long nextPos() {
long pos = hlp.hashLookupPos;
CompactOffHeapLinearHashTable hl = hl();
while (true) {
// read volatile to make a happens-before edge between entry insertion from concurrent
// thread under update lock and this thread (reading the entry)
long entry = hl.readEntryVolatile(addr(), pos);
if (hl.empty(entry)) {
hlp.setHashLookupPos(pos);
return -1L;
}
pos = hl.step(pos);
if (pos == searchStartPos)
break;
if (hl.key(entry) == searchKey) {
hlp.setHashLookupPos(pos);
return hl.value(entry);
}
}
throw new IllegalStateException(hh.h().toIdentityString() +
": HashLookup overflow should never occur");
}
public void found() {
hlp.setHashLookupPos(hl().stepBack(hlp.hashLookupPos));
}
public void remove() {
hlp.setHashLookupPos(hl().remove(addr(), hlp.hashLookupPos));
}
public void putNewVolatile(long entryPos) {
// Correctness check + make putNewVolatile() dependant on keySearch, this, in turn,
// is needed for hlp.hashLookupPos re-initialization after nextTier().
// Not an assert statement, because ks.searchStatePresent() should run regardless assertions
// enabled or not.
boolean keySearchReInit = !ks.keySearchInit();
if (ks.searchStatePresent())
throw new AssertionError();
if (keySearchReInit) {
// if key search was re-init, entry was re-init too during the search
e.readExistingEntry(entryPos);
}
hl().checkValueForPut(entryPos);
hl().writeEntryVolatile(addr(), hlp.hashLookupPos, searchKey, entryPos);
}
public boolean checkSlotContainsExpectedKeyAndValue(long value) {
// volatile read not needed here because this method is for verifying within-thread
// invariants
long entry = hl().readEntry(addr(), hlp.hashLookupPos);
return hl().key(entry) == searchKey && hl().value(entry) == value;
}
}