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 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;
}
@@ -50,7 +50,7 @@ public class LocalTeamspeakClientSocket
private volatile InetSocketAddress remote;
private Microphone microphone;
private Thread microphoneThread;
private HighPrecisionTimer microphoneThread;
private List<TS3Listener> 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.
*
@@ -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);
}
}