Search code examples
javaswingaccess-violationjna

Strange phenomenon: Java/Swing-operations cause access violation in native library (JNA)


Greetings fellow developers!

Since SO was almost always helpful with my programming problems, I decided to sign up and give it a shot with my most recent problem. It really is a strange phenomenon that neither I nor my collegue can figure out. I'm sorry I can't provide a working sample, but the project is way to complex to break it down, and specific hardware is needed to run it properly. So I'll try my best to explain it.

The foundation of our project is a native library (a 32-Bit Windows C-DLL in this case) to access project-specific hardware via a Java application (JNA). The purpose is to manage and display the proprietary file-system of the hardware (connected via USB) in a Swing UI. This is a pretty common project configuration for us, since we integrated a lot of native libraries and drivers in Java applications.

Summary: Unit-tests for enumerating devices work fine. A module of the native library allocates memory and fills it with structs, each containing information for a connected device. It is not good practice, but since we do not have any influence on this part we have to go with it. I mapped this struct in Java/JNA, call the native function, copy the struct content to a Java transfer class and print it in the console. Works just fine.

Now if there are UI-operations active while enumerating devices, the native library crashes with an access violation. Even if this UI-operations have nothing to do with the library. The JNA error message shows an EXCEPTION_ACCESS_VIOLATION (0xc0000005), which SO research revealed as invalid/empty memory.

Has anyone ever encountered such problems before? We certainly never did. It took me days to narrow down the error source to this part of the code. Debugging is not easy when native libraries are involved. Is it possible that there is a JVM memory concurrency problem? Since the native library allocates memory by itself and the JVM doesn't know anything about it - so the JVM tries to allocate memory for new Swing components in already used memory?

Code: The following snippet is from my unit-test, broken down as far as possible. The intended sequence is obvious: Remove nodes from the root-node, load connected devices and add these devices as new nodes. This code crashes with an access violation, but NOT AT THE NATIVE CALL - it crashes as soon as I access tree components.

    public void loadDevices(){
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                rootNode.removeAllChildren();
                rootNode.add(new LoadingNode());
                tree.expandPath(new TreePath(rootNode));
            }
        });

        final List<Device> devices = lib.loadDevices(); // wrapped native call

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                rootNode.removeAllChildren();
                if(!devices.isEmpty()){
                    for (Device dev : devices ) {
                        DevNode node = new DevNode(dev);
                        rootNode.add(node);
                    }
                }
            }
         });
     }

Note: The DevNode does not contain any native data, the content of each native struct is copied to a Java transfer object. The GC should not have issues when trying to move object data, because all unmanaged code is handled locally in the lib#loadDevices() method.

When I remove the calls to the SwingUtilities completely and print the resulting device information to the console instead of creating nodes, this part works fine. As soon as I try to access the JTree or TreeModel members, the code crashes. It doesn't metter if I do this in a call to SwingUtitilies#invokeLater() or in the same thread.

I know this is a very specific problem that hardly anybody would be interested in (what makes it really hard to search for solutions in SO/Google). But maybe I am lucky and somebody has already encountered this problem.

So long
xander

Edit: Originally this code was wrapped in a worker thread, leading to the same results. This is just a snippet of my unit-test.

Edit 2: It seems I didn't make myself clear enough or forgot to mention something important here, sorry. The access to the tree or its model doesn't necessarily have to do with the native library. Look at the code again: The first call to invokeLater does nothing but remove nodes from the tree. Even when I remove the second call to invokeLater, the native library crashes!


Solution

  • I have struggeled a lot with JTree untill I learned this:

    On JTree one should not edit the nodes itself, but use the methods provided on the DefaultTreeModel:

    setRoot(TreeNode root) 
    removeNodeFromParent(MutableTreeNode node) 
    insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) 
    

    Editing the nodes itself can (and will sooner or later) lead to strange behaviour.

    This ofcourse when you are using DefaultTreeModel and MutableTreeNode. I would strongly advise to do this as I have seen to many wrong implementations of TreeModel.