Olivier Guilyardi wrote:
Okay, I wrote such a test. It fails with Jack's ringbuffer (jack1 r3004) but
succeeds with Portaudio's one (r1240).
It's a very simple test. I'm starting two threads, writing incremented numbers
into a ringbuffer, and checking that they follow each other on the reading end.
The failure frequency depends on the chunk size / buffer size ratio. With
relative short runs, I'm getting about 2 failures / million, for both 256 / 512
and 256 / 1024. I'm around 0.5 failure / million with a 256 / 4096 ratio. But
longer runs would be necessary for exact statistics.
There is no such failure with Portaudio's ringbuffer, even if I deactivate its
memory barriers. But I just can't say if this bug is caused by the lack of
memory barrier or not in Jack, because the two implementations are different,
and all of this seems to depend on the CPU architecture, the movements of the
clouds, the color of my coffee, etc.. ;)
I'm running these tests on a Intel Core2 Quad Q6600 CPU, and making sure the two
threads run on a different CPU by looking at the program startup output. I
didn't test on a single-cpu architecture.
The Portaudio code looks more and more robust to me. It's also surprisingly
short. Maybe that the best would be to replace jack's ringbuffer with it? I
think it should be possible to keep the jack_ringbuffer api unchanged.
My test code is below. It expects the buffer size as first argument. Try with
512, 1024, 2048,... With a buffer size of 4096 bytes, I sometimes need to wait a
minute or so for the first failure to appear.
When saved in jack1 svn working dir, I compile it with something like:
gcc -I. -o testrb -Wall testrb.c libjack/ringbuffer.c -lpthread
#include
#include
#include
#include
#include
#define ARRAY_SIZE 64
#define MAX_VALUE 0x10000
jack_ringbuffer_t *rb;
static int
fill_int_array (int *array, int start, int count)
{
int i, j = start;
for (i = 0; i < count; i++)
{
array[i] = j;
j = (j + 1) % MAX_VALUE;
}
return j;
}
static int
cmp_array (int *array1, int *array2, int count)
{
int i;
for (i = 0; i < count; i++)
if (array1[i] != array2[i])
{
printf("%d != %d at offset %d\n", array1[i], array2[i], i);
return 0;
}
return 1;
}
static void *
reader_start (void * arg)
{
int i = 0, a[ARRAY_SIZE], b[ARRAY_SIZE];
unsigned long j = 0, nfailures = 0;
printf("reader started on cpu %d\n", sched_getcpu());
i = fill_int_array (a, i, ARRAY_SIZE);
while (1)
{
if (jack_ringbuffer_read_space (rb) >= ARRAY_SIZE * sizeof (int))
{
if (jack_ringbuffer_read (rb, (char *) b, ARRAY_SIZE * sizeof (int)))
{
if (!cmp_array (a, b, ARRAY_SIZE))
{
nfailures++;
printf("failure in chunk %lu - probability: %lu/%lu = %.3f per
million\n",
j, nfailures, j, (float) nfailures / (j + 1) * 1000000);
i = (b[0] + ARRAY_SIZE) % MAX_VALUE;
}
i = fill_int_array (a, i, ARRAY_SIZE);
j++;
}
}
}
return NULL;
}
static void *
writer_start (void * arg)
{
int i = 0, a[ARRAY_SIZE];
printf("writer started on cpu: %d\n", sched_getcpu());
i = fill_int_array (a, i, ARRAY_SIZE);
while (1)
{
if (jack_ringbuffer_write_space (rb) >= ARRAY_SIZE * sizeof (int))
{
if (jack_ringbuffer_write (rb, (char *) a, ARRAY_SIZE * sizeof (int)))
{
i = fill_int_array (a, i, ARRAY_SIZE);
}
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int size;
printf("starting ringbuffer stress test\n");
sscanf(argv[1], "%d", &size);
printf("buffer size (bytes): %d\n", size);
printf("array size (bytes): %d\n", sizeof(int) * ARRAY_SIZE);
rb = jack_ringbuffer_create(size);
pthread_t reader_thread, writer_thread;
pthread_create (&reader_thread, NULL, reader_start, NULL);
pthread_create (&writer_thread, NULL, writer_start, NULL);
while (1)
sleep(1);
return 0;
}
--
Olivier Guilyardi / Samalyse
_______________________________________________
Linux-audio-user mailing list
Linux-audio-user@lists.linuxaudio.org
http://lists.linuxaudio.org/mailman/listinfo/linux-audio-user
LINUX® is a registered trademark of Linus Torvalds in the USA and other countries.
Linuxaudio.org logo copyright Thorsten Wilms © 2006.
Hosting provided by the Virginia Tech Department of Music and DISIS.