[ 
https://issues.apache.org/jira/browse/JDO-856?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18048427#comment-18048427
 ] 

Tilmann Zäschke edited comment on JDO-856 at 12/30/25 8:03 PM:
---------------------------------------------------------------

+*Summary*+

It appears that the initialization of Q-classes in the reference implementation 
is not thread-safe. 

Reproduce:
The likelihood of the problem occuring correlates with the number of CPU cores 
of the test environment.
On a is only reproducible on Multicore CPU. It is reliably reproducible on my 
AMD Ryzen 7 7700 (8 cores / 16 Threads).
The minimum configuration is running the following two tests, where it will 
hang indefinitely in the second test:
{{org.apache.jdo.tck.query.api.QueryExtentions \
org.apache.jdo.tck.query.api.SampleReadQueries}}
JUnit appears to be starting about 20 worker threads.

+*Analysis*+

I created stack trace dumps from two hanging runs, they are attached, see  
[^tck-1-reduced.txt] and [^tck-2-reduced.txt]. I removed all unrelated stack 
entries outside the calls to the test methods.
The stack traces show that 18 threads hang while trying to call the 
candidate(...) method of a Q class. Two threads hang inside the Q-classes, 
inside a chain of constructor calls, while calling the static initialization of 
another Q-class.

Static initialization of classes is implicitly synchronized on the class 
object. It appears that the concurrent calling of class initialization causes a 
dead-lock.

+*Workaround*+

A workaround is to perform static initialization before executing tests. As 
expected, the following changes (calling candidate() for each class) to the 
SampleReadQueries test fixes the problem:

{{{}{color:#cc7832}protected void {color}{color:#ffc66d}localSetUp{color}() 
{{}}}{{{{}}{}}}
{{  addTearDownClass(CompanyModelReader.getTearDownClasses()){color:#cc7832};
{color}{color:#cc7832}  
{color}loadAndPersistCompanyModel(getPM()){color:#cc7832};
{color}{color:#cc7832}  {color}QCompany.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QDentalInsurance.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QDepartment.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QEmployee.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QFullTimeEmployee.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QInsurance.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QMedicalInsurance.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QMeetingRoom.candidate(){color:#cc7832};
{color}}}{{{color:#cc7832}  {color}QPerson.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QProject.candidate(){color:#cc7832};
{{}}}

+*Proposed solutions*+

1) We could try to introduce some kind of locking, for example, all 
constructors could move their initialization (calling other constructors) into 
a synchronized block that synchronizes on a common object, for example on the 
ExpressionImpl.class object:

synchronized {color}(ExpressionImpl.{color:#cc7832}class{color}) {}}
{{{color:#9876aa}    // call other Q-classes here{color}}}
{{}}}

{{This would be relatively easy to implement, but would require (multiple) 
thread synchronization(s) whenever a Q-class is instantiated. This synchronized 
block would need to go into every constructor in the class hierarchy.}}

2) Another solution might be to have candidate() only return a simple instance 
of the desired class. The query engine would need to be modified to create/fill 
in Q-classes into fields on demand while traversing the query result. It is not 
clear whether this is possible and would require a significant refactoring.

 

 


was (Author: tilmannz):
+*Summary*+

It appears that the initialization of Q-classes in the reference implementation 
is not thread-safe. 

Reproduce:
The likelihood of the problem occuring correlates with the number of CPU cores 
of the test environment.
On a is only reproducible on Multicore CPU. It is reliably reproducible on my 
AMD Ryzen 7 7700 (8 cores / 16 Threads).
The minimum configuration is running the following two tests, where it will 
hang indefinitely in the second test:
{{org.apache.jdo.tck.query.api.QueryExtentions \
org.apache.jdo.tck.query.api.SampleReadQueries}}
JUnit appears to be starting about 20 worker threads.

+*Analysis*+

I created stack trace dumps from two hanging runs, they are attached, see  
[^tck-1-reduced.txt] and [^tck-2-reduced.txt]. I removed all unrelated stack 
entries outside the calls to the test methods.
The stack traces show that 18 threads hang while trying to call the 
candidate(...) method of a Q class. Two threads hang inside the Q-classes, 
inside a chain of constructor calls, while calling the static initialization of 
another Q-class.

Static initialization of classes is implicitly synchronized on the class 
object. It appears that the concurrent calling of class initialization causes a 
dead-lock.

+*Workaround*+

A workaround is to perform static initialization before executing tests. As 
expected, the following changes (calling candidate() for each class) to the 
SampleReadQueries test fixes the problem:

{{{}{color:#cc7832}protected void {color}{color:#ffc66d}localSetUp{color}() 
{{}}}{{{}{}}}
{{  addTearDownClass(CompanyModelReader.getTearDownClasses()){color:#cc7832};
{color}{color:#cc7832}  
{color}loadAndPersistCompanyModel(getPM()){color:#cc7832};
{color}{color:#cc7832}  {color}QCompany.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QDentalInsurance.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QDepartment.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QEmployee.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QFullTimeEmployee.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QInsurance.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QMedicalInsurance.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QMeetingRoom.candidate(){color:#cc7832};
{color}}}{{{color:#cc7832}  {color}QPerson.candidate(){color:#cc7832};
{color}{color:#cc7832}  {color}QProject.candidate(){color:#cc7832};
{{}}}

+*Proposed solutions*+

1) We could try to introduce some kind of locking, for example, all 
constructors could move their initialization (calling other constructors) into 
a synchronized block that synchronizes on a common object, for example on the 
ExpressionImpl.class object:

synchronized {color}(ExpressionImpl.{color:#cc7832}class{color}) {}}
{{{color:#9876aa}    // call other Q-classes here{color}}}
{{}}}

{{This would be relatively easy to implement, but would require (multiple) 
thread synchronization(s) whenever a Q-class is instantiated. This synchronized 
block would need to go into every constructor in the class hierarchy.}}

2) Another solution might be to have candidate() only return a simple instance 
of the desired class. The query engine would need to be modified to create/fill 
in Q-classes into fields on demand while traversing the query result. It is not 
clear whether this is possible and would require a significant refactoring.

 

 

> TCK hangs on query.conf
> -----------------------
>
>                 Key: JDO-856
>                 URL: https://issues.apache.org/jira/browse/JDO-856
>             Project: JDO
>          Issue Type: Bug
>          Components: tck
>    Affects Versions: JDO 3.2.1
>            Reporter: Tilmann Zäschke
>            Priority: Major
>         Attachments: tck-1-reduced.txt, tck-2-reduced.txt
>
>
> Running the TCK on my machine (windows 11, Java 1.8), consistently hangs in 
> query.conf.
> The tests do not hang on Ubuntu 22 on the same machine.
> The issue can be fixed by disabling some of the concurrent executions.
> Reproduce:
> {{mvn install -Djdo.tck.cfglist="query.conf"}}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to