Skip to content

Commit 48f59c9

Browse files
Hotfix deadlock in audio connections (discord-jda#592)
Use single lock for connections to fix unlikely but possible race condition leading to a deadlock
1 parent 78ee8f8 commit 48f59c9

File tree

1 file changed

+65
-48
lines changed

1 file changed

+65
-48
lines changed

src/main/java/net/dv8tion/jda/core/audio/AudioWebSocket.java

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Map;
4040
import java.util.concurrent.*;
4141
import java.util.concurrent.atomic.AtomicInteger;
42+
import java.util.function.Consumer;
4243

4344
public class AudioWebSocket extends WebSocketAdapter
4445
{
@@ -58,7 +59,6 @@ public class AudioWebSocket extends WebSocketAdapter
5859
private final String token;
5960
private boolean connected = false;
6061
private boolean ready = false;
61-
private boolean shutdown = false;
6262
private boolean reconnecting = false;
6363
private Future<?> keepAliveHandle;
6464
private String wssEndpoint;
@@ -69,6 +69,8 @@ public class AudioWebSocket extends WebSocketAdapter
6969
private DatagramSocket udpSocket;
7070
private InetSocketAddress address;
7171

72+
private volatile boolean shutdown = false;
73+
7274
public WebSocket socket;
7375

7476
public AudioWebSocket(ConnectionListener listener, String endpoint, JDAImpl api, Guild guild, String sessionId, String token, boolean shouldReconnect)
@@ -385,37 +387,52 @@ public void startConnection()
385387
}
386388
}
387389

388-
public synchronized void reconnect(ConnectionStatus closeStatus)
390+
private void locked(Consumer<AudioManagerImpl> runnable)
391+
{
392+
AudioManagerImpl manager = (AudioManagerImpl) guild.getAudioManager();
393+
synchronized (manager.CONNECTION_LOCK)
394+
{
395+
runnable.accept(manager);
396+
}
397+
}
398+
399+
public void reconnect(ConnectionStatus closeStatus)
389400
{
390401
if (shutdown)
391402
return;
392-
connected = false;
393-
ready = false;
394-
reconnecting = true;
395-
changeStatus(closeStatus);
396-
startConnection();
403+
locked((unused) ->
404+
{
405+
if (shutdown)
406+
return;
407+
connected = false;
408+
ready = false;
409+
reconnecting = true;
410+
changeStatus(closeStatus);
411+
startConnection();
412+
});
397413
}
398414

399-
public synchronized void close(ConnectionStatus closeStatus)
415+
public void close(final ConnectionStatus closeStatus)
400416
{
401417
//Makes sure we don't run this method again after the socket.close(1000) call fires onDisconnect
402418
if (shutdown)
403419
return;
404-
connected = false;
405-
ready = false;
406-
shutdown = true;
407-
stopKeepAlive();
408-
409-
if (udpSocket != null)
410-
udpSocket.close();
411-
if (socket != null && socket.isOpen())
412-
socket.sendClose(1000);
413-
414-
VoiceChannel disconnectedChannel;
415-
AudioManagerImpl manager = (AudioManagerImpl) guild.getAudioManager();
416-
417-
synchronized (manager.CONNECTION_LOCK)
420+
locked((manager) ->
418421
{
422+
if (shutdown)
423+
return;
424+
ConnectionStatus status = closeStatus;
425+
connected = false;
426+
ready = false;
427+
shutdown = true;
428+
stopKeepAlive();
429+
430+
if (udpSocket != null)
431+
udpSocket.close();
432+
if (socket != null && socket.isOpen())
433+
socket.sendClose(1000);
434+
435+
VoiceChannel disconnectedChannel;
419436
if (audioConnection != null)
420437
audioConnection.shutdown();
421438

@@ -425,37 +442,37 @@ public synchronized void close(ConnectionStatus closeStatus)
425442
disconnectedChannel = manager.getQueuedAudioConnection();
426443

427444
manager.setAudioConnection(null);
428-
}
429445

430-
//Verify that it is actually a lost of connection and not due the connected channel being deleted.
431-
if (closeStatus == ConnectionStatus.ERROR_LOST_CONNECTION)
432-
{
433-
//Get guild from JDA, don't use [guild] field to make sure that we don't have
434-
// a problem of an out of date guild stored in [guild] during a possible mWS invalidate.
435-
Guild connGuild = api.getGuildById(guild.getIdLong());
436-
if (connGuild != null)
446+
//Verify that it is actually a lost of connection and not due the connected channel being deleted.
447+
if (status == ConnectionStatus.ERROR_LOST_CONNECTION)
437448
{
438-
if (connGuild.getVoiceChannelById(audioConnection.getChannel().getIdLong()) == null)
439-
closeStatus = ConnectionStatus.DISCONNECTED_CHANNEL_DELETED;
449+
//Get guild from JDA, don't use [guild] field to make sure that we don't have
450+
// a problem of an out of date guild stored in [guild] during a possible mWS invalidate.
451+
Guild connGuild = api.getGuildById(guild.getIdLong());
452+
if (connGuild != null)
453+
{
454+
if (connGuild.getVoiceChannelById(audioConnection.getChannel().getIdLong()) == null)
455+
status = ConnectionStatus.DISCONNECTED_CHANNEL_DELETED;
456+
}
440457
}
441-
}
442458

443-
changeStatus(closeStatus);
459+
changeStatus(status);
444460

445-
//decide if we reconnect.
446-
if (shouldReconnect
447-
&& closeStatus != ConnectionStatus.NOT_CONNECTED //indicated that the connection was purposely closed. don't reconnect.
448-
&& closeStatus != ConnectionStatus.DISCONNECTED_CHANNEL_DELETED
449-
&& closeStatus != ConnectionStatus.DISCONNECTED_REMOVED_FROM_GUILD
450-
&& closeStatus != ConnectionStatus.AUDIO_REGION_CHANGE) //Already handled.
451-
{
452-
manager.setQueuedAudioConnection(disconnectedChannel);
453-
api.getClient().queueAudioReconnect(disconnectedChannel);
454-
}
455-
else if (closeStatus != ConnectionStatus.AUDIO_REGION_CHANGE)
456-
{
457-
api.getClient().queueAudioDisconnect(guild);
458-
}
461+
//decide if we reconnect.
462+
if (shouldReconnect
463+
&& status != ConnectionStatus.NOT_CONNECTED //indicated that the connection was purposely closed. don't reconnect.
464+
&& status != ConnectionStatus.DISCONNECTED_CHANNEL_DELETED
465+
&& status != ConnectionStatus.DISCONNECTED_REMOVED_FROM_GUILD
466+
&& status != ConnectionStatus.AUDIO_REGION_CHANGE) //Already handled.
467+
{
468+
manager.setQueuedAudioConnection(disconnectedChannel);
469+
api.getClient().queueAudioReconnect(disconnectedChannel);
470+
}
471+
else if (status != ConnectionStatus.AUDIO_REGION_CHANGE)
472+
{
473+
api.getClient().queueAudioDisconnect(guild);
474+
}
475+
});
459476
}
460477

461478
public DatagramSocket getUdpSocket()

0 commit comments

Comments
 (0)