I ran tests of my library using jdk10 and they randomly failed in 50% of cases. I extracted very small fragment of code that reproduce the error and run it in the loop counting failures. Failures count is usually about 100k - 700k for 1M trials.
What I discovered until now:
- Code works in jdk8 and fails in jdk9 and jdk10
- When you change the order of second and third line in
addressToBytesmethod it works - When you inline methods of
SomeOutputStreamit works - When you disable last level of java optimization: -XX:TieredStopAtLevel=3 it works
- When you run this code in debug mode it mostly works.
I use scala 2.12.6 and javac options: -source 1.8 and -target 1.8. The problem occurs on both Windows and Linux.
The basic question is why addressToBytes method does not always return the same result?
Sometimes instead of expected array: [4, 10, 10, 10, 10] some part of address is replaced with 0. For example: [4, 10, 10, 10, 0]
Here is the code you can use to reproduce the problem:
import java.io.OutputStream
import java.net.InetSocketAddress
object RaceCondition {
class SomeOutputStream(val data: Array[Byte], var currentIndex: Int) extends OutputStream {
override def write(bytes: Array[Byte], off: Int, len: Int): Unit = {
System.arraycopy(bytes, off, data, currentIndex, len)
currentIndex += bytes.length
}
def write(b: Int): Unit = {
data.update(currentIndex, b.toByte)
currentIndex += 1
}
}
def addressToBytes(sAddress: InetSocketAddress): Array[Byte] = {
val result: Array[Byte] = new Array[Byte](sAddress.getAddress.getAddress.length + 1)
val writer = new SomeOutputStream(result, 0)
val addressBytes = sAddress.getAddress.getAddress
writer.write(addressBytes.length)
writer.write(addressBytes)
writer.data
}
def main(args: Array[String]): Unit = {
var i = 0
val expected = Array[Byte](4, 10, 10, 10, 10)
val address = new InetSocketAddress("10.10.10.10", 1234)
var count = 0
var lastOk = true
while (i < 1000000) {
i += 1
val data = addressToBytes(address)
if (!data.sameElements(expected)) {
count += 1
if(lastOk){
println(s"\n\nBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD $i\n\n")
}
lastOk = false
} else {
if(!lastOk){
println(s"\n\nGOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOD $i\n\n")
}
lastOk = true
}
}
println(s"Failures: $count")
}
}