Reentrant lock - illegalmonitorstateexception when trying to write a byte[] to a serial port using rxtx

StackOverflow https://stackoverflow.com/questions/15769220

문제

I have the following code which is giving me a lot of trouble, I think I've been staring at it too long and fresh eyes would be appreciated -

Calling method - (in RobotInterface class)

        try
        {
            serialConnection.put(candidate.getCandidate().getGenome());
            TwoWaySerialCommTest.inputAvailable.await();
        }
        catch ( Exception e )
        {
            TwoWaySerialCommTest.listPorts();
            e.printStackTrace();
        }

TwoWaySerialCommTest class - adapted from rxtx website example. Full code for reference, the important parts are the put() method and the serialwriter.

     package org.dnsdojo.ryanhost.GA.MuPlusOne;

    import gnu.io.CommPort;
    import gnu.io.CommPortIdentifier;
    import gnu.io.SerialPort;
    import gnu.io.SerialPortEvent;
    import gnu.io.SerialPortEventListener;

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.concurrent.locks.*;

    /**
     * This version of the TwoWaySerialComm example makes use of the
     * SerialPortEventListener to avoid polling.
     *
     */
    public class TwoWaySerialCommTest
    {
            static Lock lock = new ReentrantLock();
            static Condition outputAvailable = lock.newCondition();
            static Condition inputAvailable = lock.newCondition();
            public volatile byte[] inputBuffer = new byte[1024];
            public volatile byte[] outputBuffer = new byte[1024];

            public TwoWaySerialCommTest()
        {
            super();
        }

            public void put(byte[] buffer)
            {
                    try
                    {
                            lock.lock();
                            int i = 0;
                            for(; i < buffer.length; i++ )
                            {
                                    outputBuffer[i] = buffer[i];
                            }
                            System.out.println((byte) '\n');
                            outputBuffer[i] = (byte) '\n';
                            i++;
                            for(; i < outputBuffer.length; i++ )
                            {
                                    outputBuffer[i] = 0;
                            }
                            System.out.println("Output buffer after put");
                            for(int j = 0; j < outputBuffer.length; j++)
                            {
                                    System.out.print(outputBuffer[j]);
                            }

                    }
                    catch(Exception e)
                    {
                            System.out.println("Exception for lock in put");
                            e.printStackTrace();
                    }
                    finally
                    {
                            lock.unlock();
                            outputAvailable.signal();
                    }
            }


        void connect ( String portName ) throws Exception
        {
            CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
            if ( portIdentifier.isCurrentlyOwned() )
            {
                System.out.println("Error: Port is currently in use");
            }
            else
            {
                CommPort commPort = portIdentifier.open(this.getClass().getName(),2000);

                if ( commPort instanceof SerialPort )
                {
                    SerialPort serialPort = (SerialPort) commPort;
                    serialPort.setSerialPortParams(57600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);

                    InputStream in = serialPort.getInputStream();
                    OutputStream out = serialPort.getOutputStream();


                    serialPort.addEventListener(new SerialReader(in , inputBuffer));
                    serialPort.notifyOnDataAvailable(true);

                    (new Thread(new SerialWriter(out , outputBuffer))).start();

                }
                else
                {
                    System.out.println("Error: Only serial ports are handled by this example.");
                }
            }
        }

        static void listPorts()
        {
            java.util.Enumeration<CommPortIdentifier> portEnum = CommPortIdentifier.getPortIdentifiers();
            while(portEnum.hasMoreElements())
            {
                    CommPortIdentifier portIdentifier = portEnum.nextElement();
                    if(portIdentifier == null)
                    {
                            System.out.println("No ports");
                    }
                    System.out.println("Available - " + portIdentifier.getName());
            }
        }

        /**
         * Handles the input coming from the serial port. A new line character
         * is treated as the end of a block in this example.
         */
        public static class SerialReader implements SerialPortEventListener
        {
            private InputStream in;
            byte[] buffer;

            public SerialReader ( InputStream in )
            {
                this.in = in;
            }

            public SerialReader (InputStream in, byte[] buffer)
            {
                    this.in = in;
                    this.buffer = buffer;
            }

            public void serialEvent(SerialPortEvent arg0) {
                lock.lock();
                    int data;

                try
                {

                    int len = 0;
                    while ( ( data = in.read()) > -1 )
                    {
                        if ( data == '\n' )
                        {
                            break;
                        }
                        buffer[len++] = (byte) data;
                    }
                    //inputAvailable.signal();
                    //outputAvailable.signal();
                }
                catch ( IOException e )
                {
                    e.printStackTrace();
                    System.exit(-1);
                }
                finally
                {
                    lock.unlock();
                }
            }

        }

        /** */
        public static class SerialWriter implements Runnable
        {
            OutputStream out;
            volatile byte[] buffer;

            public SerialWriter ( OutputStream out )
            {
                this.out = out;
            }

            public SerialWriter ( OutputStream out, byte[] buffer)
            {
                    this.out = out;
                    this.buffer = buffer;
            }

            public void run ()
            {
                    while(true)
                    {
                            lock.lock();
                            try
                        {
                            outputAvailable.await();
                            System.out.println("Waking up");
                            int i = 0;
                            if (this.buffer != null)
                            {
                                    System.out.println("Buffer isn't empty");
                                    for (int j = 0; j < buffer.length; j++)
                                    {
                                            System.out.print(buffer[i]);
                                    }
                                    while(buffer[i] != ((byte)'\n') && i < buffer.length -1 )
                                    {
                                            this.out.write(buffer[i++]);
                                            System.out.print(buffer[i-1]);
                                            buffer[i-1] = 0;
                                    }
                            }
                            else
                            {
                                    System.out.println("Buffer is null");
                                    System.out.println(this.buffer.toString());
                            }
                        }
                        catch ( IOException e )
                        {
                            e.printStackTrace();
                            System.exit(-1);
                        }
                        catch(Exception e)
                        {
                            e.printStackTrace();
                        }

                            finally
                            {

                                    lock.unlock();
                            }
                    }
            }
        }



       /* public static void main ( String[] args )
        {
            try
            {
                (new TwoWaySerialCommTest()).connect("/dev/ttyS82");
            }
            catch ( Exception e )
            {
                TwoWaySerialCommTest.listPorts();
                e.printStackTrace();
            }
        }
*/

    }

The output is shown below. I've tried moving the lock files around, taking the lock out of the serial writer, putting a lock in the calling method and some other hacks.

The desired result is that the method will call the serialConnection(an instance of TwoWaySerialCommTest which has connected) , and wait for serialConnection to write to the serial port and receive a response before continuing execution.

I've been banging my head off this for hours now, please help :)

Output buffer after put
10101010000001101111010110101111010000100110101000000011101011100011000101111111010011101111001100111110001010011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000RXTX fhs_lock() Error: creating lock file: /var/lock/LCK..ttyS82: File exists
java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1941)
    at org.dnsdojo.ryanhost.GA.MuPlusOne.TwoWaySerialCommTest.put(TwoWaySerialCommTest.java:64)
    at org.dnsdojo.ryanhost.GA.MuPlusOne.RobotInterface.evaluate(RobotInterface.java:35)
    at org.dnsdojo.ryanhost.GA.MuPlusOne.RobotInterface.run(RobotInterface.java:58)
    at java.lang.Thread.run(Thread.java:722)

If I move the notify to within the try block as suggested the output is

Output buffer after put
    11100111000111010010111111000110100010010111000000110001010011001001110001010111110001010110010001010001010110101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Waking up
Buffer isn't empty
        1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111RXTX fhs_lock() Error: creating lock file: /var/lock/LCK..ttyS82: File exists
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110011100011101001011111100011010001001011100000011000101001100100111000101011111000101011001000101000101011010java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2040)
    at org.dnsdojo.ryanhost.GA.MuPlusOne.RobotInterface.evaluate(RobotInterface.java:36)
    at org.dnsdojo.ryanhost.GA.MuPlusOne.RobotInterface.run(RobotInterface.java:58)
    at java.lang.Thread.run(Thread.java:722)

The RobotInterface Class -

package org.dnsdojo.ryanhost.GA.MuPlusOne;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class RobotInterface implements Runnable
{
    // create a serial connection
    // transmit a string and check for response
    // wait for evaluation
    // take evaluation
    private CandidateTest candidate;
    private TwoWaySerialCommTest serialConnection;
    //private Random rng = new Random();

    protected static Logger logger = Logger.getLogger("Thread" + Thread.currentThread().getName());

    public RobotInterface(CandidateTest test, TwoWaySerialCommTest serialConnection)
    {
            this.candidate = test;
            this.serialConnection = serialConnection;
            PropertyConfigurator.configure("log4j.properties");
    }

    public void evaluate (Genome genome)
    {
            //send to robot and return fitness
            //genome.setFitness(rng.nextDouble());
            logger.debug("fitness is " + genome.getFitness());
            try
            {
                    try
        {

            serialConnection.put(candidate.getCandidate().getGenome());
            TwoWaySerialCommTest.inputAvailable.await();


        }
        catch ( Exception e )
        {
            TwoWaySerialCommTest.listPorts();
            e.printStackTrace();
        }
            }
            catch(Exception E)
            {

            }

    }

    public void run()
    {
            logger.debug("entering run of Robot Interface");
            logger.debug("Send Genome via serial and wait for a response");
            Genome testSubject = candidate.getCandidate();
            evaluate(testSubject);
            candidate.finished();
    }

}

도움이 되었습니까?

해결책

You are calling signal AFTER you release the lock whereas you should call it WHILE holding the lock:

lock.unlock();
outputAvailable.signal();

should be the opposite:

outputAvailable.signal();
lock.unlock();

although it would be better practice to call signal at the end of the try block.

다른 팁

As per this link...
Before you use lock files you need to do one of two things:
Be user 'root' or user 'uucp' on your machine whenever you use RXTX .. or ..
Add the specific user that needs to use RXTX to the group 'uucp' (preferred)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top