diff --git a/src/main/c/src/SerialImp.c b/src/main/c/src/SerialImp.c index 85833a86..aa0f048c 100644 --- a/src/main/c/src/SerialImp.c +++ b/src/main/c/src/SerialImp.c @@ -3792,23 +3792,142 @@ JNIEXPORT void JNICALL RXTXPort(setflowcontrol)( JNIEnv *env, return; } -/*---------------------------------------------------------- -unlock_monitor_thread +/** + * Write-lock the monitor thread to protect state mutation. + * + * Blocks until the write lock comes available. + * + * @param [in] env the JNI environment + * @param [in] obj an RXTXPort instance + * @return 0 on success; 1 on error + */ +int lock_monitor_thread(JNIEnv *env, jobject jobj) +{ + jfieldID monitorThreadStateWriteLockField = (*env)->GetFieldID( + env, + (*env)->GetObjectClass(env, jobj), + "monitorThreadStateWriteLock", + "Ljava/util/concurrent/locks/Lock;"); + if ((*env)->ExceptionCheck(env)) { + return 1; + } - accept: event_info_struct - perform: unlock the monitor thread so event notification can start. - return: none - exceptions: none - comments: Events can be missed otherwise. -----------------------------------------------------------*/ + jobject monitorThreadStateWriteLock = (*env)->GetObjectField( + env, + jobj, + monitorThreadStateWriteLockField); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + jmethodID lock = (*env)->GetMethodID( + env, + (*env)->GetObjectClass(env, monitorThreadStateWriteLock), + "lock", + "()V"); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + (*env)->CallVoidMethod(env, monitorThreadStateWriteLock, lock); + if ((*env)->ExceptionCheck(env)) { + return 1; + } -void unlock_monitor_thread( struct event_info_struct *eis ) + return 0; +} + +/** + * Signal that the monitor thread is ready for work. + * + * In order to signal the condition, the current thread must already hold the + * write lock. + * + * @param [in] env the JNI environment + * @param [in] obj an RXTXPort instance + * @return 0 on success; 1 on error + */ +int signal_monitor_thread_ready(JNIEnv *env, jobject jobj) { - JNIEnv *env = eis->env; - jobject jobj = *(eis->jobj); + jfieldID monitorThreadReadyField = (*env)->GetFieldID( + env, + (*env)->GetObjectClass(env, jobj), + "monitorThreadReady", + "Ljava/util/concurrent/locks/Condition;"); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + jobject monitorThreadReady = (*env)->GetObjectField( + env, + jobj, + monitorThreadReadyField); + if ((*env)->ExceptionCheck(env)) { + return 1; + } - jfieldID jfid = (*env)->GetFieldID( env, (*env)->GetObjectClass( env, jobj ), "MonitorThreadLock", "Z" ); - (*env)->SetBooleanField( env, jobj, jfid, (jboolean) 0 ); + jmethodID signal = (*env)->GetMethodID( + env, + (*env)->GetObjectClass(env, monitorThreadReady), + "signal", + "()V"); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + (*env)->CallVoidMethod(env, monitorThreadReady, signal); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + return 0; +} + +/** + * Unlock the write lock on the monitor thread to permit read and write access + * by other threads. + * + * In order to unlock the monitor thread, the current thread must already hold + * the write lock. + * + * @param [in] env the JNI environment + * @param [in] obj an RXTXPort instance + * @return 0 on success; 1 on error + */ +int unlock_monitor_thread(JNIEnv *env, jobject jobj) +{ + jfieldID monitorThreadStateWriteLockField = (*env)->GetFieldID( + env, + (*env)->GetObjectClass(env, jobj), + "monitorThreadStateWriteLock", + "Ljava/util/concurrent/locks/Lock;"); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + jobject monitorThreadStateWriteLock = (*env)->GetObjectField( + env, + jobj, + monitorThreadStateWriteLockField); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + jmethodID unlock = (*env)->GetMethodID( + env, + (*env)->GetObjectClass(env, monitorThreadStateWriteLock), + "unlock", + "()V"); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + (*env)->CallVoidMethod(env, monitorThreadStateWriteLock, unlock); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + return 0; } /*---------------------------------------------------------- @@ -4254,6 +4373,8 @@ RXTXPort.eventLoop ----------------------------------------------------------*/ JNIEXPORT void JNICALL RXTXPort(eventLoop)( JNIEnv *env, jobject jobj ) { + if (lock_monitor_thread(env, jobj)) goto end; + #ifdef WIN32 int i = 0; #endif /* WIN32 */ @@ -4266,16 +4387,37 @@ JNIEXPORT void JNICALL RXTXPort(eventLoop)( JNIEnv *env, jobject jobj ) ENTER( "eventLoop\n" ); if ( !initialise_event_info_struct( &eis ) ) goto end; if ( !init_threads( &eis ) ) goto end; - unlock_monitor_thread( &eis ); + + if (signal_monitor_thread_ready(env, jobj)) goto end; + if (unlock_monitor_thread(env, jobj)) goto end; + do{ report_time_eventLoop( ); do { if(RXTXPort(nativeavailable)( env, jobj )<0){ report("eventLoop: Hardware Missing\n"); - finalize_threads( &eis ); - finalize_event_info_struct( &eis ); - LEAVE("eventLoop:error"); - return; + /* The hardware is gone, so we need to stop the monitor thread. + * Conveniently, this function is supposed to be an infinite + * loop, so once we return from it to Java-land, the thread will + * close up shop. However, that also means that we need to make + * sure any native cleanup happens before we return, or else it + * will never run. We'll trigger that by the usual method to + * ensure platform-specific cleanup happens (e.g., killing the + * drain loop). That will also set `eis.closing`, so the + * function will return as usual in the next block. + * + * `nativeavailable()` has thrown an exception at this point, so + * in order to call anything else which uses exceptions (like + * the locking functions), we'll temporarily clear the exception + * then reset it before continuing. */ + jthrowable hardwareException = (*env)->ExceptionOccurred(env); + (*env)->ExceptionClear(env); + + if (lock_monitor_thread(env, jobj)) goto end; + RXTXPort(interruptEventLoop)(env, jobj); + if (unlock_monitor_thread(env, jobj)) goto end; + + (*env)->Throw(env, hardwareException); } /* nothing goes between this call and select */ if( eis.closing ) @@ -4964,7 +5106,22 @@ JNIEXPORT void JNICALL RXTXPort(interruptEventLoop)(JNIEnv *env, usleep(1000); } } + index->eventloop_interrupted = 1; + + jfieldID monThreadisInterruptedField = (*env)->GetFieldID( + env, + (*env)->GetObjectClass(env, jobj), + "monThreadisInterrupted", + "Z"); + if ((*env)->ExceptionCheck(env)) { + return; + } + (*env)->SetBooleanField(env, jobj, monThreadisInterruptedField, JNI_TRUE); + if ((*env)->ExceptionCheck(env)) { + return; + } + /* Many OS's need a thread running to determine if output buffer is empty. For Linux and Win32 it is not needed. So closing is used to diff --git a/src/main/java/gnu/io/RXTXPort.java b/src/main/java/gnu/io/RXTXPort.java index c9604e88..f0559661 100644 --- a/src/main/java/gnu/io/RXTXPort.java +++ b/src/main/java/gnu/io/RXTXPort.java @@ -62,6 +62,9 @@ import java.util.TooManyListenersException; import java.lang.Math; import java.util.concurrent.*; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * An extension of gnu.io.SerialPort @@ -146,7 +149,6 @@ protected void finalize() throws Throwable /** Initialize the native library */ private native static void Initialize(); - boolean MonitorThreadAlive=false; /** * Open the named port @@ -173,12 +175,16 @@ public RXTXPort( String name ) throws PortInUseException fd = open( name ); this.name = name; - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); monThread = new MonitorThread(); monThread.setName("RXTXPortMonitor("+name+")"); monThread.start(); - waitForTheNativeCodeSilly(); - MonitorThreadAlive=true; + try { + this.monitorThreadReady.await(); + } catch (InterruptedException e) { + z.reportln("Interrupted while waiting for the monitor thread to start!"); + } + this.monitorThreadState.writeLock().unlock(); // } catch ( PortInUseException e ){} timeout = -1; /* default disabled timeout */ if (debug) @@ -189,7 +195,7 @@ public RXTXPort( String name ) throws PortInUseException /* dont close the file while accessing the fd */ - private final java.util.concurrent.locks.ReentrantReadWriteLock IOLockedMutex = new java.util.concurrent.locks.ReentrantReadWriteLock(true); + private final ReentrantReadWriteLock IOLockedMutex = new ReentrantReadWriteLock(true); /** File descriptor */ private int fd = 0; @@ -666,13 +672,43 @@ protected native int readTerminatedArray( byte b[], int off, int len, byte t[] ) /** Thread to monitor data */ private MonitorThread monThread; + /** + * A lock around the monitor thread state. Used to synchronize event + * configuration changes (event types enabled/disabled). + * + * This is a {@link ReentrantReadWriteLock} rather than a plain old object + * (i.e., as used with synchronized blocks) to enable the + * InputStream/OutputStream read/write methods to + * take out a read lock on the monitor thread state. + */ + private final ReentrantReadWriteLock monitorThreadState = new ReentrantReadWriteLock(); + + /** + * Shortcut field to make acquisition of the write lock easier from JNI code. + * Saves retrieval of the {@link #monitorThreadState} field ID, then the + * value of the field, then the writeLock() method ID, + * then invoking that method. + */ + private final Lock monitorThreadStateWriteLock = this.monitorThreadState.writeLock(); + + /** + * Signalled by JNI code from inside {@link #eventLoop()} when the event + * loop has finished initialization of the native data structures and is + * ready to service events. + */ + private final Condition monitorThreadReady = this.monitorThreadState.writeLock().newCondition(); + /** Process SerialPortEvents */ native void eventLoop(); - /** - * @return boolean true if monitor thread is interrupted - */ + /** + * Whether the monitor thread has been interrupted. + * + * The flag is cleared when the monitor thread is started, and set by the + * native code in {@ #interruptEventLoop()}. + */ boolean monThreadisInterrupted=true; + private native void interruptEventLoop( ); public boolean checkMonitorThread() { @@ -854,9 +890,6 @@ public boolean sendEvent( int event, boolean state ) * @param lsnr SerialPortEventListener * TooManyListenersException */ - - boolean MonitorThreadLock = true; - public void addEventListener( SerialPortEventListener lsnr ) throws TooManyListenersException { @@ -871,94 +904,54 @@ public void addEventListener( throw new TooManyListenersException(); } SPEventListener = lsnr; - if( !MonitorThreadAlive ) + if (this.monThread == null) { - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); monThread = new MonitorThread(); monThread.setName("RXTXPortMonitor("+name+")"); monThread.start(); - waitForTheNativeCodeSilly(); - MonitorThreadAlive=true; + try { + this.monitorThreadReady.await(); + } catch (InterruptedException e) { + z.reportln("Interrupted while waiting for the monitor thread to start!"); + } + this.monitorThreadState.writeLock().unlock(); } if (debug) z.reportln( "RXTXPort:Interrupt=false"); } - /** - * Remove the serial port event listener - */ - public void removeEventListener() - { - if (debug) - z.reportln( "RXTXPort:removeEventListener() called"); - waitForTheNativeCodeSilly(); - //if( monThread != null && monThread.isAlive() ) - if( monThreadisInterrupted == true ) - { - z.reportln( " RXTXPort:removeEventListener() already interrupted"); - monThread = null; - SPEventListener = null; - return; - } - else if( monThread != null && monThread.isAlive() && !HARDWARE_FAULT) - { - if (debug) - z.reportln( " RXTXPort:Interrupt=true"); - monThreadisInterrupted=true; - /* - Notify all threads in this PID that something is up - They will call back to see if its their thread - using isInterrupted(). - */ - if (debug) - z.reportln( " RXTXPort:calling interruptEventLoop"); - interruptEventLoop( ); - - if (debug) - z.reportln( " RXTXPort:calling monThread.join()"); - try { - - // wait a reasonable moment for the death of the monitor thread - monThread.join(3000); - } catch (InterruptedException ex) { - // somebody called interrupt() on us (ie wants us to abort) - // we dont propagate InterruptedExceptions so lets re-set the flag - Thread.currentThread().interrupt(); - return; - } - - if ( debug ) { - if (monThread.isAlive()) { - z.reportln( " MonThread is still alive!"); - } - } - - } - monThread = null; - SPEventListener = null; - MonitorThreadLock = false; - MonitorThreadAlive=false; - monThreadisInterrupted=true; - z.reportln( "RXTXPort:removeEventListener() returning"); - } /** - * Give the native code a chance to start listening to the hardware - * or should we say give the native code control of the issue. + * Remove the serial port event listener. * - * This is important for applications that flicker the Monitor - * thread while keeping the port open. - * In worst case test cases this loops once or twice every time. + * Also interrupts the monitor thread. */ - - protected void waitForTheNativeCodeSilly() - { - while( MonitorThreadLock ) - { - try { - Thread.sleep(5); - } catch( Exception e ) {} + public void removeEventListener() { + this.monitorThreadState.writeLock().lock(); + try { + /* The monitor thread might already be interrupted if it + * encountered a hardware error and shut itself down. */ + if (!this.monThreadisInterrupted) { + this.interruptEventLoop(); + } + if (this.monThread != null && this.monThread.isAlive()) { + try { + // wait a reasonable moment for the death of the monitor thread + this.monThread.join(3000); + } catch (InterruptedException ex) { + // somebody called interrupt() on us (ie wants us to abort) + // we dont propagate InterruptedExceptions so lets re-set the flag + Thread.currentThread().interrupt(); + return; + } + } + this.monThread = null; + this.SPEventListener = null; + } finally { + this.monitorThreadState.writeLock().unlock(); } } + /** * @param enable */ @@ -969,14 +962,11 @@ public void notifyOnDataAvailable( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnDataAvailable( " + enable+" )"); - - waitForTheNativeCodeSilly(); - - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.DATA_AVAILABLE, enable ); monThread.Data = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** @@ -987,12 +977,11 @@ public void notifyOnOutputEmpty( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnOutputEmpty( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.OUTPUT_BUFFER_EMPTY, enable ); monThread.Output = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** @@ -1003,11 +992,10 @@ public void notifyOnCTS( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnCTS( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.CTS, enable ); monThread.CTS = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1017,11 +1005,10 @@ public void notifyOnDSR( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnDSR( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.DSR, enable ); monThread.DSR = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1031,11 +1018,10 @@ public void notifyOnRingIndicator( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnRingIndicator( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.RI, enable ); monThread.RI = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1045,11 +1031,10 @@ public void notifyOnCarrierDetect( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnCarrierDetect( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.CD, enable ); monThread.CD = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1059,11 +1044,10 @@ public void notifyOnOverrunError( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnOverrunError( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.OE, enable ); monThread.OE = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1073,11 +1057,10 @@ public void notifyOnParityError( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnParityError( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.PE, enable ); monThread.PE = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1087,11 +1070,10 @@ public void notifyOnFramingError( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnFramingError( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.FE, enable ); monThread.FE = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** * @param enable @@ -1101,11 +1083,10 @@ public void notifyOnBreakInterrupt( boolean enable ) if (debug) z.reportln( "RXTXPort:notifyOnBreakInterrupt( " + enable+" )"); - waitForTheNativeCodeSilly(); - MonitorThreadLock = true; + this.monitorThreadState.writeLock().lock(); nativeSetEventFlag( fd, SerialPortEvent.BI, enable ); monThread.BI = enable; - MonitorThreadLock = false; + this.monitorThreadState.writeLock().unlock(); } /** Close the port */ @@ -1193,8 +1174,8 @@ public void write( int b ) throws IOException return; } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); if ( fd == 0 ) { System.err.println("File Descriptor for prot zero!!"); @@ -1204,6 +1185,7 @@ public void write( int b ) throws IOException if (debug_write) z.reportln( "Leaving RXTXPort:SerialOutputStream:write( int )"); } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1224,12 +1206,13 @@ public void write( byte b[] ) throws IOException } if ( fd == 0 ) throw new IOException(); IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); writeArray( b, 0, b.length, monThreadisInterrupted ); if (debug_write) z.reportln( "Leaving RXTXPort:SerialOutputStream:write(" +b.length +")"); } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } @@ -1263,13 +1246,14 @@ public void write( byte b[], int off, int len ) return; } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); writeArray( send, 0, len, monThreadisInterrupted ); if( debug_write ) z.reportln( "Leaving RXTXPort:SerialOutputStream:write(" + send.length + " " + off + " " + len + " " +") " /*+ new String(send)*/ ); } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1288,9 +1272,9 @@ public void flush() throws IOException return; } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); /* this is probably good on all OS's but for now just sendEvent from java on Sol @@ -1302,6 +1286,7 @@ public void flush() throws IOException } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1335,10 +1320,10 @@ public synchronized int read() throws IOException z.reportln( "+++++++++ read() monThreadisInterrupted" ); } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { if (debug_read_results) z.reportln( "RXTXPort:SerialInputStream:read() L" ); - waitForTheNativeCodeSilly(); if (debug_read_results) z.reportln( "RXTXPort:SerialInputStream:read() N" ); int result = readByte(); @@ -1349,6 +1334,7 @@ public synchronized int read() throws IOException } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1375,9 +1361,9 @@ public synchronized int read( byte b[] ) throws IOException return(0); } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); result = read( b, 0, b.length); if (debug_read_results) z.reportln( "RXTXPort:SerialInputStream:read() returned " + result + " bytes" ); @@ -1385,6 +1371,7 @@ public synchronized int read( byte b[] ) throws IOException } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1486,9 +1473,9 @@ public synchronized int read( byte b[], int off, int len ) return(0); } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); result = readArray( b, off, Minimum); if (debug_read_results) z.reportln( "RXTXPort:SerialInputStream:read(" + b.length + " " + off + " " + len + ") returned " + result + " bytes" /*+ new String(b) */); @@ -1496,6 +1483,7 @@ public synchronized int read( byte b[], int off, int len ) } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1595,9 +1583,9 @@ public synchronized int read( byte b[], int off, int len, byte t[] ) return(0); } IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { - waitForTheNativeCodeSilly(); result = readTerminatedArray( b, off, Minimum, t ); if (debug_read_results) z.reportln( "RXTXPort:SerialInputStream:read(" + b.length + " " + off + " " + len + ") returned " + result + " bytes" /*+ new String(b) */); @@ -1605,6 +1593,7 @@ public synchronized int read( byte b[], int off, int len, byte t[] ) } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } } @@ -1621,6 +1610,7 @@ public synchronized int available() throws IOException if ( debug_verbose ) z.reportln( "RXTXPort:available() called" ); IOLockedMutex.readLock().lock(); + RXTXPort.this.monitorThreadState.readLock().lock(); try { int r = nativeavailable(); @@ -1631,6 +1621,7 @@ public synchronized int available() throws IOException } finally { + RXTXPort.this.monitorThreadState.readLock().unlock(); IOLockedMutex.readLock().unlock(); } }