Skip to content

Commit

Permalink
use cloned scanner to prevent concurrent access issues
Browse files Browse the repository at this point in the history
  • Loading branch information
martinlippert committed Jan 6, 2025
1 parent 05e6ea7 commit 05ea498
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,25 @@ public void testCharacterLiteral() {
String result = this.b.toString();
assertTrue(result.equals("[(eCL'q''q'eCL)]")); //$NON-NLS-1$
}
public void testCharacterLiteralConcurrent() throws Exception {
CharacterLiteral x1 = this.ast.newCharacterLiteral();
x1.setCharValue('q');

ExecutorService executorService = Executors.newFixedThreadPool(10);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
x1.charValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executorService);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
executorService.shutdown();
}
/** @deprecated using deprecated code */
public void testClassInstanceCreation() {
ClassInstanceCreation x1 = this.ast.newClassInstanceCreation();
Expand Down Expand Up @@ -2034,18 +2053,15 @@ public void testStringLiteral() {
assertTrue(result.equals("[(eSLHHeSL)]")); //$NON-NLS-1$
}
public void testStringLiteralConcurrent() throws Exception {
ASTParser parser = ASTParser.newParser(AST.JLS21);
parser.setSource("\"hello\\nworld\"".toCharArray());
parser.setKind(ASTParser.K_EXPRESSION);
ASTNode a = parser.createAST(null);
StringLiteral literal = (StringLiteral) a;
StringLiteral x1 = this.ast.newStringLiteral();
x1.setEscapedValue("\"hello\\nworld\""); //$NON-NLS-1$

ExecutorService executorService = Executors.newFixedThreadPool(10);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
literal.getLiteralValue();
x1.getLiteralValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
Expand Down
24 changes: 24 additions & 0 deletions org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java
Original file line number Diff line number Diff line change
Expand Up @@ -4003,4 +4003,28 @@ public boolean isPreviewEnabled() {
public static int getJLSLatest() {
return JLS_INTERNAL_Latest;
}

/**
* Returns a new instance of the AST scanner with the same configuration. Since the scanner is
* stateful, using a cloned copy in read methods on AST nodes (e.g. StringLiteral.getLiteralValue)
* allows those read methods to be used concurrently
*
* @return new Scanner instance with the same config as AST.scanner
* @since 3.41
*/
Scanner clonedScanner() {
Scanner astScanner = this.scanner;

return new Scanner(
astScanner.tokenizeComments,
astScanner.tokenizeWhiteSpace,
astScanner.checkNonExternalizedStringLiterals,
astScanner.sourceLevel,
astScanner.complianceLevel,
astScanner.taskTags,
astScanner.taskPriorities,
astScanner.isTaskCaseSensitive,
astScanner.previewEnabled);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ void internalSetEscapedValue(String value) {
* @exception IllegalArgumentException if the literal value cannot be converted
*/
public char charValue() {
Scanner scanner = this.ast.scanner;
Scanner scanner = this.ast.clonedScanner();
char[] source = this.escapedValue.toCharArray();
scanner.setSource(source);
scanner.resetTo(0, source.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public String getLiteralValue() {
throw new IllegalArgumentException();
}

Scanner scanner = this.ast.scanner;
Scanner scanner = this.ast.clonedScanner();
char[] source = s.toCharArray();
scanner.setSource(source);
scanner.resetTo(0, source.length);
Expand Down

0 comments on commit 05ea498

Please sign in to comment.