Skip to content

Commit e57066d

Browse files
jeet1995Copilot
andcommitted
Fix JVM <clinit> deadlock using targeted bridge methods
Alternative approach to #48667 that avoids Class.forName() entirely. Each getXxxAccessor() fallback now calls a targeted bridge method (e.g., BridgeInternal.initializeCosmosAsyncClientAccessor()) that directly invokes the specific class's initialize() method. This eliminates classloader risks and is compile-time checked. Adds per-class initialize methods to BridgeInternal (17), ModelBridgeInternal (20), and UtilBridgeInternal (1). Also includes: - Missing static { initialize(); } blocks for 3 classes - CosmosItemSerializer <clinit> ordering fix - Renamed misnamed accessor in CosmosDiagnosticsThresholdsHelper - Forked-JVM deadlock regression test (invocationCount=5, 12 threads) - Behavioral enforcement test for static { initialize(); } blocks Fixes: #48622 Fixes: #48585 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3a6b4a5 commit e57066d

14 files changed

Lines changed: 478 additions & 45 deletions

File tree

sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/ImplementationBridgeHelpersTest.java

Lines changed: 345 additions & 0 deletions
Large diffs are not rendered by default.

sdk/cosmos/azure-cosmos/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
#### Breaking Changes
88

99
#### Bugs Fixed
10-
Fixing an NPE caused due to boxed Boolean conversion. - See [PR 48656](https://github.com/Azure/azure-sdk-for-java/pull/48656/)
10+
* Fixing an NPE caused due to boxed Boolean conversion. - See [PR 48656](https://github.com/Azure/azure-sdk-for-java/pull/48656/)
11+
* Fixed JVM `<clinit>` deadlock when multiple threads concurrently trigger Cosmos SDK class loading for the first time. - See [PR 48667](https://github.com/Azure/azure-sdk-for-java/pull/48667)
1112

1213
#### Other Changes
1314

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/BridgeInternal.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,4 +625,39 @@ public static void initializeAllAccessors() {
625625
ReadConsistencyStrategy.initialize();
626626
Http2ConnectionConfig.initialize();
627627
}
628+
629+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
630+
public static void initializeCosmosClientAccessor() { CosmosClient.initialize(); }
631+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
632+
public static void initializeCosmosAsyncClientEncryptionKeyAccessor() { CosmosAsyncClientEncryptionKey.initialize(); }
633+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
634+
public static void initializeCosmosAsyncContainerAccessor() { CosmosAsyncContainer.initialize(); }
635+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
636+
public static void initializeCosmosAsyncDatabaseAccessor() { CosmosAsyncDatabase.initialize(); }
637+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
638+
public static void initializeCosmosClientBuilderAccessor() { CosmosClientBuilder.initialize(); }
639+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
640+
public static void initializeCosmosDiagnosticsAccessor() { CosmosDiagnostics.initialize(); }
641+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
642+
public static void initializeCosmosDiagnosticsContextAccessor() { CosmosDiagnosticsContext.initialize(); }
643+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
644+
public static void initializeCosmosRequestContextAccessor() { CosmosRequestContext.initialize(); }
645+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
646+
public static void initializeCosmosExceptionAccessor() { CosmosException.initialize(); }
647+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
648+
public static void initializeDirectConnectionConfigAccessor() { DirectConnectionConfig.initialize(); }
649+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
650+
public static void initializeCosmosAsyncClientAccessor() { CosmosAsyncClient.initialize(); }
651+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
652+
public static void initializeCosmosDiagnosticsThresholdsAccessor() { CosmosDiagnosticsThresholds.initialize(); }
653+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
654+
public static void initializeCosmosContainerProactiveInitConfigAccessor() { CosmosContainerProactiveInitConfig.initialize(); }
655+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
656+
public static void initializeSessionRetryOptionsAccessor() { SessionRetryOptions.initialize(); }
657+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
658+
public static void initializeCosmosItemSerializerAccessor() { CosmosItemSerializer.initialize(); }
659+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
660+
public static void initializeReadConsistencyStrategyAccessor() { ReadConsistencyStrategy.initialize(); }
661+
@Warning(value = INTERNAL_USE_ONLY_WARNING)
662+
public static void initializeHttp2ConnectionConfigAccessor() { Http2ConnectionConfig.initialize(); }
628663
}

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosDiagnosticsContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,4 +1201,6 @@ public Integer getTargetMaxMicroBatchSize(CosmosDiagnosticsContext ctx) {
12011201
}
12021202
});
12031203
}
1204+
1205+
static { initialize(); }
12041206
}

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosItemSerializer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
*/
3131
public abstract class CosmosItemSerializer {
3232

33+
// Register the accessor before any static fields that may trigger other classes'
34+
// <clinit> which need this accessor (e.g., DefaultCosmosItemSerializer).
35+
static { initialize(); }
3336

3437
/**
3538
* Gets the default Cosmos item serializer. This serializer is used by default when no custom serializer is
@@ -163,6 +166,4 @@ public ObjectMapper getItemObjectMapper(CosmosItemSerializer serializer) {
163166
}
164167
});
165168
}
166-
167-
static { initialize(); }
168169
}

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosRequestContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,6 @@ public CosmosRequestContext create(OverridableRequestOptions requestOptions) {
217217
}
218218
);
219219
}
220+
221+
static { initialize(); }
220222
}

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
*/
3131
public abstract class CosmosQueryRequestOptionsBase<T extends CosmosQueryRequestOptionsBase<?>> implements OverridableRequestOptions {
3232
private final static ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.CosmosDiagnosticsThresholdsAccessor thresholdsAccessor =
33-
ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosAsyncClientAccessor();
33+
ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosDiagnosticsThresholdsAccessor();
3434

3535
private ConsistencyLevel consistencyLevel;
3636
private ReadConsistencyStrategy readConsistencyStrategy;

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
public final class CosmosQueryRequestOptionsImpl extends CosmosQueryRequestOptionsBase<CosmosQueryRequestOptionsImpl> {
1616
private final static ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.CosmosDiagnosticsThresholdsAccessor thresholdsAccessor =
17-
ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosAsyncClientAccessor();
17+
ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosDiagnosticsThresholdsAccessor();
1818
private String partitionKeyRangeId;
1919
private Boolean scanInQueryEnabled;
2020
private Boolean emitVerboseTracesInQuery;

0 commit comments

Comments
 (0)