414652 - WebSocket's sendMessage() may hang on congested connections.

Fixed by using Locks instead of the synchronized keyword, and by using tryLock()
in flushBuffer() and flush(), that are called by WebSocketConnection.handle().

It is quite a hack, but the real fix is in Jetty 9 where the architecture is different
and there are two different paths for reading and writing, so that this problem
could not even happen.
This commit is contained in:
Simone Bordet 2013-08-08 12:08:41 +02:00
parent f983629434
commit 4ce4615597
1 changed files with 224 additions and 173 deletions

View File

@ -19,14 +19,16 @@
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
/* ------------------------------------------------------------ */
/** WebSocketGenerator.
/**
* WebSocketGenerator.
* This class generates websocket packets.
* It is fully synchronized because it is likely that async
* threads will call the addMessage methods while other
@ -34,20 +36,19 @@ import org.eclipse.jetty.io.EofException;
*/
public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
{
final private WebSocketBuffers _buffers;
final private EndPoint _endp;
private Buffer _buffer;
private final Lock _lock = new ReentrantLock();
private final WebSocketBuffers _buffers;
private final EndPoint _endp;
private final byte[] _mask = new byte[4];
private final MaskGen _maskGen;
private Buffer _buffer;
private int _m;
private boolean _opsent;
private final MaskGen _maskGen;
private boolean _closed;
public WebSocketGeneratorRFC6455(WebSocketBuffers buffers, EndPoint endp)
{
_buffers=buffers;
_endp=endp;
_maskGen=null;
this(buffers, endp, null);
}
public WebSocketGeneratorRFC6455(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen)
@ -57,15 +58,24 @@ public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
_maskGen = maskGen;
}
public synchronized Buffer getBuffer()
public Buffer getBuffer()
{
_lock.lock();
try
{
return _buffer;
}
public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException
finally
{
// System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length);
_lock.unlock();
}
}
public void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException
{
_lock.lock();
try
{
if (_closed)
throw new EofException("Closed");
if (opcode == WebSocketConnectionRFC6455.OP_CLOSE)
@ -186,8 +196,18 @@ public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
_buffer = null;
}
}
finally
{
_lock.unlock();
}
}
public synchronized int flushBuffer() throws IOException
public int flushBuffer() throws IOException
{
if (!_lock.tryLock())
return 0;
try
{
if (!_endp.isOpen())
throw new EofException();
@ -202,13 +222,23 @@ public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
return 0;
}
finally
{
_lock.unlock();
}
}
public synchronized int flush() throws IOException
public int flush() throws IOException
{
if (!_lock.tryLock())
return 0;
try
{
if (_buffer == null)
return 0;
int result = flushBuffer();
int result = flushBuffer();
if (!_endp.isBlocking())
{
long now = System.currentTimeMillis();
@ -230,13 +260,29 @@ public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
_buffer.compact();
return result;
}
finally
{
_lock.unlock();
}
}
public synchronized boolean isBufferEmpty()
public boolean isBufferEmpty()
{
_lock.lock();
try
{
return _buffer == null || _buffer.length() == 0;
}
finally
{
_lock.unlock();
}
}
public synchronized void returnBuffer()
public void returnBuffer()
{
_lock.lock();
try
{
if (_buffer != null && _buffer.length() == 0)
{
@ -244,6 +290,11 @@ public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
_buffer = null;
}
}
finally
{
_lock.unlock();
}
}
@Override
public String toString()