diff --git a/src/debug/org/luaj/debug/DebugLuaState.java b/src/debug/org/luaj/debug/DebugLuaState.java index beb533f7..0888b99c 100644 --- a/src/debug/org/luaj/debug/DebugLuaState.java +++ b/src/debug/org/luaj/debug/DebugLuaState.java @@ -279,6 +279,8 @@ public class DebugLuaState extends LuaState implements DebugRequestListener { cancelSuspendOnStart(); } else if (DebugRequestType.exit == requestType) { stop(); + } else if (DebugRequestType.disconnect == requestType) { + disconnect(); } else if (DebugRequestType.suspend == requestType) { suspend(); DebugEvent event = new DebugEvent(DebugEventType.suspendedByClient); @@ -361,8 +363,10 @@ public class DebugLuaState extends LuaState implements DebugRequestListener { */ public void resume() { synchronized (this) { - suspended = false; - stepping = STEP_NONE; + this.suspended = false; + this.stepping = STEP_NONE; + this.shouldPauseForStepping = false; + this.steppingFrame = -1; this.notify(); } } @@ -390,6 +394,27 @@ public class DebugLuaState extends LuaState implements DebugRequestListener { exit(); } + public void disconnect() { + if (this.debugSupport == null) { + throw new IllegalStateException( + "DebugStackState is not equiped with DebugSupport."); + } + + reset(); + debugSupport.disconnect(); + DebugEvent event = new DebugEvent(DebugEventType.disconnected); + debugSupport.notifyDebugEvent(event); + } + + protected void reset() { + synchronized (this) { + this.breakpoints.clear(); + if (this.suspended) { + resume(); + } + } + } + /** * terminate the execution */ diff --git a/src/debug/org/luaj/debug/DebugSupport.java b/src/debug/org/luaj/debug/DebugSupport.java index 7cf7dd22..e9cfb232 100644 --- a/src/debug/org/luaj/debug/DebugSupport.java +++ b/src/debug/org/luaj/debug/DebugSupport.java @@ -2,7 +2,6 @@ package org.luaj.debug; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.EOFException; import java.io.IOException; import org.luaj.debug.event.DebugEvent; @@ -21,12 +20,48 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi protected DebugLuaState vm; protected int debugPort; - protected Thread requestWatcherThread; + protected ClientConnectionTask clientConnectionTask; protected int state = UNKNOWN; protected DataInputStream requestReader; protected DataOutputStream eventWriter; - public DebugSupport(int debugPort) { + class ClientConnectionTask implements Runnable { + protected boolean bDisconnected = false; + + public synchronized void disconnect () { + this.bDisconnected = true; + } + + public synchronized boolean isDisconnected() { + return this.bDisconnected; + } + + public void run() { + try { + acceptClientConnection(); + + while (getState() != STOPPED && !isDisconnected()) { + loopForRequest(); + } + + if (isDisconnected()) { + releaseClientConnection(); + } else { + dispose(); + } + } catch (IOException e) { + e.printStackTrace(); + + // the connected debugging client aborted abnormally + // discard the current connection and start new + vm.reset(); + releaseClientConnection(); + newClientConnection(); + } + } + } + + public DebugSupport(int debugPort) throws IOException { if (debugPort == -1) { throw new IllegalArgumentException("requestPort is invalid"); } @@ -36,7 +71,9 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi public void setDebugStackState(DebugLuaState vm) { this.vm = vm; } - + + protected abstract void releaseClientConnection(); + protected void dispose() { if (DebugUtils.IS_DEBUG) DebugUtils.println("releasing the networkig resources..."); @@ -44,6 +81,7 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi if (requestReader != null) { try { requestReader.close(); + requestReader = null; } catch (IOException e) { } } @@ -51,6 +89,7 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi if (eventWriter != null) { try { eventWriter.close(); + eventWriter = null; } catch (IOException e) { e.printStackTrace(); } @@ -67,18 +106,17 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi "DebugLuaState is not set. Please call setDebugStackState first."); } - this.requestWatcherThread = new Thread(new Runnable() { - public void run() { - loopForRequests(); - cleanup(); - } - }); - this.requestWatcherThread.start(); + newClientConnection(); this.state = RUNNING; System.out.println("LuaJ debug server is listening on port: " + debugPort); } + private void newClientConnection() { + this.clientConnectionTask = new ClientConnectionTask(); + new Thread(clientConnectionTask).start(); + } + protected synchronized int getState() { return this.state; } @@ -89,37 +127,21 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi this.state = STOPPED; } - public abstract Object getClientConnection(); + public abstract void acceptClientConnection() throws IOException; - protected void loopForRequests() { - try { - while (getState() != STOPPED) { - byte[] data = null; - int size = requestReader.readInt(); - data = new byte[size]; - requestReader.readFully(data); - - DebugRequest request = (DebugRequest) SerializationHelper - .deserialize(data); - if (DebugUtils.IS_DEBUG) { - DebugUtils.println("SERVER receives request: " + request.toString()); - } - - handleRequest(request); - } - } catch (EOFException e) { - // expected. it may occur depending on the timing during the termination - } catch (Exception e) { - e.printStackTrace(); + protected void loopForRequest() throws IOException { + byte[] data = null; + int size = requestReader.readInt(); + data = new byte[size]; + requestReader.readFully(data); + + DebugRequest request = (DebugRequest) SerializationHelper + .deserialize(data); + if (DebugUtils.IS_DEBUG) { + DebugUtils.println("SERVER receives request: " + request.toString()); } - } - - private void cleanup() { - if (DebugUtils.IS_DEBUG) - DebugUtils.println("SERVER terminated..."); - - dispose(); - System.exit(0); + + handleRequest(request); } /** @@ -136,10 +158,12 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi * * @param event */ - protected void sendEvent(DebugEvent event) { + protected synchronized void sendEvent(DebugEvent event) { if (DebugUtils.IS_DEBUG) DebugUtils.println("SERVER sending event: " + event.toString()); + if (eventWriter == null) return; + try { byte[] data = SerializationHelper.serialize(event); eventWriter.writeInt(data.length); @@ -171,4 +195,11 @@ public abstract class DebugSupport implements DebugRequestListener, DebugEventLi vm.handleRequest(request); } + + public synchronized void disconnect() { + clientConnectionTask.disconnect(); + clientConnectionTask = null; + + newClientConnection(); + } } \ No newline at end of file diff --git a/src/debug/org/luaj/debug/event/DebugEventType.java b/src/debug/org/luaj/debug/event/DebugEventType.java index e4caa29c..8d683e15 100644 --- a/src/debug/org/luaj/debug/event/DebugEventType.java +++ b/src/debug/org/luaj/debug/event/DebugEventType.java @@ -49,6 +49,8 @@ public class DebugEventType extends EnumType { = new DebugEventType("clientRequestStackReply", 15); public static DebugEventType clientRequestGlobalReply = new DebugEventType("clientRequestGlobalReply", 16); + public static DebugEventType disconnected + = new DebugEventType("disconnected", 17); protected static DebugEventType[] ENUMS = new DebugEventType[] { started, @@ -67,7 +69,8 @@ public class DebugEventType extends EnumType { terminated, clientRequestCallgraphReply, clientRequestStackReply, - clientRequestGlobalReply + clientRequestGlobalReply, + disconnected }; protected DebugEventType(String name, int ordinal) { diff --git a/src/debug/org/luaj/debug/j2me/DebugSupportImpl.java b/src/debug/org/luaj/debug/j2me/DebugSupportImpl.java index 7a7e16b3..a28d463c 100644 --- a/src/debug/org/luaj/debug/j2me/DebugSupportImpl.java +++ b/src/debug/org/luaj/debug/j2me/DebugSupportImpl.java @@ -7,15 +7,17 @@ import javax.microedition.io.ServerSocketConnection; import javax.microedition.io.SocketConnection; import org.luaj.debug.DebugSupport; -import org.luaj.debug.event.DebugEvent; public class DebugSupportImpl extends DebugSupport { protected ServerSocketConnection serverConnection; protected SocketConnection clientDebugConnection; protected SocketConnection eventSocketConnection; - public DebugSupportImpl(int debugPort) { + public DebugSupportImpl(int debugPort) throws IOException { super(debugPort); + this.serverConnection + = (ServerSocketConnection) Connector.open( + "socket://:" + this.debugPort, 1); } /* @@ -23,12 +25,10 @@ public class DebugSupportImpl extends DebugSupport { * * @see lua.debug.j2se.DebugSupport#start() */ - public synchronized void start() throws IOException { + public void acceptClientConnection() throws IOException { // Set up the request socket and request input + event output streams - this.serverConnection = (ServerSocketConnection) Connector - .open("socket://:" + this.debugPort); - this.clientDebugConnection = (SocketConnection) serverConnection - .acceptAndOpen(); + this.clientDebugConnection + = (SocketConnection) serverConnection.acceptAndOpen(); clientDebugConnection.setSocketOption(SocketConnection.DELAY, 0); clientDebugConnection.setSocketOption(SocketConnection.LINGER, 0); clientDebugConnection.setSocketOption(SocketConnection.KEEPALIVE, 1); @@ -38,34 +38,29 @@ public class DebugSupportImpl extends DebugSupport { this.eventWriter = clientDebugConnection.openDataOutputStream(); System.out.println("Lua debug server is started on ports: " + debugPort); - super.start(); } - protected void dispose() { + protected void releaseClientConnection() { super.dispose(); if (this.clientDebugConnection != null) { try { clientDebugConnection.close(); + clientDebugConnection = null; } catch (IOException e) { } - } - + } + } + + protected void dispose() { + releaseClientConnection(); + if (this.serverConnection != null) { try { serverConnection.close(); + serverConnection = null; } catch (IOException e) { } } } - - public Object getClientConnection() { - return clientDebugConnection; - } - - protected void sendEvent(DebugEvent event) { - synchronized (eventSocketConnection) { - super.sendEvent(event); - } - } } diff --git a/src/debug/org/luaj/debug/j2se/DebugSupportImpl.java b/src/debug/org/luaj/debug/j2se/DebugSupportImpl.java index 038edcd5..795371f0 100644 --- a/src/debug/org/luaj/debug/j2se/DebugSupportImpl.java +++ b/src/debug/org/luaj/debug/j2se/DebugSupportImpl.java @@ -28,71 +28,44 @@ import java.net.ServerSocket; import java.net.Socket; import org.luaj.debug.DebugSupport; -import org.luaj.debug.event.DebugEvent; public class DebugSupportImpl extends DebugSupport { protected ServerSocket serverSocket; protected Socket clientSocket; - public DebugSupportImpl(int debugPort) { + public DebugSupportImpl(int debugPort) throws IOException { super(debugPort); + this.serverSocket = new ServerSocket(debugPort, 1); } - /* (non-Javadoc) - * @see lua.debug.j2se.DebugSupport#start() - */ - public synchronized void start() throws IOException { - this.serverSocket = new ServerSocket(debugPort, 1); + public void acceptClientConnection() throws IOException { this.clientSocket = serverSocket.accept(); this.requestReader = new DataInputStream(clientSocket.getInputStream()); this.eventWriter = new DataOutputStream(clientSocket.getOutputStream()); - - super.start(); } - protected void dispose() { - super.dispose(); - + protected void releaseClientConnection() { + super.dispose(); + if (this.clientSocket != null) { try { clientSocket.close(); - } catch (IOException e) {} - } - - if (this.serverSocket != null) { - try { - serverSocket.close(); + clientSocket = null; } catch (IOException e) {} } } - public Object getClientConnection() { - return clientSocket; - } - - /** - * This method provides the second communication channel with the debugging - * client. The server can send events via this channel to notify the client - * about debug events (see below) asynchronously. - * - * The following events can be fired: - * 1. started -- the vm is started and ready to receive debugging requests - * (guaranteed to be the first event sent) - * 2. terminated -- the vm is terminated (guaranteed to be the last event sent) - * 3. suspended client|step|breakpoint N - * -- the vm is suspended by client, due to a stepping request or - * the breakpoint at line N is hit - * 4. resumed client|step - * -- the vm resumes execution by client or step - * - * @param event - */ - protected void sendEvent(DebugEvent event) { - synchronized (clientSocket) { - super.sendEvent(event); - } + protected void dispose() { + releaseClientConnection(); + + if (this.serverSocket != null) { + try { + serverSocket.close(); + serverSocket = null; + } catch (IOException e) {} + } } } diff --git a/src/debug/org/luaj/debug/request/DebugRequestType.java b/src/debug/org/luaj/debug/request/DebugRequestType.java index 0cff6a10..7ed4a6a5 100644 --- a/src/debug/org/luaj/debug/request/DebugRequestType.java +++ b/src/debug/org/luaj/debug/request/DebugRequestType.java @@ -43,6 +43,7 @@ public class DebugRequestType extends EnumType { public static final DebugRequestType stepOver = new DebugRequestType("stepOver", 11); public static final DebugRequestType stepReturn = new DebugRequestType("stepReturn", 12); public static final DebugRequestType global = new DebugRequestType("global", 13); + public static final DebugRequestType disconnect = new DebugRequestType("disconnect", 14); protected static final DebugRequestType[] ENUMS = new DebugRequestType[] { start, @@ -58,7 +59,8 @@ public class DebugRequestType extends EnumType { stepInto, stepOver, stepReturn, - global + global, + disconnect }; public DebugRequestType(String name, int ordinal) {