From 614b3d9d698fc5afe783dcff1b9def37718e7b8f Mon Sep 17 00:00:00 2001 From: Manevolent Date: Sun, 24 Nov 2019 19:42:23 -0700 Subject: [PATCH] Address #9 and fix thread exits, add InterruptedException handlers, and make Microphone handling more stateful (don't run a thread all the time if nobody ever sets it) --- .../client/AbstractTeamspeakClientSocket.java | 24 +++++----- .../client/LocalTeamspeakClientSocket.java | 44 +++++++++++++------ .../manevolent/ts3j/ServerConnectionTest.java | 27 +++++++----- 3 files changed, 60 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java index c2c111f..364c0be 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java @@ -147,12 +147,12 @@ public abstract class AbstractTeamspeakClientSocket protected abstract Class getHandlerClass(ClientConnectionState state); protected void start() { - Thread networkThread = new Thread(networkReader); + networkThread = new Thread(networkReader); networkThread.setName("TS3J/NetworkReader/" + networkThread.getId()); networkThread.setDaemon(true); networkThread.start(); - Thread handlerThread = new Thread(networkHandler); + handlerThread = new Thread(networkHandler); handlerThread.setName("TS3J/NetworkHandler/" + handlerThread.getId()); handlerThread.setDaemon(true); handlerThread.start(); @@ -177,13 +177,14 @@ public abstract class AbstractTeamspeakClientSocket Ts3Debugging.debug("State changing: " + state.name()); boolean initialState = this.connectionState == null; - boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED; - if (wasDisconnected) setReading(true); + if (state == ClientConnectionState.CONNECTING) + setReading(true); this.connectionState = state; - if (state == ClientConnectionState.DISCONNECTED) setReading(false); + if (state == ClientConnectionState.DISCONNECTED) + setReading(false); connectionStateLock.notifyAll(); @@ -855,7 +856,7 @@ public abstract class AbstractTeamspeakClientSocket return reading; } - protected void setReading(boolean b) { + protected boolean setReading(boolean b) { if (this.reading != b) { this.reading = b; @@ -865,7 +866,11 @@ public abstract class AbstractTeamspeakClientSocket } else { start(); } + + return true; } + + return false; } public int getClientId() { @@ -945,14 +950,9 @@ public abstract class AbstractTeamspeakClientSocket while (isReading()) { try { readQueue.put(readPacket()); - } catch (EOFException e) { + } catch (EOFException | InterruptedException e) { break; } catch (Throwable e) { - if (e instanceof InterruptedException) { - Thread.yield(); - continue; - } - exceptionHandler.accept(e); break; } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java index cfd337d..8b14cfa 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java @@ -50,7 +50,7 @@ public class LocalTeamspeakClientSocket private volatile InetSocketAddress remote; private Microphone microphone; - private Thread microphoneThread; + private HighPrecisionTimer microphoneThread; private List listeners = new LinkedList<>(); @@ -281,6 +281,9 @@ public class LocalTeamspeakClientSocket protected void onConnect() { super.onConnect(); + if (microphoneThread != null && !microphoneThread.isAlive()) + microphoneThread.start(); + final ConnectedEvent connectedEvent = new ConnectedEvent(Collections.emptyMap()); commandExecutionService.submit(() -> listeners.forEach(e -> e.onConnected(connectedEvent))); } @@ -391,15 +394,6 @@ public class LocalTeamspeakClientSocket setState(ClientConnectionState.CONNECTING); waitForState(ClientConnectionState.CONNECTED, timeout); - - microphoneThread = new HighPrecisionTimer( - 20, - 5F, // up to 100ms of drift/catchup - new MicrophoneTask() - ); - - microphoneThread.setDaemon(true); - microphoneThread.start(); } catch (TimeoutException e) { setState(ClientConnectionState.DISCONNECTED); @@ -407,7 +401,7 @@ public class LocalTeamspeakClientSocket } catch (Throwable e) { setState(ClientConnectionState.DISCONNECTED); - throw new IOException(e); + throw new IOException("Problem establishing client connection", e); } } @@ -424,6 +418,7 @@ public class LocalTeamspeakClientSocket @Override public void close() throws IOException { socket.close(); + commandExecutionService.shutdown(); } @Override @@ -520,13 +515,36 @@ public class LocalTeamspeakClientSocket if (this.microphone != microphone) { this.microphone = microphone; - if (microphone == null && microphoneThread != null) { - microphoneThread.interrupt(); + if (microphone != null && microphoneThread == null) { + microphoneThread = new HighPrecisionTimer( + 20, + 5F, // up to 100ms of drift/catchup + new MicrophoneTask() + ); + microphoneThread.setDaemon(true); + + if (getState() != ClientConnectionState.DISCONNECTED) + microphoneThread.start(); + } else if (microphone == null && microphoneThread != null) { + microphoneThread.gracefulStop(); microphoneThread = null; } } } + @Override + protected boolean setReading(boolean reading) { + if (super.setReading(reading)) { + if (!reading) { + if (microphoneThread != null) microphoneThread.gracefulStop(); + } + + return true; + } + + return false; + } + /** * Gets the next command return_code we will create and buffer for sending. * diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index 86f6d09..19639ac 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -1,6 +1,8 @@ package com.github.manevolent.ts3j; +import com.github.manevolent.ts3j.audio.*; import com.github.manevolent.ts3j.command.*; +import com.github.manevolent.ts3j.enums.*; import com.github.manevolent.ts3j.event.*; import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; @@ -22,6 +24,19 @@ public class ServerConnectionTest { client.setIdentity(identity); client.setNickname(ServerConnectionTest.class.getSimpleName()); client.setHWID("TestTestTest"); + client.setMicrophone(new Microphone() { + @Override public boolean isReady() { + return false; + } + + @Override public CodecType getCodec() { + return null; + } + + @Override public byte[] provide() { + return new byte[0]; + } + }); try { client.connect( @@ -31,19 +46,11 @@ public class ServerConnectionTest { assertEquals(client.getState(), ClientConnectionState.CONNECTED); - Thread.sleep(1000); - - client.setDescription("Heyo"); - - client.subscribeAll(); - - Thread.sleep(100000); - client.disconnect("BYE"); } catch (Exception ex) { ex.printStackTrace(); + } finally { + client.close(); } - - Thread.sleep(3000L); } }