Сохрани накопленные исправления аудио, async и диагностики

This commit is contained in:
evgeny committed 2026-10-02 01:49:32 +03:00
1 parent b1e3193899
commit bdcf8b5634
59 files changed
+4611 -1441

No files matched your search

+73 -17
View File
@@ -32,12 +32,38 @@ static int g_conn_count = 0, g_read_count = 0, g_err_count = 0;
static pid_t g_children[CHILDREN];
static int g_port = 0;
static void* g_mon_id = NULL;
static void* g_timeout_id = NULL;
static void* g_listener_id = NULL;
static int g_closed_count;
struct race_conn {
struct race_conn* next;
struct tcp_conn* tc;
int received;
};
static struct race_conn* g_connections;
static void destroy_connection(struct tcp_conn* tc) {
struct race_conn* c = tc->arg;
struct race_conn** p = &g_connections;
while (*p && *p != c) p = &(*p)->next;
if (*p) *p = c->next;
else { printf("[FAIL] closing untracked connection\n"); g_done = -1; }
g_closed_count++;
tcp_conn_destroy(tc); u_free(c);
}
static void on_read_cb(struct ll_queue* q, void* arg) {
struct tcp_conn* tc = (struct tcp_conn*)arg;
struct race_conn* c = tc->arg;
struct ll_entry* e = queue_data_get(q);
if (!e) { queue_resume_callback(q); return; }
if (e->len == 3 && memcmp(e->dgram, "OK\n", 3) == 0) g_read_count++;
/* TCP вправе разделить три байта между несколькими queue entries. */
if (c->received > 3 || e->len > 3 - c->received || memcmp(e->dgram, "OK\n" + c->received, e->len)) {
printf("[FAIL] unexpected stream data: received=%d len=%u\n", c->received, e->len); g_done = -1;
} else {
c->received += e->len;
if (c->received == 3 && e->len) g_read_count++;
}
memory_pool_free(tc->data_pool, e->dgram); queue_entry_free(e);
queue_resume_callback(q);
}
@@ -45,7 +71,8 @@ static void on_read_cb(struct ll_queue* q, void* arg) {
static void on_error_cb(struct tcp_conn* tc, int err, void* arg) {
(void)err; (void)arg;
g_err_count++;
tcp_conn_destroy(tc);
printf("[FAIL] connection error=%d\n", err);
g_done = -1; destroy_connection(tc);
}
static void on_fin_cb(struct tcp_conn* tc, void* arg) {
@@ -55,7 +82,7 @@ static void on_fin_cb(struct tcp_conn* tc, void* arg) {
static void on_closed_cb(struct tcp_conn* tc, void* arg) {
(void)arg;
tcp_conn_destroy(tc);
destroy_connection(tc);
}
static void on_accept_cb(int fd, void* arg) {
@@ -63,11 +90,18 @@ static void on_accept_cb(int fd, void* arg) {
while (1) {
struct sockaddr_in addr; socklen_t alen = sizeof(addr);
int csock = accept(fd, (struct sockaddr*)&addr, &alen);
if (csock < 0) return;
if (csock < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) return;
if (errno == EINTR) continue;
printf("[FAIL] accept: %s\n", strerror(errno)); g_done = -1; return;
}
if (csock == 0) { int r = open("/dev/null", O_RDONLY); if (r > 0 && r != 0) { dup2(r, 0); close(r); } continue; }
socket_set_nonblocking(csock);
struct tcp_conn* tc = tcp_conn_create(g_ua, csock, 512, 512, 4, 0, 0, on_fin_cb, on_error_cb, NULL);
if (!tc) { socket_close_wrapper(csock); continue; }
if (socket_set_nonblocking(csock) < 0) { close(csock); g_done = -1; return; }
struct race_conn* c = u_calloc(1, sizeof(*c));
if (!c) { printf("[FAIL] allocate connection context\n"); close(csock); g_done = -1; return; }
struct tcp_conn* tc = tcp_conn_create(g_ua, csock, 512, 512, 4, 0, 0, on_fin_cb, on_error_cb, c);
if (!tc) { printf("[FAIL] create connection\n"); u_free(c); close(csock); g_done = -1; return; }
c->tc = tc; c->next = g_connections; g_connections = c;
tc->on_closed = on_closed_cb;
queue_set_callback(tc->read_queue, on_read_cb, tc);
g_conn_count++;
@@ -91,20 +125,31 @@ static int child_main(int port, int id, int count) {
static void monitor(void* arg) {
(void)arg;
g_mon_id = NULL;
int alive = 0;
for (int i = 0; i < CHILDREN; i++) {
if (g_children[i] <= 0) continue;
int status; pid_t r = waitpid(g_children[i], &status, WNOHANG);
if (r == 0) { alive++; continue; }
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { g_done = -1; return; }
if (r < 0) {
if (errno == EINTR) { alive++; continue; }
printf("[FAIL] waitpid: %s\n", strerror(errno)); g_done = -1; return;
}
g_children[i] = 0;
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
printf("[FAIL] child exit status=%d\n", status); g_done = -1; return;
}
}
if (alive == 0 && g_read_count == CHILDREN * ITER_PER_CHILD && g_closed_count == g_conn_count) { g_ok = 1; g_done = 1; }
if (!g_done) {
g_mon_id = uasync_set_timeout(g_ua, 500, NULL, monitor, "mon");
if (!g_mon_id) { printf("[FAIL] restart monitor timer\n"); g_done = -1; }
}
if (alive == 0 && g_read_count >= CHILDREN * ITER_PER_CHILD) { g_ok = 1; g_done = 1; }
if (!g_done) g_mon_id = uasync_set_timeout(g_ua, 500, NULL, monitor, "mon");
}
static void test_timeout(void* arg) {
(void)arg;
g_timeout_id = NULL;
printf("[FAIL] timeout conn=%d read=%d err=%d\n", g_conn_count, g_read_count, g_err_count);
g_done = -1;
}
@@ -125,20 +170,25 @@ int main(void) {
int fd0 = open("/dev/null", O_RDONLY);
if (fd0 == 0) { } else if (fd0 > 0) { dup2(fd0, 0); close(fd0); } else { close(0); open("/dev/null", O_RDONLY); }
g_port = 25000 + (rand() % 10000);
g_ua = uasync_create();
if (!g_ua) { printf("[FAIL] uasync_create\n"); return 1; }
int lsock = socket(AF_INET, SOCK_STREAM, 0);
if (lsock < 0) { printf("[FAIL] socket\n"); goto cleanup; }
if (lsock < 0) { printf("[FAIL] socket: %s\n", strerror(errno)); goto cleanup; }
socket_set_reuseaddr(lsock, 1);
socket_set_nonblocking(lsock);
struct sockaddr_in la; memset(&la, 0, sizeof(la));
la.sin_family = AF_INET; la.sin_port = htons((uint16_t)g_port);
la.sin_family = AF_INET; la.sin_port = 0;
la.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(lsock, (struct sockaddr*)&la, sizeof(la)) < 0) { printf("[FAIL] bind: %s\n", strerror(errno)); goto cleanup; }
socklen_t la_len = sizeof(la);
if (getsockname(lsock, (struct sockaddr*)&la, &la_len) < 0) { printf("[FAIL] getsockname: %s\n", strerror(errno)); goto cleanup; }
g_port = ntohs(la.sin_port);
if (listen(lsock, 1024) < 0) { printf("[FAIL] listen: %s\n", strerror(errno)); goto cleanup; }
uasync_add_socket(g_ua, lsock, on_accept_cb, NULL, NULL, "test_listen", NULL);
g_listener_id = uasync_add_socket(g_ua, lsock, on_accept_cb, NULL, NULL, "test_listen", NULL);
if (!g_listener_id) {
printf("[FAIL] register listener\n"); goto cleanup;
}
for (int i = 0; i < CHILDREN; i++) {
pid_t pid = fork();
@@ -149,14 +199,15 @@ int main(void) {
printf(" %d children × %d iterations on port %d\n", CHILDREN, ITER_PER_CHILD, g_port); fflush(stdout);
g_mon_id = uasync_set_timeout(g_ua, 100, NULL, monitor, "mon");
void* to_id = uasync_set_timeout(g_ua, TIMEOUT_MS * 10, NULL, test_timeout, "to");
g_timeout_id = uasync_set_timeout(g_ua, TIMEOUT_MS * 10, NULL, test_timeout, "to");
if (!g_mon_id || !g_timeout_id) { printf("[FAIL] create timers\n"); goto cleanup; }
while (!g_done) uasync_poll(g_ua, 50);
if (to_id) uasync_cancel_timeout(g_ua, to_id);
if (g_timeout_id) { uasync_cancel_timeout(g_ua, g_timeout_id); g_timeout_id = NULL; }
int expected = CHILDREN * ITER_PER_CHILD;
if (g_ok && g_conn_count == expected && g_read_count == expected) {
if (g_ok && g_conn_count == expected && g_read_count == expected && g_closed_count == expected && !g_err_count) {
printf("[PASS] test_uasync_socket_race — %d/%d/%d conn/read/err\n", g_conn_count, g_read_count, g_err_count);
} else {
printf("[FAIL] test_uasync_socket_race — expected %d, got %d/%d/%d\n", expected, g_conn_count, g_read_count, g_err_count);
@@ -166,6 +217,11 @@ int main(void) {
cleanup:
for (int i = 0; i < CHILDREN; i++) if (g_children[i] > 0) { kill(g_children[i], SIGKILL); waitpid(g_children[i], NULL, 0); }
if (g_mon_id) uasync_cancel_timeout(g_ua, g_mon_id);
if (g_timeout_id) uasync_cancel_timeout(g_ua, g_timeout_id);
if (g_listener_id) uasync_remove_socket(g_ua, g_listener_id);
if (lsock >= 0) close(lsock);
while (g_connections) destroy_connection(g_connections->tc);
uasync_drain_immediate(g_ua);
if (g_ua) uasync_destroy(g_ua, 0);
return g_ok ? 0 : 1;
#endif