LuciferYang commented on code in PR #13249:
URL: https://github.com/apache/gravitino/pull/13249#discussion_r4078521160


##########
catalogs/catalog-common/src/main/java/org/apache/gravitino/utils/ClassLoaderResourceCleanerUtils.java:
##########
@@ -301,20 +301,36 @@ static boolean definedBy(@Nullable Object value, 
ClassLoader classLoader) {
    * <p>All shutdown hooks are run with the system class loader, so we need to 
manually clear the
    * shutdown hooks registered by the target class loader.
    *
+   * <p>The map is JVM-global and {@code 
ApplicationShutdownHooks.add/remove/runHooks} synchronize
+   * on the {@code ApplicationShutdownHooks} class monitor, so reading the 
field, null-checking it,
+   * and mutating the map must all hold that monitor. {@code runHooks} nulls 
the field under the
+   * monitor once shutdown starts, in which case there is nothing left for us 
to clear.
+   *
    * @param targetClassLoader the classloader where the shutdown hooks are 
registered.
    */
-  private static void clearShutdownHooks(ClassLoader targetClassLoader) throws 
Exception {
+  @VisibleForTesting
+  static void clearShutdownHooks(ClassLoader targetClassLoader) throws 
Exception {
     Class<?> shutdownHooks = 
Class.forName("java.lang.ApplicationShutdownHooks");
-    IdentityHashMap<Thread, Thread> hooks =
-        (IdentityHashMap<Thread, Thread>) 
FieldUtils.readStaticField(shutdownHooks, "hooks", true);
-
-    hooks
-        .entrySet()
-        .removeIf(
-            entry -> {
-              Thread thread = entry.getKey();
-              return thread.getContextClassLoader() == targetClassLoader;
-            });
+
+    // Read the field, null-check it, and mutate the map all under the same 
monitor the JDK's
+    // add/remove/runHooks hold. During shutdown runHooks nulls the field 
while holding this
+    // monitor, so reading it outside the lock could see a stale map or throw 
an NPE.
+    synchronized (shutdownHooks) {
+      IdentityHashMap<Thread, Thread> hooks =
+          (IdentityHashMap<Thread, Thread>)
+              FieldUtils.readStaticField(shutdownHooks, "hooks", true);
+      if (hooks == null) {

Review Comment:
   Not defensive, it is a real state the JDK produces. 
`ApplicationShutdownHooks.hooks` is nulled under this same class monitor once 
shutdown starts: `runHooks()` does `synchronized 
(ApplicationShutdownHooks.class) { threads = hooks.keySet(); hooks = null; }`, 
and the static initializer also leaves it null when the class is first loaded 
after shutdown has already begun (which is why `add()`/`remove()` throw 
`IllegalStateException("Shutdown in progress")` when it is null). So a 
`clearShutdownHooks` call that races JVM shutdown genuinely observes null, and 
when it does the JDK has taken ownership of the hooks and will run them itself, 
so there is nothing left for us to clear and we return. Doing the read and 
null-check inside the monitor (not outside) is what makes that observation 
reliable rather than a stale read or an NPE.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to