SUGGESTED FIX
Applet's manager is stored in a thread local storage for all threads spawned from
the applet's lifecycle thread (e.g. threads created from init(), start(), stop(),
etc.) and the liveconnect worker thread for that applet.
Plugin2Manager.getCurrentManager() called on those threads should get
the correct Plugin2Manager.
The only problem left is the awt event queue because this thread is shared among
all applets in the AppContext. If EventQueue.invoke*() or SwingUtilities.invoke*()
are used, Plugin2Manager.getCurrentManager() won't get precise answer running
on the event queue thread.
|
EVALUATION
A simple test case for this issue can be found in Attachments section.
To reproduce, just open Test1.html with IE (whether from a local disk or
a web server) and press the button two times. The problem happens with
Plugin2 only.
A Java thread dump collected using jstack indicates that the "Applet 2
LiveConnect Worker Thread" in the client VM (java.exe) is stuck at
sun.plugin2.main.client.MessagePassingExecutionContext.getProxyList(),
and the browser VM (iexplore.exe) is inside runMessagePump().
Testing with an instrumented JDK confirmed this. The "Applet 2 LiveConnect
Worker Thread" issues an asynchronous GET_PROXY request to some other
thread. This request goes to a pipe, and the issuing thread waits for
a response, but it never gets any notification. Probably it happens because
the targeted thread is already waiting for some other event, and doesn't
read the message from the pipe. There is also some periodic message exchange
going through the same pipe. It happens every 10 seconds, even after IE hangs.
Thanks to Hao Dong's help, the root cause is found. The problem is that
Plugin2 could not get the correct Applet2Manager for the running applet.
When the applet in the second window tries to get a proxy, Plugin2 gets
a wrong Applet2Manager (it got the manager for the applet in the first window)
and used a wrong ExecutionContext to get the proxy list. The GetProxyMessage
has the AppletID of the first applet. On the browser side, the message is
routed to the main thread of the window where the first applet resides in.
That main thread is blocked doing a JavaScript call and no message pump is
running. Thus the browser deadlocked.
The message should be routed to the main thread of the second window on which
a message pump is running. There is actually a good FIXME in Plugin2Manager.java
about this semantic problem.
/** Fetches the Plugin2Manager instance associated with the
current AppContext. <P>
NOTE: (FIXME) this method may return imprecise answers, in
particular in the case where the old "class loader caching"
mechanism is being used and we have multiple applets all
running in the same AppContext. This definitely isn't the
execution model we want to support long term going forward; it
causes all sorts of encapsulation and termination problems. In
the meantime, callers of this method should implicitly assume
that it might return a "random" Plugin2Manager if multiple
applets are running in the same AppContext. If more precise
answers are required, such as for Java-to-JavaScript calls,
different mechanisms should be used.
*/
public static Plugin2Manager getCurrentManager() {
Well, the root is the class loader caching that Plugin2 want to get rid of
very much.
One possible workaround may be to disable classloading sharing in applet's
param <param name="classloader_cache" value="false">. We checked it with
the customer, and unfortunately it doesn't work in their application.
It causes their applet to be blank and hung. Their application relies
on the fact that the applets should be loaded by the same classloader.
Another workaround is to close a pop up applet window and open it again,
but the customer is reluctant to use it.
|