/*
* 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.map.impl.stage.entry;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.hash.Data;
import net.openhft.chronicle.map.impl.ReplicatedChronicleMapHolder;
import net.openhft.chronicle.map.impl.stage.replication.ReplicationUpdate;
import net.openhft.chronicle.map.replication.MapReplicableEntry;
import net.openhft.sg.Stage;
import net.openhft.sg.StageRef;
import net.openhft.sg.Staged;
import static net.openhft.chronicle.hash.replication.TimeProvider.currentTime;
import static net.openhft.chronicle.map.ReplicatedChronicleMap.ADDITIONAL_ENTRY_BYTES;
@Staged
public abstract class ReplicatedMapEntryStages<K, V> extends MapEntryStages<K, V>
implements MapReplicableEntry<K, V> {
@StageRef ReplicatedChronicleMapHolder<?, ?, ?> mh;
@StageRef ReplicationUpdate ru;
@Stage("ReplicationState") long replicationBytesOffset = -1;
void initReplicationState() {
replicationBytesOffset = keyEnd();
}
void updateReplicationState(byte identifier, long timestamp) {
initDelayedUpdateChecksum(true);
Bytes segmentBytes = s.segmentBytesForWrite();
segmentBytes.writePosition(replicationBytesOffset);
segmentBytes.writeLong(timestamp);
segmentBytes.writeByte(identifier);
}
private long timestampOffset() {
return replicationBytesOffset;
}
public long timestamp() {
return s.segmentBS.readLong(replicationBytesOffset);
}
private long identifierOffset() {
return replicationBytesOffset + 8L;
}
byte identifier() {
return s.segmentBS.readByte(identifierOffset());
}
private long entryDeletedOffset() {
return replicationBytesOffset + 9L;
}
@Override
public boolean entryDeleted() {
return s.segmentBS.readBoolean(entryDeletedOffset());
}
public void writeEntryPresent() {
s.segmentBS.writeBoolean(entryDeletedOffset(), false);
}
public void writeEntryDeleted() {
s.segmentBS.writeBoolean(entryDeletedOffset(), true);
}
@Override
public byte originIdentifier() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
return identifier();
}
@Override
public long originTimestamp() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
return timestamp();
}
@Override
long countValueSizeOffset() {
return super.countValueSizeOffset() + ADDITIONAL_ENTRY_BYTES;
}
@Override
public void updateOrigin(byte newIdentifier, long newTimestamp) {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerWriteLock.lock();
updateReplicationState(newIdentifier, newTimestamp);
}
@Override
public void dropChanged() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerUpdateLock.lock();
ru.dropChange();
}
@Override
public void dropChangedFor(byte remoteIdentifier) {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerUpdateLock.lock();
ru.dropChangeFor(remoteIdentifier);
}
@Override
public void raiseChanged() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerUpdateLock.lock();
ru.raiseChange();
}
@Override
public void raiseChangedFor(byte remoteIdentifier) {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerUpdateLock.lock();
ru.raiseChangeFor(remoteIdentifier);
}
@Override
public void raiseChangedForAllExcept(byte remoteIdentifier) {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerUpdateLock.lock();
ru.raiseChangeForAllExcept(remoteIdentifier);
}
@Override
public boolean isChanged() {
checkOnEachPublicOperation.checkOnEachPublicOperation();
s.innerReadLock.lock();
return ru.changed();
}
public void updatedReplicationStateOnPresentEntry() {
if (!ru.replicationUpdateInit()) {
s.innerWriteLock.lock();
long timestamp = Math.max(timestamp() + 1, currentTime());
updateReplicationState(mh.m().identifier(), timestamp);
}
}
public void updatedReplicationStateOnAbsentEntry() {
if (!ru.replicationUpdateInit()) {
s.innerWriteLock.lock();
updateReplicationState(mh.m().identifier(), currentTime());
}
}
@Override
protected void relocation(Data<V> newValue, long newSizeOfEverythingBeforeValue) {
long oldPos = pos;
long oldTierIndex = s.tierIndex;
super.relocation(newValue, newSizeOfEverythingBeforeValue);
ru.moveChange(oldTierIndex, oldPos, pos);
}
@Override
long sizeOfEverythingBeforeValue(long keySize, long valueSize) {
return super.sizeOfEverythingBeforeValue(keySize, valueSize) + ADDITIONAL_ENTRY_BYTES;
}
}