Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Atomic Unpadded queues #389

Merged
merged 2 commits into from Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -15,20 +15,23 @@
import com.github.javaparser.ast.type.PrimitiveType;
import com.github.javaparser.ast.type.Type;

import static org.jctools.queues.util.GeneratorUtils.formatMultilineJavadoc;
import static org.jctools.queues.util.GeneratorUtils.runJCToolsGenerator;

/**
* This generator takes in an JCTools 'ArrayQueue' Java source file and patches {@link sun.misc.Unsafe} accesses into
* atomic {@link java.util.concurrent.atomic.AtomicLongFieldUpdater}. It outputs a Java source file with these patches.
* <p>
* An 'ArrayQueue' is one that is backed by a circular array and use a <code>producerLimit</code> and a
* <code>consumerLimit</code> field to track the positions of each.
*/
public final class JavaParsingAtomicArrayQueueGenerator extends JavaParsingAtomicQueueGenerator {
public class JavaParsingAtomicArrayQueueGenerator extends JavaParsingAtomicQueueGenerator {

public static void main(String[] args) throws Exception {
main(JavaParsingAtomicArrayQueueGenerator.class, args);
runJCToolsGenerator(JavaParsingAtomicArrayQueueGenerator.class, args);
}

JavaParsingAtomicArrayQueueGenerator(String sourceFileName) {
public JavaParsingAtomicArrayQueueGenerator(String sourceFileName) {
super(sourceFileName);
}

Expand Down Expand Up @@ -67,7 +70,7 @@ public void visit(ClassOrInterfaceDeclaration node, Void arg) {

node.setJavadocComment(formatMultilineJavadoc(0,
"NOTE: This class was automatically generated by "
+ JavaParsingAtomicArrayQueueGenerator.class.getName(),
+ getClass().getName(),
"which can found in the jctools-build module. The original source file is " + sourceFileName + ".")
+ node.getJavadocComment().orElse(new JavadocComment("")).getContent());
}
Expand Down
Expand Up @@ -13,22 +13,27 @@
import com.github.javaparser.ast.type.PrimitiveType;
import com.github.javaparser.ast.type.Type;

import static org.jctools.queues.util.GeneratorUtils.formatMultilineJavadoc;
import static org.jctools.queues.util.GeneratorUtils.runJCToolsGenerator;

/**
* This generator takes in an JCTools 'LinkedQueue' Java source file and patches {@link sun.misc.Unsafe} accesses into
* atomic {@link java.util.concurrent.atomic.AtomicLongFieldUpdater}. It outputs a Java source file with these patches.
* <p>
* An 'LinkedQueue' is one that is backed by a linked list and use a <code>producerNode</code> and a
* <code>consumerNode</code> field to track the positions of each.
*/
public final class JavaParsingAtomicLinkedQueueGenerator extends JavaParsingAtomicQueueGenerator {
private static final String MPSC_LINKED_ATOMIC_QUEUE_NAME = "MpscLinkedAtomicQueue";
public class JavaParsingAtomicLinkedQueueGenerator extends JavaParsingAtomicQueueGenerator {

private final String mpscLinkedQueueName;

public static void main(String[] args) throws Exception {
main(JavaParsingAtomicLinkedQueueGenerator.class, args);
runJCToolsGenerator(JavaParsingAtomicLinkedQueueGenerator.class, args);
}

JavaParsingAtomicLinkedQueueGenerator(String sourceFileName) {
public JavaParsingAtomicLinkedQueueGenerator(String sourceFileName) {
super(sourceFileName);
this.mpscLinkedQueueName = atomicQueueName();
}

@Override
Expand All @@ -40,13 +45,17 @@ public void visit(ConstructorDeclaration n, Void arg) {
if (nameAsString.equals("WeakIterator"))
return;
n.setName(translateQueueName(nameAsString));
if (MPSC_LINKED_ATOMIC_QUEUE_NAME.equals(nameAsString)) {
if (mpscLinkedQueueName.equals(nameAsString)) {
// Special case for MPSC because the Unsafe variant has a static factory method and a protected constructor.
n.setModifier(Keyword.PROTECTED, false);
n.setModifier(Keyword.PUBLIC, true);
}
}

private String atomicQueueName() {
return "MpscLinked" + queueClassNamePrefix() + "Queue";
}

@Override
public void visit(ClassOrInterfaceDeclaration node, Void arg) {
super.visit(node, arg);
Expand All @@ -56,7 +65,7 @@ public void visit(ClassOrInterfaceDeclaration node, Void arg) {
String nameAsString = node.getNameAsString();
if (nameAsString.contains("Queue"))
node.setName(translateQueueName(nameAsString));
if (MPSC_LINKED_ATOMIC_QUEUE_NAME.equals(nameAsString)) {
if (mpscLinkedQueueName.equals(nameAsString)) {
/*
* Special case for MPSC
*/
Expand All @@ -77,7 +86,7 @@ public void visit(ClassOrInterfaceDeclaration node, Void arg) {

node.setJavadocComment(formatMultilineJavadoc(0,
"NOTE: This class was automatically generated by "
+ JavaParsingAtomicLinkedQueueGenerator.class.getName(),
+ getClass().getName(),
"which can found in the jctools-build module. The original source file is " + sourceFileName + ".")
+ node.getJavadocComment().orElse(new JavadocComment("")).getContent());
}
Expand Down
@@ -1,7 +1,5 @@
package org.jctools.queues.atomic;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -33,6 +31,7 @@
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import org.jctools.queues.util.JCToolsGenerator;

/**
* Base class of the atomic queue generators. These generators work by parsing a Java source file using
Expand All @@ -44,7 +43,7 @@
* These generators are coupled with the structure and naming of fields, variables and methods and are not suitable for
* general purpose use.
*/
abstract class JavaParsingAtomicQueueGenerator extends VoidVisitorAdapter<Void> {
public abstract class JavaParsingAtomicQueueGenerator extends VoidVisitorAdapter<Void> implements JCToolsGenerator {

/**
* When set on a class using a single line comment, the class has fields that have unsafe 'ordered' reads and
Expand All @@ -57,34 +56,14 @@ abstract class JavaParsingAtomicQueueGenerator extends VoidVisitorAdapter<Void>
*/
protected static final String GEN_DIRECTIVE_METHOD_IGNORE = "$gen:ignore";

protected static final String INDENT_LEVEL = " ";
protected final String sourceFileName;

static void main(Class<? extends JavaParsingAtomicQueueGenerator> generatorClass, String[] args) throws Exception {

if (args.length < 2) {
throw new IllegalArgumentException("Usage: outputDirectory inputSourceFiles");
}

File outputDirectory = new File(args[0]);

for (int i = 1; i < args.length; i++) {
File file = new File(args[i]);
System.out.println("Processing " + file);
CompilationUnit cu = new JavaParser().parse(file).getResult().get();
JavaParsingAtomicQueueGenerator generator = buildGenerator(generatorClass, file.getName());
generator.visit(cu, null);

generator.organiseImports(cu);

String outputFileName = generator.translateQueueName(file.getName().replace(".java", "")) + ".java";

try (FileWriter writer = new FileWriter(new File(outputDirectory, outputFileName))) {
writer.write(cu.toString());
}
protected String outputPackage() {
return "org.jctools.queues.atomic";
}

System.out.println("Saved to " + outputFileName);
}
protected String queueClassNamePrefix() {
return "Atomic";
}

JavaParsingAtomicQueueGenerator(String sourceFileName) {
Expand All @@ -98,7 +77,7 @@ static void main(Class<? extends JavaParsingAtomicQueueGenerator> generatorClass
public void visit(PackageDeclaration n, Void arg) {
super.visit(n, arg);
// Change the package of the output
n.setName("org.jctools.queues.atomic");
n.setName(outputPackage());
}

@Override
Expand Down Expand Up @@ -149,13 +128,14 @@ protected void removeStaticFieldsAndInitialisers(ClassOrInterfaceDeclaration nod
}
}

protected String translateQueueName(String qName) {
@Override
public String translateQueueName(String qName) {
if (qName.contains("LinkedQueue") || qName.contains("LinkedArrayQueue")) {
return qName.replace("Linked", "LinkedAtomic");
return qName.replace("Linked", "Linked" + queueClassNamePrefix());
}

if (qName.contains("ArrayQueue")) {
return qName.replace("ArrayQueue", "AtomicArrayQueue");
return qName.replace("ArrayQueue", queueClassNamePrefix() + "ArrayQueue");
}

throw new IllegalArgumentException("Unexpected queue name: " + qName);
Expand Down Expand Up @@ -219,7 +199,7 @@ protected void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) {
// ignore the JDK parent
break;
case "BaseLinkedQueue":
parent.setName("BaseLinkedAtomicQueue");
parent.setName("BaseLinked" + queueClassNamePrefix() + "Queue");
break;
case "ConcurrentCircularArrayQueue":
parent.setName("AtomicReferenceArrayQueue");
Expand All @@ -234,7 +214,14 @@ protected void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) {
}
}
}
protected void organiseImports(CompilationUnit cu) {

@Override
public void cleanupComments(CompilationUnit cu) {
// nop
}

@Override
public void organiseImports(CompilationUnit cu) {
List<ImportDeclaration> importDecls = new ArrayList<>();

// remove irrelevant imports
Expand All @@ -258,27 +245,13 @@ protected void organiseImports(CompilationUnit cu) {
cu.addImport(new ImportDeclaration("java.util.concurrent.atomic", false, true));

cu.addImport(new ImportDeclaration("org.jctools.queues", false, true));
cu.addImport(staticImportDeclaration("org.jctools.queues.atomic.AtomicQueueUtil"));
cu.addImport(staticImportDeclaration(outputPackage() + ".AtomicQueueUtil"));
}

protected String capitalise(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}

protected String formatMultilineJavadoc(int indent, String... lines) {
String indentation = "";
for (int i = 0; i < indent; i++) {
indentation += INDENT_LEVEL;
}

String out = "\n";
for (String line : lines) {
out += indentation + " * " + line + "\n";
}
out += indentation + " ";
return out;
}

/**
* Generates something like
* <code>P_INDEX_UPDATER.lazySet(this, newValue)</code>
Expand Down
@@ -0,0 +1,41 @@
package org.jctools.queues.atomic.unpadded;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import org.jctools.queues.atomic.JavaParsingAtomicArrayQueueGenerator;

import static org.jctools.queues.util.GeneratorUtils.cleanupPaddingComments;
import static org.jctools.queues.util.GeneratorUtils.removePaddingFields;
import static org.jctools.queues.util.GeneratorUtils.runJCToolsGenerator;

public class JavaParsingAtomicUnpaddedArrayQueueGenerator extends JavaParsingAtomicArrayQueueGenerator {
public static void main(String[] args) throws Exception {
runJCToolsGenerator(JavaParsingAtomicUnpaddedArrayQueueGenerator.class, args);
}

public JavaParsingAtomicUnpaddedArrayQueueGenerator(String sourceFileName) {
super(sourceFileName);
}

@Override
public void cleanupComments(CompilationUnit cu) {
super.cleanupComments(cu);
cleanupPaddingComments(cu);
}

@Override
public void visit(ClassOrInterfaceDeclaration node, Void arg) {
super.visit(node, arg);
removePaddingFields(node);
}

@Override
protected String outputPackage() {
return "org.jctools.queues.atomic.unpadded";
}

@Override
protected String queueClassNamePrefix() {
return "AtomicUnpadded";
}
}
@@ -0,0 +1,41 @@
package org.jctools.queues.atomic.unpadded;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import org.jctools.queues.atomic.JavaParsingAtomicLinkedQueueGenerator;

import static org.jctools.queues.util.GeneratorUtils.cleanupPaddingComments;
import static org.jctools.queues.util.GeneratorUtils.removePaddingFields;
import static org.jctools.queues.util.GeneratorUtils.runJCToolsGenerator;

public class JavaParsingAtomicUnpaddedLinkedQueueGenerator extends JavaParsingAtomicLinkedQueueGenerator {
public static void main(String[] args) throws Exception {
runJCToolsGenerator(JavaParsingAtomicUnpaddedLinkedQueueGenerator.class, args);
}

public JavaParsingAtomicUnpaddedLinkedQueueGenerator(String sourceFileName) {
super(sourceFileName);
}

@Override
public void cleanupComments(CompilationUnit cu) {
super.cleanupComments(cu);
cleanupPaddingComments(cu);
}

@Override
public void visit(ClassOrInterfaceDeclaration node, Void arg) {
super.visit(node, arg);
removePaddingFields(node);
}

@Override
protected String outputPackage() {
return "org.jctools.queues.atomic.unpadded";
}

@Override
protected String queueClassNamePrefix() {
return "AtomicUnpadded";
}
}