package ch.unibe.scg.cells; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import javax.inject.Inject; import javax.inject.Provider; import ch.unibe.scg.cells.CounterModule.CounterLong; import ch.unibe.scg.cells.CounterModule.CounterName; /** * Represents a local {@link Counter}, that can be used inside {@link InMemoryPipeline} * * <p> * This class is {@link java.io.Serializable}, but there's a caveat. * Cells serializes this classes using {@link ShallowSerializingCopy}. * However, if serialization is attempted using a classical {@link java.io.ObjectOutputStream}, * it will throw a {@link UnsupportedOperationException}. */ class LocalCounter implements Counter { private static final long serialVersionUID = 1L; final private String counterName; final private Provider<AtomicLong> count; final private Provider<Set<LocalCounter>> registry; // Provider for scoping. /** * A snapshot of registry at the moment counter registered itself. Used to determine, when * the current registration becomes invalid. */ private volatile Set<LocalCounter> registeredAt; @Inject LocalCounter(@CounterName String counterName, @CounterLong Provider<AtomicLong> count, @CounterRegistry Provider<Set<LocalCounter>> registry) { this.counterName = checkNotNull(counterName); this.count = checkNotNull(count); this.registry = checkNotNull(registry); } @Override public void increment(long cnt) { // In a multi-threading situation, this could be executed multiply. // However, the registry is a synchronized set. It isn't incorrect or expensive: // Counter MAY register itself in the stale registry, but that will be fixed on next increment. Set<LocalCounter> curRegistry = registry.get(); if (curRegistry != registeredAt) { // the old registration is invalid - need to update. curRegistry.add(this); registeredAt = curRegistry; } count.get().addAndGet(cnt); } @Override public String toString() { //TODO: the full annotation name could be a little too much to display. return String.format("%s: %s", counterName, count.get()); } private Object writeReplace() { return new ShallowSerializingCopy.SerializableLiveObject(this); } }