211 lines
4.8 KiB
C
211 lines
4.8 KiB
C
|
#include "xring.h"
|
||
|
#include "pixie-threads.h"
|
||
|
#include "pixie-timer.h"
|
||
|
#include "util-safefunc.h"
|
||
|
#include <stdio.h>
|
||
|
|
||
|
|
||
|
typedef uint64_t Element;
|
||
|
|
||
|
#define XRING_SIZE 16
|
||
|
|
||
|
struct XRing
|
||
|
{
|
||
|
volatile unsigned long long head;
|
||
|
volatile unsigned long long tail;
|
||
|
volatile Element ring[XRING_SIZE];
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static Element
|
||
|
xring_remove(struct XRing *xring)
|
||
|
{
|
||
|
volatile Element *ring = xring->ring;
|
||
|
Element num;
|
||
|
|
||
|
if (xring->tail >= xring->head)
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
num = ring[xring->tail & (XRING_SIZE-1)];
|
||
|
if (num) {
|
||
|
ring[xring->tail & (XRING_SIZE-1)] = 0;
|
||
|
xring->tail++;
|
||
|
return num;
|
||
|
/*
|
||
|
int x = pixie_locked_CAS64(&ring[xring->tail & (XRING_SIZE-1)], 0, num);
|
||
|
if (x) {
|
||
|
xring->tail++;
|
||
|
return num;
|
||
|
} else {
|
||
|
goto again;
|
||
|
}*/
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum {XringSuccess, XringFailure};
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static int
|
||
|
xring_add(struct XRing *xring, Element value)
|
||
|
{
|
||
|
volatile Element *ring = xring->ring;
|
||
|
Element num;
|
||
|
|
||
|
if (value == 0) {
|
||
|
return XringFailure;
|
||
|
}
|
||
|
|
||
|
if (xring->head >= xring->tail + XRING_SIZE) {
|
||
|
//printf("-");
|
||
|
return XringFailure;
|
||
|
}
|
||
|
num = xring->ring[xring->head & (XRING_SIZE-1)];
|
||
|
if (num == 0) {
|
||
|
ring[xring->head & (XRING_SIZE-1)] = value;
|
||
|
xring->head++;
|
||
|
return XringSuccess;
|
||
|
/*int x = pixie_locked_CAS64(&ring[xring->head & (XRING_SIZE-1)], value, 0);
|
||
|
if (x) {
|
||
|
xring->head++;
|
||
|
return XringSuccess;
|
||
|
} else {
|
||
|
return XringFailure;
|
||
|
}*/
|
||
|
}
|
||
|
return XringFailure;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
struct Test
|
||
|
{
|
||
|
struct XRing xring[1];
|
||
|
unsigned producer_started;
|
||
|
unsigned producer_done;
|
||
|
unsigned consumer_done;
|
||
|
unsigned long long total_count;
|
||
|
volatile int not_active;
|
||
|
};
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static void
|
||
|
test_consumer_thread(void *v)
|
||
|
{
|
||
|
struct Test *test = (struct Test *)v;
|
||
|
struct XRing *xring = test->xring;
|
||
|
|
||
|
|
||
|
while (!test->not_active) {
|
||
|
Element e;
|
||
|
|
||
|
e = xring_remove(xring);
|
||
|
if (e == 0)
|
||
|
;
|
||
|
else {
|
||
|
test->total_count += e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (xring->tail < xring->head) {
|
||
|
Element e;
|
||
|
|
||
|
e = xring_remove(xring);
|
||
|
if (e == 0)
|
||
|
;
|
||
|
else {
|
||
|
test->total_count += e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
test->consumer_done = 1;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static void
|
||
|
test_producer_thread(void *v)
|
||
|
{
|
||
|
struct Test *test = (struct Test *)v;
|
||
|
unsigned i = 1000;
|
||
|
struct XRing *xring = test->xring;
|
||
|
|
||
|
pixie_locked_add_u32(&test->producer_started, 1);
|
||
|
while (i) {
|
||
|
while (xring_add(xring, i) == XringFailure)
|
||
|
;
|
||
|
i--;
|
||
|
}
|
||
|
pixie_locked_add_u32(&test->producer_done, 1);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static uint64_t
|
||
|
run_test(struct Test *test)
|
||
|
{
|
||
|
unsigned i;
|
||
|
const unsigned THREADS = 1;
|
||
|
|
||
|
memset(test, 0, sizeof(*test));
|
||
|
|
||
|
/* Generate producer threads */
|
||
|
for (i=0; i<THREADS; i++) {
|
||
|
pixie_begin_thread(test_producer_thread, 0, test);
|
||
|
}
|
||
|
|
||
|
/* Wait for threads to start */
|
||
|
while (test->producer_started < THREADS)
|
||
|
pixie_usleep(10);
|
||
|
/* Now start consuming */
|
||
|
pixie_begin_thread(test_consumer_thread, 0, test);
|
||
|
|
||
|
/* Wait for producer threads to end */
|
||
|
while (test->producer_done < THREADS)
|
||
|
pixie_usleep(10);
|
||
|
|
||
|
|
||
|
/* Tell consumer thread to end */
|
||
|
test->not_active = 1;
|
||
|
|
||
|
|
||
|
/* Wait for consumer thread to end */
|
||
|
while (!test->consumer_done)
|
||
|
pixie_usleep(10);
|
||
|
|
||
|
return test->total_count;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
int
|
||
|
xring_selftest(void)
|
||
|
{
|
||
|
unsigned i;
|
||
|
|
||
|
for (i=0; i<1000; i++) {
|
||
|
uint64_t result;
|
||
|
struct Test test[1];
|
||
|
|
||
|
result = run_test(test);
|
||
|
if (result != 500500) {
|
||
|
printf("xring: selftest failed with %" PRIu64 "\n", result);
|
||
|
return 1;
|
||
|
} else
|
||
|
;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|