Bug 508586 - [dom ast] StringLiteral.getLiteralValue() throws IllegalArgumentException when called in multiple threads
Summary: [dom ast] StringLiteral.getLiteralValue() throws IllegalArgumentException whe...
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.7   Edit
Hardware: PC Windows NT
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact: Sarika Sinha CLA
URL:
Whiteboard: stalebug
Keywords: helpwanted
Depends on:
Blocks:
 
Reported: 2016-12-02 04:34 EST by Ivan Ivan CLA
Modified: 2023-03-05 16:42 EST (History)
6 users (show)

See Also:


Attachments
Line 54, "com.android.browser.BrowserActivity" (7.49 KB, application/octet-stream)
2016-12-02 04:34 EST, Ivan Ivan CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Ivan Ivan CLA 2016-12-02 04:34:27 EST
Created attachment 265697 [details]
Line 54, "com.android.browser.BrowserActivity"

This is a really weird bug. I attached the source where it happens (on line 54, "com.android.browser.BrowserActivity"). Exception is thrown only in case when I use my code which doesn't modify ast, but just call getLiteralValue() (in wrong moment?), I wasn't able to reproduce it by making compilation unit of the file and calling getLiteralValue() for each StringLiteral (via ASTVisitor). The bug doesn't occur if I use debugger to catch the moment of "throw" to give you more details. I've observed that if I hit breakpoints for all 

throw new InvalidInputException(smth);

in class org.eclipse.jdt.internal.compiler.parser.Scanner because

	public String getLiteralValue() {
		String s = getEscapedValue();
		int len = s.length();
		if (len < 2 || s.charAt(0) != '\"' || s.charAt(len-1) != '\"' ) {
			throw new IllegalArgumentException();
		}

		Scanner scanner = this.ast.scanner;
		char[] source = s.toCharArray();
		scanner.setSource(source);
		scanner.resetTo(0, source.length);
		try {
			int tokenType = scanner.getNextToken(); // exception is thrown somewhere here

exception is not thrown and code finishes fine. But if breakpoints are missing, it always throws IllegalArgumentException
Comment 1 Ivan Ivan CLA 2016-12-02 04:45:37 EST
Probably the source is looking like fine, and the details are not enough. Please tell me what else I can provide
Comment 2 Olivier Thomann CLA 2016-12-02 09:33:25 EST
This is typically the type of problem that cannot be solved without being able to debug the problem directly­. So unless you can provide a reproducable test case, I doubt there is much we can do to help.
Comment 3 Ivan Ivan CLA 2016-12-14 11:29:05 EST
Hi. I figured out, it can be reproduced pretty easy

Just call in multiple threads 
sl.getLiteralValue(); // StringLiteral sl
for any string literal

Let me know if you were not able to reproduce this, and I will attach my sources

I receive the exception sent previously or this one:

java.lang.StringIndexOutOfBoundsException: String index out of range: -1
	at java.lang.String.<init>(Unknown Source)
	at org.eclipse.jdt.internal.compiler.parser.Scanner.getCurrentStringLiteral(Scanner.java:534)
	at org.eclipse.jdt.core.dom.StringLiteral.getLiteralValue(StringLiteral.java:238)
Comment 4 Ivan Ivan CLA 2016-12-22 06:22:47 EST
Hi, please let me know that you read my message. There's a poc

public static void main(String[] args) {
	ASTParser astParser = ASTParser.newParser(AST.JLS8);
	astParser.setKind(ASTParser.K_COMPILATION_UNIT);
	astParser.setSource("class Test { public String x = \"x\"; }".toCharArray());
	ASTNode astNode = astParser.createAST(null);
	
	AtomicReference<StringLiteral> ref = new AtomicReference<StringLiteral>();
	astNode.accept(new ASTVisitor() {
		public boolean visit(StringLiteral st) {
			ref.set(st);
			return false;
		}
	});

	StringLiteral stringLiteral = ref.get();
	for(int i = 1; i <= 10; i++) {
		Thread thread = new Thread(new Runnable() {
			public void run() {
				while(true) {
					stringLiteral.getLiteralValue();
				}
			}
		});
		thread.start();
	}
}
Comment 5 Olivier Thomann CLA 2016-12-22 12:07:53 EST
I am not sure the AST is supposed to be used in a multithread environment. You might want to handle concurrent calls to the nodes.
Jay, any thoughts?
Comment 6 Ivan Ivan CLA 2017-07-26 10:20:14 EDT
Hi. Any changes? Will it be fixed? I've noticed a lot of synchronized wrappers, so I assume that multiple threads should be supported
Comment 7 Jay Arthanareeswaran CLA 2017-07-26 11:05:56 EDT
(In reply to Olivier Thomann from comment #5)
> I am not sure the AST is supposed to be used in a multithread environment.
> You might want to handle concurrent calls to the nodes.
> Jay, any thoughts?

Sorry, I missed this.

Copying Manoj, who is the right person to answer this.
Comment 8 Ivan Ivan CLA 2019-04-18 15:31:40 EDT
Hi, any changes? Doesn't seem to be fixed, but last comment was long time ago
Comment 9 Manoj N Palat CLA 2019-04-19 00:46:39 EDT
(In reply to Jay Arthanareeswaran from comment #7)
> (In reply to Olivier Thomann from comment #5)
> > I am not sure the AST is supposed to be used in a multithread environment.
> > You might want to handle concurrent calls to the nodes.
> > Jay, any thoughts?
> 
> Sorry, I missed this.
> 
> Copying Manoj, who is the right person to answer this.

Same here.. missed this. For a starter, AST is not meant to be used in a multi-thread environment.

@Sarika: Can you please investigate and take it forward? TIA.
Comment 10 Ivan Ivan CLA 2019-04-19 07:45:25 EDT
I talked with Stephan Hermann, and he told that some binding APIs are not designed to work like this, but not ASTs. So JDT can resolve string literal values during AST build time, but not each time when called.

Can you please also clarify to me, why there are synchronized statements in DefaultBindingResolver class? Or it tries to handle some multitheading calls?
Comment 11 Sarika Sinha CLA 2019-08-27 03:29:46 EDT
@Manoj,
I will need some inputs to proceed.

 AST is not meant to be used in a multi-thread environment -> Should we restrict the usage then in a must-thread environment?
Comment 12 Manoj N Palat CLA 2019-11-25 10:49:39 EST
Bulk move out of 4.14
Comment 13 Stephan Herrmann CLA 2020-02-28 18:15:26 EST
The javadoc of class ASTNode has this:

"AST nodes are thread-safe for readers provided there are no active writers."

This promise is violated by the accessor StringLiteral.getLiteralValue() which changes state of the shared scanner, an operation that cannot be thread-safe without further protection.

Options:

(1) Protect all critical sections using the scanner, e.g., using a protocol of
  Scanner acquireScanner(); // blocks if already acquired by another thread
  void releaseScanner();

(2) Introduce a method for cloning a Scanner.


Option (1) introduces new risks of deadlock.

Option (2) could incur some performance penalty, but if not called in a tight loop it should not be too bad. We should check how much state must actually cloned for the given operations. I believe it is mostly the source reference and compliance settings.

At a quick scan I only found one more usage of scanner that changes state in an operation that is supposed to be thread safe: CharLiteral.charValue().
Comment 14 Sarika Sinha CLA 2020-05-19 03:24:32 EDT
(In reply to Stephan Herrmann from comment #13)
> The javadoc of class ASTNode has this:
> 
> "AST nodes are thread-safe for readers provided there are no active writers."
> 
> This promise is violated by the accessor StringLiteral.getLiteralValue()
> which changes state of the shared scanner, an operation that cannot be
> thread-safe without further protection.
> 
> Options:
> 
> (1) Protect all critical sections using the scanner, e.g., using a protocol
> of
>   Scanner acquireScanner(); // blocks if already acquired by another thread
>   void releaseScanner();
> 
> (2) Introduce a method for cloning a Scanner.
> 
> 
> Option (1) introduces new risks of deadlock.
> 
> Option (2) could incur some performance penalty, but if not called in a
> tight loop it should not be too bad. We should check how much state must
> actually cloned for the given operations. I believe it is mostly the source
> reference and compliance settings.
> 
> At a quick scan I only found one more usage of scanner that changes state in
> an operation that is supposed to be thread safe: CharLiteral.charValue().

I agree to cloning as a better option. And we can move the static declarations to a different class to avoid duplication.
Comment 15 Manoj N Palat CLA 2021-01-07 00:10:20 EST
Bulk move out of 4.19 M1
Comment 16 Sarika Sinha CLA 2021-02-10 02:25:28 EST
Removing the target milestone as we don't have any owner of this bug.
Comment 17 Eclipse Genie CLA 2023-03-05 16:42:23 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.