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)

This commit is contained in:
Manevolent
2019-11-24 19:42:23 -07:00
parent 92d97f96dc
commit 614b3d9d69
3 changed files with 60 additions and 35 deletions
@@ -147,12 +147,12 @@ public abstract class AbstractTeamspeakClientSocket
protected abstract Class<? extends PacketHandler> getHandlerClass(ClientConnectionState state); protected abstract Class<? extends PacketHandler> getHandlerClass(ClientConnectionState state);
protected void start() { protected void start() {
Thread networkThread = new Thread(networkReader); networkThread = new Thread(networkReader);
networkThread.setName("TS3J/NetworkReader/" + networkThread.getId()); networkThread.setName("TS3J/NetworkReader/" + networkThread.getId());
networkThread.setDaemon(true); networkThread.setDaemon(true);
networkThread.start(); networkThread.start();
Thread handlerThread = new Thread(networkHandler); handlerThread = new Thread(networkHandler);
handlerThread.setName("TS3J/NetworkHandler/" + handlerThread.getId()); handlerThread.setName("TS3J/NetworkHandler/" + handlerThread.getId());
handlerThread.setDaemon(true); handlerThread.setDaemon(true);
handlerThread.start(); handlerThread.start();
@@ -177,13 +177,14 @@ public abstract class AbstractTeamspeakClientSocket
Ts3Debugging.debug("State changing: " + state.name()); Ts3Debugging.debug("State changing: " + state.name());
boolean initialState = this.connectionState == null; boolean initialState = this.connectionState == null;
boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
if (wasDisconnected) setReading(true); if (state == ClientConnectionState.CONNECTING)
setReading(true);
this.connectionState = state; this.connectionState = state;
if (state == ClientConnectionState.DISCONNECTED) setReading(false); if (state == ClientConnectionState.DISCONNECTED)
setReading(false);
connectionStateLock.notifyAll(); connectionStateLock.notifyAll();
@@ -855,7 +856,7 @@ public abstract class AbstractTeamspeakClientSocket
return reading; return reading;
} }
protected void setReading(boolean b) { protected boolean setReading(boolean b) {
if (this.reading != b) { if (this.reading != b) {
this.reading = b; this.reading = b;
@@ -865,7 +866,11 @@ public abstract class AbstractTeamspeakClientSocket
} else { } else {
start(); start();
} }
return true;
} }
return false;
} }
public int getClientId() { public int getClientId() {
@@ -945,14 +950,9 @@ public abstract class AbstractTeamspeakClientSocket
while (isReading()) { while (isReading()) {
try { try {
readQueue.put(readPacket()); readQueue.put(readPacket());
} catch (EOFException e) { } catch (EOFException | InterruptedException e) {
break; break;
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof InterruptedException) {
Thread.yield();
continue;
}
exceptionHandler.accept(e); exceptionHandler.accept(e);
break; break;
} }
@@ -50,7 +50,7 @@ public class LocalTeamspeakClientSocket
private volatile InetSocketAddress remote; private volatile InetSocketAddress remote;
private Microphone microphone; private Microphone microphone;
private Thread microphoneThread; private HighPrecisionTimer microphoneThread;
private List<TS3Listener> listeners = new LinkedList<>(); private List<TS3Listener> listeners = new LinkedList<>();
@@ -281,6 +281,9 @@ public class LocalTeamspeakClientSocket
protected void onConnect() { protected void onConnect() {
super.onConnect(); super.onConnect();
if (microphoneThread != null && !microphoneThread.isAlive())
microphoneThread.start();
final ConnectedEvent connectedEvent = new ConnectedEvent(Collections.emptyMap()); final ConnectedEvent connectedEvent = new ConnectedEvent(Collections.emptyMap());
commandExecutionService.submit(() -> listeners.forEach(e -> e.onConnected(connectedEvent))); commandExecutionService.submit(() -> listeners.forEach(e -> e.onConnected(connectedEvent)));
} }
@@ -391,15 +394,6 @@ public class LocalTeamspeakClientSocket
setState(ClientConnectionState.CONNECTING); setState(ClientConnectionState.CONNECTING);
waitForState(ClientConnectionState.CONNECTED, timeout); 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) { } catch (TimeoutException e) {
setState(ClientConnectionState.DISCONNECTED); setState(ClientConnectionState.DISCONNECTED);
@@ -407,7 +401,7 @@ public class LocalTeamspeakClientSocket
} catch (Throwable e) { } catch (Throwable e) {
setState(ClientConnectionState.DISCONNECTED); setState(ClientConnectionState.DISCONNECTED);
throw new IOException(e); throw new IOException("Problem establishing client connection", e);
} }
} }
@@ -424,6 +418,7 @@ public class LocalTeamspeakClientSocket
@Override @Override
public void close() throws IOException { public void close() throws IOException {
socket.close(); socket.close();
commandExecutionService.shutdown();
} }
@Override @Override
@@ -520,13 +515,36 @@ public class LocalTeamspeakClientSocket
if (this.microphone != microphone) { if (this.microphone != microphone) {
this.microphone = microphone; this.microphone = microphone;
if (microphone == null && microphoneThread != null) { if (microphone != null && microphoneThread == null) {
microphoneThread.interrupt(); 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; 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. * Gets the next command return_code we will create and buffer for sending.
* *
@@ -1,6 +1,8 @@
package com.github.manevolent.ts3j; package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.audio.*;
import com.github.manevolent.ts3j.command.*; import com.github.manevolent.ts3j.command.*;
import com.github.manevolent.ts3j.enums.*;
import com.github.manevolent.ts3j.event.*; import com.github.manevolent.ts3j.event.*;
import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
@@ -22,6 +24,19 @@ public class ServerConnectionTest {
client.setIdentity(identity); client.setIdentity(identity);
client.setNickname(ServerConnectionTest.class.getSimpleName()); client.setNickname(ServerConnectionTest.class.getSimpleName());
client.setHWID("TestTestTest"); 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 { try {
client.connect( client.connect(
@@ -31,19 +46,11 @@ public class ServerConnectionTest {
assertEquals(client.getState(), ClientConnectionState.CONNECTED); assertEquals(client.getState(), ClientConnectionState.CONNECTED);
Thread.sleep(1000);
client.setDescription("Heyo");
client.subscribeAll();
Thread.sleep(100000);
client.disconnect("BYE"); client.disconnect("BYE");
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
} finally {
client.close();
} }
Thread.sleep(3000L);
} }
} }