|
|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
#include "u_async.h" |
|
|
|
|
#include "platform_compat.h" |
|
|
|
|
#include "debug_config.h" |
|
|
|
|
#include "mem.h" |
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <stdlib.h> |
|
|
|
|
@ -46,7 +47,7 @@ struct socket_node {
|
|
|
|
|
socket_t_callback_t write_cbk_sock; // For SOCK type
|
|
|
|
|
socket_callback_t except_cbk; |
|
|
|
|
void* user_data; |
|
|
|
|
int active; // 1 if socket is active, 0 if freed (for reuse)
|
|
|
|
|
int active; // 1 if socket is active, 0 if u_freed (for reuse)
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Array-based socket management for O(1) operations
|
|
|
|
|
@ -72,20 +73,20 @@ static struct socket_node* socket_array_get(struct socket_array* sa, int fd);
|
|
|
|
|
static struct socket_array* socket_array_create(int initial_capacity) { |
|
|
|
|
if (initial_capacity < 4) initial_capacity = 4; // Minimum capacity
|
|
|
|
|
|
|
|
|
|
struct socket_array* sa = malloc(sizeof(struct socket_array)); |
|
|
|
|
struct socket_array* sa = u_malloc(sizeof(struct socket_array)); |
|
|
|
|
if (!sa) return NULL; |
|
|
|
|
|
|
|
|
|
sa->sockets = calloc(initial_capacity, sizeof(struct socket_node)); |
|
|
|
|
sa->fd_to_index = calloc(initial_capacity, sizeof(int)); |
|
|
|
|
sa->index_to_fd = calloc(initial_capacity, sizeof(int)); |
|
|
|
|
sa->active_indices = calloc(initial_capacity, sizeof(int)); |
|
|
|
|
sa->sockets = u_calloc(initial_capacity, sizeof(struct socket_node)); |
|
|
|
|
sa->fd_to_index = u_calloc(initial_capacity, sizeof(int)); |
|
|
|
|
sa->index_to_fd = u_calloc(initial_capacity, sizeof(int)); |
|
|
|
|
sa->active_indices = u_calloc(initial_capacity, sizeof(int)); |
|
|
|
|
|
|
|
|
|
if (!sa->sockets || !sa->fd_to_index || !sa->index_to_fd || !sa->active_indices) { |
|
|
|
|
free(sa->sockets); |
|
|
|
|
free(sa->fd_to_index); |
|
|
|
|
free(sa->index_to_fd); |
|
|
|
|
free(sa->active_indices); |
|
|
|
|
free(sa); |
|
|
|
|
u_free(sa->sockets); |
|
|
|
|
u_free(sa->fd_to_index); |
|
|
|
|
u_free(sa->index_to_fd); |
|
|
|
|
u_free(sa->active_indices); |
|
|
|
|
u_free(sa); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -108,11 +109,11 @@ static struct socket_array* socket_array_create(int initial_capacity) {
|
|
|
|
|
static void socket_array_destroy(struct socket_array* sa) { |
|
|
|
|
if (!sa) return; |
|
|
|
|
|
|
|
|
|
free(sa->sockets); |
|
|
|
|
free(sa->fd_to_index); |
|
|
|
|
free(sa->index_to_fd); |
|
|
|
|
free(sa->active_indices); |
|
|
|
|
free(sa); |
|
|
|
|
u_free(sa->sockets); |
|
|
|
|
u_free(sa->fd_to_index); |
|
|
|
|
u_free(sa->index_to_fd); |
|
|
|
|
u_free(sa->active_indices); |
|
|
|
|
u_free(sa); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int socket_array_add_internal(struct socket_array* sa, int fd, socket_t sock, int type, |
|
|
|
|
@ -129,17 +130,17 @@ static int socket_array_add_internal(struct socket_array* sa, int fd, socket_t s
|
|
|
|
|
int new_capacity = sa->capacity * 2; |
|
|
|
|
if (fd >= new_capacity) new_capacity = fd + 16; // Ensure enough space
|
|
|
|
|
|
|
|
|
|
struct socket_node* new_sockets = realloc(sa->sockets, new_capacity * sizeof(struct socket_node)); |
|
|
|
|
int* new_fd_to_index = realloc(sa->fd_to_index, new_capacity * sizeof(int)); |
|
|
|
|
int* new_index_to_fd = realloc(sa->index_to_fd, new_capacity * sizeof(int)); |
|
|
|
|
int* new_active_indices = realloc(sa->active_indices, new_capacity * sizeof(int)); |
|
|
|
|
struct socket_node* new_sockets = u_realloc(sa->sockets, new_capacity * sizeof(struct socket_node)); |
|
|
|
|
int* new_fd_to_index = u_realloc(sa->fd_to_index, new_capacity * sizeof(int)); |
|
|
|
|
int* new_index_to_fd = u_realloc(sa->index_to_fd, new_capacity * sizeof(int)); |
|
|
|
|
int* new_active_indices = u_realloc(sa->active_indices, new_capacity * sizeof(int)); |
|
|
|
|
|
|
|
|
|
if (!new_sockets || !new_fd_to_index || !new_index_to_fd || !new_active_indices) { |
|
|
|
|
// Allocation failed
|
|
|
|
|
free(new_sockets); |
|
|
|
|
free(new_fd_to_index); |
|
|
|
|
free(new_index_to_fd); |
|
|
|
|
free(new_active_indices); |
|
|
|
|
u_free(new_sockets); |
|
|
|
|
u_free(new_fd_to_index); |
|
|
|
|
u_free(new_index_to_fd); |
|
|
|
|
u_free(new_active_indices); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -162,7 +163,7 @@ static int socket_array_add_internal(struct socket_array* sa, int fd, socket_t s
|
|
|
|
|
// Check if FD already exists
|
|
|
|
|
if (sa->fd_to_index[fd] != -1) return -1; // FD already exists
|
|
|
|
|
|
|
|
|
|
// Find first free slot
|
|
|
|
|
// Find first u_free slot
|
|
|
|
|
int index = -1; |
|
|
|
|
for (int i = 0; i < sa->capacity; i++) { |
|
|
|
|
if (!sa->sockets[i].active) { |
|
|
|
|
@ -171,7 +172,7 @@ static int socket_array_add_internal(struct socket_array* sa, int fd, socket_t s
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (index == -1) return -1; // No free slots (shouldn't happen)
|
|
|
|
|
if (index == -1) return -1; // No u_free slots (shouldn't happen)
|
|
|
|
|
|
|
|
|
|
// Add the socket
|
|
|
|
|
sa->sockets[index].fd = fd; |
|
|
|
|
@ -272,13 +273,13 @@ static struct socket_node* socket_array_get_by_sock(struct socket_array* sa, soc
|
|
|
|
|
return &sa->sockets[index]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Callback to free timeout node and update counters
|
|
|
|
|
// Callback to u_free timeout node and update counters
|
|
|
|
|
static void timeout_node_free_callback(void* user_data, void* data) { |
|
|
|
|
struct UASYNC* ua = (struct UASYNC*)user_data; |
|
|
|
|
struct timeout_node* node = (struct timeout_node*)data; |
|
|
|
|
(void)node; // Not used directly, but keep for consistency
|
|
|
|
|
ua->timer_free_count++; |
|
|
|
|
free(data); |
|
|
|
|
u_free(data); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Helper to get current time
|
|
|
|
|
@ -329,19 +330,20 @@ static void drain_wakeup_pipe(struct UASYNC* ua) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Process posted tasks (lock-free during execution)
|
|
|
|
|
// Process posted tasks (lock-u_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; |
|
|
|
|
ua->posted_tasks_head = ua->posted_tasks_tail = NULL; |
|
|
|
|
#ifdef _WIN32 |
|
|
|
|
LeaveCriticalSection(&ua->posted_lock); |
|
|
|
|
#else |
|
|
|
|
@ -349,13 +351,14 @@ static void process_posted_tasks(struct UASYNC* ua) {
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
while (list) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "POSTed task get"); |
|
|
|
|
struct posted_task* t = list; |
|
|
|
|
list = list->next; |
|
|
|
|
|
|
|
|
|
if (t->callback) { |
|
|
|
|
t->callback(t->arg); |
|
|
|
|
} |
|
|
|
|
free(t); |
|
|
|
|
u_free(t); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -417,11 +420,11 @@ static void process_timeouts(struct UASYNC* ua) {
|
|
|
|
|
node->callback(node->arg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Always free the node after processing
|
|
|
|
|
// Always u_free the node after processing
|
|
|
|
|
if (node && node->ua) { |
|
|
|
|
node->ua->timer_free_count++; |
|
|
|
|
} |
|
|
|
|
free(node); |
|
|
|
|
u_free(node); |
|
|
|
|
continue; // Process next expired timeout
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -465,7 +468,7 @@ void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_c
|
|
|
|
|
|
|
|
|
|
// DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: timeout=%d.%d ms, arg=%p, callback=%p", timeout_tb/10, timeout_tb%10, arg, callback);
|
|
|
|
|
|
|
|
|
|
struct timeout_node* node = malloc(sizeof(struct timeout_node)); |
|
|
|
|
struct timeout_node* node = u_malloc(sizeof(struct timeout_node)); |
|
|
|
|
if (!node) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to allocate node"); |
|
|
|
|
return NULL; |
|
|
|
|
@ -486,7 +489,7 @@ void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_c
|
|
|
|
|
// Add to heap
|
|
|
|
|
if (timeout_heap_push(ua->timeout_heap, node->expiration_ms, node) != 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to push to heap"); |
|
|
|
|
free(node); |
|
|
|
|
u_free(node); |
|
|
|
|
ua->timer_free_count++; // Balance the alloc counter
|
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
@ -508,7 +511,7 @@ err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) {
|
|
|
|
|
|
|
|
|
|
// Try to cancel from heap first
|
|
|
|
|
if (timeout_heap_cancel(ua->timeout_heap, node->expiration_ms, node) == 0) { |
|
|
|
|
// Successfully marked as deleted - free will happen lazily in heap
|
|
|
|
|
// Successfully marked as deleted - u_free will happen lazily in heap
|
|
|
|
|
node->cancelled = 1; |
|
|
|
|
node->callback = NULL; |
|
|
|
|
// DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: successfully cancelled timer %p from heap", node);
|
|
|
|
|
@ -666,7 +669,7 @@ static void rebuild_poll_fds(struct UASYNC* ua) {
|
|
|
|
|
int new_capacity = total_fds * 2; |
|
|
|
|
if (new_capacity < 16) new_capacity = 16; |
|
|
|
|
|
|
|
|
|
struct pollfd* new_poll_fds = realloc(ua->poll_fds, sizeof(struct pollfd) * new_capacity); |
|
|
|
|
struct pollfd* new_poll_fds = u_realloc(ua->poll_fds, sizeof(struct pollfd) * new_capacity); |
|
|
|
|
if (!new_poll_fds) return; // Keep old allocation on failure
|
|
|
|
|
|
|
|
|
|
ua->poll_fds = new_poll_fds; |
|
|
|
|
@ -1091,7 +1094,7 @@ struct UASYNC* uasync_create(void) {
|
|
|
|
|
// Initialize socket platform (Winsock on Windows)
|
|
|
|
|
socket_platform_init(); |
|
|
|
|
|
|
|
|
|
struct UASYNC* ua = calloc(1, sizeof(struct UASYNC)); |
|
|
|
|
struct UASYNC* ua = u_calloc(1, sizeof(struct UASYNC)); |
|
|
|
|
if (!ua) return NULL; |
|
|
|
|
|
|
|
|
|
ua->timer_alloc_count = 0; |
|
|
|
|
@ -1120,7 +1123,7 @@ struct UASYNC* uasync_create(void) {
|
|
|
|
|
close(ua->wakeup_pipe[1]); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
free(ua); |
|
|
|
|
u_free(ua); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1136,11 +1139,11 @@ struct UASYNC* uasync_create(void) {
|
|
|
|
|
close(ua->wakeup_pipe[1]); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
free(ua); |
|
|
|
|
u_free(ua); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Set callback to free timeout nodes and update counters
|
|
|
|
|
// Set callback to u_free timeout nodes and update counters
|
|
|
|
|
timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); |
|
|
|
|
|
|
|
|
|
// Initialize epoll on Linux
|
|
|
|
|
@ -1171,7 +1174,7 @@ struct UASYNC* uasync_create(void) {
|
|
|
|
|
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); |
|
|
|
|
u_free(ua); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1186,7 +1189,7 @@ struct UASYNC* uasync_create(void) {
|
|
|
|
|
closesocket(r); |
|
|
|
|
closesocket(w); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_UASYNC, "Wakeup socket setup failed: %d", WSAGetLastError()); |
|
|
|
|
free(ua); |
|
|
|
|
u_free(ua); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1206,7 +1209,7 @@ struct UASYNC* uasync_create(void) {
|
|
|
|
|
// POSIX pipe
|
|
|
|
|
if (pipe(ua->wakeup_pipe) != 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_UASYNC, "pipe() failed: %s", strerror(errno)); |
|
|
|
|
free(ua); |
|
|
|
|
u_free(ua); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
ua->wakeup_initialized = 1; |
|
|
|
|
@ -1232,10 +1235,10 @@ void uasync_print_resources(struct UASYNC* ua, const char* prefix) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
printf("\n🔍 %s: UASYNC Resource Report for %p\n", prefix, ua); |
|
|
|
|
printf(" Timer Statistics: allocated=%zu, freed=%zu, active=%zd\n", |
|
|
|
|
printf(" Timer Statistics: allocated=%zu, u_freed=%zu, active=%zd\n", |
|
|
|
|
ua->timer_alloc_count, ua->timer_free_count, |
|
|
|
|
(ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); |
|
|
|
|
printf(" Socket Statistics: allocated=%zu, freed=%zu, active=%zd\n", |
|
|
|
|
printf(" Socket Statistics: allocated=%zu, u_freed=%zu, active=%zd\n", |
|
|
|
|
ua->socket_alloc_count, ua->socket_free_count, |
|
|
|
|
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); |
|
|
|
|
|
|
|
|
|
@ -1288,10 +1291,10 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
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", |
|
|
|
|
ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Timer leak: allocated=%zu, freed=%zu, diff=%zd", |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Timer leak: allocated=%zu, u_freed=%zu, diff=%zd", |
|
|
|
|
ua->timer_alloc_count, ua->timer_free_count,
|
|
|
|
|
(ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Socket leak: allocated=%zu, freed=%zu, diff=%zd",
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Socket leak: allocated=%zu, u_freed=%zu, diff=%zd",
|
|
|
|
|
ua->socket_alloc_count, ua->socket_free_count, |
|
|
|
|
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); |
|
|
|
|
// Continue cleanup, will abort after if leaks remain
|
|
|
|
|
@ -1299,16 +1302,16 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
|
|
|
|
|
// Free all remaining timeouts
|
|
|
|
|
if (ua->timeout_heap) { |
|
|
|
|
size_t freed_count = 0; |
|
|
|
|
size_t u_freed_count = 0; |
|
|
|
|
while (1) { |
|
|
|
|
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)
|
|
|
|
|
// Free all timer nodes (avoid double-u_free bug)
|
|
|
|
|
if (node) { |
|
|
|
|
ua->timer_free_count++; |
|
|
|
|
free(node); |
|
|
|
|
u_free(node); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
timeout_heap_destroy(ua->timeout_heap); |
|
|
|
|
@ -1316,8 +1319,8 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
|
|
|
|
|
// Free all socket nodes using array approach
|
|
|
|
|
if (ua->sockets) { |
|
|
|
|
// Count and free all active sockets
|
|
|
|
|
int freed_count = 0; |
|
|
|
|
// Count and u_free all active sockets
|
|
|
|
|
int u_freed_count = 0; |
|
|
|
|
for (int i = 0; i < ua->sockets->capacity; i++) { |
|
|
|
|
if (ua->sockets->sockets[i].active) { |
|
|
|
|
if (close_fds && ua->sockets->sockets[i].fd >= 0) { |
|
|
|
|
@ -1332,10 +1335,10 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
ua->socket_free_count++; |
|
|
|
|
freed_count++; |
|
|
|
|
u_freed_count++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", u_freed_count); |
|
|
|
|
socket_array_destroy(ua->sockets); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1351,7 +1354,7 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Free cached poll_fds
|
|
|
|
|
free(ua->poll_fds); |
|
|
|
|
u_free(ua->poll_fds); |
|
|
|
|
|
|
|
|
|
// Close epoll fd on Linux
|
|
|
|
|
#if HAS_EPOLL |
|
|
|
|
@ -1364,10 +1367,10 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
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", |
|
|
|
|
ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, freed=%zu, diff=%zd", |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, u_freed=%zu, diff=%zd", |
|
|
|
|
ua->timer_alloc_count, ua->timer_free_count, |
|
|
|
|
(ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, freed=%zu, diff=%zd", |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, u_freed=%zu, diff=%zd", |
|
|
|
|
ua->socket_alloc_count, ua->socket_free_count, |
|
|
|
|
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); |
|
|
|
|
abort(); |
|
|
|
|
@ -1378,7 +1381,7 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
while (ua->posted_tasks_head) { |
|
|
|
|
struct posted_task* t = ua->posted_tasks_head; |
|
|
|
|
ua->posted_tasks_head = t->next; |
|
|
|
|
free(t); |
|
|
|
|
u_free(t); |
|
|
|
|
} |
|
|
|
|
#ifdef _WIN32 |
|
|
|
|
DeleteCriticalSection(&ua->posted_lock); |
|
|
|
|
@ -1386,7 +1389,7 @@ void uasync_destroy(struct UASYNC* ua, int close_fds) {
|
|
|
|
|
pthread_mutex_destroy(&ua->posted_lock); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
free(ua); |
|
|
|
|
u_free(ua); |
|
|
|
|
|
|
|
|
|
// Cleanup socket platform (WSACleanup on Windows)
|
|
|
|
|
socket_platform_cleanup(); |
|
|
|
|
@ -1409,18 +1412,18 @@ void uasync_init_instance(struct UASYNC* ua) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Debug statistics
|
|
|
|
|
void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) { |
|
|
|
|
void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_u_free, size_t* socket_alloc, size_t* socket_u_free) { |
|
|
|
|
if (!ua) return; |
|
|
|
|
if (timer_alloc) *timer_alloc = ua->timer_alloc_count; |
|
|
|
|
if (timer_free) *timer_free = ua->timer_free_count; |
|
|
|
|
if (timer_u_free) *timer_u_free = ua->timer_free_count; |
|
|
|
|
if (socket_alloc) *socket_alloc = ua->socket_alloc_count; |
|
|
|
|
if (socket_free) *socket_free = ua->socket_free_count; |
|
|
|
|
if (socket_u_free) *socket_u_free = ua->socket_free_count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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)); |
|
|
|
|
struct posted_task* task = u_malloc(sizeof(struct posted_task)); |
|
|
|
|
if (!task) return; |
|
|
|
|
|
|
|
|
|
task->callback = callback; |
|
|
|
|
@ -1432,15 +1435,22 @@ void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* arg)
|
|
|
|
|
#else |
|
|
|
|
pthread_mutex_lock(&ua->posted_lock); |
|
|
|
|
#endif |
|
|
|
|
task->next = ua->posted_tasks_head; |
|
|
|
|
ua->posted_tasks_head = task; |
|
|
|
|
|
|
|
|
|
if (ua->posted_tasks_tail) { |
|
|
|
|
// есть конец списка — добавляем туда
|
|
|
|
|
ua->posted_tasks_tail->next = task; |
|
|
|
|
ua->posted_tasks_tail = task; |
|
|
|
|
} else { |
|
|
|
|
// список пустой — это первая задача
|
|
|
|
|
ua->posted_tasks_head = ua->posted_tasks_tail = task; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef _WIN32 |
|
|
|
|
LeaveCriticalSection(&ua->posted_lock); |
|
|
|
|
#else |
|
|
|
|
pthread_mutex_unlock(&ua->posted_lock); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "POST: wakeup send");
|
|
|
|
|
uasync_wakeup(ua); // будим mainloop
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1466,7 +1476,7 @@ int uasync_get_wakeup_fd(struct UASYNC* ua) {
|
|
|
|
|
return ua->wakeup_pipe[1]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Lookup socket by file descriptor - returns current pointer even after realloc */ |
|
|
|
|
/* Lookup socket by file descriptor - returns current pointer even after u_realloc */ |
|
|
|
|
int uasync_lookup_socket(struct UASYNC* ua, int fd, void** socket_id) { |
|
|
|
|
if (!ua || !ua->sockets || !socket_id || fd < 0 || fd >= FD_SETSIZE) { |
|
|
|
|
return -1; |
|
|
|
|
|