Permalink
Browse files

changed lock style for OSX to ioctl TIOEXCL

fixed some InterruptedException bugs
sync with 2.1 version
  • Loading branch information...
1 parent 7cd41db commit 179e1d36191e05ec216b15585130756fd51dd551 jarvi committed Jun 20, 2006
Showing with 113 additions and 36 deletions.
  1. +73 −6 src/SerialImp.c
  2. +3 −1 src/SerialImp.h
  3. +37 −29 src/gnu/io/RXTXPort.java
View
@@ -684,6 +684,24 @@ JNIEXPORT jint JNICALL RXTXPort(open)(
fd=OPEN (filename, O_RDWR | O_NOCTTY | O_NONBLOCK );
} while (fd < 0 && errno==EINTR);
+#ifdef OPEN_EXCL
+ // Note that open() follows POSIX semantics: multiple open() calls to
+ // the same file will succeed unless the TIOCEXCL ioctl is issued.
+ // This will prevent additional opens except by root-owned processes.
+ // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
+
+ if (fd >= 0 && (ioctl(fd, TIOCEXCL) == -1))
+ {
+ sprintf( message, "open: exclusive access denied for %s\n",
+ filename );
+ report( message );
+ report_error( message );
+
+ close(fd);
+ goto fail;
+ }
+#endif /* OPEN_EXCL */
+
if( configure_port( fd ) ) goto fail;
(*env)->ReleaseStringUTFChars( env, jstr, filename );
sprintf( message, "open: fd returned is %i\n", fd );
@@ -742,8 +760,19 @@ JNIEXPORT void JNICALL RXTXPort(nativeClose)( JNIEnv *env,
ENTER( "RXTXPort:nativeClose" );
if (fd > 0)
{
- do {
+ report("nativeClose: discarding remaining datai (tcflush)\n");
+ /* discard any incoming+outgoing data not yet read/sent */
+ tcflush(fd, TCIOFLUSH);
+// /* send a break with default time to the other side */
+// /* attention this tries to drain all data first */
+// tcsendbreak(fd, 0);
+ do {
+ report("nativeClose: calling close\n");
result=CLOSE (fd);
+ report("###JOE returned from close\n");
+if (result < 0 && errno == EINTR) {
+report("###JOE returned from close because of EINTR\n");
+}
} while ( result < 0 && errno == EINTR );
UNLOCK( filename, pid );
}
@@ -1222,11 +1251,19 @@ void *drain_loop( void *arg )
for(i=0;;i++)
{
report_verbose("drain_loop: looping\n");
+ if( eis->eventloop_interrupted )
+ {
+ goto end;
+ }
#if defined(__sun__)
/* FIXME: No time to test on all OS's for production */
- usleep(5000);
+ if (usleep(5000)) {
+ report("drain_loop: received EINTR");
+ }
#else
- usleep(1000000);
+ if (usleep(1000000)) {
+ report("drain_loop: received EINTR");
+ }
#endif /* __sun__ */
/*
system_wait();
@@ -1255,11 +1292,15 @@ void *drain_loop( void *arg )
report_verbose("drain_loop: writing not set\n");
}
}
- else
+ else if (errno != EINTR)
{
report("drain_loop: tcdrain bad fd\n");
goto end;
}
+ else
+ {
+ report("drain_loop: received EINTR in tcdrain\n");
+ }
}
end:
report("------------------ drain_loop exiting ---------------------\n");
@@ -1302,9 +1343,9 @@ void finalize_threads( struct event_info_struct *eis )
#if !defined(TIOCSERGETLSR) && !defined( WIN32 )
static void warn_sig_abort( int signo )
{
+ /*
char msg[80];
sprintf( msg, "RXTX Recieved Signal %i\n", signo );
- /*
report_error( msg );
*/
}
@@ -1318,6 +1359,9 @@ init_threads( )
return: none
exceptions: none
comments:
+ this function is called from the event_loop or in other words
+ from the monitor thread. On systems !WIN32 and without TIOCSERGETLSR
+ it will create a new thread looping a call to tcdrain.
----------------------------------------------------------*/
int init_threads( struct event_info_struct *eis )
{
@@ -1329,15 +1373,19 @@ int init_threads( struct event_info_struct *eis )
report_time_start( );
report("init_threads: start\n");
+ /* ignore child thread status changes */
sigemptyset(&newmask);
sigaddset(&newmask, SIGCHLD);
+
+ /* install our own signal hander */
newaction.sa_handler = warn_sig_abort;
sigemptyset( &newaction.sa_mask );
#ifdef SA_INTERRUPT
newaction.sa_flags = SA_INTERRUPT;
#endif /* SA_INTERRUPT */
#ifdef SA_RESTART
- newaction.sa_flags = SA_RESTART;
+ /* JOE: do not demand restart! we are handling EINTR */
+/* newaction.sa_flags = SA_RESTART;*/
#endif /* SA_RESTART */
sigaction(SIGABRT, &newaction, &oldaction);
@@ -1361,6 +1409,7 @@ int init_threads( struct event_info_struct *eis )
report("init_threads: creating drain_loop\n");
pthread_create( &tid, NULL, drain_loop, (void *) eis );
pthread_detach( tid );
+ eis->drain_tid = tid;
#endif /* TIOCSERGETLSR */
report("init_threads: get eis\n");
jeis = (*eis->env)->GetFieldID( eis->env, eis->jclazz, "eis", "J" );
@@ -4777,6 +4826,24 @@ JNIEXPORT void JNICALL RXTXPort(interruptEventLoop)(JNIEnv *env,
#ifdef WIN32
termios_interrupt_event_loop( index->fd, 1 );
#endif /* WIN32 */
+#if !defined(TIOCSERGETLSR) && !defined(WIN32)
+ /* make sure that the drainloop unblocks from tcdrain */
+ pthread_kill(index->drain_tid, SIGABRT);
+ /* TODO use wait/join/SIGCHLD/?? instead of sleep? */
+ usleep(50 * 1000);
+ /*
+ Under normal conditions, SIGABRT will unblock tcdrain. However
+ a non-responding USB device combined with an unclean driver
+ may still block. This is very ugly because it may block the call
+ to close indefinetly.
+ */
+ if (index->closing != 1) {
+ /* good bye tcdrain, and thanks for all the fish */
+ report("interruptEventLoop: canceling blocked drain thread\n");
+ pthread_cancel(index->drain_tid);
+ index->closing = 1;
+ }
+#endif
report("interruptEventLoop: interrupted\n");
}
View
@@ -128,6 +128,7 @@ struct event_info_struct
#if !defined(TIOCSERGETLSR) && !defined(WIN32)
int writing;
int output_buffer_empty_flag;
+ pthread_t drain_tid;
#endif /* !TIOCSERGETLSR !WIN32 */
# if defined(TIOCGICOUNT)
struct serial_icounter_struct osis;
@@ -163,7 +164,8 @@ struct event_info_struct
/*# define LOCKDIR "/var/spool/uucp"*/
# define LOCKDIR "/var/lock"
# define LOCKFILEPREFIX "LK."
-# define UUCP
+/*# define UUCP*/
+# define OPEN_EXCL
#endif /* __APPLE__ */
#if defined(__NetBSD__)
# define DEVICEDIR "/dev/"
View
@@ -842,27 +842,23 @@ using isInterrupted().
if (debug)
z.reportln( " RXTXPort:calling monThread.join()");
try {
- monThread.join(1000);
- } catch (Exception ex) {
- /* yikes */
- ex.printStackTrace();
- }
- if (debug)
- z.reportln( " RXTXPort:waiting on isAlive()");
- while( monThread.isAlive() )
+
+ // 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 && monThread.isAlive() )
{
- if ( debug )
- z.reportln( " MonThread is still alive!");
- try {
- monThread.join(1000);
- Thread.sleep( 1000 );
- } catch( Exception e ){}
- //monThread.stop();
+ z.reportln( " MonThread is still alive!");
+
}
}
- if (debug)
- z.reportln( " RXTXPort:calling gc()");
monThread = null;
SPEventListener = null;
MonitorThreadLock = false;
@@ -1042,20 +1038,32 @@ public void notifyOnBreakInterrupt( boolean enable )
/**
*/
boolean closeLock = false;
- public synchronized void close()
+ public void close()
{
- if (debug)
- z.reportln( "RXTXPort:close( " + this.name + " )");
- if( closeLock ) return;
- closeLock = true;
- while( IOLocked > 0 )
- {
- if( debug )
- z.reportln("IO is locked " + IOLocked);
- try {
- Thread.sleep(500);
- } catch( Exception e ) {}
+ synchronized (this) {
+ if (debug)
+ z.reportln( "RXTXPort:close( " + this.name + " )");
+
+ while( IOLocked > 0 )
+ {
+ if( debug )
+ z.reportln("IO is locked " + IOLocked);
+ try {
+ this.wait(500);
+ } catch( InterruptedException ie ) {
+ // somebody called interrupt() on us
+ // we obbey and return without without closing the socket
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+
+ // we set the closeLock after the above check because we might
+ // have returned without proceeding
+ if( closeLock ) return;
+ closeLock = true;
}
+
if ( fd <= 0 )
{
z.reportln( "RXTXPort:close detected bad File Descriptor" );

0 comments on commit 179e1d3

Please sign in to comment.