7122142 : (ann) Race condition between isAnnotationPresent and getAnnotations

Peter Levart peter.levart at gmail.com
Thu Jul 4 14:40:27 UTC 2013

Hi Alan, Joel,

Thanks to both for taking time to review the patch.

Here's the 3rd revision:


Answers inline...

On 07/03/2013 04:51 PM, Alan Bateman wrote:
> Sorry for the delay getting back to you on this, I've been busy with 
> other things.

Me too.

> I'm not an expert on the annotation code but the updated approach to 
> break the recursion looks good good to me (and a clever approach). 
> Joel has been on vacation but he told me that he plans to look at this 
> too (I'm happy to sponsor it in the mean-time as I think the approach 
> is good and we should get this fix in).
> There's another usage of AnnotationParser.parseAnnotation in 
> TypeAnnotationParser that will need to be updated (I only noticed it 
> when I grabbed your patch to try it).

I rather restored the package-private API so that TypeAnnotationParser 
doesn't have to be changed. It also makes for less changes in internal 
AnnotationParser code.

> A minor comment is that the alignment of the parameter declarations 
> when they go over multiple lines should probably be fixed up to be 
> consistent with the other usages.

I tailored new method to be consistent with the alignment of the other 
public method. There are 4 different multi-line-parameter alignments 
used in AnnotationParser though. I also renamed new method to 
"parseSelectAnnotations" and made it package-private, since it is only 
used from the AnnotationType.

> Thanks for adding tests. One comment on AnnotationTypeDeadlockTest is 
> that the join(500) might not be sufficient on a very busy system (say 
> when running tests concurrently) or on a low-end embedded system. So I 
> think it would be good to bump this up x10. 


> An alternative would be to not set the daemon status and just let the 
> test timeout if there is a deadlock. The spin-waits can consume cycles 
> when running tests concurrently but I don't expect it's an issue here.

Right, There can be a substantial delay from Thread.start() to thread 
actually executing it's run() method. So I added another synchronization 
latch that makes main thread park  and wait until both new threads 
start-up. Both new threads then spin-wait until main thread wakes up and 
resets the AtomicInteger value. Experimenting on my Intel i7 box shows 
that one new thread spins about 80x and the other about 3000x before 
continuing. This spin-waiting is necessary so that both threads continue 
coherently and have a better chance to provoke deadlock.

> A typo in the @summary of AnnotationTypeRuntimeAssumptionTest ("si" -> 
> "is"). I guess I'd go for slightly shorter lines to make future 
> side-by-side reviews easier.


On 07/03/2013 07:09 PM, Joel Borggrén-Franck wrote:
> Hi Peter,
> As Alan said, a big thanks for looking into this.
> I have been toying with a slightly different approach to breaking the infinite recursion by pre-construct AnnotationType instances for Retention and Inherited. While cleaner in some places others became uglier. I'm fine with this approach.

I'm interested in your approach. How do you handle construction of 
AnnotationType instances for other annotations apart from @Retention and 
@Inherited? What if there are two mutually recursive annotations like:

     public @interface AnnA {

     public @interface AnnB {

How do you construct the AnnotationType instance for any one of the 
above annotations and break recursion?

> As Alan said, you need to update the patch with a modification in TypeAnnotationParser. After doing that all java/lang tests in the jdk/test directory passes.

I missed that, yes. I changed the patch so that no changes are needed now.

> Also, can you please explain to me why the update race is safe. I have done the/some research myself but I would like to know which angles you have covered.

Well, one thing is that AnnotationType class is now effectively final, 
meaning that all it's state is referenced using final fields. If a 
reference to an instance of such class is obtained via data-race, all 
it's state is observed consistent by the thread that obtained the 
reference. The other thing is racy caching. If two or more threads are 
concurrently requesting AnnotationType for the same Class, many of them 
might construct and use it's own instance and the one published "latest" 
will finally be the one being cached. Since all such AnnotationType 
instances are constructed from the same input data, they are equivalent 
and it doesn't matter which one is used by which thread.

There is a caveat though. What if class is redefined? That's a separate 
issue and will have to be resolved in a separate patch. I'm planing to 
prepare a patch after this one gets through. The patch will remove 
contention from caching of annotations and may also take care of 
AnnotationType caching at the same time.

Regards, Peter

> Given that and that you fix Alan's comments I think this is good to go.
> Thanks again!
> cheers
> /Joel

More information about the core-libs-dev mailing list