Files
linuxcncAllWasm/tests/lowlevel/emcmot-error-mpsc/test.c
2026-08-20 06:17:39 +08:00

113 lines
3.1 KiB
C

/*
* Concurrency / correctness test for the emcmotError MPSC ring buffer.
*
* Spawns NPROD producer threads pumping NMSG messages each, plus one
* consumer that drains. Verifies every message arrives exactly once
* and that messages from each individual producer arrive in producer-
* local sequence order. Across producers any interleaving is allowed.
*/
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <emcmotcfg.h>
#include "motion.h"
#include "dbuf.h"
#include "stashf.h"
#include <rtapi_atomic.h>
#define NPROD 8
#define NMSG 100000
static emcmot_error_t errlog;
static rtapi_atomic_int producers_done = 0;
static void *producer_fn(void *arg) {
long id = (long)arg;
char buf[64];
for (int i = 0; i < NMSG; i++) {
snprintf(buf, sizeof(buf), "P%ld:%010d", id, i);
while (emcmotErrorPut(&errlog, buf) < 0) {
sched_yield();
}
}
atomic_fetch_add(&producers_done, 1);
return NULL;
}
static void *consumer_fn(void *arg) {
(void)arg;
long total = (long)NPROD * NMSG;
long received = 0;
int per_producer[NPROD] = {0};
char raw[EMCMOT_ERROR_LEN];
char msg[EMCMOT_ERROR_LEN];
int errors = 0;
while (received < total) {
struct dbuf d;
struct dbuf_iter di;
/* dbuf_init zero-fills the buffer, so call it BEFORE
emcmotErrorGet, which then writes the received bytes in. */
dbuf_init(&d, (unsigned char *)raw, EMCMOT_ERROR_LEN);
if (emcmotErrorGet(&errlog, raw) == 0) {
dbuf_iter_init(&di, &d);
if (snprintdbuf(msg, sizeof(msg), &di) < 0) {
fprintf(stderr, "snprintdbuf failed\n");
errors++;
received++;
continue;
}
long pid;
int seq;
if (sscanf(msg, "P%ld:%d", &pid, &seq) != 2
|| pid < 0 || pid >= NPROD) {
fprintf(stderr, "bad msg: '%s'\n", msg);
errors++;
} else if (seq != per_producer[pid]) {
fprintf(stderr, "out-of-order P%ld: got %d, want %d\n",
pid, seq, per_producer[pid]);
errors++;
} else {
per_producer[pid]++;
}
received++;
} else {
if (atomic_load(&producers_done) >= NPROD) {
struct timespec ts = {0, 1000000};
nanosleep(&ts, NULL);
} else {
sched_yield();
}
}
}
if (errors == 0) {
printf("test passed\n");
} else {
printf("test FAILED with %d errors\n", errors);
}
return errors == 0 ? (void *)0 : (void *)1;
}
int main(void) {
emcmotErrorInit(&errlog);
pthread_t prods[NPROD];
pthread_t cons;
pthread_create(&cons, NULL, consumer_fn, NULL);
for (long i = 0; i < NPROD; i++) {
pthread_create(&prods[i], NULL, producer_fn, (void *)i);
}
for (long i = 0; i < NPROD; i++) {
pthread_join(prods[i], NULL);
}
void *cret;
pthread_join(cons, &cret);
return cret ? 1 : 0;
}