Skip to content

Commit 467d834

Browse files
concurrent-api: defer logging during CapturedContextProviders initialization
Motivation: The MDC utilities depend on the AsyncContext for storing properties. This can create a cyclical dependency since we try to log the loaded providers as part of the initialization. Modifications: Submit the logging to a different thread so that it will happen 'later' after the class initialization is complete.
1 parent 57fac32 commit 467d834

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

servicetalk-concurrent-api/src/main/java/io/servicetalk/concurrent/api/CapturedContextProviders.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,28 @@ static List<CapturedContextProvider> providers() {
4343
return PROVIDERS;
4444
}
4545

46-
// TODO: this was copied from `ServiceLoaderUtils` because the `CapturedContextProvider` interface is package
47-
// private and the call to `ServiceLoader.load(..)` can only load implementations for types that are visible
48-
// to the package from which it is called (because it checks if it's accessible by checking stack trace).
49-
// One we make CapturedContextProvider public we can return to using ServiceLoaderUtils.
46+
// This was copied from `ServiceLoaderUtils` because the implementation there logs the result, which normally
47+
// wouldn't be a problem but as it turns out things like the MDC logging utils depend on the AsyncContext which
48+
// may result in a cyclical dependency in class initialization.
5049
private static <T> List<T> loadProviders(final Class<T> clazz, final ClassLoader classLoader, final Logger logger) {
5150
final List<T> list = new ArrayList<>(0);
5251
for (T provider : ServiceLoader.load(clazz, classLoader)) {
5352
list.add(provider);
5453
}
5554
if (list.isEmpty()) {
56-
logger.debug("ServiceLoader {}(s) registered: []", clazz.getSimpleName());
55+
runLater(() -> logger.debug("ServiceLoader {}(s) registered: []", clazz.getSimpleName()));
5756
return emptyList();
5857
}
59-
logger.info("ServiceLoader {}(s) registered: {}", clazz.getSimpleName(), list);
58+
runLater(() -> logger.info("ServiceLoader {}(s) registered: {}", clazz.getSimpleName(), list));
6059
return unmodifiableList(list);
6160
}
61+
62+
// We have to run logging code 'later' because of cyclical dependency issues that can arise if logging depends
63+
// on the AsyncContext.
64+
private static void runLater(Runnable runnable) {
65+
Thread t = new Thread(runnable);
66+
t.setDaemon(true);
67+
t.setName(CapturedContextProviders.class.getSimpleName() + "-logging");
68+
t.start();
69+
}
6270
}

0 commit comments

Comments
 (0)