/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.internal.util.counters;
import com.hazelcast.internal.memory.GlobalMemoryAccessorRegistry;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import static com.hazelcast.internal.memory.GlobalMemoryAccessorRegistry.MEM;
import static com.hazelcast.util.EmptyStatement.ignore;
import static java.util.concurrent.atomic.AtomicLongFieldUpdater.newUpdater;
/**
* A {@link Counter} that is made to be used by a single writing thread.
* <p>
* It makes use of the lazySet to provide a lower overhead than a volatile write on X86 systems.
* The volatile write requires waiting for the store buffer to be drained which isn't needed for the lazySet.
* <p>
* This counter does not provide padding to prevent false sharing.
* <p>
* One might wonder why not use the AtomicLong.inc. The problem here is that AtomicLong requires a full fence,
* so there is waiting for store and load buffers to be drained. This is more expensive.
* <p>
* One might also wonder why not use the following:
* <pre>
* atomicLong.lazySet(atomicLong.get()+1)
* </pre>
* This causes a lot of syntactic noise due to lack of abstraction.
* A counter.inc() gives a better clue what the intent is.
*/
public abstract class SwCounter implements Counter {
private SwCounter() {
}
/**
* Creates a new SwCounter with 0 as initial value.
*
* @return the created SwCounter, set to zero.
*/
public static SwCounter newSwCounter() {
return newSwCounter(0);
}
/**
* Creates a new SwCounter with the given initial value.
*
* @param initialValue the initial value for the SwCounter.
* @return the created SwCounter.
*/
public static SwCounter newSwCounter(long initialValue) {
return GlobalMemoryAccessorRegistry.MEM_AVAILABLE
? new UnsafeSwCounter(initialValue) : new SafeSwCounter(initialValue);
}
/**
* The UnsafeSwCounter relies on the same {@link sun.misc.Unsafe#putOrderedLong(Object, long, long)} as the
* {@link AtomicLongFieldUpdater#lazySet(Object, long)} but it removes all kinds of checks.
* <p>
* For the AtomicLongFieldUpdater, these checks are needed since an arbitrary object can be passed to the
* lazySet method and that needs to be verified. In our case we always pass the UnsafeSwCounter instance so
* there is no need for these checks.
*/
static final class UnsafeSwCounter extends SwCounter {
private static final long OFFSET;
static {
Field field = null;
try {
field = UnsafeSwCounter.class.getDeclaredField("value");
} catch (NoSuchFieldException ignore) {
ignore(ignore);
}
OFFSET = MEM.objectFieldOffset(field);
}
private long localValue;
private volatile long value;
protected UnsafeSwCounter(long initialValue) {
this.value = initialValue;
}
@Override
@SuppressWarnings("checkstyle:innerassignment")
public long inc() {
long newLocalValue = localValue += 1;
MEM.putOrderedLong(this, OFFSET, newLocalValue);
return newLocalValue;
}
@Override
@SuppressWarnings("checkstyle:innerassignment")
public long inc(long amount) {
long newLocalValue = localValue += amount;
MEM.putOrderedLong(this, OFFSET, newLocalValue);
return newLocalValue;
}
@Override
public long get() {
return value;
}
@Override
public String toString() {
return "Counter{value=" + value + '}';
}
}
/**
* Makes use of the AtomicLongFieldUpdater.lazySet.
*/
static final class SafeSwCounter extends SwCounter {
private static final AtomicLongFieldUpdater<SafeSwCounter> COUNTER = newUpdater(SafeSwCounter.class, "value");
private volatile long value;
protected SafeSwCounter(long initialValue) {
this.value = initialValue;
}
@Override
public long inc() {
final long newValue = value + 1;
COUNTER.lazySet(this, newValue);
return newValue;
}
@Override
public long inc(long amount) {
final long newValue = value + amount;
COUNTER.lazySet(this, newValue);
return newValue;
}
@Override
public long get() {
return value;
}
@Override
public String toString() {
return "Counter{value=" + value + '}';
}
}
}