From 0e1dd48e01f184b52a9a3d59a43bff37845ca1fc Mon Sep 17 00:00:00 2001
From: Martin Oberhuber <martin.oberhuber@windriver.com>
Date: Tue, 30 Oct 2007 23:38:06 +0000
Subject: [PATCH] [208029] COM port not released after quick
 disconnect/reconnect

---
 .../terminal/serial/SerialConnectWorker.java  |  23 +++-
 .../terminal/serial/SerialConnector.java      | 110 ++++++++++++------
 2 files changed, 96 insertions(+), 37 deletions(-)

diff --git a/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnectWorker.java b/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnectWorker.java
index 02cb974f8d7..0685f00a4cf 100644
--- a/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnectWorker.java
+++ b/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnectWorker.java
@@ -14,6 +14,7 @@
  * Michael Scharf (Wind River) - extracted from TerminalControl 
  * Martin Oberhuber (Wind River) - fixed copyright headers and beautified
  * Martin Oberhuber (Wind River) - [207158] improve error message when port not available
+ * Martin Oberhuber (Wind River) - [208029] COM port not released after quick disconnect/reconnect
  *******************************************************************************/
 package org.eclipse.tm.internal.terminal.serial;
 
@@ -26,6 +27,7 @@ import java.util.Arrays;
 import java.util.Enumeration;
 
 import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
 import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
 
 public class SerialConnectWorker extends Thread {
@@ -41,7 +43,6 @@ public class SerialConnectWorker extends Thread {
 		super();
 		fControl = control;
 		fConn = conn;
-		fControl.setState(TerminalState.CONNECTING);
 	}
 	
 	/**
@@ -91,7 +92,7 @@ public class SerialConnectWorker extends Thread {
 			// nothing to do -- should never happen...
 			return;
 		}
-		// Reinitialise the ports because we have changed the list of known ports
+		// Reinitialize the ports because we have changed the list of known ports
 		CommPortIdentifier.getPortIdentifiers();
 	}
 	
@@ -101,6 +102,7 @@ public class SerialConnectWorker extends Thread {
 		//TODO [206884] This is part of API and should be changed for the next release
 		final String strID = getClass().getPackage().getName();
 		//final String strID = "org.eclipse.tm.terminal.serial"; //$NON-NLS-1$
+		SerialPort serialPort = null;
 		try {
 			fControl.setState(TerminalState.OPENED);
 			ISerialSettings s=fConn.getSerialSettings();
@@ -116,7 +118,7 @@ public class SerialConnectWorker extends Thread {
 			fConn.setSerialPortIdentifier(CommPortIdentifier.getPortIdentifier(portName));
 			int timeoutInMs = s.getTimeout() * 1000;
 
-			SerialPort serialPort=(SerialPort) fConn.getSerialPortIdentifier().open(strID,timeoutInMs);
+			serialPort=(SerialPort) fConn.getSerialPortIdentifier().open(strID,timeoutInMs);
 			serialPort.setSerialPortParams(s.getBaudRate(), s.getDataBits(), s.getStopBits(), s.getParity());
 			serialPort.setFlowControlMode(s.getFlowControl());
 			serialPort.addEventListener(fConn.getSerialPortHandler());
@@ -139,7 +141,22 @@ public class SerialConnectWorker extends Thread {
 			fControl.displayTextInTerminal("No such port: \"" + msg+"\"\r\n"); //$NON-NLS-1$ //$NON-NLS-2$
 			
 		} catch (Exception exception) {
+			Logger.logException(exception);
+			if (serialPort!=null) {
+				//Event listener is removed as part of close(), 
+				//but exceptions need to be caught to ensure that close() really succeeds
+				try {
+					serialPort.removeEventListener();
+					Thread.sleep(50); //allow a little time for RXTX Native to catch up - makes stuff more stable
+				} catch(Exception e) {
+					Logger.logException(e);
+				}
+				serialPort.close();
+				fConn.getSerialPortIdentifier().removePortOwnershipListener(fConn.getSerialPortHandler());
+			}
 			fControl.setState(TerminalState.CLOSED);
+		} finally {
+			fConn.doneConnect();
 		}
 	}
 }
\ No newline at end of file
diff --git a/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnector.java b/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnector.java
index 2a3dfd00a93..c63fec23d53 100644
--- a/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnector.java
+++ b/org.eclipse.tm.terminal.serial/src/org/eclipse/tm/internal/terminal/serial/SerialConnector.java
@@ -13,7 +13,8 @@
  * Contributors:
  * Michael Scharf (Wind River) - extracted from TerminalControl 
  * Martin Oberhuber (Wind River) - fixed copyright headers and beautified
- * Martin Oberhuber (Wind River) - [206892] Dont connect if already connecting
+ * Martin Oberhuber (Wind River) - [206892] Don't connect if already connecting
+ * Martin Oberhuber (Wind River) - [208029] COM port not released after quick disconnect/reconnect
  *******************************************************************************/
 package org.eclipse.tm.internal.terminal.serial;
 
@@ -42,7 +43,9 @@ public class SerialConnector implements ITerminalConnector {
     private CommPortIdentifier fSerialPortIdentifier;
 	private SerialPortHandler fTerminalSerialPortHandler;
 	private SerialSettings fSettings;
-	private SerialConnectWorker fConnectWorker;
+	private SerialConnectWorker fConnectWorker = null;
+	private volatile boolean fDisconnectGoingOn = false;
+	
 	public SerialConnector() {
 	}
 	public SerialConnector(SerialSettings settings) {
@@ -58,16 +61,37 @@ public class SerialConnector implements ITerminalConnector {
 	}
 	public void connect(ITerminalControl control) {
 		Logger.log("entered."); //$NON-NLS-1$
-		if (fConnectWorker!=null && fConnectWorker.isAlive()) {
-			//already connecting
-			return;
+		synchronized(this) {
+			if (fConnectWorker!=null || fDisconnectGoingOn) {
+				//avoid multiple background connect/disconnect threads at the same time		
+				return;
+			}
+			fConnectWorker = new SerialConnectWorker(this, control);
 		}
-		fConnectWorker = new SerialConnectWorker(this, control);
 		fControl=control;
+		fControl.setState(TerminalState.CONNECTING);
 		fConnectWorker.start();
 	}
+	/**
+	 * Indicate that the connectWorker is finished. 
+	 */
+	void doneConnect() {
+		synchronized(this) {
+			fConnectWorker = null;
+		}
+	}
 	public void disconnect() {
 		Logger.log("entered."); //$NON-NLS-1$
+		synchronized(this) {
+			//avoid multiple background connect/disconnect threads at the same time		
+			if (fConnectWorker!=null) {
+				fConnectWorker.interrupt();
+				return;
+			} else if (fDisconnectGoingOn) {
+				return;
+			}
+			fDisconnectGoingOn = true;
+		}
 	
 		// Fix for SPR 112422.  When output is being received from the serial port, the
 		// below call to removePortOwnershipListener() attempts to lock the serial port
@@ -85,37 +109,53 @@ public class SerialConnector implements ITerminalConnector {
 		new Thread("Terminal View Serial Port Disconnect Worker") //$NON-NLS-1$
 		{
 			public void run() {
-	
-				if (getSerialPortIdentifier() != null) {
-					getSerialPortIdentifier()
-							.removePortOwnershipListener(getSerialPortHandler());
-				}
-	
-				if (getSerialPort() != null) {
-					getSerialPort().removeEventListener();
-					Logger.log("Calling close() on serial port ..."); //$NON-NLS-1$
-					getSerialPort().close();
-				}
-	
-				if (getInputStream() != null) {
-					try {
-						getInputStream().close();
-					} catch (Exception exception) {
-						Logger.logException(exception);
+				try {
+					if (getSerialPortIdentifier() != null) {
+						try {
+							getSerialPortIdentifier()
+								.removePortOwnershipListener(getSerialPortHandler());
+						} catch(Exception e) {
+							Logger.logException(e);
+						}
 					}
-				}
-	
-				if (getOutputStream() != null) {
-					try {
-						getOutputStream().close();
-					} catch (Exception exception) {
-						Logger.logException(exception);
+		
+					if (getSerialPort() != null) {
+						//Event listener is removed as part of close(), 
+						//but exceptions need to be caught to ensure that close() really succeeds
+						try {
+							getSerialPort().removeEventListener();
+							Thread.sleep(50); //allow a little time for RXTX Native to catch up - makes stuff more stable
+						} catch(Exception e) {
+							Logger.logException(e);
+						}
+						Logger.log("Calling close() on serial port ..."); //$NON-NLS-1$
+						getSerialPort().close();
 					}
+			
+					if (getInputStream() != null) {
+						try {
+							getInputStream().close();
+						} catch (Exception exception) {
+							Logger.logException(exception);
+						}
+					}
+			
+					if (getOutputStream() != null) {
+						try {
+							getOutputStream().close();
+						} catch (Exception exception) {
+							Logger.logException(exception);
+						}
+					}
+			
+					setSerialPortIdentifier(null);
+					cleanSerialPort();
+					setSerialPortHandler(null);
+				} catch(Exception e) {
+					Logger.logException(e);
+				} finally {
+					fDisconnectGoingOn = false;
 				}
-	
-				setSerialPortIdentifier(null);
-				cleanSerialPort();
-				setSerialPortHandler(null);
 			}
 
 		}.start();
@@ -165,6 +205,7 @@ public class SerialConnector implements ITerminalConnector {
 		return fSerialPortIdentifier;
 	}
 	protected void setSerialPortIdentifier(CommPortIdentifier serialPortIdentifier) {
+		//System.out.println("setSerialPortId: "+Thread.currentThread().getName()+ " - "+serialPortIdentifier + " - "+System.currentTimeMillis());
 		fSerialPortIdentifier = serialPortIdentifier;
 	}
 	void setSerialPortHandler(SerialPortHandler serialPortHandler) {
@@ -175,6 +216,7 @@ public class SerialConnector implements ITerminalConnector {
 	}
 	public ISerialSettings getSerialSettings() {
 		return fSettings;
+		
 	}
 	public ISettingsPage makeSettingsPage() {
 		return new SerialSettingsPage(fSettings);