| To avoid deadlocks, class initializers should not reference subtypes of the |
| current class. |
| |
| For example: |
| |
| ```java |
| class Foo { |
| public static final Bar INSTANCE = new Bar(); |
| public static class Bar extends Foo {} |
| } |
| ``` |
| |
| There is a circular reference between the class initializers for `Foo` and |
| `Bar`: `Foo` depends on `Bar` in the initializer for a `static` field, and |
| initializing `Bar` requires initializing its supertype `Foo`. If one thread |
| starts initializing `Foo` and another thread simultaneously starts initializing |
| `Bar`, it will result in a deadlock. |
| |
| ## Suggested fixes |
| |
| The best solution is to refactor to break the cycle, by defining the constant |
| field in a separate class from the supertype of the field. |
| |
| For example, using this approach to fix the sample above would result in |
| something like: |
| |
| ```java |
| class Foos { |
| public static final Bar INSTANCE = new Bar(); |
| public static class Foo {} |
| public static class Bar extends Foo {} |
| } |
| ``` |
| |
| That refactoring may be too invasive (say the code is part of an API, and there |
| are many references to the current structure). |
| |
| If the subclass is never referenced outside the current file (i.e. `Bar` is |
| never used outside of `Foo`, it is only referenced via `Foo.INSTANCE`), making |
| `Bar` `private` makes deadlocks less likely (see caveats below in the discussion |
| about `private` classes): |
| |
| ```java |
| class Foo { |
| public static final Foo INSTANCE = new Bar(); |
| private static class Bar extends Foo {} |
| } |
| ``` |
| |
| If the subclass *is* referenced outside the current field, deadlocks can be |
| avoided by ensuring that the subclass has only private constructors (or `static` |
| factory methods), so that the only way to initialize the subclass is to first |
| initialize the containing class: |
| |
| ```java |
| class Foo { |
| public static final Foo INSTANCE = new Bar(); |
| private static class Bar extends Foo { |
| private Bar() {} |
| } |
| } |
| ``` |
| |
| If the subclass needs to be directly created by code outside the current file, a |
| static factory can be added as a member of the outer class, for example: |
| |
| ```java |
| class Foo { |
| public static final Foo INSTANCE = new Bar(); |
| |
| private static class Bar extends Foo { |
| private Bar() {} |
| } |
| |
| public static Bar createBar() { |
| return new Bar(); |
| } |
| } |
| ``` |
| |
| -------------------------------------------------------------------------------- |
| |
| ## AutoValue |
| |
| AutoValue implementation classes are necessarily non-`private`, since they are |
| generated into separate files. However examples like the following can't |
| deadlock as long as the only reference to the `AutoValue_Base` class is inside |
| `Base`, since there is no way for a thread to cause `AutoValue_Base` to be |
| initialized without first having initialized `Base`: |
| |
| ```java |
| @AutoValue |
| abstract class Base { |
| abstract String bar(); |
| |
| static final Object DEFAULT = new AutoValue_Base("bar"); |
| |
| static Base of(String bar) { |
| return new AutoValue_Base(bar); |
| } |
| } |
| ``` |
| |
| There is a separate Error Prone check, https://errorprone.info/bugpattern/AutoValueSubclassLeaked, to |
| prevent `AutoValue_` classes from being accessed outside the file containing the |
| corresponding `@AutoValue` base class. |
| |
| ## Private member classes |
| |
| The check ignores references that cross from a `private` inner class (or any |
| class inside it) to its immediately enclosing class, e.g. |
| |
| ```java |
| public class A { |
| private static Object benignCycle = new B.C(); |
| private static class B { |
| public static class C extends A { } |
| } |
| } |
| ``` |
| |
| There is a cycle `A` -> `A.B.C` -> `A`, but (without reflection) it's not |
| possible to access `A.B.C` in a way that causes initialization until after A is |
| initialized. |
| |
| There are situations where deadlocks involving `private` classes can still |
| occur, but the heuristic of ignoring paths cycles from `private` members is good |
| enough for most real-world examples of deadlocks that have been observed. |
| |
| In the following, `A.C` can trigger initialization of `B`, despite `B` being |
| private. |
| |
| ```java |
| public class A { |
| private static Object bad_cycle = new B(); |
| private static class B extends A { } |
| public static class C extends B { } |
| } |
| ``` |