|
Description
|
FULL PRODUCT VERSION :
java version "1.5.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_01-b08)
Java HotSpot(TM) Server VM (build 1.5.0_01-b08, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Found on SLES 9, kernel 2.6.5-7.139-smp but not OS specific
EXTRA RELEVANT SYSTEM CONFIGURATION :
Dual CPU w/ hyperthreading for 4 hardware threads
A DESCRIPTION OF THE PROBLEM :
The class com.sun.media.sound.UlawCodec provides conversion between PCM and ULAW audio formats. When a conversion stream is requested it returns a new instance of an inner class (UlawCodecStream). This class references the member tempBuffer in UlawCodec. Only one instance of UlawCodec is loaded per JVM but many conversion streams can be created (via multiple calls to javax.sound.sampled.AudioSystem.getAudioInputStream(AudioFormat, AudioInputStream)). The access to tempBuffer is not protected by any synchronization mechanism. This leads to audio data swapping between multiple streams that are undergoing conversion at the same time. There is really no reason for tempBuffer to reside in UlawCodec. It is very small and should be a member of UlawCodecStream. Moving it there would remove the need for any synchronization.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Using SMP hardware start many threads (20+) that read from a PCM wav file, convert to ULAW, and write the results out to a new wav file. Some of the resulting output files will have corrupted sound. With this many threads the problem is always reproducible in a short period of time.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There should be no audio corruption in the output files.
ACTUAL -
Some output files are corrupt.
REPRODUCIBILITY :
This bug can be reproduced always.
CUSTOMER SUBMITTED WORKAROUND :
Streams must be converted serially, not in parallel.
xxxxx@xxxxx 2005-04-19 16:42:45 GMT
|
|
Evaluation
|
This is a bug that was filed through the jdk-contributions project and
the submitter filed the attached fix as well.
Below is the testcase for this bug (It has to be run on a multi-cpu
machine, preferrably something over 4 way. I can easily reproduce
the bug on jano, but hardly ever happens on my pc.
/*
* NOTE: This bug has only been demonstrated on SMP hardware, so this test
* may pass when run on a single CPU even though the bug is present.
*/
import javax.sound.sampled.*;
import java.io.*;
import junit.framework.*;
import junit.textui.*;
public class UlawConversionTest extends TestCase {
// one second sound clip
private static final int sampleSize = 8000;
private byte[] pcmData = new byte[sampleSize * 2];
private byte[] ulawData = new byte[sampleSize];
protected void setUp() throws IOException {
int value;
AudioInputStream ais;
AudioInputStream conversion;
byte[] testData = new byte[64];
int readCount;
// generate a 300hz tone as test data
for(int i = 0; i < sampleSize; i++) {
value =(int)(Math.sin(i * 2.0 * Math.PI * 300.0 / 8000.0) * 10000);
pcmData[i * 2] = (byte)(value >>> 8);
pcmData[i * 2 + 1] = (byte)value;
}
ais = getAudioInputStream();
try {
conversion = getConversionStream(ais);
try {
for(int j = 0; j < sampleSize; j += readCount) {
readCount = conversion.read(testData);
for(int k = 0; k < readCount; k++) {
ulawData[j + k] = testData[k];
}
}
} finally {
conversion.close();
}
} finally {
ais.close();
}
}
protected void tearDown() {
pcmData = null;
ulawData = null;
}
public void testConversion() {
UlawConversionTestThread[] threads = new UlawConversionTestThread[10];
for(int i = 0; i < threads.length; i++) {
threads[i] = new UlawConversionTestThread();
}
for(int i = 0; i < threads.length; i++) {
threads[i].start();
}
for(int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch(InterruptedException ex) {}
}
for(int i = 0; i < threads.length; i++) {
if(threads[i].exception != null) {
fail("IOException during conversion: " + threads[i].exception);
break;
}
if(threads[i].failed) {
fail("Data converted in parallel did not match clean sample");
break;
}
}
}
private AudioInputStream getAudioInputStream() {
return new AudioInputStream(new ByteArrayInputStream(pcmData),
new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
8000.0f, 16, 1, 2, 8000.0f, true), 16000);
}
private AudioInputStream getConversionStream(AudioInputStream ais) {
return AudioSystem.getAudioInputStream(
new AudioFormat(AudioFormat.Encoding.ULAW,
8000.0f, 8, 1, 1, 8000.0f, true), ais);
}
private class UlawConversionTestThread extends Thread {
private boolean failed = false;
private IOException exception = null;
public void run() {
AudioInputStream ais;
AudioInputStream conversion;
byte[] testData = new byte[64];
int readCount;
try {
for(int i = 0; i < 10; i++) {
ais = getAudioInputStream();
try {
conversion = getConversionStream(ais);
try {
for(int j = 0; j < sampleSize; j += readCount) {
readCount = conversion.read(testData);
for(int k = 0; k < readCount; k++) {
if(ulawData[j + k] != testData[k]) {
failed = true;
break;
}
}
if(failed) {
break;
}
}
} finally {
conversion.close();
}
} finally {
ais.close();
}
}
} catch(IOException e) {
exception = e;
}
}
}
public static void main(String[] args) {
TestRunner.run(UlawConversionTest.class);
}
}
This fix and testcase were contributed by Jesse Stir (java.net user id : sarnoth).
xxxxx@xxxxx 2005-04-19 20:25:46 GMT
xxxxx@xxxxx 2005-04-19 21:53:49 GMT
|