Support AOT compilation

This forum is used by users to request and discuss new product features. Please do not use this forum for technical support including bug reports.

Moderator: JIDE Support

Forum rules
Product suggestions only. Please do not use this forum for technical support including bug reports.

Support AOT compilation

Postby toolforger » Fri Aug 08, 2025 1:22 pm

Let me describe the problem first:

I'm trying to compile jide_demo to machine code using GraalVM, which fails with the error message: unbalanced monitors - locked objects do not match

"Monitor" is bytecode speak for "lock", which happens at the Java level in `synchronized` blocks, and at the JVM for some internal operations (including class loading/initialization IIRC).
Unbalanced monitors are typically associated with exceptions.

The root cause is that the unbalanced monitors check is optional.
The JVM can deal with unbalanced monitors, which has been described as "runtime trickery using exception tables"; AOT compilers don't (maybe can't) do it, and if they don't report the situation, they risk run-time crashes.

The suggestion it to vary the Java idiom that triggers the error message, and use one that does not trigger it instead.

I can offer help in two ways:

  • Publish the project on GitHub so the exact messages can be reproduced. (The project uses the obfuscated jide_demo.jar.)
  • If I am provided with the source code of a class, I can quickly try out different Java idioms and check which of them will avoid the problem.

Anything else I can do to help?

Regards,
Jo

=============================

Here's one of the error messages.
The class/method that it's complaining about is in the "Parser context:" line.

Code: Select all
Error: Frame states being merged are incompatible: unbalanced monitors - locked objects do not match
 This frame state: [locals: [_,_,_,_,_,_,_,_,_,_,_,_,_] stack: [] locks: [533 / 32]]
Other frame state: [locals: [_,_,_,_,_,_,_,_,_,_,_,_,_] stack: [] locks: []]
Parser context: com.jidesoft.pane.FloorTabbedPane.insertTab(Unknown Source) [bci: 253, intrinsic: false]
 253: goto          264
 256: astore        12
 258: aload         7
 260: monitorexit   
 261: aload         12
 263: athrow       

Call path from entry point to com.jidesoft.pane.FloorTabbedPane.insertTab(String, Icon, Component, String, int):
   at com.jidesoft.pane.FloorTabbedPane.insertTab(Unknown Source)
   at javax.swing.JTabbedPane.addTab(JTabbedPane.java:802)
   at com.jidesoft.docking.FrameContainer.addTab(Unknown Source)
   at com.jidesoft.docking.DefaultDockingManager.a(Unknown Source)
   at com.jidesoft.docking.DefaultDockingManager$r.run(Unknown Source)
   at java.lang.Thread.runWith(Thread.java:1596)
   at java.lang.Thread.run(Thread.java:1583)
   at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:902)
Last edited by toolforger on Sat Aug 09, 2025 2:56 am, edited 2 times in total.
toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am

Re: Support AOT compilation

Postby toolforger » Fri Aug 08, 2025 2:05 pm

toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am

Re: Support AOT compilation

Postby JIDE Support » Sat Aug 09, 2025 10:32 am

Thanks for the info. It is the first time heard of this tool. Here is the code. Maybe you can help me to nail down the issue? It indeeds has a sync on treelock .

Code: Select all
    @Override
    public void insertTab(String title, Icon icon, Component component, String tip, int index) {
        synchronized (getTreeLock()) {
            super.insertTab(title, icon, component, tip, index);

            AbstractButton button = createButton(createSwitchPageAction(title, icon, index));
            if (!(button instanceof UIResource)) {
                throw new IllegalArgumentException("The button returned from createButton(Action action) method is not an instance of UIResource.");
            }
            if (button instanceof Alignable)
                ((Alignable) button).setOrientation(adjustOrientation(getOrientation()));
            button.setName(title);
            button.setToolTipText(tip);
            customizeButton(button);
            _buttons.insertElementAt(button, index);
            add(button);

            setPercentage(0);
            component.setVisible(true);

            // need to update the index in the SwitchPageAction for all
            // buttons after adding a button

            int buttonCount = _buttons.size();
            for (int i = index; i < buttonCount; i++) {
                button = _buttons.elementAt(i);
                SwitchPageAction action = (SwitchPageAction) button.getAction();
                action.setIndex(i);
            }

            updateButtonSelectionState(getSelectedIndex());
        }
    }
JIDE Software Technical Support Team
JIDE Support
Site Admin
 
Posts: 37279
Joined: Sun Sep 14, 2003 10:49 am

Re: Support AOT compilation

Postby toolforger » Sat Aug 09, 2025 12:25 pm

JIDE Support wrote:Thanks for the info. It is the first time heard of this tool.


No wonder.
It has been around since somewhat before Java 17, but it took them quite the time to nail down all the quirks and problems.
And as this feature request shows, it's not fully there yet - assuming they can fix the issue, I don't know.

Here is the code. Maybe you can help me to nail down the issue? It indeeds has a sync on treelock .


Commencing work on that :-)
toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am

Re: Support AOT compilation

Postby toolforger » Sat Aug 09, 2025 2:13 pm

I couldn't reproduce the problem.

The method is calling various internal methods that I had to stub out, maybe the problem will reproduce if these are added?

  • createSwitchPageAction(title, icon, index)
  • createButton(action)
  • customizeButton(button)
  • setPercentage(0)
  • updateButtonSelectionState(index)
  • SwitchPageAction.setIndex
toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am

Re: Support AOT compilation

Postby toolforger » Sat Aug 09, 2025 2:26 pm

BTW there is a totally different approach: Remove the getTreeLock() synchronization!

Sounds dangerous, but I read up on it and found https://stackoverflow.com/a/34725319/6944068 .
Essentially, it says that all Swing code is declared not to be thread-safe and must be run from the EDT (1), in which case there's no multithreading and no need for locks (2).

(1) is not true strictly speaking, as there a handful of Swing functions that are thread safe.
However, all of them use a different lock than getTreeLock(); I'm appending the analysis at the end of this message.

(2) is obviously true. If all access it through a single thread, synchronization is a no-op.

---

Analysis:

Of the hits with the regex pattern `thread.*safe` inside the `javax.swing` sources, only 23 were actually stating positive thread safety (the others said "Swing methods are generally not thread safe").

Of these, 19 are in AbstractDocument and children, which use a different lock (obtained via a call to #writeLock()).
There is one mention in ComponentView#setComponentParent, which is a bit vague and in fact the method is not thread safe at all (it has unsynchronized access to the `parent` member variable, which may be inconsistent with the parent's list of children).
There is another mention DefaultCaret#repaint(), which delegates to Component#paint, which in turn merely updates the list of repaint regions and synchronizes on the instance, not the tree lock. So actually most if not all #repaint methods are thread safe, it's just not documented *sigh*.
UndoManager is a thread-safe class, which synchronizes on the instance, so it is not protected by tree lock either.

So... yes, the tree lock is nowhere used to make any Swing methods thread-safe.

UPDATE:
Full disclosure: I did not do a 100% complete analysis, just to the point that I could make an assumption about the various class' designs and had them confirmed.
I.e. I'm 95% confident that the above analysis is correct, but some more confidence building might be in order.
Or have the JIDE docs recommend not relying on Swing Javadoc that claims thread safety, because it's not realistic that they were able to thoroughly test their code for locking bugs. Instead, recommend doing all multithreaded calls to Swing through SwingUtilities.invokeLater, and maybe .invokeAndWait. (It's what I've been doing in my own Swing code.)
Last edited by toolforger on Sun Aug 10, 2025 1:40 am, edited 1 time in total.
toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am

Re: Support AOT compilation

Postby toolforger » Sun Aug 10, 2025 1:22 am

Another thought: It may actually be the obfuscator.
It seems to modify the bytecode, and if it wasn't tested with tools like GraalVM or Android's ART, it may well emit bytecode sequences that work on the JVM but not under stricter checking.

Can you disclose what obfuscator is being used?
If the message reproduces with an obfuscated class, I'd take the issue to them.
toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am

Re: Support AOT compilation

Postby JIDE Support » Sun Aug 10, 2025 7:07 pm

We are using ZKM as obfuscator.
JIDE Software Technical Support Team
JIDE Support
Site Admin
 
Posts: 37279
Joined: Sun Sep 14, 2003 10:49 am

Re: Support AOT compilation

Postby toolforger » Mon Aug 11, 2025 2:19 am

Unfortunately, the evaluation version of ZKM will obfuscate only one or two methods per class, which makes it unsuitable to check if obfuscation causes the problem.

To continue testing the "it's the obfuscator" theory, somebody with the source code will have to try my SSCCE with unobfuscated code.
SSCCE download link: https://github.com/toolforger/demo-graalvm-native-image-swing/archive/refs/heads/jide_demo.zip
That "somebody with the source code" could be Jidesoft, or a customer with a source license, or myself iF Jidesoft gives me a license.

Alternatively, somebody could donate a ZKM Small Developer license to me (290 USD). Just saying, for completeness, the other options are probably easier :D
toolforger
 
Posts: 7
Joined: Fri Aug 08, 2025 4:25 am


Return to Product Suggestions

Who is online

Users browsing this forum: No registered users and 4 guests