Build: incremental build support, Windows uasync fixes, TUN cross-platform refactoring
- build.sh: added --full flag for full rebuild (autoreconf + configure + make) - build_direct.sh: incremental compilation (skips unchanged files) - build_full.bat: new Windows batch for full rebuild - configure.ac: fixed Windows detection using AC_COMPILE_IFELSE - lib/u_async.c/h: Windows wakeup socket support, uasync_post for thread-safe callbacks - src/tun_if.c/h: cross-platform TUN refactoring, Windows read thread support - src/tun_windows.c: Windows TUN implementation improvements - src/control_server.c: removed premature return statement - src/routing.c: include ordering fix - src/utun.c: windows compat includes - Deleted obsolete net_emulator/Makefile and tinycrypt/Makefile
This commit is contained in:
1 parent
734851329f
commit
d10bddd1f7
15 files changed
+950
-915
No files matched your search
+242
-51
@@ -8,6 +8,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
// Platform-specific includes
|
||||
#ifdef __linux__
|
||||
@@ -276,7 +283,22 @@ static void timeout_node_free_callback(void* user_data, void* data) {
|
||||
|
||||
// Helper to get current time
|
||||
static void get_current_time(struct timeval* tv) {
|
||||
#ifdef _WIN32
|
||||
// Для Windows используем GetTickCount или другие механизмы
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
|
||||
ULARGE_INTEGER ul;
|
||||
ul.LowPart = ft.dwLowDateTime;
|
||||
ul.HighPart = ft.dwHighDateTime;
|
||||
|
||||
// Конвертируем 100-наносекундные интервалы в секунды и микросекунды
|
||||
tv->tv_sec = (long)((ul.QuadPart - 116444736000000000LL) / 10000000);
|
||||
tv->tv_usec = (long)((ul.QuadPart % 10000000) / 10);
|
||||
#else
|
||||
// Для Linux и других Unix-подобных систем используем gettimeofday
|
||||
gettimeofday(tv, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -292,6 +314,55 @@ static void drain_wakeup_pipe(struct UASYNC* ua) {
|
||||
}
|
||||
}
|
||||
|
||||
// Process posted tasks (lock-free during execution)
|
||||
static void process_posted_tasks(struct UASYNC* ua) {
|
||||
if (!ua) return;
|
||||
|
||||
struct posted_task* list = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(&ua->posted_lock);
|
||||
#else
|
||||
pthread_mutex_lock(&ua->posted_lock);
|
||||
#endif
|
||||
list = ua->posted_tasks_head;
|
||||
ua->posted_tasks_head = NULL;
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(&ua->posted_lock);
|
||||
#else
|
||||
pthread_mutex_unlock(&ua->posted_lock);
|
||||
#endif
|
||||
|
||||
while (list) {
|
||||
struct posted_task* t = list;
|
||||
list = list->next;
|
||||
|
||||
if (t->callback) {
|
||||
t->callback(t->arg);
|
||||
}
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Unified wakeup handler (drain + execute posted callbacks)
|
||||
static void handle_wakeup(struct UASYNC* ua) {
|
||||
if (!ua || !ua->wakeup_initialized) return;
|
||||
|
||||
// Drain the wakeup pipe/socket
|
||||
#ifdef _WIN32
|
||||
char buf[64];
|
||||
SOCKET s = (SOCKET)(intptr_t)ua->wakeup_pipe[0];
|
||||
while (recv(s, buf, sizeof(buf), 0) > 0) {}
|
||||
#else
|
||||
char buf[64];
|
||||
while (read(ua->wakeup_pipe[0], buf, sizeof(buf)) > 0) {}
|
||||
#endif
|
||||
|
||||
// Execute all posted callbacks (in main thread)
|
||||
process_posted_tasks(ua);
|
||||
}
|
||||
|
||||
|
||||
// Helper to add timeval: tv += dt (timebase units)
|
||||
static void timeval_add_tb(struct timeval* tv, int dt) {
|
||||
tv->tv_usec += (dt % 10000) * 100;
|
||||
@@ -978,73 +1049,137 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb) {
|
||||
}
|
||||
|
||||
|
||||
// Put this near the top of u_async.c, after includes and before uasync_create
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static void wakeup_read_callback_win(socket_t sock, void* arg) {
|
||||
(void)sock; // не нужен
|
||||
handle_wakeup((struct UASYNC*)arg);
|
||||
}
|
||||
#else
|
||||
static void wakeup_read_callback_posix(int fd, void* arg) {
|
||||
(void)fd;
|
||||
handle_wakeup((struct UASYNC*)arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// ========== Instance management functions ==========
|
||||
|
||||
// Modified function in u_async.c: uasync_create
|
||||
// Changes: Use self-connected UDP socket for wakeup on Windows instead of pipe.
|
||||
// This ensures the wakeup is a selectable SOCKET.
|
||||
|
||||
struct UASYNC* uasync_create(void) {
|
||||
// Initialize socket platform first (WSAStartup on Windows)
|
||||
if (socket_platform_init() != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "Failed to initialize socket platform");
|
||||
struct UASYNC* ua = calloc(1, sizeof(struct UASYNC));
|
||||
if (!ua) return NULL;
|
||||
|
||||
ua->timer_alloc_count = 0;
|
||||
ua->timer_free_count = 0;
|
||||
ua->socket_alloc_count = 0;
|
||||
ua->socket_free_count = 0;
|
||||
|
||||
ua->poll_fds = NULL;
|
||||
ua->poll_fds_capacity = 0;
|
||||
ua->poll_fds_count = 0;
|
||||
ua->poll_fds_dirty = 1;
|
||||
|
||||
ua->wakeup_pipe[0] = -1;
|
||||
ua->wakeup_pipe[1] = -1;
|
||||
ua->wakeup_initialized = 0;
|
||||
ua->posted_tasks_head = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: self-connected UDP socket for wakeup
|
||||
SOCKET r = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
SOCKET w = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (r == INVALID_SOCKET || w == INVALID_SOCKET) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup sockets");
|
||||
free(ua);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct UASYNC* ua = malloc(sizeof(struct UASYNC));
|
||||
if (!ua) {
|
||||
socket_platform_cleanup();
|
||||
struct sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
if (bind(r, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR ||
|
||||
getsockname(r, (struct sockaddr*)&addr, &(int){sizeof(addr)}) == SOCKET_ERROR ||
|
||||
connect(w, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||
closesocket(r);
|
||||
closesocket(w);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_UASYNC, "Wakeup socket setup failed: %d", WSAGetLastError());
|
||||
free(ua);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ua, 0, sizeof(struct UASYNC));
|
||||
ua->wakeup_pipe[0] = -1;
|
||||
ua->wakeup_pipe[1] = -1;
|
||||
ua->wakeup_initialized = 0;
|
||||
|
||||
// Create wakeup pipe (skip on Windows - pipes don't work with poll/WSAPoll)
|
||||
#ifndef _WIN32
|
||||
if (pipe(ua->wakeup_pipe) < 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup pipe: %s", strerror(errno));
|
||||
// Continue without wakeup mechanism
|
||||
ua->wakeup_pipe[0] = -1;
|
||||
ua->wakeup_pipe[1] = -1;
|
||||
} else {
|
||||
ua->wakeup_initialized = 1;
|
||||
// Set non-blocking on read end to avoid blocking if pipe is full
|
||||
int flags = fcntl(ua->wakeup_pipe[0], F_GETFL, 0);
|
||||
if (flags >= 0) {
|
||||
fcntl(ua->wakeup_pipe[0], F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
ua->wakeup_pipe[0] = (int)(intptr_t)r;
|
||||
ua->wakeup_pipe[1] = (int)(intptr_t)w;
|
||||
ua->wakeup_initialized = 1;
|
||||
|
||||
u_long mode = 1;
|
||||
ioctlsocket(r, FIONBIO, &mode);
|
||||
|
||||
// Register the read socket with uasync
|
||||
uasync_add_socket_t(ua, r, wakeup_read_callback_win, NULL, NULL, ua); // ← ua как user_data
|
||||
// uasync_add_socket_t(ua, r, wakeup_read_callback_win, NULL, NULL, NULL);
|
||||
InitializeCriticalSection(&ua->posted_lock);
|
||||
|
||||
#else
|
||||
// On Windows, pipes don't work with WSAPoll - skip wakeup mechanism
|
||||
ua->wakeup_pipe[0] = -1;
|
||||
ua->wakeup_pipe[1] = -1;
|
||||
ua->wakeup_initialized = 0;
|
||||
// POSIX pipe
|
||||
if (pipe(ua->wakeup_pipe) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_UASYNC, "pipe() failed: %s", strerror(errno));
|
||||
free(ua);
|
||||
return NULL;
|
||||
}
|
||||
ua->wakeup_initialized = 1;
|
||||
|
||||
fcntl(ua->wakeup_pipe[0], F_SETFL, fcntl(ua->wakeup_pipe[0], F_GETFL, 0) | O_NONBLOCK);
|
||||
fcntl(ua->wakeup_pipe[1], F_SETFL, fcntl(ua->wakeup_pipe[1], F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
if (!ua->use_epoll) {
|
||||
uasync_add_socket(ua, ua->wakeup_pipe[0], wakeup_read_callback_posix, NULL, NULL, ua); // ← ua
|
||||
}
|
||||
pthread_mutex_init(&ua->posted_lock, NULL);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ua->sockets = socket_array_create(16);
|
||||
if (!ua->sockets) {
|
||||
if (ua->wakeup_initialized) {
|
||||
#ifdef _WIN32
|
||||
closesocket((SOCKET)(intptr_t)ua->wakeup_pipe[0]);
|
||||
closesocket((SOCKET)(intptr_t)ua->wakeup_pipe[1]);
|
||||
#else
|
||||
close(ua->wakeup_pipe[0]);
|
||||
close(ua->wakeup_pipe[1]);
|
||||
#endif
|
||||
}
|
||||
free(ua);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ua->timeout_heap = timeout_heap_create(16);
|
||||
if (!ua->timeout_heap) {
|
||||
socket_array_destroy(ua->sockets);
|
||||
if (ua->wakeup_initialized) {
|
||||
#ifdef _WIN32
|
||||
closesocket((SOCKET)(intptr_t)ua->wakeup_pipe[0]);
|
||||
closesocket((SOCKET)(intptr_t)ua->wakeup_pipe[1]);
|
||||
#else
|
||||
close(ua->wakeup_pipe[0]);
|
||||
close(ua->wakeup_pipe[1]);
|
||||
#endif
|
||||
}
|
||||
free(ua);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Set callback to free timeout nodes and update counters
|
||||
timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback);
|
||||
|
||||
|
||||
// Initialize epoll on Linux
|
||||
ua->epoll_fd = -1;
|
||||
ua->use_epoll = 0;
|
||||
@@ -1066,7 +1201,7 @@ struct UASYNC* uasync_create(void) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create epoll fd, falling back to poll: %s", strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return ua;
|
||||
}
|
||||
|
||||
@@ -1119,14 +1254,17 @@ void uasync_print_resources(struct UASYNC* ua, const char* prefix) {
|
||||
printf("🔚 %s: End of resource report\n\n", prefix);
|
||||
}
|
||||
|
||||
// Modified function in u_async.c: uasync_destroy
|
||||
// Changes: Close wakeup sockets properly on Windows.
|
||||
|
||||
void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
if (!ua) return;
|
||||
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua);
|
||||
|
||||
|
||||
// Диагностика ресурсов перед очисткой
|
||||
uasync_print_resources(ua, "BEFORE_DESTROY");
|
||||
|
||||
|
||||
// Check for potential memory leaks
|
||||
if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected before cleanup: timers %zu/%zu, sockets %zu/%zu",
|
||||
@@ -1139,7 +1277,7 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count));
|
||||
// Continue cleanup, will abort after if leaks remain
|
||||
}
|
||||
|
||||
|
||||
// Free all remaining timeouts
|
||||
if (ua->timeout_heap) {
|
||||
size_t freed_count = 0;
|
||||
@@ -1147,7 +1285,7 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
TimeoutEntry entry;
|
||||
if (timeout_heap_pop(ua->timeout_heap, &entry) != 0) break;
|
||||
struct timeout_node* node = (struct timeout_node*)entry.data;
|
||||
|
||||
|
||||
// Free all timer nodes (avoid double-free bug)
|
||||
if (node) {
|
||||
ua->timer_free_count++;
|
||||
@@ -1156,7 +1294,7 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
}
|
||||
timeout_heap_destroy(ua->timeout_heap);
|
||||
}
|
||||
|
||||
|
||||
// Free all socket nodes using array approach
|
||||
if (ua->sockets) {
|
||||
// Count and free all active sockets
|
||||
@@ -1164,7 +1302,15 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
for (int i = 0; i < ua->sockets->capacity; i++) {
|
||||
if (ua->sockets->sockets[i].active) {
|
||||
if (close_fds && ua->sockets->sockets[i].fd >= 0) {
|
||||
#ifdef _WIN32
|
||||
if (ua->sockets->sockets[i].type == SOCKET_NODE_TYPE_SOCK) {
|
||||
closesocket(ua->sockets->sockets[i].sock);
|
||||
} else {
|
||||
close(ua->sockets->sockets[i].fd); // For pipes/FDs
|
||||
}
|
||||
#else
|
||||
close(ua->sockets->sockets[i].fd);
|
||||
#endif
|
||||
}
|
||||
ua->socket_free_count++;
|
||||
freed_count++;
|
||||
@@ -1173,23 +1319,28 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count);
|
||||
socket_array_destroy(ua->sockets);
|
||||
}
|
||||
|
||||
// Close wakeup pipe
|
||||
|
||||
// Close wakeup pipe/sockets
|
||||
if (ua->wakeup_initialized) {
|
||||
#ifdef _WIN32
|
||||
closesocket((SOCKET)(intptr_t)ua->wakeup_pipe[0]);
|
||||
closesocket((SOCKET)(intptr_t)ua->wakeup_pipe[1]);
|
||||
#else
|
||||
close(ua->wakeup_pipe[0]);
|
||||
close(ua->wakeup_pipe[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Free cached poll_fds
|
||||
free(ua->poll_fds);
|
||||
|
||||
|
||||
// Close epoll fd on Linux
|
||||
#if HAS_EPOLL
|
||||
if (ua->epoll_fd >= 0) {
|
||||
close(ua->epoll_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Final leak check
|
||||
if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected after cleanup: timers %zu/%zu, sockets %zu/%zu",
|
||||
@@ -1202,10 +1353,22 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
||||
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count));
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: completed successfully for ua=%p", ua);
|
||||
|
||||
while (ua->posted_tasks_head) {
|
||||
struct posted_task* t = ua->posted_tasks_head;
|
||||
ua->posted_tasks_head = t->next;
|
||||
free(t);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
DeleteCriticalSection(&ua->posted_lock);
|
||||
#else
|
||||
pthread_mutex_destroy(&ua->posted_lock);
|
||||
#endif
|
||||
|
||||
free(ua);
|
||||
|
||||
|
||||
// Cleanup socket platform (WSACleanup on Windows)
|
||||
socket_platform_cleanup();
|
||||
}
|
||||
@@ -1235,14 +1398,42 @@ void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free
|
||||
if (socket_free) *socket_free = ua->socket_free_count;
|
||||
}
|
||||
|
||||
// Get global instance for backward compatibility
|
||||
void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* arg) {
|
||||
if (!ua || !callback) return;
|
||||
|
||||
struct posted_task* task = malloc(sizeof(struct posted_task));
|
||||
if (!task) return;
|
||||
|
||||
task->callback = callback;
|
||||
task->arg = arg;
|
||||
task->next = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(&ua->posted_lock);
|
||||
#else
|
||||
pthread_mutex_lock(&ua->posted_lock);
|
||||
#endif
|
||||
task->next = ua->posted_tasks_head;
|
||||
ua->posted_tasks_head = task;
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(&ua->posted_lock);
|
||||
#else
|
||||
pthread_mutex_unlock(&ua->posted_lock);
|
||||
#endif
|
||||
|
||||
uasync_wakeup(ua); // будим mainloop
|
||||
}
|
||||
|
||||
// Wakeup mechanism
|
||||
int uasync_wakeup(struct UASYNC* ua) {
|
||||
if (!ua || !ua->wakeup_initialized) return -1;
|
||||
|
||||
|
||||
char byte = 0;
|
||||
#ifdef _WIN32
|
||||
int ret = send((SOCKET)(intptr_t)ua->wakeup_pipe[1], &byte, 1, 0);
|
||||
#else
|
||||
ssize_t ret = write(ua->wakeup_pipe[1], &byte, 1);
|
||||
#endif
|
||||
if (ret != 1) {
|
||||
// Don't print error from signal handler
|
||||
return -1;
|
||||
|
||||
Reference in new issue
Block a user