From 557d93ad5c47f6aedb04131c818e74c358c4df5c Mon Sep 17 00:00:00 2001 From: Samuel Coleman Date: Mon, 1 Feb 2021 11:10:00 +0000 Subject: [PATCH 1/2] Use common event loop interrupt on hardware error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just cleaning up the event info struct isn't enough: one some platforms, there's a “drain loop” thread which runs underneath the monitor thread to watch for an empty output buffer. This thread needs to be stopped, too, or it'll segfault and bring down the whole JVM after the event info struct is freed. --- src/main/c/src/SerialImp.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/c/src/SerialImp.c b/src/main/c/src/SerialImp.c index 85833a86..83b26227 100644 --- a/src/main/c/src/SerialImp.c +++ b/src/main/c/src/SerialImp.c @@ -4272,10 +4272,23 @@ JNIEXPORT void JNICALL RXTXPort(eventLoop)( JNIEnv *env, jobject jobj ) 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. + * + * Note that `nativeavailable()` has thrown an exception at this + * point, so we need to be careful about calling further JNI + * functions. Conventional wisdom states that JNI functions + * called when an exception is pending “may lead to unexpected + * results”. Empirically, this seems to work okay; I suspect, at + * worst, the functions might return an error. */ + RXTXPort(interruptEventLoop)(env, jobj); } /* nothing goes between this call and select */ if( eis.closing ) From e53d7b6c1746befc1d8c2fae39a2e4ea375b11de Mon Sep 17 00:00:00 2001 From: Samuel Coleman Date: Mon, 1 Feb 2021 12:15:18 +0000 Subject: [PATCH 2/2] Use a real read/write lock on the monitor thread. The old `MonitorThreadLock` boolean field was only checked at a very slow interval (5s!), and, itself not being synchronized, was prone to race conditions. More importantly, it wasn't set during the monitor thread's self-cleanup after hardware failure, so under typical access patterns, the monitor thread and the application thread would both try to clean up the monitor thread simultaneously. This race condition could occasionally lead to a segfault (only reproduced on macOS, but I've no doubt it could happen elsewhere). I've also attempted to clean up some redundant flag fields, and consolidate setting the remaining fields to further avoid concurrency/reentrancy issues. --- src/main/c/src/SerialImp.c | 184 +++++++++++++++++++--- src/main/java/gnu/io/RXTXPort.java | 241 ++++++++++++++--------------- 2 files changed, 280 insertions(+), 145 deletions(-) diff --git a/src/main/c/src/SerialImp.c b/src/main/c/src/SerialImp.c index 83b26227..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; + } -void unlock_monitor_thread( struct event_info_struct *eis ) + 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; + } + + 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; + } - jfieldID jfid = (*env)->GetFieldID( env, (*env)->GetObjectClass( env, jobj ), "MonitorThreadLock", "Z" ); - (*env)->SetBooleanField( env, jobj, jfid, (jboolean) 0 ); + jobject monitorThreadReady = (*env)->GetObjectField( + env, + jobj, + monitorThreadReadyField); + if ((*env)->ExceptionCheck(env)) { + return 1; + } + + 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,7 +4387,10 @@ 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 { @@ -4282,13 +4406,18 @@ JNIEXPORT void JNICALL RXTXPort(eventLoop)( JNIEnv *env, jobject jobj ) * drain loop). That will also set `eis.closing`, so the * function will return as usual in the next block. * - * Note that `nativeavailable()` has thrown an exception at this - * point, so we need to be careful about calling further JNI - * functions. Conventional wisdom states that JNI functions - * called when an exception is pending “may lead to unexpected - * results”. Empirically, this seems to work okay; I suspect, at - * worst, the functions might return an error. */ + * `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 ) @@ -4977,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(); } }