RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1151372 - (ann) Race condition between isAnnotationPresent and getAnnotations
Summary: (ann) Race condition between isAnnotationPresent and getAnnotations
Keywords:
Status: CLOSED CURRENTRELEASE
Alias: None
Product: Red Hat Enterprise Linux 6
Classification: Red Hat
Component: java-1.6.0-openjdk
Version: 6.5
Hardware: All
OS: Linux
urgent
urgent
Target Milestone: rc
: ---
Assignee: Andrew John Hughes
QA Contact: Lukáš Zachar
URL:
Whiteboard:
Depends On:
Blocks: 1158799 1159926
TreeView+ depends on / blocked
 
Reported: 2014-10-10 09:00 UTC by Abhijit humbe
Modified: 2018-12-09 18:49 UTC (History)
3 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Prior to this update, a race condition in some cases occurred between two system calls used to access Java annotations: isAnnotationPresent() and getAnnotation(). This caused the system to enter a deadlock. With this update, Java annotation data and the reflection API data in the Java class object have been moved into a wrapper class, and access to the data has been restricted to a set of atomic operations. As a result, the race condition no longer occurs, which in turn prevents the mentioned deadlock. In addition, the value type applied when retrieving a static field on an OpenJDK 6 virtual machine has been corrected.
Clone Of:
: 1158799 (view as bug list)
Environment:
Last Closed: 2015-06-12 15:43:24 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
openjdk bug system 7122142 0 None None None Never

Description Abhijit humbe 2014-10-10 09:00:27 UTC
FULL PRODUCT VERSION :
java version "1.6.0_27"
Java(TM) SE Runtime Environment (build 1.6.0_27-b07)
Java HotSpot(TM) 64-Bit Server VM (build 20.2-b06, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=11.04
DISTRIB_CODENAME=natty
DISTRIB_DESCRIPTION="Ubuntu 11.04"

A DESCRIPTION OF THE PROBLEM :
The race condition is as manifests as following:

One thread calls isAnnotationPresent on an annotated class where the annotation is not yet initialised for its defining classloader. This will result in a call on AnnotationType.getInstance, locking the class object for sun.reflect.annotation.AnnotationType. getInstance will result in a Class.initAnnotationsIfNecessary for that annotation, trying to acquire a lock on the class object of that annotation.

In the meanwhile, another thread has requested Class.getAnnotations for that annotation(!). Since getAnnotations locks the class object it was requested on, the first thread can't lock it when it runs into Class.initAnnotationsIfNecessary for that annotation. But the thread holding the lock will try to acquire the lock for the class object of sun.reflect.annotation.AnnotationType in AnnotationType.getInstance which is hold by the first thread, thus resulting in the deadlock.

Below is a kill -3 thread dump from the program I mention in "Steps to Reproduce":

Found one Java-level deadlock:
=============================
"pool-1-thread-2":
  waiting to lock monitor 0x00007f89d4026248 (object 0x000000073ecac700, a java.lang.Class),
  which is held by "pool-1-thread-1"
"pool-1-thread-1":
  waiting to lock monitor 0x0000000041f16cb0 (object 0x000000073ecc9f88, a java.lang.Class),
  which is held by "pool-1-thread-2"

Java stack information for the threads listed above:
===================================================
"pool-1-thread-2":
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3067)
- waiting to lock <0x000000073ecac700> (a java.lang.Class for annotationinitialisationrace.domain.Annotation)
at java.lang.Class.getAnnotation(Class.java:3029)
at sun.reflect.annotation.AnnotationType.<init>(AnnotationType.java:113)
at sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java:66)
- locked <0x000000073ecc9f88> (a java.lang.Class for sun.reflect.annotation.AnnotationType)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:202)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3070)
- locked <0x000000073ecb2c28> (a java.lang.Class for annotationinitialisationrace.domain.AnnotatedClass)
at java.lang.Class.getAnnotation(Class.java:3029)
at java.lang.Class.isAnnotationPresent(Class.java:3042)
at annotationinitialisationrace.run.IsAnnotationPresentRunnable.synchronisedRun(IsAnnotationPresentRunnable.java:19)
at annotationinitialisationrace.run.SynchronisedRunnableTemplate.run(SynchronisedRunnableTemplate.java:24)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
"pool-1-thread-1":
at sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java:63)
- waiting to lock <0x000000073ecc9f88> (a java.lang.Class for sun.reflect.annotation.AnnotationType)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:202)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3070)
- locked <0x000000073ecac700> (a java.lang.Class for annotationinitialisationrace.domain.Annotation)
at java.lang.Class.getAnnotations(Class.java:3050)
at annotationinitialisationrace.run.GetAnnotationsRunnable.synchronisedRun(GetAnnotationsRunnable.java:18)
at annotationinitialisationrace.run.SynchronisedRunnableTemplate.run(SynchronisedRunnableTemplate.java:24)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)

Found 1 deadlock.

In the real world, this deadlock was provoked for us by a Spring application context initialisation in an OSGi container (Spring DM):

   at java.lang.Class.getAnnotations(Class.java:3050)
   at org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor$1.visitEnd(AnnotationMetadataReadingVisitor.java:79)
   at org.springframework.asm.ClassReader.a(Unknown Source)
   at org.springframework.asm.ClassReader.accept(Unknown Source)
   at org.springframework.asm.ClassReader.accept(Unknown Source)
   at org.springframework.core.type.classreading.SimpleMetadataReader.getAnnotationMetadata(SimpleMetadataReader.java:55)

while another thread did the Class.isAnnotationPresent call for an anotated class.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Clone and compile https://bitbucket.org/fakraemer/annotation-initialisation-race, I was reliably running into the deadlock on every execution (4 cores).

If not, I'd suggest starting the program with a debugger attached, breaking annotationinitialisationrace.run.GetAnnotationsRunnable l18 and java.lang.Class.getAnnotation(Class<A>) l3029 (see above for the JSE I used, it should be on the initAnnotationsIfNecessary() call. You'll just have to step over the first hit on getAnnotation, thus you'll end up locking sun.reflect.annotation.AnnotationType but not yet annotationinitialisationrace.domain.Annotation. Then you switch over to the other thread which must be suspended on GetAnnotationsRunnable l18, and just resume it. It should be blocked now due to the wait for the AnnotationType lock. Then you switch over to the other thread and resume it, thus this thread should wait for locking annotationinitialisationrace.domain.Annotation.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Also the situation is rather exotic, I would expect the synchronisation to work since both calls are part of the standard reflection API.

REPRODUCIBILITY :
This bug can be reproduced occasionally.

Comment 14 Andrew John Hughes 2015-06-12 15:43:24 UTC
Closing as fixed in the current 6.6 release of java-1.6.0-openjdk by bug #1158799.


Note You need to log in before you can comment on or make changes to this bug.