Search code examples
javax11jna

JNA X11 and XFree


When doing low level X11 programming it is usually necessary to call XFree() on returned structures. For example XGetWMName, I've done this in the following code, which then blows up after a few seconds with

AssertionMessage: *** Error in `/opt/jdk1.8.0_40/bin/java': double free or corruption (fasttop): 0x00007f7ca822bdd0 ***

This is caused by the XFree() call, if that is removed the problem disappears. However monitoring the process with top shows steady memory growth suggesting I do need it...

public class X11WindowFinder {

    private X11 x11;

    public X11WindowFinder() {
        x11 = X11.INSTANCE;
    }

    public List<Window> find(Pattern title) {
        Display display = x11.XOpenDisplay(null);
        Window root = x11.XDefaultRootWindow(display);
        List<Window> windows = recurse(x11, display, root, title);
        x11.XCloseDisplay(display);
        return windows;
    }

    private synchronized List<Window> recurse(X11 x11, Display display, Window root, Pattern pattern) {
        List<Window> windows = new ArrayList<>(1);
        X11.WindowByReference windowRef = new X11.WindowByReference();
        X11.WindowByReference parentRef = new X11.WindowByReference();
        PointerByReference childrenRef = new PointerByReference();
        IntByReference childCountRef = new IntByReference();

        x11.XQueryTree(display, root, windowRef, parentRef, childrenRef, childCountRef);
        if (childrenRef.getValue() == null) {
            return Collections.emptyList();
        }

        long[] ids = {};

        if (Native.LONG_SIZE == Long.BYTES) {
            ids = childrenRef.getValue().getLongArray(0, childCountRef.getValue());
        } else if (Native.LONG_SIZE == Integer.BYTES) {
            int[] intIds = childrenRef.getValue().getIntArray(0, childCountRef.getValue());
            ids = new long[intIds.length];
            for (int i = 0; i < intIds.length; i++) {
                ids[i] = intIds[i];
            }
        }

        for (long id : ids) {
            Window child = new Window(id);
            X11.XTextProperty name = new X11.XTextProperty();
            x11.XGetWMName(display, child, name);
            String value = name.value;
            if (value != null) {
                System.out.println(String.format("Found window %s free %s", value, name));
            }
            if (value != null && pattern.matcher(value).matches()) {
                windows.add(child);
            }
            x11.XFree(name.getPointer());
            windows.addAll(recurse(x11, display, child, pattern));
        }
        return windows;
    }

    public static void main(String[] args) {
        X11WindowFinder finder = new X11WindowFinder();
        while (true) {
            finder.find(Pattern.compile(".*Firefox.*"));
        }
    }

}

The pointers in JNA appear to use finalize() to free memory i.e. com.sun.jna.Memory.finalize()

/** Properly dispose of native memory when this object is GC'd. */
protected void finalize() {
    dispose();
}

So if I call XFree() I risk it being double deallocated... Is this assumption correct. I can find few examples of JNA, especially X11 platform related.


Solution

  • You want to free the pointer in the value field of the XTextProperty structure, not the structure itself.

    From the XTextProperty man page:

    To free the storage for the value field, use XFree.
    

    From your code (note that the value field should be a Pointer rather than a String):

    x11.XTextProperty name = new X11.XTextProperty();
    x11.XGetWMName(display, child, name);
    // ... do some stuff
    x11.XFree(name.getPointer());
    

    EDIT Note that with the current definition of XTextProperty, The value field is mapped to Java String, which effectively hides the original pointer. You'd need to use XTextProperty.getPointer(XXX) (where XXX is the offset of the value field) in order to access the original pointer value to pass to XFree. Since the value field comes first in the struct, using the base address of the Structure itself will also work (i.e. Structure.getPointer()).

    This call to XFree should really be part of the XTextProperty's own memory cleanup (finalizer).