package org.infinispan.counter.impl.function; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collections; import java.util.Set; import org.infinispan.commons.api.functional.EntryView; import org.infinispan.commons.logging.LogFactory; import org.infinispan.commons.marshall.AdvancedExternalizer; import org.infinispan.counter.api.CounterState; import org.infinispan.counter.api.CounterType; import org.infinispan.counter.impl.entries.CounterKey; import org.infinispan.counter.impl.entries.CounterValue; import org.infinispan.counter.impl.externalizers.ExternalizerIds; import org.infinispan.counter.impl.metadata.ConfigurationMetadata; import org.infinispan.counter.logging.Log; import org.infinispan.util.ByteString; /** * The compare-and-set function to update the {@link CounterValue}. * <p> * If the value is different from {@code expect}, it returns {@code null}. * <p> * For a bounded counter, if the {@code value} is outside the bounds, it returns {@link * CounterState#LOWER_BOUND_REACHED} or {@link CounterState#UPPER_BOUND_REACHED} if the lower bound or upper bound is * violated. * <p> * If the compare-and-set is successful, it returns {@link CounterState#VALID}. * * @author Pedro Ruivo * @since 9.0 */ public class CompareAndSetFunction<K extends CounterKey> extends BaseFunction<K, CounterState> { public static final AdvancedExternalizer<CompareAndSetFunction> EXTERNALIZER = new Externalizer(); private static final Log log = LogFactory.getLog(CompareAndSetFunction.class, Log.class); private final long expect; private final long value; public CompareAndSetFunction(long expect, long value) { this.expect = expect; this.value = value; } @Override void logCounterNotFound(ByteString counterName) { log.noSuchCounterCAS(expect, value, counterName); } @Override CounterState apply(EntryView.ReadWriteEntryView<K, CounterValue> entryView, ConfigurationMetadata metadata) { CounterValue existing = entryView.get(); if (expect == existing.getValue()) { if (metadata.get().type() == CounterType.BOUNDED_STRONG) { if (value < metadata.get().lowerBound()) { return CounterState.LOWER_BOUND_REACHED; } else if (value > metadata.get().upperBound()) { return CounterState.UPPER_BOUND_REACHED; } } entryView.set(CounterValue.newCounterValue(value, CounterState.VALID), metadata); return CounterState.VALID; } else { return null; } } @Override protected Log getLog() { return log; } private static class Externalizer implements AdvancedExternalizer<CompareAndSetFunction> { @Override public Set<Class<? extends CompareAndSetFunction>> getTypeClasses() { return Collections.singleton(CompareAndSetFunction.class); } @Override public Integer getId() { return ExternalizerIds.CAS_FUNCTION; } @Override public void writeObject(ObjectOutput output, CompareAndSetFunction object) throws IOException { output.writeLong(object.expect); output.writeLong(object.value); } @Override public CompareAndSetFunction readObject(ObjectInput input) throws IOException, ClassNotFoundException { return new CompareAndSetFunction(input.readLong(), input.readLong()); } } }