TheCaseOfTheUnsafeSynchronizedList

July 20, 2008
guts of the issue, have to write this up later


DatagramSocket socket = new DatagramSocket(hostHandler.getReceivePort());
logger.info("UDP listener server up on port " + hostHandler.getReceivePort() + ". Waiting for data to arrive.");
byte[] buffer = MouseEventPacket.getReceiveBuffer();
do
{
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // blocking call
packets.add(packet);
} while (!killed); // TODO: need a timeout on the receive call to make this killed check worthwhile


packets is synchronized with Collections.synchro...

other thread

if (!killed)
{
DatagramPacket packet = packets.remove(0);
byte[] data = packet.getData();
MouseEventPacket mousePacket = MouseEventPacket.parseMouseMovePacket(data);
PointDouble point = mousePacket.getPoint();
logger.finest("Recvd packet: <" + point + "> " + ByteUtil.toHexString(data));
SwingHost host = getHost(mousePacket.getDeviceID(), mousePacket.getName());
host.moveMouse(mousePacket);
}



public void moveMouse(MouseEventPacket mousePacket)
{
packetsTotal++;
double mouseX = mousePacket.getPoint().x;
double mouseY = mousePacket.getPoint().y;

// need to map mouse coordinates (0..1) to screen
int pixelX = 0;
int pixelY = 0;
pixelX = (int) ((hostWidth * mouseX) + (originPoint.x));
pixelY = (int) ((hostHeight * mouseY) + (originPoint.y));
hostCursor = new Point(pixelX, pixelY);
logger.log(logLevel, "mouse, pixel: <" + mouseX + ", " + mouseY + ">, <" + pixelX + ", " + pixelY + ">");

if ((prevX > 0) && (prevY > 0) && (mousePacket.getLeftButtonDown()))
{
synchronized (renderMutex)
{
Graphics g = drawingImage.createGraphics();
g.setColor(color);
g.drawLine(prevX, prevY, (int) pixelX, (int) pixelY);
}
}

synchronized (renderMutex)
{
// repaint always so cursors can be rendered
displayPanel.repaint();
}
prevX = pixelX;
prevY = pixelY;
lastActive = System.currentTimeMillis();
checkForGap(mousePacket);
}


When this is setup properly and run, occasionally, under a load - the drawLine call would have the prevX and prevY of an instance, but the pixelX and pixelY of another instance.

The buffer in the first line of code, would be rewritten in the process. Just because you're collection is thread safe doesn't mean the data inside the collection is. The buffer, inside DatagramPacket, would presumably just be a pointer all the way ‘round - or somesuch.

The fix? Create a new byte buffer everytime through that loop - just swap the line with the brace.

tags: ComputersAndTechnology