< prev index next >

src/java.base/share/classes/java/io/ObjectInputStream.java

Print this page

        

@@ -417,10 +417,23 @@
             return readObjectOverride();
         }
 
         // if nested read, passHandle contains handle of enclosing object
         int outerHandle = passHandle;
+        // save currentReader
+        Object outerReader = currentReader;
+
+        Thread thread  = Thread.currentThread();
+        if (outerReader == null ||
+            outerReader == thread ||
+            (outerReader instanceof ReaderContext &&
+             ((ReaderContext) outerReader).thread == thread)) {
+            // If no other thead caches ReaderContext or this is recursive entry,
+            // place mark so that latestUserDefinedLoader() can cache loader in
+            // this context
+            currentReader = thread;
+        }
         try {
             Object obj = readObject0(false);
             handles.markDependency(outerHandle, passHandle);
             ClassNotFoundException ex = handles.lookupException(passHandle);
             if (ex != null) {

@@ -434,10 +447,17 @@
         } finally {
             passHandle = outerHandle;
             if (closed && depth == 0) {
                 clear();
             }
+            if (outerReader == null ||
+                outerReader == thread ||
+                (outerReader instanceof ReaderContext &&
+                 ((ReaderContext) outerReader).thread == thread)) {
+                // restore/clear currentReader when in correct thread/non-nested call
+                currentReader = outerReader;
+            }
         }
     }
 
     /**
      * This method is called by trusted subclasses of ObjectOutputStream that

@@ -512,10 +532,23 @@
      * @since   1.4
      */
     public Object readUnshared() throws IOException, ClassNotFoundException {
         // if nested read, passHandle contains handle of enclosing object
         int outerHandle = passHandle;
+        // save currentReader
+        Object outerReader = currentReader;
+
+        Thread thread  = Thread.currentThread();
+        if (outerReader == null ||
+            outerReader == thread ||
+            (outerReader instanceof ReaderContext &&
+             ((ReaderContext) outerReader).thread == thread)) {
+            // If no other thead caches ReaderContext or this is recursive entry,
+            // place mark so that latestUserDefinedLoader() can cache loader in
+            // this context
+            currentReader = thread;
+        }
         try {
             Object obj = readObject0(true);
             handles.markDependency(outerHandle, passHandle);
             ClassNotFoundException ex = handles.lookupException(passHandle);
             if (ex != null) {

@@ -529,10 +562,17 @@
         } finally {
             passHandle = outerHandle;
             if (closed && depth == 0) {
                 clear();
             }
+            if (outerReader == null ||
+                outerReader == thread ||
+                (outerReader instanceof ReaderContext &&
+                 ((ReaderContext) outerReader).thread == thread)) {
+                // restore/clear currentReader when in correct thread/non-nested call
+                currentReader = outerReader;
+            }
         }
     }
 
     /**
      * Read the non-static and non-transient fields of the current class from

@@ -2387,18 +2427,64 @@
     // REMIND: remove once hotspot inlines Double.longBitsToDouble
     private static native void bytesToDoubles(byte[] src, int srcpos,
                                               double[] dst, int dstpos,
                                               int ndoubles);
 
+    // Holds a pair of latestUserDefinedLoader() result and the thread that called
+    // the method, so that latestUserDefinedLoader() can identify if it can reuse
+    // the cached result.
+    private static class ReaderContext {
+        final ClassLoader loader;
+        final Thread thread = Thread.currentThread();
+
+        ReaderContext(ClassLoader loader) {
+            this.loader = loader;
+        }
+    }
+
+    // Records the thread that can cache latestUserDefinedLoader. This field containes
+    // either null (when not called via public readObject() / readUnshared()), or
+    // a Thread instance (marking the thread that entered public readObject() /
+    //    readUnshared() and is responsible for cleanup too), or
+    // a ReaderContext instance with cached loader.
+    // On recursive entry to public readObject() / readUnshared(), the current value
+    // of this field is saved in a local variable and reset to the Thread instance,
+    // so that the nexted latestUserDefinedLoader() can evaluate loader in the context.
+    // This field is left non-volatile because this field does not intend to limit
+    // only one thread to use ReaderContext but to make sure a thread uses correct
+    // ReaderContext and cleans it up on return from readObject() / readUnshared().
+    private Object currentReader;
+
     /**
      * Returns the first non-null and non-platform class loader (not counting
      * class loaders of generated reflection implementation classes) up the
      * execution stack, or the platform class loader if only code from the
      * bootstrap and platform class loader is on the stack.
      */
-    private static ClassLoader latestUserDefinedLoader() {
-        return jdk.internal.misc.VM.latestUserDefinedLoader();
+    private ClassLoader latestUserDefinedLoader() {
+        Object readerCtx = currentReader;
+        ClassLoader loader;
+        Thread thread = Thread.currentThread();
+        if (readerCtx == thread) {
+            // entered via public readObject() / readUnshared()
+            // so we must evaluate loader lazily and cache it
+            loader = jdk.internal.misc.VM.latestUserDefinedLoader();
+            currentReader = new ReaderContext(loader);
+        } else if (readerCtx instanceof ReaderContext &&
+                   ((ReaderContext) readerCtx).thread == thread) {
+            // use cached value if correct thread
+            loader = ((ReaderContext) readerCtx).loader;
+        } else {
+            // not called via public readObject() / readUnshared():
+            //   (readerCtx == null) or
+            // invalid multi threaded use:
+            //   (readerCtx != Thread.currentThread() &&
+            //    readerCtx.thread != Thread.currentThread())
+            // - don't cache
+            loader = jdk.internal.misc.VM.latestUserDefinedLoader();
+        }
+        return loader;
     }
 
     /**
      * Default GetField implementation.
      */
< prev index next >