GUI/Threading Instability in Java on OS X; seems to be related to the Swing/AWT -> Cocoa bridge
I've run up on a really frustrating problem involving OS X and Java Swing GUIs; specifically I'm having a problem with Cocoa Compatibility mode.
I work at a robotics lab, and a great deal of our software is run through an in-house simulation suite. The code for this simulation suite is nearly 10 years old and was created by one of our head researchers while he was in college. The simulation runs in a Swing GUI. The simulations all run fine on Linux and Windows computers, but they don't run on OS X.
To be clear, the code I am writing is not GUI code. I'm not a "GUI Guy". The code he wrote however long ago provides an API for quickly creating simulations to model robots based on standard mechanical systems of joints and links, and these simulations appear with a GUI to facilitate operations such as starting, stopping, recording, playing back, tracking variables, creating plots, etc. So I'm not invoking any Swing code directly; this is all "bog standard" (to our situation) stuff that every single one of our simulations uses. And every single one crashes on OS X while running just fine under Windows and Linux.
I've poked around the internet, and while I haven't been able to find a solution to my issue, the root of the problem seems to be that Cocoa Compatibility Mode expects the programmer to adhere to strict threading guidelines for graphical interfaces. The one example that seems to pop up the most is that if you don't thread your GUI using SwingUtilities.invokeLater()
then your code will be unstable. The guy who wrote the code acknowledges that he did not adhere to the best possible threading practices when he originally created the API.
Cocoa Compatibility Mode also seems to enforce some sort of timeout for some kind of Swing operation, not quite sure what it is, but I'm on a 2011 MacBook Pro with a 2.3 GHz quad core i7, 8 gigs of RAM, an SSD, and a 1 gig Radeon 6750M. I'd like to think I'm not running up on a time-out of some sort.
We develop in Eclipse, and launch our sims from Eclipse as well (we have no need to turn them in to executables of any form). When a simulation is launched, the GUI appears and is fully populated. However, it cannot be interacted with. Even the "traffic lights" never activate, so the only way to kill the GUI is by using the terminate button in Eclipse.
Here is a screenshot of what the console looks like. I'm not at Cocoa developer so the errors themselves don't mean too much to me, but I know that the NS prefix means that the errors are coming from Cocoa and I understand that what appears to be happening is that some sort of thread manager (Grand Central Dispatch maybe?) is attempting to act on null lock objects and at some point or another there's an issue with a thread pool.
From what I've gathered there is no way to currently disable Cocoa Compatibility Mode (it seems there used to be by using an Apple specific java system property, but that property no longer appears on the list of valid System Properties provided on the Apple Developer web site). I also tried using the -XstartOnFirstThread
argument to no avail. While we are primarily Linux and Windows driven in our lab, our organization is large and a different group wants to start using some of the simulations we've created for their projects and this division is a primarily Mac using division (I'm also an OS X user myself and so I'd like to continue working off of my MBP).
Can anyone out there tell me if there is a way to disable Cocoa compatibility mode or at the very least if my suspicions are correct re: what's causing the errors? We're not a bureaucracy and our boss knows that the code has errors so if my hunch is correct then performing a gut and a rewrite isn't out of the question like it would be at a large corporation. The simulations have no stability issues on other OSes as I have already stated, and a lot of the stability issues stemming from the restrictions of OS X seem to be related to providing an enjoyable user experience, which we don't really care about because this isn't a user-facing product. It isn't a product at all.
I'm not looking for comments or answers that talk to me about UX/UIX, non-blocking UI's, or best practices in threading/UI design. These are all things that I'm familiar with, and these are all things that my boss is more than familiar with as well. I just want someone to either help me eliminate the restrictions that OS X is arbitrarily placing on our Java code, or someone who can point me in the right direction so that we can get our current code in line w开发者_StackOverflow中文版ith the OS's restrictions.
I'm using the most up-to-date JVM/JDK available for OS X, the most up-to-date OS X dev tools, and the most up-to-date version of Snow Leopard.
EDIT: Here's a Gist that is self-contained and compiles. It is completely unresponsive, like above, and while it doesn't have all the null lock errors, it does have the autorelease errors and doesn't work on OS X.
I was facing the same problems (on the same macbook configuration) and found a bug description here.
First, I installed openjdk 7 on OSX:
- OpenJDK 7 and 8 for OS/X Snow and Lion
- Installing Java 7 and JRuby on Mac OS X Lion
I altered my project build path in eclipse to point to the newly installed jdk.1.7.0u
(on my Mac: /Library/Java/JavaVirtualMachines/1.7.0u.jdk/Contents/Home/
) and had no UI freezing anymore.
Apart from a missing trailing brace, your example and the variation below operate as expected on Mac OS 10.5.8 using Java 1.6.0_24. In particular, mouse, keyboard and focus all appear normal. Note that JFrame
forwards add()
and setLayout()
(among others) to the content pane. I'm just not used to replacing the content pane.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class FrameExample2 extends JFrame {
private JPanel panel = new JPanel();
private JTextField xAccelTextField = new JTextField(10);
private JSlider xAccelSlider = new JSlider();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
FrameExample2 frame = new FrameExample2();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
});
}
public FrameExample2() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setLayout(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createLineBorder(Color.gray, 8));
panel.add(new JLabel("Linear Acceleration along X", JLabel.CENTER));
panel.add(xAccelSlider);
xAccelTextField.setEditable(false);
xAccelTextField.setText("0");
panel.add(xAccelTextField);
xAccelTextField.setColumns(10);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
xAccelSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
xAccelTextField.setText(
String.valueOf(xAccelSlider.getValue()));
}
});
}
}
精彩评论