1

I'm trying to utilize the Java PriorityQueue class in the following code:

private static PriorityQueue<String> commandQueue = new PriorityQueue<String>();
private static boolean looping = false;

public static void startCommandHandling() {
    new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
            if (!looping) {
                while (!commandQueue.isEmpty()) {
                    looping = true;
                    String command = commandQueue.poll();
                    if (Main.communicationPort.isOpen()) {
                        String string = "!" + command + "\r\n";
                        byte[] data = string.getBytes();
                        int dataLength = data.length;
                        Main.communicationPort.writeBytes(data, dataLength);
                    }
                }
                looping = false;
            }
        }
    }, 1, 1);
}

public static void sendCommand(String command) {
    commandQueue.add(command);
}

public static void sendCommands(List<String> commands) {
    commandQueue.addAll(commands);
}

It's used to queue serial port commands in order to send them one after another. I'm sure that I'm not feeding any null strings to sendCommand() or sendCommands() methods — there is a null check where these methods are called. It sometimes doesn't throw this exception immediately after the task is scheduled and first command(s) are added to the queue, but a lot of the time adding new command(s) will result in NPE and Java application crashing:

Exception in thread "Timer-0" Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:61)
Caused by: java.lang.NullPointerException
    at java.lang.String.compareTo(String.java:1155)
    at java.lang.String.compareTo(String.java:111)
    at java.util.PriorityQueue.siftUpComparable(PriorityQueue.java:656)
    at java.util.PriorityQueue.siftUp(PriorityQueue.java:647)
    at java.util.PriorityQueue.offer(PriorityQueue.java:344)
    at java.util.PriorityQueue.add(PriorityQueue.java:321)
    at java.util.AbstractQueue.addAll(AbstractQueue.java:187)
    at com.rpiled.SerialCommands.sendCommands(SerialCommands.java:114)
    at com.rpiled.UserSettings.apply(UserSettings.java:31)
    at com.rpiled.Main.main(Main.java:32)
    ... 5 more
java.lang.NullPointerException
    at java.util.PriorityQueue.siftDownComparable(PriorityQueue.java:701)
    at java.util.PriorityQueue.siftDown(PriorityQueue.java:689)
    at java.util.PriorityQueue.poll(PriorityQueue.java:595)
    at com.rpiled.SerialCommands$1.run(SerialCommands.java:46)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)
root@raspberrypi:~#

I suspect that it has something to do with the fact that the command polling is done in task called every 1 millisecond, but I need it to be running so often because I need the commands to be sent as fast as possible while still not allowing them to mix with each other - i.e. two commands can't be sent at the same time (that's why the queue system is needed). I've experimented a lot with this code and find out that creating the new PriorityQueue object with the added initialCapacity value of for example 500 (...= new PriorityQueue<String>(500);) results in no more application crashes and a bit different NPE:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:61)
Caused by: java.lang.NullPointerException
    at java.lang.String.compareTo(String.java:1155)
    at java.lang.String.compareTo(String.java:111)
    at java.util.PriorityQueue.siftUpComparable(PriorityQueue.java:656)
    at java.util.PriorityQueue.siftUp(PriorityQueue.java:647)
    at java.util.PriorityQueue.offer(PriorityQueue.java:344)
    at java.util.PriorityQueue.add(PriorityQueue.java:321)
    at java.util.AbstractQueue.addAll(AbstractQueue.java:187)
    at com.rpiled.SerialCommands.sendCommands(SerialCommands.java:114)
    at com.rpiled.UserSettings.apply(UserSettings.java:31)
    at com.rpiled.Main.main(Main.java:32)
    ... 5 more

How can I resolve this? What is causing the NPE?

AnB
  • 119
  • 3
  • 10
  • 1
    The callstack indicates that the exception is thrown in `addAll` which means that you are in fact adding a `null` element in the list – UnholySheep Aug 04 '20 at 08:01
  • @UnholySheep `public static void apply() { List commands = new ArrayList(); for (SerialCommand command : SerialCommand.getCommands().values()) { String cmd = command.command(command.getParameters()); if (cmd != null) commands.add(cmd); } SerialCommands.sendCommands(commands); }` - Here is the method from the callstacks I provided, as you can see there is a != null check, so is it really possible? – AnB Aug 04 '20 at 08:11
  • 1
    Well *something* is adding null elements into your queue, you have to step through the code with a debugger to find out what – UnholySheep Aug 04 '20 at 08:16

0 Answers0