Skip to content

Commit

Permalink
Use VarHandles in generated code (#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-manes committed Jan 4, 2021
1 parent eca2e50 commit f1ba429
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ private void writeJavaFile() throws IOException {

for (TypeSpec typeSpec : factoryTypes) {
JavaFile.builder(getClass().getPackage().getName(), typeSpec)
.addFileComment(header, Year.now(timeZone))
.indent(" ")
.build()
.writeTo(directory);
.addFileComment(header, Year.now(timeZone))
.indent(" ")
.build()
.writeTo(directory);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import java.lang.reflect.Constructor;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;

Expand All @@ -35,8 +34,8 @@ public final class LocalCacheSelectorCode {

private LocalCacheSelectorCode() {
block = CodeBlock.builder()
.addStatement("$1T sb = new $1T(\"$2N.\")", StringBuilder.class,
((ClassName)LOCAL_CACHE_FACTORY).packageName());
.addStatement("$1T sb = new $1T(\"$2N.\")",
StringBuilder.class, LOCAL_CACHE_FACTORY.packageName());
}

private LocalCacheSelectorCode keys() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import com.github.benmanes.caffeine.cache.node.Finalize;
import com.github.benmanes.caffeine.cache.node.NodeContext;
import com.github.benmanes.caffeine.cache.node.NodeRule;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
Expand Down Expand Up @@ -142,10 +143,10 @@ private void writeJavaFile() throws IOException {

for (TypeSpec node : nodeTypes) {
JavaFile.builder(getClass().getPackage().getName(), node)
.addFileComment(header, Year.now(timeZone))
.indent(" ")
.build()
.writeTo(directory);
.addFileComment(header, Year.now(timeZone))
.indent(" ")
.build()
.writeTo(directory);
}
}

Expand All @@ -172,7 +173,16 @@ private void addClassJavaDoc() {
}

private void addConstants() {
Modifier[] modifiers = {Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL};
List<String> constants = ImmutableList.of("key", "value", "accessTime", "writeTime");
for (String constant : constants) {
String name = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, constant);
nodeFactory.addField(FieldSpec.builder(String.class, name)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", constant)
.build());
}

Modifier[] modifiers = { Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL };
nodeFactory.addField(FieldSpec.builder(Object.class, RETIRED_STRONG_KEY, modifiers)
.initializer("new Object()")
.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@
*/
package com.github.benmanes.caffeine.cache;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.ReferenceQueue;

import javax.lang.model.element.Modifier;

import com.google.common.base.CaseFormat;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
Expand Down Expand Up @@ -63,10 +61,11 @@ public final class Specifications {
public static final ParameterSpec valueRefQueueSpec =
ParameterSpec.builder(vRefQueueType, "valueReferenceQueue").build();

public static final TypeName UNSAFE_ACCESS =
ClassName.get("com.github.benmanes.caffeine.cache", "UnsafeAccess");
public static final TypeName METHOD_HANDLES = ClassName.get(MethodHandles.class);
public static final TypeName LOOKUP = ClassName.get(MethodHandles.Lookup.class);
public static final TypeName VAR_HANDLE = ClassName.get(VarHandle.class);

public static final TypeName LOCAL_CACHE_FACTORY =
public static final ClassName LOCAL_CACHE_FACTORY =
ClassName.get(PACKAGE_NAME, "LocalCacheFactory");
public static final ParameterizedTypeName NODE_FACTORY = ParameterizedTypeName.get(
ClassName.get(PACKAGE_NAME, "NodeFactory"), kTypeVar, vTypeVar);
Expand Down Expand Up @@ -109,20 +108,4 @@ public final class Specifications {
ClassName.get(PACKAGE_NAME, "FrequencySketch"), kTypeVar);

private Specifications() {}

/** Returns the offset constant to this variable. */
public static String offsetName(String varName) {
return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, varName) + "_OFFSET";
}

/** Creates a public static field with an Unsafe address offset. */
public static FieldSpec newFieldOffset(String className, String varName) {
String fieldName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, varName);
return FieldSpec
.builder(long.class, offsetName(varName),
Modifier.PROTECTED, Modifier.STATIC, Modifier.FINAL)
.initializer("$T.objectFieldOffset($T.class, $L.$L)", UNSAFE_ACCESS,
ClassName.bestGuess(className), LOCAL_CACHE_FACTORY, fieldName)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
*/
package com.github.benmanes.caffeine.cache.node;

import static com.github.benmanes.caffeine.cache.Specifications.UNSAFE_ACCESS;
import static com.github.benmanes.caffeine.cache.Specifications.keyRefQueueSpec;
import static com.github.benmanes.caffeine.cache.Specifications.keyRefSpec;
import static com.github.benmanes.caffeine.cache.Specifications.keySpec;
import static com.github.benmanes.caffeine.cache.Specifications.offsetName;
import static com.github.benmanes.caffeine.cache.Specifications.valueRefQueueSpec;
import static com.github.benmanes.caffeine.cache.Specifications.valueSpec;

Expand Down Expand Up @@ -91,14 +89,14 @@ private void callSiblingConstructor() {
}

private void assignKeyRefAndValue() {
context.constructorByKeyRef.addStatement("$T.UNSAFE.putObject(this, $N, $N)",
UNSAFE_ACCESS, offsetName("key"), "keyReference");
context.constructorByKeyRef.addStatement("$L.set(this, $N)",
varHandleName("key"), "keyReference");
if (isStrongValues()) {
context.constructorByKeyRef.addStatement("$T.UNSAFE.putObject(this, $N, $N)",
UNSAFE_ACCESS, offsetName("value"), "value");
context.constructorByKeyRef.addStatement("$L.set(this, $N)",
varHandleName("value"), "value");
} else {
context.constructorByKeyRef.addStatement("$T.UNSAFE.putObject(this, $N, new $T($N, $N, $N))",
UNSAFE_ACCESS, offsetName("value"), valueReferenceType(), "keyReference",
context.constructorByKeyRef.addStatement("$L.set(this, new $T($N, $N, $N))",
varHandleName("value"), valueReferenceType(), "keyReference",
"value", "valueReferenceQueue");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
package com.github.benmanes.caffeine.cache.node;

import static com.github.benmanes.caffeine.cache.Specifications.NODE;
import static com.github.benmanes.caffeine.cache.Specifications.UNSAFE_ACCESS;
import static com.github.benmanes.caffeine.cache.Specifications.newFieldOffset;
import static com.github.benmanes.caffeine.cache.Specifications.offsetName;
import static org.apache.commons.lang3.StringUtils.capitalize;

import javax.lang.model.element.Modifier;
Expand Down Expand Up @@ -86,23 +83,21 @@ private void addLink(String method, String varName) {
private void addVariableTime(String varName) {
MethodSpec getter = MethodSpec.methodBuilder("getVariableTime")
.addModifiers(Modifier.PUBLIC)
.addStatement("return $T.UNSAFE.getLong(this, $N)",
UNSAFE_ACCESS, offsetName(varName))
.addStatement("return (long) $L.get(this)", varHandleName(varName))
.returns(long.class)
.build();
MethodSpec setter = MethodSpec.methodBuilder("setVariableTime")
.addModifiers(Modifier.PUBLIC)
.addParameter(long.class, varName)
.addStatement("$T.UNSAFE.putLong(this, $N, $N)",
UNSAFE_ACCESS, offsetName(varName), varName)
.addStatement("$L.set(this, $N)", varHandleName(varName), varName)
.build();
MethodSpec cas = MethodSpec.methodBuilder("casVariableTime")
.addModifiers(Modifier.PUBLIC)
.addParameter(long.class, "expect")
.addParameter(long.class, "update")
.returns(boolean.class)
.addStatement("return ($N == $N)\n&& $T.UNSAFE.compareAndSwapLong(this, $N, $N, $N)",
varName, "expect", UNSAFE_ACCESS, offsetName(varName), "expect", "update")
.addStatement("return ($N == $N)\n&& $L.compareAndSet(this, $N, $N)",
varName, "expect", varHandleName(varName), "expect", "update")
.build();
context.nodeSubtype
.addMethod(getter)
Expand All @@ -114,21 +109,24 @@ private void addAccessExpiration() {
if (!context.generateFeatures.contains(Feature.EXPIRE_ACCESS)) {
return;
}
context.nodeSubtype.addField(newFieldOffset(context.className, "accessTime"))

context.nodeSubtype
.addField(long.class, "accessTime", Modifier.VOLATILE)
.addMethod(newGetter(Strength.STRONG, TypeName.LONG, "accessTime", Visibility.LAZY))
.addMethod(newSetter(TypeName.LONG, "accessTime", Visibility.LAZY));
.addMethod(newGetter(Strength.STRONG, TypeName.LONG, "accessTime", Visibility.PLAIN))
.addMethod(newSetter(TypeName.LONG, "accessTime", Visibility.PLAIN));
addVarHandle("accessTime", long.class);
addTimeConstructorAssignment(context.constructorByKey, "accessTime");
addTimeConstructorAssignment(context.constructorByKeyRef, "accessTime");
}

private void addWriteExpiration() {
if (!Feature.useWriteTime(context.parentFeatures)
&& Feature.useWriteTime(context.generateFeatures)) {
context.nodeSubtype.addField(newFieldOffset(context.className, "writeTime"))
context.nodeSubtype
.addField(long.class, "writeTime", Modifier.VOLATILE)
.addMethod(newGetter(Strength.STRONG, TypeName.LONG, "writeTime", Visibility.LAZY))
.addMethod(newSetter(TypeName.LONG, "writeTime", Visibility.LAZY));
.addMethod(newGetter(Strength.STRONG, TypeName.LONG, "writeTime", Visibility.PLAIN))
.addMethod(newSetter(TypeName.LONG, "writeTime", Visibility.PLAIN));
addVarHandle("writeTime", long.class);
addTimeConstructorAssignment(context.constructorByKey, "writeTime");
addTimeConstructorAssignment(context.constructorByKeyRef, "writeTime");
}
Expand All @@ -143,14 +141,13 @@ private void addRefreshExpiration() {
.addParameter(long.class, "expect")
.addParameter(long.class, "update")
.returns(boolean.class)
.addStatement("return ($N == $N)\n&& $T.UNSAFE.compareAndSwapLong(this, $N, $N, $N)",
"writeTime", "expect", UNSAFE_ACCESS, offsetName("writeTime"), "expect", "update")
.addStatement("return ($N == $N)\n&& $L.compareAndSet(this, $N, $N)",
"writeTime", "expect", varHandleName("writeTime"), "expect", "update")
.build());
}

/** Adds a long constructor assignment. */
private void addTimeConstructorAssignment(MethodSpec.Builder constructor, String field) {
constructor.addStatement("$T.UNSAFE.putLong(this, $N, $N)",
UNSAFE_ACCESS, offsetName(field), "now");
constructor.addStatement("$L.set(this, $N)", varHandleName(field), "now");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
import static com.github.benmanes.caffeine.cache.Specifications.DEAD_WEAK_KEY;
import static com.github.benmanes.caffeine.cache.Specifications.RETIRED_STRONG_KEY;
import static com.github.benmanes.caffeine.cache.Specifications.RETIRED_WEAK_KEY;
import static com.github.benmanes.caffeine.cache.Specifications.UNSAFE_ACCESS;
import static com.github.benmanes.caffeine.cache.Specifications.offsetName;

import java.lang.ref.Reference;

Expand Down Expand Up @@ -76,13 +74,12 @@ private void addState(String checkName, String actionName, String arg, boolean f
// Set the value to null only when dead, as otherwise the explicit removal of an expired async
// value will be notified as explicit rather than expired due to the isComputingAsync() check
if (finalized) {
action.addStatement("$T.UNSAFE.putObject(this, $N, null)",
UNSAFE_ACCESS, offsetName("value"));
action.addStatement("$L.set(this, null)", varHandleName("value"));
}
} else {
action.addStatement("(($T<V>) getValueReference()).clear()", Reference.class);
}
action.addStatement("$T.UNSAFE.putObject(this, $N, $N)", UNSAFE_ACCESS, offsetName("key"), arg);
action.addStatement("$L.set(this, $N)", varHandleName("key"), arg);
context.nodeSubtype.addMethod(action.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package com.github.benmanes.caffeine.cache.node;

import static com.github.benmanes.caffeine.cache.Specifications.kTypeVar;
import static com.github.benmanes.caffeine.cache.Specifications.newFieldOffset;

import javax.lang.model.element.Modifier;

Expand All @@ -38,10 +37,10 @@ protected boolean applies() {
@Override
protected void execute() {
context.nodeSubtype
.addField(newFieldOffset(context.className, "key"))
.addField(newKeyField())
.addMethod(newGetter(keyStrength(), kTypeVar, "key", Visibility.LAZY))
.addMethod(newGetter(keyStrength(), kTypeVar, "key", Visibility.PLAIN))
.addMethod(newGetRef("key"));
addVarHandle("key", Object.class);
}

private FieldSpec newKeyField() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
*/
package com.github.benmanes.caffeine.cache.node;

import static com.github.benmanes.caffeine.cache.Specifications.UNSAFE_ACCESS;
import static com.github.benmanes.caffeine.cache.Specifications.newFieldOffset;
import static com.github.benmanes.caffeine.cache.Specifications.offsetName;
import static com.github.benmanes.caffeine.cache.Specifications.vRefQueueType;
import static com.github.benmanes.caffeine.cache.Specifications.vTypeVar;

Expand Down Expand Up @@ -45,12 +42,12 @@ protected boolean applies() {
@Override
protected void execute() {
context.nodeSubtype
.addField(newFieldOffset(context.className, "value"))
.addField(newValueField())
.addMethod(newGetter(valueStrength(), vTypeVar, "value", Visibility.LAZY))
.addMethod(newGetter(valueStrength(), vTypeVar, "value", Visibility.PLAIN))
.addMethod(newGetRef("value"))
.addMethod(makeSetValue())
.addMethod(makeContainsValue());
addVarHandle("value", Object.class);
}

private FieldSpec newValueField() {
Expand All @@ -68,12 +65,11 @@ private MethodSpec makeSetValue() {
.addParameter(vRefQueueType, "referenceQueue");

if (isStrongValues()) {
setter.addStatement("$T.UNSAFE.putObject(this, $N, $N)",
UNSAFE_ACCESS, offsetName("value"), "value");
setter.addStatement("$L.set(this, $N)", varHandleName("value"), "value");
} else {
setter.addStatement("(($T<V>) getValueReference()).clear()", Reference.class);
setter.addStatement("$T.UNSAFE.putObject(this, $N, new $T($L, $N, referenceQueue))",
UNSAFE_ACCESS, offsetName("value"), valueReferenceType(), "getKeyReference()", "value");
setter.addStatement("$L.set(this, new $T($L, $N, referenceQueue))",
varHandleName("value"), valueReferenceType(), "getKeyReference()", "value");
}

return setter.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
*/
package com.github.benmanes.caffeine.cache.node;

import static com.github.benmanes.caffeine.cache.Specifications.LOOKUP;
import static com.github.benmanes.caffeine.cache.Specifications.METHOD_HANDLES;

import com.squareup.javapoet.CodeBlock;

/**
* Finishes construction of the node.
*
Expand All @@ -33,5 +38,23 @@ protected void execute() {
.addMethod(context.constructorDefault.build())
.addMethod(context.constructorByKey.build())
.addMethod(context.constructorByKeyRef.build());
addStaticBlock();
}

public void addStaticBlock() {
if (context.varHandles.isEmpty()) {
return;
}
var codeBlock = CodeBlock.builder()
.addStatement("$T lookup = $T.lookup()", LOOKUP, METHOD_HANDLES)
.beginControlFlow("try");
for (var varHandle : context.varHandles) {
varHandle.accept(codeBlock);
}
codeBlock
.nextControlFlow("catch ($T e)", ReflectiveOperationException.class)
.addStatement("throw new ExceptionInInitializerError(e)")
.endControlFlow();
context.nodeSubtype.addStaticBlock(codeBlock.build());
}
}
Loading

0 comments on commit f1ba429

Please sign in to comment.