Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PropertyNamingStrategy class initialization depends on its subclass, this can lead to class loading deadlock #2715

Closed
fangwentong opened this issue May 9, 2020 · 6 comments

Comments

@fangwentong
Copy link

If a class refer to its subclasses in its static initializers or in static fields, this references can cause JVM-level deadlocks in multithreaded environment, when one thread tries to load superclass and another thread tries to load subclass at the same time.

See: https://bugs.openjdk.java.net/browse/JDK-8037567

The following demo code can reproduce this deadlock

public class ClassInitDeadLock {

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                // trigger supper class initialize
                PascalCaseStrategy strategy = PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE;
            }
        }, "Thread-A");

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                // trigger sub class initialize
                PascalCaseStrategy strategy = new PascalCaseStrategy();
            }
        }, "Thread-B");

        threadA.start();
        Thread.sleep(100);
        threadB.start();
    }

    public static class PropertyNamingStrategy {
        static {
            try {
                // sleep 1s for deadlock reproduction
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignore
            }
        }

        public static final PascalCaseStrategy PASCAL_CASE_TO_CAMEL_CASE = new PascalCaseStrategy();
    }

    public static class PascalCaseStrategy extends PropertyNamingStrategy {
    }
}

When the demo program ClassInitDeadLock started, it could not exit automatically. Thread-A holding PropertyNamingStrategy class , and waiting PascalCaseStrategy class initialize, while Thread-B holding PascalCaseStrategy class, and waiting PropertyNamingStrategy class initialize, deadlock occurred!

jstack of these two thread:

"Thread-A" #11 prio=5 os_prio=31 tid=0x00007fa8c29d8000 nid=0xa803 in Object.wait() [0x000070000a4c4000]
   java.lang.Thread.State: RUNNABLE
	at ClassInitDeadLock$PropertyNamingStrategy.<clinit>(ClassInitDeadLock.java:35)
	at ClassInitDeadLock$1.run(ClassInitDeadLock.java:8)
	at java.lang.Thread.run(Thread.java:748)

"Thread-B" #12 prio=5 os_prio=31 tid=0x00007fa8c4a9b800 nid=0x5603 in Object.wait() [0x000070000a5c7000]
   java.lang.Thread.State: RUNNABLE
	at ClassInitDeadLock$2.run(ClassInitDeadLock.java:16)
	at java.lang.Thread.run(Thread.java:748)

In the actual environment, PropertyNamingStrategy and its subclass defined in jackson-databind initialize very fast, so It's hard to produce the scene that multithread load superclass and subclass at the same time.

I happened to meet this deadlock recently, It turn out to be JsonErrorUnmarshaller.java#L37 defined in aws-java-sdk-core, trying to initialize subclass PascalCaseStrategy, at the same time, another thread is initilizing superclass PropertyNamingStrategy

private static final ObjectMapper MAPPER = new ObjectMapper().configure(
        DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).setPropertyNamingStrategy(
        new PascalCaseStrategy());
@cowtowncoder
Copy link
Member

Interesting. Thank you for reporting this, sounds like a potentially severe issue.

Do you know version of Jackson problem was encountered with? (AWS jdk core used relatively old version last I checked, 2.6.7?, but that was a while ago).
I assume issue has been around for a while but just wanted to check.

@cowtowncoder
Copy link
Member

Also... may be really really difficult to actually resolve, wrt backwards compatibility problems.

@fangwentong
Copy link
Author

fangwentong commented May 9, 2020

As a workarround, we can change the code of aws-java-sdk-core, new PascalCaseStrategy() => PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE, this change can bypass the problem.

@cowtowncoder
Copy link
Member

cowtowncoder commented May 9, 2020

@fangwentong Yes. I think for databind itself, will probably need to introduce something like PropertyNamingStrategies to hold instances (which would have been good to do from beginning as a pattern). And then deprecate sub-type static instance.

@fangwentong
Copy link
Author

OK, thanks, backwards compatibility is really important,I'll create a PR to aws-java-sdk-core.

fangwentong added a commit to fangwentong/aws-sdk-java that referenced this issue May 9, 2020
bypass the potential deadlock when PropertyNamingStrategy class initialization
see: FasterXML/jackson-databind#2715
fangwentong added a commit to fangwentong/ksc-sdk-java that referenced this issue May 9, 2020
bypass the potential deadlock when PropertyNamingStrategy class initialization
see: FasterXML/jackson-databind#2715
@cowtowncoder cowtowncoder added 2.12 and removed 2.10 labels May 23, 2020
@cowtowncoder
Copy link
Member

Unfortunately I think the solution can only go in 2.12, as I need to add PropertyNamingStrategies as the place for static instances, deprecate existing entries; that is, making API changes that need to go in a new minor version.

cowtowncoder added a commit that referenced this issue Aug 25, 2020
@cowtowncoder cowtowncoder changed the title PropertyNamingStrategy class initialization depends on its subclass, this might lead to class loading deadlock PropertyNamingStrategy class initialization depends on its subclass, this can lead to class loading deadlock Aug 25, 2020
cowtowncoder added a commit that referenced this issue Oct 13, 2020
adhirajsinghchauhan added a commit to oxygen-updater/oxygen-updater that referenced this issue Dec 10, 2020
Also replaced Jackson's PropertyNamingStrategy deprecation: FasterXML/jackson-databind#2715
rhowe added a commit to rhowe/dropwizard that referenced this issue Jan 3, 2021
…akeCaseStrategy

This has been replaced by PropertyNamingStrategies.SnakeCaseStrategy due to a
possible classloader deadlock:
FasterXML/jackson-databind#2715
joschi pushed a commit to dropwizard/dropwizard that referenced this issue Jan 4, 2021
…3634)

This has been replaced by `PropertyNamingStrategies.SnakeCaseStrategy` due to a possible class loader deadlock.

Refs FasterXML/jackson-databind#2715
joschi pushed a commit to dropwizard/dropwizard that referenced this issue Jan 6, 2021
…3634)

This has been replaced by `PropertyNamingStrategies.SnakeCaseStrategy` due to a possible class loader deadlock.

Refs FasterXML/jackson-databind#2715

(cherry picked from commit 710c9a7)
finaglehelper pushed a commit to twitter/util that referenced this issue Feb 24, 2022
Problem

In release 2.12,  Jackson introduced `PropertyNamingStrategies` as a work-around
for [#2715](FasterXML/jackson-databind#2715) which deprecated using the like `PropertyNamingStrategy` static fields.

Solution

Move to using the equivalent static fields in `PropertyNamingStrategies`.

Result

Less deprection tech-debt.

Differential Revision: https://phabricator.twitter.biz/D838232
luca-digrazia pushed a commit to luca-digrazia/DatasetCommitsDiffSearch that referenced this issue Sep 4, 2022
… (#3634)

    This has been replaced by `PropertyNamingStrategies.SnakeCaseStrategy` due to a possible class loader deadlock.

    Refs FasterXML/jackson-databind#2715
patelila added a commit to hmcts/ccd-definition-store-api that referenced this issue May 13, 2024
…ropertyNamingStrategy$SnakeCaseStrategy PropertyNamingStrategy.SnakeCaseStrategy is used but it has been deprecated due to risk of deadlock. Consider using PropertyNamingStrategies.SnakeCaseStrategy instead. See FasterXML/jackson-databind#2715 for more details.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants