diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 87f26122..8571ea39 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -391,7 +391,7 @@ public class ConfigurationKeys { * If set, any usage of {@code @Synchronized} results in a warning / error. */ public static final ConfigurationKey SYNCHRONIZED_FLAG_USAGE = new ConfigurationKey("lombok.synchronized.flagUsage", "Emit a warning or error if @Synchronized is used.") {}; - + // ----- val ----- /** @@ -623,18 +623,18 @@ public class ConfigurationKeys { * If set, any usage of {@code @Helper} results in a warning / error. */ public static final ConfigurationKey HELPER_FLAG_USAGE = new ConfigurationKey("lombok.helper.flagUsage", "Emit a warning or error if @Helper is used.") {}; - + // ----- LOCKED ----- - + /** * lombok configuration: {@code lombok.locked.flagUsage} = {@code WARNING} | {@code ERROR}. * * If set, any usage of {@code @Locked} results in a warning / error. */ public static final ConfigurationKey LOCKED_FLAG_USAGE = new ConfigurationKey("lombok.locked.flagUsage", "Emit a warning or error if @Locked is used.") {}; - + // ----- onX ----- - + /** * lombok configuration: {@code lombok.onX.flagUsage} = {@code WARNING} | {@code ERROR}. * diff --git a/src/core/lombok/Locked.java b/src/core/lombok/Locked.java new file mode 100644 index 00000000..37adf553 --- /dev/null +++ b/src/core/lombok/Locked.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021-2023 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Guards all statements in an annotation method with a {@link java.util.concurrent.locks.Lock}. + *

+ * For non-static methods, a field named {@code $lock} is used, and for static methods, + * {@code $LOCK} is used. These will be generated if needed and if they aren't already present. + *

+ * Because {@link Locked} uses a different type of lock from {@link Locked.Read} and {@link Locked.Write}, using both in + * the same class using the default names will result in a compile time error. + *

+ * Complete documentation is found at the project lombok features page for @Locked. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Locked { + /** + * Locks using a {@link java.util.concurrent.locks.ReadWriteLock#readLock()}. + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface Read { + /** + * Optional: specify the name of a different field to lock on. It is a compile time error if this field + * doesn't already exist (the fields are automatically generated only if you don't specify a specific name). + * + * @return Name of the field to lock on (blank = generate one). + */ + String value() default ""; + } + + /** + * Locks using a {@link java.util.concurrent.locks.ReadWriteLock#writeLock()}. + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface Write { + /** + * Optional: specify the name of a different field to lock on. It is a compile time error if this field + * doesn't already exist (the fields are automatically generated only if you don't specify a specific name). + * + * @return Name of the field to lock on (blank = generate one). + */ + String value() default ""; + } + + /** + * Optional: specify the name of a different field to lock on. It is a compile time error if this field + * doesn't already exist (the fields are automatically generated only if you don't specify a specific name). + * + * @return Name of the field to lock on (blank = generate one). + */ + String value() default ""; +} diff --git a/src/core/lombok/Synchronized.java b/src/core/lombok/Synchronized.java index 5dff0fb2..1089bea8 100644 --- a/src/core/lombok/Synchronized.java +++ b/src/core/lombok/Synchronized.java @@ -42,7 +42,7 @@ import java.lang.annotation.Target; public @interface Synchronized { /** * Optional: specify the name of a different field to lock on. It is a compile time error if this field - * doesn't already exist (the fields are automatically generated only if you don't specify a specific name. + * doesn't already exist (the fields are automatically generated only if you don't specify a specific name). * * @return Name of the field to lock on (blank = generate one). */ diff --git a/src/core/lombok/eclipse/handlers/HandleLocked.java b/src/core/lombok/eclipse/handlers/HandleLocked.java index 5c3049d2..240a6498 100644 --- a/src/core/lombok/eclipse/handlers/HandleLocked.java +++ b/src/core/lombok/eclipse/handlers/HandleLocked.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; -import lombok.experimental.Locked; +import lombok.Locked; import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; @@ -39,16 +39,18 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleLocked extends EclipseAnnotationHandler { private static final String ANNOTATION_NAME = "@Locked"; - private static final char[][] LOCK_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, - "concurrent".toCharArray(), "locks".toCharArray(), "ReentrantLock".toCharArray() }; - + private static final char[][] LOCK_TYPE_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, + "concurrent".toCharArray(), "locks".toCharArray(), "Lock".toCharArray() }; + private static final char[][] LOCK_IMPL_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, + "concurrent".toCharArray(), "locks".toCharArray(), "ReentrantLock".toCharArray() }; + @Override public void handle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.handle(annotationValue, source, annotationNode, ANNOTATION_NAME, LOCK_CLASS); + HandleLockedUtil.handle(annotationValue, source, annotationNode, ANNOTATION_NAME, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS); } - + @Override public void preHandle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.preHandle(annotationValue, LOCK_CLASS, annotationNode); + HandleLockedUtil.preHandle(annotationValue, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, annotationNode); } } diff --git a/src/core/lombok/eclipse/handlers/HandleLockedRead.java b/src/core/lombok/eclipse/handlers/HandleLockedRead.java index 623a0a1c..a293467a 100644 --- a/src/core/lombok/eclipse/handlers/HandleLockedRead.java +++ b/src/core/lombok/eclipse/handlers/HandleLockedRead.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; -import lombok.experimental.Locked; +import lombok.Locked; import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; @@ -40,16 +40,18 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; public class HandleLockedRead extends EclipseAnnotationHandler { private static final char[] LOCK_METHOD = "readLock".toCharArray(); private static final String ANNOTATION_NAME = "@Locked.Read"; - private static final char[][] LOCK_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, - "concurrent".toCharArray(), "locks".toCharArray(), "ReentrantReadWriteLock".toCharArray() }; - + private static final char[][] LOCK_TYPE_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, + "concurrent".toCharArray(), "locks".toCharArray(), "ReadWriteLock".toCharArray() }; + private static final char[][] LOCK_IMPL_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, + "concurrent".toCharArray(), "locks".toCharArray(), "ReentrantReadWriteLock".toCharArray() }; + @Override public void handle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.handle(annotationValue, source, annotationNode, ANNOTATION_NAME, LOCK_CLASS, LOCK_METHOD); + HandleLockedUtil.handle(annotationValue, source, annotationNode, ANNOTATION_NAME, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, LOCK_METHOD); } - + @Override public void preHandle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.preHandle(annotationValue, LOCK_CLASS, annotationNode); + HandleLockedUtil.preHandle(annotationValue, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, annotationNode); } } diff --git a/src/core/lombok/eclipse/handlers/HandleLockedUtil.java b/src/core/lombok/eclipse/handlers/HandleLockedUtil.java index ceb05709..64fe3591 100644 --- a/src/core/lombok/eclipse/handlers/HandleLockedUtil.java +++ b/src/core/lombok/eclipse/handlers/HandleLockedUtil.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2021 The Project Lombok Authors. +/* Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package lombok.eclipse.handlers; import lombok.ConfigurationKeys; import lombok.core.AST; import lombok.eclipse.EclipseNode; + import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -44,45 +45,44 @@ import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; -import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; -import static lombok.eclipse.handlers.EclipseHandlerUtil.upToTypeNode; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; /** * Container for static utility methods used by the Locked[.Read/Write] annotations for eclipse. */ public final class HandleLockedUtil { - private static final char[] INSTANCE_LOCK_NAME = "lock".toCharArray(); - private static final char[] STATIC_LOCK_NAME = "LOCK".toCharArray(); + private static final char[] INSTANCE_LOCK_NAME = "$lock".toCharArray(); + private static final char[] STATIC_LOCK_NAME = "$LOCK".toCharArray(); private static final char[] LOCK_METHOD = "lock".toCharArray(); private static final char[] UNLOCK_METHOD = "unlock".toCharArray(); - + private HandleLockedUtil() { //Prevent instantiation } - - public static void preHandle(String annotationValue, char[][] lockClass, EclipseNode annotationNode) { + + public static void preHandle(String annotationValue, char[][] lockTypeClass, char[][] lockImplClass, EclipseNode annotationNode) { EclipseNode methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != AST.Kind.METHOD || !(methodNode.get() instanceof MethodDeclaration)) return; MethodDeclaration method = (MethodDeclaration) methodNode.get(); if (method.isAbstract()) return; - - createLockField(annotationValue, annotationNode, lockClass, new AtomicBoolean(method.isStatic()), false); + + createLockField(annotationValue, annotationNode, lockTypeClass, lockImplClass, new AtomicBoolean(method.isStatic()), false); } - - private static char[] createLockField(String name, EclipseNode annotationNode, char[][] lockClass, AtomicBoolean isStatic, boolean reportErrors) { + + private static char[] createLockField(String name, EclipseNode annotationNode, char[][] lockTypeClass, char[][] lockImplClass, AtomicBoolean isStatic, boolean reportErrors) { char[] lockName = name.toCharArray(); - + Annotation source = (Annotation) annotationNode.get(); if (lockName.length == 0) { lockName = isStatic.get() ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; } - + EclipseNode typeNode = upToTypeNode(annotationNode); MemberExistsResult exists = MemberExistsResult.NOT_EXISTS; - - QualifiedTypeReference lockType = new QualifiedTypeReference(lockClass, new long[] { 0, 0, 0, 0, 0 }); - + + QualifiedTypeReference lockType = new QualifiedTypeReference(lockTypeClass, new long[] { 0, 0, 0, 0, 0 }); + if (typeNode != null && typeNode.get() instanceof TypeDeclaration) { TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get(); if (typeDecl.fields != null) for (FieldDeclaration def : typeDecl.fields) { @@ -96,41 +96,41 @@ public final class HandleLockedUtil { return null; } isStatic.set(st); - + if (exists == MemberExistsResult.EXISTS_BY_LOMBOK && !Arrays.deepEquals(lockType.getTypeName(), def.type.getTypeName())) { annotationNode.addError("Expected field " + new String(lockName) + " to be of type " + lockType + - " but got type " + def.type + "! Did you mix @Locked with @Locked.Read/Write on the same generated field?"); + " but got type " + def.type + ". Did you mix @Locked with @Locked.Read/Write on the same generated field?"); return null; } break; } } } - + if (exists == MemberExistsResult.NOT_EXISTS) { FieldDeclaration fieldDecl = setGeneratedBy(new FieldDeclaration(lockName, 0, -1), source); fieldDecl.declarationSourceEnd = -1; - + fieldDecl.modifiers = (isStatic.get() ? Modifier.STATIC : 0) | Modifier.FINAL | Modifier.PRIVATE; - + AllocationExpression lockAlloc = setGeneratedBy(new AllocationExpression(), source); - lockAlloc.type = setGeneratedBy(new QualifiedTypeReference(lockClass, new long[] { 0, 0, 0, 0, 0 }), source); - fieldDecl.type = setGeneratedBy(new QualifiedTypeReference(lockClass, new long[] { 0, 0, 0, 0, 0 }), source); + lockAlloc.type = setGeneratedBy(new QualifiedTypeReference(lockImplClass, new long[] { 0, 0, 0, 0, 0 }), source); + fieldDecl.type = setGeneratedBy(new QualifiedTypeReference(lockTypeClass, new long[] { 0, 0, 0, 0, 0 }), source); fieldDecl.initialization = lockAlloc; injectField(annotationNode.up().up(), fieldDecl); } - + return lockName; } - + /** * See {@link #handle(String, Annotation, EclipseNode, String, char[][], char[])} for * {@code lockableMethodName = null}. */ - public static void handle(String annotationValue, Annotation ast, EclipseNode annotationNode, String annotationName, char[][] lockClass) { - handle(annotationValue, ast, annotationNode, annotationName, lockClass, null); + public static void handle(String annotationValue, Annotation ast, EclipseNode annotationNode, String annotationName, char[][] lockTypeClass, char[][] lockImplClass) { + handle(annotationValue, ast, annotationNode, annotationName, lockTypeClass, lockImplClass, null); } - + /** * Called when an annotation is found that is likely to match the annotation you're interested in. * @@ -144,69 +144,72 @@ public final class HandleLockedUtil { * to travel back up the chain (something javac AST can't do) to the parent of the annotation, as well * as access useful methods such as generating warnings or errors focused on the annotation. * @param annotationName The name of the annotation to use when referencing it in errors. - * @param lockClass The fully qualified name of the type of lock to use (e.g. Reentrant[Read/Write]Lock). + * @param lockTypeClass The fully qualified type of the variable when generating a lock to use. + * @param lockImplClass Call the constructor of this fully qualified classname to generate a lock to use. * @param lockableMethodName The name of the method in the {@code lockClass} that returns a * {@link java.util.concurrent.locks.Lock} object. When this is {@code null}, it is assumed that {@code lockClass} * itself can be locked/unlocked. */ public static void handle(String annotationValue, Annotation source, EclipseNode annotationNode, - String annotationName, char[][] lockClass, char[] lockableMethodName) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.LOCKED_FLAG_USAGE, annotationName); - + String annotationName, char[][] lockTypeClass, char[][] lockImplClass, char[] lockableMethodName) { + + handleFlagUsage(annotationNode, ConfigurationKeys.LOCKED_FLAG_USAGE, annotationName); + int p1 = source.sourceStart -1; int p2 = source.sourceStart -2; long pos = (((long) p1) << 32) | p2; - + EclipseNode methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != AST.Kind.METHOD || !(methodNode.get() instanceof MethodDeclaration)) { annotationNode.addError(annotationName + " is legal only on methods."); return; } - + MethodDeclaration method = (MethodDeclaration) methodNode.get(); if (method.isAbstract()) { annotationNode.addError(annotationName + " is legal only on concrete methods."); return; } - + EclipseNode typeNode = upToTypeNode(annotationNode); if (!isClassOrEnum(typeNode)) { annotationNode.addError(annotationName + " is legal only on methods in classes and enums."); return; } - + AtomicBoolean isStatic = new AtomicBoolean(method.isStatic()); - char[] lockName = createLockField(annotationValue, annotationNode, lockClass, isStatic, true); + char[] lockName = createLockField(annotationValue, annotationNode, lockTypeClass, lockImplClass, isStatic, true); if (lockName == null) return; if (method.statements == null) return; - + Block block = new Block(0); block.statements = method.statements; setGeneratedBy(block, source); - + // Positions for in-method generated nodes are special block.sourceEnd = method.bodyEnd; block.sourceStart = method.bodyStart; - + Statement acquireLock = getLockingStatement(source, typeNode, LOCK_METHOD, lockName, lockableMethodName, isStatic.get(), p1, p2, pos); Statement unLock = getLockingStatement(source, typeNode, UNLOCK_METHOD, lockName, lockableMethodName, isStatic.get(), p1, p2, pos); - + TryStatement tryStatement = new TryStatement(); tryStatement.tryBlock = block; tryStatement.finallyBlock = new Block(0); tryStatement.finallyBlock.statements = new Statement[] { unLock }; - + method.statements = new Statement[] { acquireLock, tryStatement }; - + // Positions for in-method generated nodes are special method.statements[0].sourceEnd = method.bodyEnd; method.statements[0].sourceStart = method.bodyStart; - + methodNode.rebuild(); } - + private static Statement getLockingStatement(ASTNode source, EclipseNode typeNode, char[] lockMethod, - char[] lockableObjectName, char[] lockableMethodName, boolean isStatic, int p1, int p2, long pos) { + char[] lockableObjectName, char[] lockableMethodName, boolean isStatic, int p1, int p2, long pos) { + MessageSend lockStat = setGeneratedBy(new MessageSend(), source); lockStat.receiver = getLockable(source, typeNode, lockableObjectName, lockableMethodName, isStatic, p1, p2, pos); lockStat.selector = lockMethod; @@ -215,9 +218,10 @@ public final class HandleLockedUtil { lockStat.sourceEnd = lockStat.statementEnd = p2; return lockStat; } - + private static Expression getLockable(ASTNode source, EclipseNode typeNode, char[] lockName, - char[] lockableMethodName, boolean isStatic, int p1, int p2, long pos) { + char[] lockableMethodName, boolean isStatic, int p1, int p2, long pos) { + Reference lockVariable; if (isStatic) { char[][] n = getQualifiedInnerName(typeNode, lockName); @@ -231,7 +235,7 @@ public final class HandleLockedUtil { ((FieldReference) lockVariable).receiver = thisReference; } setGeneratedBy(lockVariable, source); - + Expression lockable; if (lockableMethodName == null) lockable = lockVariable; else { diff --git a/src/core/lombok/eclipse/handlers/HandleLockedWrite.java b/src/core/lombok/eclipse/handlers/HandleLockedWrite.java index 9aecfbf3..6192c76b 100644 --- a/src/core/lombok/eclipse/handlers/HandleLockedWrite.java +++ b/src/core/lombok/eclipse/handlers/HandleLockedWrite.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; -import lombok.experimental.Locked; +import lombok.Locked; import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; @@ -40,16 +40,18 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; public class HandleLockedWrite extends EclipseAnnotationHandler { private static final char[] LOCK_METHOD = "writeLock".toCharArray(); private static final String ANNOTATION_NAME = "@Locked.Write"; - private static final char[][] LOCK_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, - "concurrent".toCharArray(), "locks".toCharArray(), "ReentrantReadWriteLock".toCharArray() }; + private static final char[][] LOCK_TYPE_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, + "concurrent".toCharArray(), "locks".toCharArray(), "ReadWriteLock".toCharArray() }; + private static final char[][] LOCK_IMPL_CLASS = new char[][] { TypeConstants.JAVA, TypeConstants.UTIL, + "concurrent".toCharArray(), "locks".toCharArray(), "ReentrantReadWriteLock".toCharArray() }; @Override public void handle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.handle(annotationValue, source, annotationNode, ANNOTATION_NAME, LOCK_CLASS, LOCK_METHOD); + HandleLockedUtil.handle(annotationValue, source, annotationNode, ANNOTATION_NAME, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, LOCK_METHOD); } @Override public void preHandle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.preHandle(annotationValue, LOCK_CLASS, annotationNode); + HandleLockedUtil.preHandle(annotationValue, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, annotationNode); } } diff --git a/src/core/lombok/experimental/Locked.java b/src/core/lombok/experimental/Locked.java deleted file mode 100644 index 164a5c6c..00000000 --- a/src/core/lombok/experimental/Locked.java +++ /dev/null @@ -1,62 +0,0 @@ -package lombok.experimental; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.concurrent.locks.ReadWriteLock; - -/** - * Uses a {@link java.util.concurrent.locks.Lock} as a more flexible alternative to the 'synchronized' keyword with - * optional increased throughput when making use of read and write locks. - *

- * For non-static methods, a field named {@code lock} is used, and for static methods, - * {@code LOCK} is used. These will be generated if needed and if they aren't already present. The contents - * of the fields will be serializable. - *

- * Because {@link Locked} uses a different type of lock from {@link Locked.Read} and {@link Locked.Write}, using both in - * the same class using the default names will result in a compile time error. - *

- * Complete documentation is found at the project lombok features page for @Locked. - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.SOURCE) -public @interface Locked { - /** - * Locks using a {@link ReadWriteLock#readLock()}. - */ - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.SOURCE) - public @interface Read { - /** - * Optional: specify the name of a different field to lock on. If this field doesn't already exist, it is generated - * automatically. - * - * @return Name of the field to lock on (blank = generate one). - */ - String value() default ""; - } - - /** - * Locks using a {@link ReadWriteLock#writeLock()}. - */ - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.SOURCE) - public @interface Write { - /** - * Optional: specify the name of a different field to lock on. If this field doesn't already exist, it is generated - * automatically. - * - * @return Name of the field to lock on (blank = generate one). - */ - String value() default ""; - } - - /** - * Optional: specify the name of a different field to lock on. If this field doesn't already exist, it is generated - * automatically. - * - * @return Name of the field to lock on (blank = generate one). - */ - String value() default ""; -} diff --git a/src/core/lombok/javac/handlers/HandleLocked.java b/src/core/lombok/javac/handlers/HandleLocked.java index 7eb1c5c9..538ec29b 100644 --- a/src/core/lombok/javac/handlers/HandleLocked.java +++ b/src/core/lombok/javac/handlers/HandleLocked.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package lombok.javac.handlers; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.experimental.Locked; +import lombok.Locked; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.spi.Provides; @@ -37,10 +37,11 @@ import static com.sun.tools.javac.tree.JCTree.*; @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleLocked extends JavacAnnotationHandler { private static final String ANNOTATION_NAME = "@Locked"; - private static final String[] LOCK_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReentrantLock"}; - + private static final String[] LOCK_TYPE_CLASS = new String[]{"java", "util", "concurrent", "locks", "Lock"}; + private static final String[] LOCK_IMPL_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReentrantLock"}; + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.handle(annotationValue, ast, annotationNode, Locked.class, ANNOTATION_NAME, LOCK_CLASS); + HandleLockedUtil.handle(annotationValue, ast, annotationNode, Locked.class, ANNOTATION_NAME, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS); } } diff --git a/src/core/lombok/javac/handlers/HandleLockedRead.java b/src/core/lombok/javac/handlers/HandleLockedRead.java index ab22082c..be3a14a6 100644 --- a/src/core/lombok/javac/handlers/HandleLockedRead.java +++ b/src/core/lombok/javac/handlers/HandleLockedRead.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package lombok.javac.handlers; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.experimental.Locked; +import lombok.Locked; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.spi.Provides; @@ -38,10 +38,11 @@ import static com.sun.tools.javac.tree.JCTree.*; public class HandleLockedRead extends JavacAnnotationHandler { private static final String LOCK_METHOD = "readLock"; private static final String ANNOTATION_NAME = "@Locked.Read"; - private static final String[] LOCK_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReentrantReadWriteLock"}; - + private static final String[] LOCK_TYPE_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReadWriteLock"}; + private static final String[] LOCK_IMPL_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReentrantReadWriteLock"}; + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.handle(annotationValue, ast, annotationNode, Locked.Read.class, ANNOTATION_NAME, LOCK_CLASS, LOCK_METHOD); + HandleLockedUtil.handle(annotationValue, ast, annotationNode, Locked.Read.class, ANNOTATION_NAME, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, LOCK_METHOD); } } diff --git a/src/core/lombok/javac/handlers/HandleLockedUtil.java b/src/core/lombok/javac/handlers/HandleLockedUtil.java index 9f9deaa2..70dc9073 100644 --- a/src/core/lombok/javac/handlers/HandleLockedUtil.java +++ b/src/core/lombok/javac/handlers/HandleLockedUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,38 +24,38 @@ package lombok.javac.handlers; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.List; -import lombok.ConfigurationKeys; import lombok.core.AST; -import lombok.experimental.Locked; +import lombok.ConfigurationKeys; +import lombok.Locked; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import java.lang.annotation.Annotation; import static com.sun.tools.javac.tree.JCTree.*; -import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; /** * Container for static utility methods used by the Locked[.Read/Write] annotations. */ public final class HandleLockedUtil { - private static final String INSTANCE_LOCK_NAME = "lock"; - private static final String STATIC_LOCK_NAME = "LOCK"; + private static final String INSTANCE_LOCK_NAME = "$lock"; + private static final String STATIC_LOCK_NAME = "$LOCK"; private static final List NIL_EXPRESSION = List.nil(); - + private HandleLockedUtil() { //Prevent instantiation } - + /** - * See {@link #handle(String, JCAnnotation, JavacNode, Class, String, String[], String)} for + * See {@link #handle(String, JCAnnotation, JavacNode, Class, String, String[], String[], String)} for * {@code lockableMethodName = null}. */ - public static void handle(String annotationValue, JCTree.JCAnnotation ast, JavacNode annotationNode, Class annotationClass, String annotationName, String[] lockClass) { - handle(annotationValue, ast, annotationNode, annotationClass, annotationName, lockClass, null); + public static void handle(String annotationValue, JCTree.JCAnnotation ast, JavacNode annotationNode, Class annotationClass, String annotationName, String[] lockTypeClass, String[] lockImplClass) { + handle(annotationValue, ast, annotationNode, annotationClass, annotationName, lockTypeClass, lockImplClass, null); } - + /** * Called when an annotation is found that is likely to match {@link Locked}, {@link Locked.Read}, or * {@link Locked.Write}. @@ -72,50 +72,54 @@ public final class HandleLockedUtil { * @param annotationClass The specific annotation class. This should be one of {@code Locked}, {@code Locked.Read}, * or {@code Locked.Write}. * @param annotationName The name of the annotation to use when referencing it in errors. - * @param lockClass The type of lock to use. + * @param lockTypeClass The type of the variable when generating a lock to use. + * See {@link JavacHandlerUtil#chainDots(JavacNode, String, String, String...)}. + * @param lockImplClass Call the constructor of this class to generate a lock to use. * See {@link JavacHandlerUtil#chainDots(JavacNode, String, String, String...)}. * @param lockableMethodName The name of the method in the {@code lockClass} that returns a * {@link java.util.concurrent.locks.Lock} object. When this is {@code null}, it is assumed that {@code lockClass} * itself can be locked/unlocked. * @param The annotation type. */ - public static void handle(String annotationValue, JCTree.JCAnnotation ast, JavacNode annotationNode, Class annotationClass, String annotationName, String[] lockClass, String lockableMethodName) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.LOCKED_FLAG_USAGE, annotationName); - + public static void handle(String annotationValue, JCTree.JCAnnotation ast, JavacNode annotationNode, Class annotationClass, String annotationName, String[] lockTypeClass, String[] lockImplClass, String lockableMethodName) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOCKED_FLAG_USAGE, annotationName); + if (inNetbeansEditor(annotationNode)) return; deleteAnnotationIfNeccessary(annotationNode, annotationClass); - + JavacNode methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != AST.Kind.METHOD || !(methodNode.get() instanceof JCMethodDecl)) { annotationNode.addError(annotationName + " is legal only on methods."); return; } - + JCMethodDecl method = (JCMethodDecl) methodNode.get(); if ((method.mods.flags & Flags.ABSTRACT) != 0) { annotationNode.addError(annotationName + " is legal only on concrete methods."); return; } - + JavacNode typeNode = upToTypeNode(annotationNode); if (!isClassOrEnum(typeNode)) { annotationNode.addError(annotationName + " is legal only on methods in classes and enums."); return; } - + boolean isStatic = (method.mods.flags & Flags.STATIC) != 0; String lockName = annotationValue; - + boolean autoMake = false; + if (lockName.length() == 0) { + autoMake = true; lockName = isStatic ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; } - + JavacTreeMaker maker = methodNode.getTreeMaker().at(ast.pos); - + MemberExistsResult exists = MemberExistsResult.NOT_EXISTS; - - JCExpression lockType = chainDots(methodNode, ast.pos, null, null, lockClass); - + + JCExpression lockVarType = chainDots(methodNode, ast.pos, null, null, lockTypeClass); + if (typeNode != null && typeNode.get() instanceof JCClassDecl) { for (JCTree def : ((JCClassDecl) typeNode.get()).defs) { if (def instanceof JCVariableDecl) { @@ -123,49 +127,54 @@ public final class HandleLockedUtil { JCVariableDecl varDeclDef = (JCVariableDecl) def; exists = getGeneratedBy(varDeclDef) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; boolean st = ((varDeclDef.mods.flags) & Flags.STATIC) != 0; - + if (isStatic != st && exists == MemberExistsResult.EXISTS_BY_LOMBOK) { annotationNode.addError("The generated field " + lockName + " does not match the static status of this method"); return; } isStatic = st; - - if (exists == MemberExistsResult.EXISTS_BY_LOMBOK && !lockType.toString().equals(varDeclDef.vartype.toString())) { - annotationNode.addError("Expected field " + lockName + " to be of type " + lockType + " but got type " + varDeclDef.vartype + "! Did you mix @Locked with @Locked.Read/Write on the same generated field?"); + + if (exists == MemberExistsResult.EXISTS_BY_LOMBOK && !lockVarType.toString().equals(varDeclDef.vartype.toString())) { + annotationNode.addError("Expected field " + lockName + " to be of type " + lockVarType + " but got type " + varDeclDef.vartype + ". Did you mix @Locked with @Locked.Read/Write on the same generated field?"); return; } } } } } - + if (exists == MemberExistsResult.NOT_EXISTS) { - JCNewClass lockInstance = maker.NewClass(null, NIL_EXPRESSION, cloneType(maker, lockType, typeNode), NIL_EXPRESSION, null); + if (!autoMake) { + annotationNode.addError("The field " + lockName + " does not exist."); + return; + } + JCExpression lockImplType = chainDots(methodNode, ast.pos, null, null, lockImplClass); + JCNewClass lockInstance = maker.NewClass(null, NIL_EXPRESSION, lockImplType, NIL_EXPRESSION, null); JCVariableDecl newLockField = recursiveSetGeneratedBy(maker.VarDef( maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic ? Flags.STATIC : 0)), - methodNode.toName(lockName), lockType, lockInstance), annotationNode); + methodNode.toName(lockName), lockVarType, lockInstance), annotationNode); injectFieldAndMarkGenerated(methodNode.up(), newLockField); } - + if (method.body == null) return; - + JCExpression lockNode; if (isStatic) { lockNode = namePlusTypeParamsToTypeReference(maker, typeNode, methodNode.toName(lockName), false, List.nil()); } else { lockNode = maker.Select(maker.Ident(methodNode.toName("this")), methodNode.toName(lockName)); } - + JCExpressionStatement acquireLock = maker.Exec(maker.Apply(NIL_EXPRESSION, maker.Select(getLockable(maker, typeNode, methodNode, lockableMethodName, lockNode), annotationNode.toName("lock")), NIL_EXPRESSION)); JCExpressionStatement releaseLock = maker.Exec(maker.Apply(NIL_EXPRESSION, maker.Select(getLockable(maker, typeNode, methodNode, lockableMethodName, lockNode), annotationNode.toName("unlock")), NIL_EXPRESSION)); - + JCTry tryBlock = setGeneratedBy(maker.Try(method.body, List.nil(), recursiveSetGeneratedBy(maker.Block(0, List.of(releaseLock)), annotationNode)), annotationNode); - + method.body = setGeneratedBy(maker.Block(0, List.of(recursiveSetGeneratedBy(acquireLock, annotationNode), tryBlock)), annotationNode); - + methodNode.rebuild(); } - + private static JCExpression getLockable(JavacTreeMaker maker, JavacNode typeNode, JavacNode methodNode, String lockableMethodName, JCExpression lockNode) { if (lockableMethodName == null) { return cloneType(maker, lockNode, typeNode); diff --git a/src/core/lombok/javac/handlers/HandleLockedWrite.java b/src/core/lombok/javac/handlers/HandleLockedWrite.java index 0a8283be..c726c456 100644 --- a/src/core/lombok/javac/handlers/HandleLockedWrite.java +++ b/src/core/lombok/javac/handlers/HandleLockedWrite.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2021-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package lombok.javac.handlers; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.experimental.Locked; +import lombok.Locked; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.spi.Provides; @@ -38,10 +38,11 @@ import static com.sun.tools.javac.tree.JCTree.*; public class HandleLockedWrite extends JavacAnnotationHandler { private static final String LOCK_METHOD = "writeLock"; private static final String ANNOTATION_NAME = "@Locked.Write"; - private static final String[] LOCK_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReentrantReadWriteLock"}; - + private static final String[] LOCK_TYPE_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReadWriteLock"}; + private static final String[] LOCK_IMPL_CLASS = new String[]{"java", "util", "concurrent", "locks", "ReentrantReadWriteLock"}; + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { String annotationValue = annotation.getInstance().value(); - HandleLockedUtil.handle(annotationValue, ast, annotationNode, Locked.Write.class, ANNOTATION_NAME, LOCK_CLASS, LOCK_METHOD); + HandleLockedUtil.handle(annotationValue, ast, annotationNode, Locked.Write.class, ANNOTATION_NAME, LOCK_TYPE_CLASS, LOCK_IMPL_CLASS, LOCK_METHOD); } } diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java index 0bf7cbe8..e4f960e7 100644 --- a/src/core/lombok/javac/handlers/HandleSynchronized.java +++ b/src/core/lombok/javac/handlers/HandleSynchronized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2023 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -60,8 +60,8 @@ public class HandleSynchronized extends JavacAnnotationHandler { handleFlagUsage(annotationNode, ConfigurationKeys.SYNCHRONIZED_FLAG_USAGE, "@Synchronized"); if (inNetbeansEditor(annotationNode)) return; - deleteAnnotationIfNeccessary(annotationNode, Synchronized.class); + JavacNode methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != Kind.METHOD || !(methodNode.get() instanceof JCMethodDecl)) { @@ -82,12 +82,12 @@ public class HandleSynchronized extends JavacAnnotationHandler { return; } - boolean[] isStatic = new boolean[] {(method.mods.flags & Flags.STATIC) != 0}; + boolean isStatic = (method.mods.flags & Flags.STATIC) != 0; String lockName = annotation.getInstance().value(); boolean autoMake = false; if (lockName.length() == 0) { autoMake = true; - lockName = isStatic[0] ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; + lockName = isStatic ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; } JavacTreeMaker maker = methodNode.getTreeMaker().at(ast.pos); @@ -100,11 +100,11 @@ public class HandleSynchronized extends JavacAnnotationHandler { if (((JCVariableDecl) def).name.contentEquals(lockName)) { exists = getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; boolean st = ((((JCVariableDecl) def).mods.flags) & Flags.STATIC) != 0; - if (isStatic[0] && !st) { + if (isStatic && !st) { annotationNode.addError("The field " + lockName + " is non-static and this cannot be used on this static method"); return; } - isStatic[0] = st; + isStatic = st; } } } @@ -120,7 +120,7 @@ public class HandleSynchronized extends JavacAnnotationHandler { JCNewArray newObjectArray = maker.NewArray(genJavaLangTypeRef(methodNode, ast.pos, "Object"), List.of(maker.Literal(CTC_INT, 0)), null); JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef( - maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic[0] ? Flags.STATIC : 0)), + maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic ? Flags.STATIC : 0)), methodNode.toName(lockName), objectType, newObjectArray), annotationNode); injectFieldAndMarkGenerated(methodNode.up(), fieldDecl); } @@ -128,7 +128,7 @@ public class HandleSynchronized extends JavacAnnotationHandler { if (method.body == null) return; JCExpression lockNode; - if (isStatic[0]) { + if (isStatic) { lockNode = namePlusTypeParamsToTypeReference(maker, typeNode, methodNode.toName(lockName), false, List.nil()); } else { lockNode = maker.Select(maker.Ident(methodNode.toName("this")), methodNode.toName(lockName)); diff --git a/test/transform/resource/after-delombok/LockedName.java b/test/transform/resource/after-delombok/LockedName.java index 1c6e1083..1121ce1c 100644 --- a/test/transform/resource/after-delombok/LockedName.java +++ b/test/transform/resource/after-delombok/LockedName.java @@ -1,9 +1,7 @@ +import java.util.concurrent.locks.*; class LockedName { - @java.lang.SuppressWarnings("all") - private final java.util.concurrent.locks.ReentrantLock basicLock = new java.util.concurrent.locks.ReentrantLock(); - @java.lang.SuppressWarnings("all") - private final java.util.concurrent.locks.ReentrantReadWriteLock rwLock = new java.util.concurrent.locks.ReentrantReadWriteLock(); - + private final Lock basicLock = new ReentrantLock(); + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); void test() { this.basicLock.lock(); try { diff --git a/test/transform/resource/after-delombok/LockedOnStatic.java b/test/transform/resource/after-delombok/LockedOnStatic.java index 1280b858..766b6ea1 100644 --- a/test/transform/resource/after-delombok/LockedOnStatic.java +++ b/test/transform/resource/after-delombok/LockedOnStatic.java @@ -1,6 +1,6 @@ class LockedOnStatic { static class Inner { - private static final java.util.concurrent.locks.ReentrantLock LCK = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock LCK = new java.util.concurrent.locks.ReentrantLock(); public void foo() { LockedOnStatic.Inner.LCK.lock(); try { diff --git a/test/transform/resource/after-delombok/LockedPlain.java b/test/transform/resource/after-delombok/LockedPlain.java index 302b967b..bdc4e2a4 100644 --- a/test/transform/resource/after-delombok/LockedPlain.java +++ b/test/transform/resource/after-delombok/LockedPlain.java @@ -1,80 +1,80 @@ class LockedPlain { @java.lang.SuppressWarnings("all") - private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock $lock = new java.util.concurrent.locks.ReentrantLock(); void test() { - this.lock.lock(); + this.$lock.lock(); try { System.out.println("one"); } finally { - this.lock.unlock(); + this.$lock.unlock(); } } void test2() { - this.lock.lock(); + this.$lock.lock(); try { System.out.println("two"); } finally { - this.lock.unlock(); + this.$lock.unlock(); } } } class LockedPlainStatic { @java.lang.SuppressWarnings("all") - private static final java.util.concurrent.locks.ReentrantLock LOCK = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock $LOCK = new java.util.concurrent.locks.ReentrantLock(); static void test() { - LockedPlainStatic.LOCK.lock(); + LockedPlainStatic.$LOCK.lock(); try { System.out.println("three"); } finally { - LockedPlainStatic.LOCK.unlock(); + LockedPlainStatic.$LOCK.unlock(); } } static void test2() { - LockedPlainStatic.LOCK.lock(); + LockedPlainStatic.$LOCK.lock(); try { System.out.println("four"); } finally { - LockedPlainStatic.LOCK.unlock(); + LockedPlainStatic.$LOCK.unlock(); } } } class LockedPlainRead { @java.lang.SuppressWarnings("all") - private static final java.util.concurrent.locks.ReentrantReadWriteLock LOCK = new java.util.concurrent.locks.ReentrantReadWriteLock(); + private static final java.util.concurrent.locks.ReadWriteLock $LOCK = new java.util.concurrent.locks.ReentrantReadWriteLock(); static void test() { - LockedPlainRead.LOCK.readLock().lock(); + LockedPlainRead.$LOCK.readLock().lock(); try { System.out.println("five"); } finally { - LockedPlainRead.LOCK.readLock().unlock(); + LockedPlainRead.$LOCK.readLock().unlock(); } } static void test2() { - LockedPlainRead.LOCK.readLock().lock(); + LockedPlainRead.$LOCK.readLock().lock(); try { System.out.println("six"); } finally { - LockedPlainRead.LOCK.readLock().unlock(); + LockedPlainRead.$LOCK.readLock().unlock(); } } } class LockedPlainWrite { @java.lang.SuppressWarnings("all") - private final java.util.concurrent.locks.ReentrantReadWriteLock lock = new java.util.concurrent.locks.ReentrantReadWriteLock(); + private final java.util.concurrent.locks.ReadWriteLock $lock = new java.util.concurrent.locks.ReentrantReadWriteLock(); void test() { - this.lock.writeLock().lock(); + this.$lock.writeLock().lock(); try { System.out.println("seven"); } finally { - this.lock.writeLock().unlock(); + this.$lock.writeLock().unlock(); } } void test2() { - this.lock.writeLock().lock(); + this.$lock.writeLock().lock(); try { System.out.println("eight"); } finally { - this.lock.writeLock().unlock(); + this.$lock.writeLock().unlock(); } } } diff --git a/test/transform/resource/after-delombok/LockedStaticMix.java b/test/transform/resource/after-delombok/LockedStaticMix.java index 201c9d7e..e5461248 100644 --- a/test/transform/resource/after-delombok/LockedStaticMix.java +++ b/test/transform/resource/after-delombok/LockedStaticMix.java @@ -1,12 +1,12 @@ class LockedGeneratedStaticMismatch { @java.lang.SuppressWarnings("all") - private static final java.util.concurrent.locks.ReentrantLock LOCK = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock $LOCK = new java.util.concurrent.locks.ReentrantLock(); static void test() { - LockedGeneratedStaticMismatch.LOCK.lock(); + LockedGeneratedStaticMismatch.$LOCK.lock(); try { System.out.println("one"); } finally { - LockedGeneratedStaticMismatch.LOCK.unlock(); + LockedGeneratedStaticMismatch.$LOCK.unlock(); } } void test2() { @@ -14,7 +14,7 @@ class LockedGeneratedStaticMismatch { } } class LockedUserStaticMismatch { - private static final java.util.concurrent.locks.ReentrantLock userLock = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock userLock = new java.util.concurrent.locks.ReentrantLock(); static void test() { LockedUserStaticMismatch.userLock.lock(); try { diff --git a/test/transform/resource/after-delombok/LockedTypeMismatch.java b/test/transform/resource/after-delombok/LockedTypeMismatch.java index bbd916f4..eb0a4b19 100644 --- a/test/transform/resource/after-delombok/LockedTypeMismatch.java +++ b/test/transform/resource/after-delombok/LockedTypeMismatch.java @@ -1,12 +1,12 @@ class LockedGeneratedTypeMismatch { @java.lang.SuppressWarnings("all") - private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock $lock = new java.util.concurrent.locks.ReentrantLock(); void test() { - this.lock.lock(); + this.$lock.lock(); try { System.out.println("one"); } finally { - this.lock.unlock(); + this.$lock.unlock(); } } void test2() { @@ -14,7 +14,7 @@ class LockedGeneratedTypeMismatch { } } class LockedUserTypeMismatch { - private final java.util.concurrent.locks.ReentrantLock userLock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock userLock = new java.util.concurrent.locks.ReentrantLock(); void test() { this.userLock.lock(); try { diff --git a/test/transform/resource/after-ecj/LockedInRecord.java b/test/transform/resource/after-ecj/LockedInRecord.java index ebb660c4..9801227a 100644 --- a/test/transform/resource/after-ecj/LockedInRecord.java +++ b/test/transform/resource/after-ecj/LockedInRecord.java @@ -1,8 +1,8 @@ -import lombok.experimental.Locked; +import lombok.Locked; public record LockedInRecord(String a, String b) { /* Implicit */ private final String a; /* Implicit */ private final String b; - private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock $lock = new java.util.concurrent.locks.ReentrantLock(); public LockedInRecord(String a, String b) { super(); .a = a; diff --git a/test/transform/resource/after-ecj/LockedName.java b/test/transform/resource/after-ecj/LockedName.java index 3fb996de..e42fdf92 100644 --- a/test/transform/resource/after-ecj/LockedName.java +++ b/test/transform/resource/after-ecj/LockedName.java @@ -1,10 +1,11 @@ +import java.util.concurrent.locks.*; class LockedName { - private final java.util.concurrent.locks.ReentrantLock basicLock = new java.util.concurrent.locks.ReentrantLock(); - private final java.util.concurrent.locks.ReentrantReadWriteLock rwLock = new java.util.concurrent.locks.ReentrantReadWriteLock(); + private final Lock basicLock = new ReentrantLock(); + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); LockedName() { super(); } - @lombok.experimental.Locked("basicLock") void test() { + @lombok.Locked("basicLock") void test() { this.basicLock.lock(); try { @@ -15,7 +16,7 @@ class LockedName { this.basicLock.unlock(); } } - @lombok.experimental.Locked.Read("rwLock") void test2() { + @lombok.Locked.Read("rwLock") void test2() { this.rwLock.readLock().lock(); try { @@ -26,7 +27,7 @@ class LockedName { this.rwLock.readLock().unlock(); } } - @lombok.experimental.Locked.Write("rwLock") void test3() { + @lombok.Locked.Write("rwLock") void test3() { this.rwLock.writeLock().lock(); try { diff --git a/test/transform/resource/after-ecj/LockedOnStatic.java b/test/transform/resource/after-ecj/LockedOnStatic.java index 02d00721..a994b2f5 100644 --- a/test/transform/resource/after-ecj/LockedOnStatic.java +++ b/test/transform/resource/after-ecj/LockedOnStatic.java @@ -1,12 +1,12 @@ class LockedOnStatic { static class Inner { - private static final java.util.concurrent.locks.ReentrantLock LCK = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock LCK = new java.util.concurrent.locks.ReentrantLock(); () { } Inner() { super(); } - public @lombok.experimental.Locked("LCK") void foo() { + public @lombok.Locked("LCK") void foo() { LockedOnStatic.Inner.LCK.lock(); try { @@ -23,7 +23,7 @@ class LockedOnStatic { Inner2() { super(); } - public @lombok.experimental.Locked("LCK") void foo() { + public @lombok.Locked("LCK") void foo() { this.LCK.lock(); try { diff --git a/test/transform/resource/after-ecj/LockedPlain.java b/test/transform/resource/after-ecj/LockedPlain.java index b0b91719..f311c193 100644 --- a/test/transform/resource/after-ecj/LockedPlain.java +++ b/test/transform/resource/after-ecj/LockedPlain.java @@ -1,117 +1,117 @@ -import lombok.experimental.Locked; +import lombok.Locked; class LockedPlain { - private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock $lock = new java.util.concurrent.locks.ReentrantLock(); LockedPlain() { super(); } - @lombok.experimental.Locked void test() { - this.lock.lock(); + @Locked void test() { + this.$lock.lock(); try { System.out.println("one"); } finally { - this.lock.unlock(); + this.$lock.unlock(); } } @Locked void test2() { - this.lock.lock(); + this.$lock.lock(); try { System.out.println("two"); } finally { - this.lock.unlock(); + this.$lock.unlock(); } } } class LockedPlainStatic { - private static final java.util.concurrent.locks.ReentrantLock LOCK = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock $LOCK = new java.util.concurrent.locks.ReentrantLock(); () { } LockedPlainStatic() { super(); } - static @lombok.experimental.Locked void test() { - LockedPlainStatic.LOCK.lock(); + static @Locked void test() { + LockedPlainStatic.$LOCK.lock(); try { System.out.println("three"); } finally { - LockedPlainStatic.LOCK.unlock(); + LockedPlainStatic.$LOCK.unlock(); } } static @Locked void test2() { - LockedPlainStatic.LOCK.lock(); + LockedPlainStatic.$LOCK.lock(); try { System.out.println("four"); } finally { - LockedPlainStatic.LOCK.unlock(); + LockedPlainStatic.$LOCK.unlock(); } } } class LockedPlainRead { - private static final java.util.concurrent.locks.ReentrantReadWriteLock LOCK = new java.util.concurrent.locks.ReentrantReadWriteLock(); + private static final java.util.concurrent.locks.ReadWriteLock $LOCK = new java.util.concurrent.locks.ReentrantReadWriteLock(); () { } LockedPlainRead() { super(); } - static @lombok.experimental.Locked.Read void test() { - LockedPlainRead.LOCK.readLock().lock(); + static @Locked.Read void test() { + LockedPlainRead.$LOCK.readLock().lock(); try { System.out.println("five"); } finally { - LockedPlainRead.LOCK.readLock().unlock(); + LockedPlainRead.$LOCK.readLock().unlock(); } } static @Locked.Read void test2() { - LockedPlainRead.LOCK.readLock().lock(); + LockedPlainRead.$LOCK.readLock().lock(); try { System.out.println("six"); } finally { - LockedPlainRead.LOCK.readLock().unlock(); + LockedPlainRead.$LOCK.readLock().unlock(); } } } class LockedPlainWrite { - private final java.util.concurrent.locks.ReentrantReadWriteLock lock = new java.util.concurrent.locks.ReentrantReadWriteLock(); + private final java.util.concurrent.locks.ReadWriteLock $lock = new java.util.concurrent.locks.ReentrantReadWriteLock(); LockedPlainWrite() { super(); } - @lombok.experimental.Locked.Write void test() { - this.lock.writeLock().lock(); + @Locked.Write void test() { + this.$lock.writeLock().lock(); try { System.out.println("seven"); } finally { - this.lock.writeLock().unlock(); + this.$lock.writeLock().unlock(); } } @Locked.Write void test2() { - this.lock.writeLock().lock(); + this.$lock.writeLock().lock(); try { System.out.println("eight"); } finally { - this.lock.writeLock().unlock(); + this.$lock.writeLock().unlock(); } } } diff --git a/test/transform/resource/after-ecj/LockedStaticMix.java b/test/transform/resource/after-ecj/LockedStaticMix.java index 3ec8ca2d..98c8e804 100644 --- a/test/transform/resource/after-ecj/LockedStaticMix.java +++ b/test/transform/resource/after-ecj/LockedStaticMix.java @@ -1,33 +1,33 @@ class LockedGeneratedStaticMismatch { - private static final java.util.concurrent.locks.ReentrantLock LOCK = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock $LOCK = new java.util.concurrent.locks.ReentrantLock(); () { } LockedGeneratedStaticMismatch() { super(); } - static @lombok.experimental.Locked void test() { - LockedGeneratedStaticMismatch.LOCK.lock(); + static @lombok.Locked void test() { + LockedGeneratedStaticMismatch.$LOCK.lock(); try { System.out.println("one"); } finally { - LockedGeneratedStaticMismatch.LOCK.unlock(); + LockedGeneratedStaticMismatch.$LOCK.unlock(); } } - @lombok.experimental.Locked("LOCK") void test2() { + @lombok.Locked("$LOCK") void test2() { System.out.println("two"); } } class LockedUserStaticMismatch { - private static final java.util.concurrent.locks.ReentrantLock userLock = new java.util.concurrent.locks.ReentrantLock(); + private static final java.util.concurrent.locks.Lock userLock = new java.util.concurrent.locks.ReentrantLock(); () { } LockedUserStaticMismatch() { super(); } - static @lombok.experimental.Locked("userLock") void test() { + static @lombok.Locked("userLock") void test() { LockedUserStaticMismatch.userLock.lock(); try { @@ -38,7 +38,7 @@ class LockedUserStaticMismatch { LockedUserStaticMismatch.userLock.unlock(); } } - @lombok.experimental.Locked("userLock") void test2() { + @lombok.Locked("userLock") void test2() { LockedUserStaticMismatch.userLock.lock(); try { diff --git a/test/transform/resource/after-ecj/LockedTypeMismatch.java b/test/transform/resource/after-ecj/LockedTypeMismatch.java index 57e3f44a..390ec572 100644 --- a/test/transform/resource/after-ecj/LockedTypeMismatch.java +++ b/test/transform/resource/after-ecj/LockedTypeMismatch.java @@ -1,29 +1,29 @@ class LockedGeneratedTypeMismatch { - private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock $lock = new java.util.concurrent.locks.ReentrantLock(); LockedGeneratedTypeMismatch() { super(); } - @lombok.experimental.Locked void test() { - this.lock.lock(); + @lombok.Locked void test() { + this.$lock.lock(); try { System.out.println("one"); } finally { - this.lock.unlock(); + this.$lock.unlock(); } } - @lombok.experimental.Locked.Read void test2() { + @lombok.Locked.Read void test2() { System.out.println("two"); } } class LockedUserTypeMismatch { - private final java.util.concurrent.locks.ReentrantLock userLock = new java.util.concurrent.locks.ReentrantLock(); + private final java.util.concurrent.locks.Lock userLock = new java.util.concurrent.locks.ReentrantLock(); LockedUserTypeMismatch() { super(); } - @lombok.experimental.Locked("userLock") void test() { + @lombok.Locked("userLock") void test() { this.userLock.lock(); try { @@ -34,7 +34,7 @@ class LockedUserTypeMismatch { this.userLock.unlock(); } } - @lombok.experimental.Locked.Read("userLock") void test2() { + @lombok.Locked.Read("userLock") void test2() { this.userLock.readLock().lock(); try { diff --git a/test/transform/resource/before/LockedInRecord.java b/test/transform/resource/before/LockedInRecord.java index 9bb16001..8d368998 100644 --- a/test/transform/resource/before/LockedInRecord.java +++ b/test/transform/resource/before/LockedInRecord.java @@ -1,6 +1,6 @@ // version 14: -import lombok.experimental.Locked; +import lombok.Locked; public record LockedInRecord(String a, String b) { @Locked diff --git a/test/transform/resource/before/LockedName.java b/test/transform/resource/before/LockedName.java index 3a73e5a3..66fe7d95 100644 --- a/test/transform/resource/before/LockedName.java +++ b/test/transform/resource/before/LockedName.java @@ -1,11 +1,14 @@ +import java.util.concurrent.locks.*; class LockedName { - @lombok.experimental.Locked("basicLock") void test() { + private final Lock basicLock = new ReentrantLock(); + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + @lombok.Locked("basicLock") void test() { System.out.println("one"); } - @lombok.experimental.Locked.Read("rwLock") void test2() { + @lombok.Locked.Read("rwLock") void test2() { System.out.println("two"); } - @lombok.experimental.Locked.Write("rwLock") void test3() { + @lombok.Locked.Write("rwLock") void test3() { System.out.println("three"); } } diff --git a/test/transform/resource/before/LockedOnStatic.java b/test/transform/resource/before/LockedOnStatic.java index 4da5af4c..4c1cb2a7 100644 --- a/test/transform/resource/before/LockedOnStatic.java +++ b/test/transform/resource/before/LockedOnStatic.java @@ -1,14 +1,14 @@ class LockedOnStatic { static class Inner { - private static final java.util.concurrent.locks.ReentrantLock LCK = new java.util.concurrent.locks.ReentrantLock(); - @lombok.experimental.Locked("LCK") + private static final java.util.concurrent.locks.Lock LCK = new java.util.concurrent.locks.ReentrantLock(); + @lombok.Locked("LCK") public void foo() { System.out.println(); } } class Inner2 { private final java.util.concurrent.locks.ReentrantLock LCK = new java.util.concurrent.locks.ReentrantLock(); - @lombok.experimental.Locked("LCK") + @lombok.Locked("LCK") public void foo() { System.out.println(); } diff --git a/test/transform/resource/before/LockedPlain.java b/test/transform/resource/before/LockedPlain.java index 7bcba019..5ebe25f4 100644 --- a/test/transform/resource/before/LockedPlain.java +++ b/test/transform/resource/before/LockedPlain.java @@ -1,6 +1,6 @@ -import lombok.experimental.Locked; +import lombok.Locked; class LockedPlain { - @lombok.experimental.Locked void test() { + @Locked void test() { System.out.println("one"); } @Locked void test2() { @@ -8,7 +8,7 @@ class LockedPlain { } } class LockedPlainStatic { - @lombok.experimental.Locked static void test() { + @Locked static void test() { System.out.println("three"); } @Locked static void test2() { @@ -16,7 +16,7 @@ class LockedPlainStatic { } } class LockedPlainRead { - @lombok.experimental.Locked.Read static void test() { + @Locked.Read static void test() { System.out.println("five"); } @Locked.Read static void test2() { @@ -24,7 +24,7 @@ class LockedPlainRead { } } class LockedPlainWrite { - @lombok.experimental.Locked.Write void test() { + @Locked.Write void test() { System.out.println("seven"); } @Locked.Write void test2() { diff --git a/test/transform/resource/before/LockedStaticMix.java b/test/transform/resource/before/LockedStaticMix.java index 9506ac04..8d8605a0 100644 --- a/test/transform/resource/before/LockedStaticMix.java +++ b/test/transform/resource/before/LockedStaticMix.java @@ -1,17 +1,17 @@ class LockedGeneratedStaticMismatch { - @lombok.experimental.Locked static void test() { + @lombok.Locked static void test() { System.out.println("one"); } - @lombok.experimental.Locked("LOCK") void test2() { + @lombok.Locked("$LOCK") void test2() { System.out.println("two"); } } class LockedUserStaticMismatch { - private static final java.util.concurrent.locks.ReentrantLock userLock = new java.util.concurrent.locks.ReentrantLock(); - @lombok.experimental.Locked("userLock") static void test() { + private static final java.util.concurrent.locks.Lock userLock = new java.util.concurrent.locks.ReentrantLock(); + @lombok.Locked("userLock") static void test() { System.out.println("one"); } - @lombok.experimental.Locked("userLock") void test2() { + @lombok.Locked("userLock") void test2() { System.out.println("two"); } } diff --git a/test/transform/resource/before/LockedTypeMismatch.java b/test/transform/resource/before/LockedTypeMismatch.java index 67e96b56..8ee3a932 100644 --- a/test/transform/resource/before/LockedTypeMismatch.java +++ b/test/transform/resource/before/LockedTypeMismatch.java @@ -1,17 +1,17 @@ class LockedGeneratedTypeMismatch { - @lombok.experimental.Locked void test() { + @lombok.Locked void test() { System.out.println("one"); } - @lombok.experimental.Locked.Read void test2() { + @lombok.Locked.Read void test2() { System.out.println("two"); } } class LockedUserTypeMismatch { - private final java.util.concurrent.locks.ReentrantLock userLock = new java.util.concurrent.locks.ReentrantLock(); - @lombok.experimental.Locked("userLock") void test() { + private final java.util.concurrent.locks.Lock userLock = new java.util.concurrent.locks.ReentrantLock(); + @lombok.Locked("userLock") void test() { System.out.println("one"); } - @lombok.experimental.Locked.Read("userLock") void test2() { + @lombok.Locked.Read("userLock") void test2() { System.out.println("two"); } } diff --git a/test/transform/resource/messages-delombok/LockedTypeMismatch.java.messages b/test/transform/resource/messages-delombok/LockedTypeMismatch.java.messages index ad329880..c53b9f29 100644 --- a/test/transform/resource/messages-delombok/LockedTypeMismatch.java.messages +++ b/test/transform/resource/messages-delombok/LockedTypeMismatch.java.messages @@ -1 +1 @@ -5 Expected field lock to be of type java.util.concurrent.locks.ReentrantReadWriteLock but got type java.util.concurrent.locks.ReentrantLock! Did you mix @Locked with @Locked.Read/Write on the same generated field? +5 Expected field lock to be of type java.util.concurrent.locks.ReadWriteLock but got type java.util.concurrent.locks.Lock. Did you mix @Locked with @Locked.Read/Write on the same generated field? diff --git a/test/transform/resource/messages-ecj/LockedTypeMismatch.java.messages b/test/transform/resource/messages-ecj/LockedTypeMismatch.java.messages index ac159d72..a1a81b4a 100644 --- a/test/transform/resource/messages-ecj/LockedTypeMismatch.java.messages +++ b/test/transform/resource/messages-ecj/LockedTypeMismatch.java.messages @@ -1,2 +1,2 @@ -5 Expected field lock to be of type java.util.concurrent.locks.ReentrantReadWriteLock but got type java.util.concurrent.locks.ReentrantLock! Did you mix @Locked with @Locked.Read/Write on the same generated field? -14 The method readLock() is undefined for the type ReentrantLock +5 Expected field lock to be of type java.util.concurrent.locks.ReadWriteLock but got type java.util.concurrent.locks.Lock. Did you mix @Locked with @Locked.Read/Write on the same generated field? +14 The method readLock() is undefined for the type Lock diff --git a/website/templates/features/Locked.html b/website/templates/features/Locked.html new file mode 100644 index 00000000..105bf6dc --- /dev/null +++ b/website/templates/features/Locked.html @@ -0,0 +1,38 @@ +<#import "../_features.html" as f> + +<@f.scaffold title="@Locked" logline="Pop it and lock it! ReentrantLock, now with less hassle."> + <@f.history> +

+ @Locked was introduced in lombok v1.20. +

+ + + <@f.overview> +

+ @Locked wraps all code in a method into a block that acquires a java.util.concurrent.locks.ReentrantLock first, and unlocks it when exiting the method. It is a lot like @Synchronized. +

+ You can optionally name a field, which must be a ReentrantLock; in that case, lombok locks on that field. Otherwise, the annotation defaults to a field named $LOCK (on static methods) / $lock (on instance methods), which lombok will generate if the field does not exist yet. +

+ Additionally, there are the @Locked.Read and @Locked.Write annotations. These use a java.util.concurrent.locks.ReadWriteLock (specifically, ReentrantReadWriteLock). Methods annotated with @Locked.Write will lock on the write lock and methods annotated with @Locked.Read will lock on the read lock. When required, a java.util.concurrent.locks.ReentrantReadWriteLock is generated. +

+ When using Virtual threads (introduced in Java 20), these locks are recommended compared to what @Synchronized does. +

+ + + <@f.snippets name="Locked" /> + + <@f.confKeys> +
+ lombok.locked.flagUsage = [warning | error] (default: not set) +
+ Lombok will flag any usage of @Locked as a warning or error if configured. +
+ + + <@f.smallPrint> +

+ Because @Locked.Read and @Locked.Write use a different type of lock than @Locked, these annotations cannot be used on the same lock object without explicitly specifying the name of the field containing the lock object.
+ The name of the default field of the @Locked, @Locked.Read, and @Locked.Write annotations is the same, so it is not possible to mix the basic @Locked annotation with the other two using the default name. +

+ + diff --git a/website/templates/features/experimental/Locked.html b/website/templates/features/experimental/Locked.html deleted file mode 100644 index cd4f599c..00000000 --- a/website/templates/features/experimental/Locked.html +++ /dev/null @@ -1,45 +0,0 @@ -<#import "../_features.html" as f> - -<@f.scaffold title="@Locked" logline="TODO"> - <@f.history> -

- @Locked was introduced as an experimental feature in lombok v1.18.21. -

- - - <@f.experimental> -
    -
  • - New feature – community feedback requested. -
  • -
- Current status: unknown - - - <@f.overview> -

- @Locked is an alternative to @Synchronized that uses Java locks for more extensive locking operations. The annotation locks on the provided field if one is provided. If no name is provided, the annotation will default to LOCK when used on static methods and to lock for non-static methods. If no field with the specified name exists, one will be generated for you. -

- The base @Locked annotation uses a java.util.concurrent.locks.Lock for locking. When required, a java.util.concurrent.locks.ReentrantLock is generated.
- Additionally, there are the @Locked.Read and @Locked.Write annotations. These use a java.util.concurrent.locks.ReadWriteLock. Methods annotated with @Locked.Write will lock on the write lock and methods annotated with @Locked.Read will lock on the read lock. When required, a java.util.concurrent.locks.ReentrantReadWriteLock is generated. -

- - - <@f.snippets name="experimental/Locked" /> - - <@f.confKeys> -
- lombok.locked.flagUsage = [warning | error] (default: not set) -
- Lombok will flag any usage of @Locked as a warning or error if configured. -
- - - <@f.smallPrint> -

- Because @Locked.Read and @Locked.Write use a different type of lock than @Locked, these annotations cannot be used on the same lock object.
- The name of the default field of the @Locked, @Locked.Read, and @Locked.Write annotations is the same, so it is not possible to mix the basic @Locked annotation with the other two using the default name. -

- - - diff --git a/website/templates/features/experimental/index.html b/website/templates/features/experimental/index.html index 12c404bb..a50a4670 100644 --- a/website/templates/features/experimental/index.html +++ b/website/templates/features/experimental/index.html @@ -75,10 +75,6 @@ <@main.feature title="@StandardException" href="StandardException"> Standard.. Exceptional? This is not just an oxymoron, it's convenient! - - <@main.feature title="@Locked" href="Locked"> - TODO - <@f.confKeys> diff --git a/website/templates/features/index.html b/website/templates/features/index.html index 9e2f53e8..37d98d65 100644 --- a/website/templates/features/index.html +++ b/website/templates/features/index.html @@ -66,6 +66,10 @@ synchronized done right: Don't expose your locks. + <@main.feature title="@Locked" href="Locked"> + Pop it and lock it! ReentrantLock, now with less hassle. + + <@main.feature title="@With" href="With"> Immutable 'setters' - methods that create a clone but with one changed field. diff --git a/website/usageExamples/experimental/LockedExample_post.jpage b/website/usageExamples/experimental/LockedExample_post.jpage index 86bce0e9..0a20ebec 100644 --- a/website/usageExamples/experimental/LockedExample_post.jpage +++ b/website/usageExamples/experimental/LockedExample_post.jpage @@ -1,8 +1,8 @@ public class LockedExample { - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private final ReentrantLock baseLock = new ReentrantLock(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final Lock baseLock = new ReentrantLock(); private int value = 0; - + public int getValue() { this.lock.readLock().lock(); try { @@ -11,7 +11,7 @@ public class LockedExample { this.lock.readLock().unlock(); } } - + public void setValue(int newValue) { this.lock.writeLock().lock(); try { @@ -20,7 +20,7 @@ public class LockedExample { this.lock.writeLock().unlock(); } } - + public void foo() { this.baseLock.lock(); try { diff --git a/website/usageExamples/experimental/LockedExample_pre.jpage b/website/usageExamples/experimental/LockedExample_pre.jpage index fdfc7446..b741d235 100644 --- a/website/usageExamples/experimental/LockedExample_pre.jpage +++ b/website/usageExamples/experimental/LockedExample_pre.jpage @@ -1,18 +1,18 @@ -import lombok.experimental.Locked; +import lombok.Locked; public class LockedExample { private int value = 0; - + @Locked.Read public int getValue() { return value; } - + @Locked.Write public void setValue(int newValue) { value = newValue; } - + @Locked("baseLock") public void foo() { System.out.println("bar");