fix: race condition in accept_loop, auto-detect valgrind
CI / lint (pull_request) Successful in 7s
CI / sanitizers (address) (pull_request) Successful in 15s
CI / sanitizers (undefined) (pull_request) Successful in 15s
CI / coverage (pull_request) Successful in 9s
CI / fuzz-build (pull_request) Successful in 13s
CI / valgrind (pull_request) Successful in 11s
CI / build-and-test (pull_request) Successful in 54s

This commit is contained in:
2026-07-20 20:57:00 +02:00
parent da6b42a418
commit ffa5f3bf57
5 changed files with 28 additions and 2 deletions
+1
View File
@@ -117,6 +117,7 @@ static volatile sig_atomic_t g_server_cleanup_requested = 0;
static void cleanup(int sig) { static void cleanup(int sig) {
(void)sig; (void)sig;
server_request_shutdown();
g_server_cleanup_requested = 1; g_server_cleanup_requested = 1;
} }
+10 -1
View File
@@ -12,6 +12,15 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
/* Flag set by server_request_shutdown() to request graceful shutdown
of the accept loop. Accessed only from transport_tcp.c so it won't
cause linker errors when this file is compiled into client/test targets. */
static volatile sig_atomic_t g_tcp_cleanup_requested = 0;
void server_request_shutdown(void) {
g_tcp_cleanup_requested = 1;
}
Server* server_create(int port) { Server* server_create(int port) {
Server* server = (Server*)malloc(sizeof(Server)); Server* server = (Server*)malloc(sizeof(Server));
if (server == NULL) { if (server == NULL) {
@@ -70,7 +79,7 @@ static void accept_loop(Server* server, void (*child_fn)(int, void*), void* chil
return; return;
} }
signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, SIG_IGN);
while (1) { while (!g_tcp_cleanup_requested) {
struct sockaddr_in client_addr; struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr); socklen_t client_len = sizeof(client_addr);
int fd = accept(server->file_descriptor, (struct sockaddr*)&client_addr, &client_len); int fd = accept(server->file_descriptor, (struct sockaddr*)&client_addr, &client_len);
+1
View File
@@ -26,6 +26,7 @@ bool server_listen(Server* server, void (*handler)(int file_descriptor));
void server_accept_loop(Server* server, void (*child_fn)(int, void*), void* child_ctx, void server_accept_loop(Server* server, void (*child_fn)(int, void*), void* child_ctx,
const char* log_fmt); const char* log_fmt);
void server_delete(Server** server); void server_delete(Server** server);
void server_request_shutdown(void);
Client* client_create(); Client* client_create();
bool client_connect(Client* client, char* host, int port); bool client_connect(Client* client, char* host, int port);
void client_disconnect(Client* client); void client_disconnect(Client* client);
+1 -1
View File
@@ -271,7 +271,7 @@ void test_file() {
test_to_disk_basic(); test_to_disk_basic();
test_to_disk_creates_dirs(); test_to_disk_creates_dirs();
test_file_content_to_buffer(); test_file_content_to_buffer();
if (!getenv("FASTSYNC_UNDER_VALGRIND")) { if (!is_running_under_valgrind()) {
// Fork tests are skipped under valgrind because the parent process runs // Fork tests are skipped under valgrind because the parent process runs
// orders of magnitude slower than the child (parent is instrumented, child // orders of magnitude slower than the child (parent is instrumented, child
// is not), which causes pipe-based protocol handshake timeouts. The parent // is not), which causes pipe-based protocol handshake timeouts. The parent
+15
View File
@@ -2,9 +2,24 @@
#define TEST_UTILS_H #define TEST_UTILS_H
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
// Detect if running under valgrind by checking /proc/self/maps for vgpreload.
// This is used to skip fork-based tests that are incompatible with valgrind
// (the instrumented parent runs too slowly, causing pipe timeouts).
static inline bool is_running_under_valgrind(void) {
FILE* f = fopen("/proc/self/maps", "r");
if (!f)
return false;
char buf[4096];
size_t n = fread(buf, 1, sizeof(buf) - 1, f);
fclose(f);
buf[n] = '\0';
return strstr(buf, "vgpreload") != NULL;
}
// Global test suite status // Global test suite status
extern int tests_run; extern int tests_run;
extern int tests_failed; extern int tests_failed;