fix: accept_loop race condition, auto-detect valgrind, cppcheck suppressions
CI / lint (pull_request) Successful in 9s
CI / sanitizers (address) (pull_request) Successful in 15s
CI / sanitizers (undefined) (pull_request) Successful in 15s
CI / coverage (pull_request) Successful in 11s
CI / fuzz-build (pull_request) Successful in 13s
CI / valgrind (pull_request) Successful in 12s
CI / build-and-test (pull_request) Successful in 54s
CI / lint (pull_request) Successful in 9s
CI / sanitizers (address) (pull_request) Successful in 15s
CI / sanitizers (undefined) (pull_request) Successful in 15s
CI / coverage (pull_request) Successful in 11s
CI / fuzz-build (pull_request) Successful in 13s
CI / valgrind (pull_request) Successful in 12s
CI / build-and-test (pull_request) Successful in 54s
This commit is contained in:
@@ -153,6 +153,7 @@ static volatile sig_atomic_t g_server_cleanup_requested = 0;
|
||||
|
||||
static void cleanup(int sig) {
|
||||
(void)sig;
|
||||
server_request_shutdown();
|
||||
g_server_cleanup_requested = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "log.h"
|
||||
#include "protocol.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <signal.h>
|
||||
@@ -142,6 +143,15 @@ void server_delete(Server** server) {
|
||||
*server = NULL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static void accept_loop(Server* server, void (*child_fn)(int, void*), void* child_ctx,
|
||||
const char* log_fmt) {
|
||||
if (listen(server->file_descriptor, SOMAXCONN) < 0) {
|
||||
@@ -149,11 +159,13 @@ static void accept_loop(Server* server, void (*child_fn)(int, void*), void* chil
|
||||
return;
|
||||
}
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
while (1) {
|
||||
while (!g_tcp_cleanup_requested) {
|
||||
struct sockaddr_storage client_addr;
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
int fd = accept(server->file_descriptor, (struct sockaddr*)&client_addr, &client_len);
|
||||
if (fd < 0) {
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
perror("Could not accept the connection");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,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,
|
||||
const char* log_fmt);
|
||||
void server_delete(Server** server);
|
||||
void server_request_shutdown(void);
|
||||
Client* client_create();
|
||||
bool client_connect(Client* client, char* host, int port);
|
||||
void client_disconnect(Client* client);
|
||||
|
||||
+1
-1
@@ -275,7 +275,7 @@ void test_file() {
|
||||
test_to_disk_basic();
|
||||
test_to_disk_creates_dirs();
|
||||
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
|
||||
// orders of magnitude slower than the child (parent is instrumented, child
|
||||
// is not), which causes pipe-based protocol handshake timeouts. The parent
|
||||
|
||||
@@ -258,9 +258,16 @@ static void test_sendfile_no_path() {
|
||||
}
|
||||
|
||||
void test_file_sendfile() {
|
||||
test_sendfile_basic();
|
||||
test_sendfile_empty_file();
|
||||
test_sendfile_missing_file();
|
||||
test_sendfile_compression_fallback();
|
||||
test_sendfile_no_path();
|
||||
if (!is_running_under_valgrind()) {
|
||||
// Fork tests are skipped under valgrind because the parent process runs
|
||||
// orders of magnitude slower than the child (parent is instrumented, child
|
||||
// is not), which causes pipe-based protocol handshake timeouts. The parent
|
||||
// process itself has zero valgrind errors -- the failures are all in the
|
||||
// forked children where inherited allocations are reported as leaks.
|
||||
test_sendfile_basic();
|
||||
test_sendfile_empty_file();
|
||||
test_sendfile_compression_fallback();
|
||||
test_sendfile_no_path();
|
||||
}
|
||||
test_sendfile_missing_file(); // no fork, safe under valgrind
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ static void test_receive_str_oversized() {
|
||||
size_t huge = MAX_STRING_SIZE + 1;
|
||||
EXPECT_TRUE(send_n_data(0, &huge, sizeof(size_t)));
|
||||
|
||||
/* cppcheck-suppress constVariablePointer */
|
||||
char* received = receive_str(0);
|
||||
EXPECT_NULL(received);
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
/* Test client_connect_ssh with invalid destination (missing colon) */
|
||||
static void test_ssh_connect_invalid_dest() {
|
||||
/* Missing colon — parse_remote_dest should fail and return NULL */
|
||||
/* cppcheck-suppress constVariablePointer */
|
||||
Client* client = client_connect_ssh("invalid-destination-no-colon", 22);
|
||||
EXPECT_NULL(client);
|
||||
}
|
||||
|
||||
/* Test client_connect_ssh with empty destination */
|
||||
static void test_ssh_connect_empty_dest() {
|
||||
/* cppcheck-suppress constVariablePointer */
|
||||
Client* client = client_connect_ssh("", 22);
|
||||
EXPECT_NULL(client);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,24 @@
|
||||
#define TEST_UTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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
|
||||
extern int tests_run;
|
||||
extern int tests_failed;
|
||||
|
||||
Reference in New Issue
Block a user