Skip to content

Commit

Permalink
AccessMode.CONCURRENT's VarHandle should be a constant (Fixes #5216)
Browse files Browse the repository at this point in the history
  • Loading branch information
franz1981 authored and vietj committed May 30, 2024
1 parent d32655c commit d262280
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 35 deletions.
36 changes: 1 addition & 35 deletions src/main/java/io/vertx/core/spi/context/storage/AccessMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,41 +22,7 @@ public interface AccessMode {
/**
* This access mode provides concurrent access to context local storage with thread safety and atomicity.
*/
AccessMode CONCURRENT = new AccessMode() {

private final VarHandle LOCALS_UPDATER = MethodHandles.arrayElementVarHandle(Object[].class);

@Override
public Object get(Object[] locals, int idx) {
return LOCALS_UPDATER.getVolatile(locals, idx);
}

@Override
public void put(Object[] locals, int idx, Object value) {
LOCALS_UPDATER.setRelease(locals, idx, value);
}

@Override
public Object getOrCreate(Object[] locals, int idx, Supplier<Object> initialValueSupplier) {
Object res;
while (true) {
res = LOCALS_UPDATER.getVolatile(locals, idx);
if (res != null) {
break;
}
Object initial = initialValueSupplier.get();
if (initial == null) {
throw new IllegalStateException();
}
if (LOCALS_UPDATER.compareAndSet(locals, idx, null, initial)) {
res = initial;
break;
}
}
return res;
}
};

AccessMode CONCURRENT = ConcurrentAccessMode.INSTANCE;
/**
* Return the object at index {@code idx} in the {@code locals} array.
* @param locals the array
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.vertx.core.spi.context.storage;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.function.Supplier;

final class ConcurrentAccessMode implements AccessMode {

public static final ConcurrentAccessMode INSTANCE = new ConcurrentAccessMode();

private static final VarHandle LOCALS_UPDATER = MethodHandles.arrayElementVarHandle(Object[].class);

private ConcurrentAccessMode() {
}

@Override
public Object get(Object[] locals, int idx) {
return LOCALS_UPDATER.getVolatile(locals, idx);
}

@Override
public void put(Object[] locals, int idx, Object value) {
LOCALS_UPDATER.setRelease(locals, idx, value);
}

@Override
public Object getOrCreate(Object[] locals, int idx, Supplier<Object> initialValueSupplier) {
Object res;
while (true) {
res = LOCALS_UPDATER.getVolatile(locals, idx);
if (res != null) {
break;
}
Object initial = initialValueSupplier.get();
if (initial == null) {
throw new IllegalStateException();
}
if (LOCALS_UPDATER.compareAndSet(locals, idx, null, initial)) {
res = initial;
break;
}
}
return res;
}
}


122 changes: 122 additions & 0 deletions src/test/benchmarks/io/vertx/benchmarks/AccessModeBenchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package io.vertx.benchmarks;

import io.vertx.core.spi.context.storage.AccessMode;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@State(Scope.Benchmark)
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Threads(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(2)
public class AccessModeBenchmark {

/**
* This is to mimic https://shipilev.net/jvm/anatomy-quarks/17-trust-nonstatic-final-fields/#_practice
* the static_final case, despite anonymous classes "seems" to not accept static final fields while declared
* on interfaces, they won't provide the same semantics for final fields, which instead are "just" final fields (!!)
*/
private interface OldAccessMode {

AccessMode CONCURRENT = new AccessMode() {

final VarHandle LOCALS_UPDATER = MethodHandles.arrayElementVarHandle(Object[].class);

@Override
public Object get(Object[] locals, int idx) {
return LOCALS_UPDATER.getVolatile(locals, idx);
}

@Override
public void put(Object[] locals, int idx, Object value) {
LOCALS_UPDATER.setRelease(locals, idx, value);
}

@Override
public Object getOrCreate(Object[] locals, int index, Supplier<Object> initialValueSupplier) {
Object res;
while (true) {
res = LOCALS_UPDATER.getVolatile(locals, index);
if (res != null) {
break;
}
Object initial = initialValueSupplier.get();
if (initial == null) {
throw new IllegalStateException();
}
if (LOCALS_UPDATER.compareAndSet(locals, index, null, initial)) {
res = initial;
break;
}
}
return res;
}
};
}

@State(Scope.Thread)
public static class FakeLocalStorage {

final Object[] locals;

public FakeLocalStorage() {
locals = new Object[1];
}

public Object get(int index, AccessMode accessMode) {
return accessMode.get(locals, index);
}

public void put(int index, Object value, AccessMode accessMode) {
accessMode.put(locals, index, value);
}
}

public enum AccessModeType {
OLD, NEW
}

private int index;

@Setup
public void setup() {
index = 0;
}

@Benchmark
public Object getOld(FakeLocalStorage fakeLocalStorage) {
return fakeLocalStorage.get(index, OldAccessMode.CONCURRENT);
}

@Benchmark
public void putOld(FakeLocalStorage fakeLocalStorage) {
fakeLocalStorage.put(index, Boolean.TRUE, OldAccessMode.CONCURRENT);
}

@Benchmark
public Object getNew(FakeLocalStorage fakeLocalStorage) {
return fakeLocalStorage.get(index, AccessMode.CONCURRENT);
}

@Benchmark
public void putNew(FakeLocalStorage fakeLocalStorage) {
fakeLocalStorage.put(index, Boolean.TRUE, AccessMode.CONCURRENT);
}

}

0 comments on commit d262280

Please sign in to comment.