Skip to content

Commit

Permalink
Adding Atomic Unpadded queues
Browse files Browse the repository at this point in the history
  • Loading branch information
franz1981 committed Feb 7, 2024
1 parent 0dae71c commit 4b295bb
Show file tree
Hide file tree
Showing 53 changed files with 4,655 additions and 129 deletions.
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 @@ -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 Down
Expand Up @@ -33,6 +33,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 +45,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 +58,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 outoutPackage() {
return "org.jctools.queues.atomic";
}

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

JavaParsingAtomicQueueGenerator(String sourceFileName) {
Expand All @@ -98,7 +79,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(outoutPackage());
}

@Override
Expand Down Expand Up @@ -149,13 +130,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 +201,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 +216,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 +247,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(outoutPackage() + ".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 outoutPackage() {
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 outoutPackage() {
return "org.jctools.queues.atomic.unpadded";
}

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

0 comments on commit 4b295bb

Please sign in to comment.