Browse Source

update timeout_heap and u_async: O(1) cancel, heap cancel bubble-up fix, epoll callback safety, uasync_stop, socket_array u_realloc

congestion
Evgeny 5 months ago
parent
commit
8994d7a834
  1. 184
      lib/timeout_heap.c
  2. 5
      lib/timeout_heap.h
  3. 843
      lib/u_async.c
  4. 13
      lib/u_async.h
  5. 2
      tests/bench_timeout_heap.c
  6. 2
      tests/test_u_async_comprehensive.c

184
lib/timeout_heap.c

@ -11,21 +11,21 @@
#define LEFT_CHILD(i) (2 * (i)) #define LEFT_CHILD(i) (2 * (i))
#define RIGHT_CHILD(i) (2 * (i) + 1) #define RIGHT_CHILD(i) (2 * (i) + 1)
TimeoutHeap *timeout_heap_create(size_t initial_capacity) { TimeoutHeap *timeout_heap_create(size_t initial_capacity) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating TH1..."); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Creating TH1...");
TimeoutHeap *h = u_malloc(sizeof(TimeoutHeap)); TimeoutHeap *h = u_malloc(sizeof(TimeoutHeap));
if (!h) { if (!h) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "TH0 error..."); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "TH0 error...");
return NULL; return NULL;
} }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating TH2..."); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Creating TH2...");
h->heap = u_malloc(sizeof(TimeoutEntry) * initial_capacity); h->heap = u_malloc(sizeof(TimeoutEntry) * initial_capacity);
if (!h->heap) { if (!h->heap) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "TH1 error..."); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "TH1 error...");
u_free(h); u_free(h);
return NULL; return NULL;
} }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating TH3..."); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Creating TH3...");
h->size = 0; h->size = 0;
h->capacity = initial_capacity; h->capacity = initial_capacity;
h->freed_count = 0; h->freed_count = 0;
@ -50,70 +50,81 @@ void timeout_heap_destroy(TimeoutHeap *h) {
} }
static void bubble_up(TimeoutHeap *h, size_t i) { static void update_index(TimeoutHeap *h, size_t idx) {
// i is 1-based if (h->heap[idx].index_ptr)
while (i > 1 && h->heap[PARENT(i) - 1].expiration > h->heap[i - 1].expiration) { *h->heap[idx].index_ptr = idx;
// Swap with parent }
TimeoutEntry temp = h->heap[PARENT(i) - 1];
h->heap[PARENT(i) - 1] = h->heap[i - 1]; static void swap_entries(TimeoutHeap *h, size_t a, size_t b) {
h->heap[i - 1] = temp; TimeoutEntry temp = h->heap[a];
i = PARENT(i); h->heap[a] = h->heap[b];
} h->heap[b] = temp;
update_index(h, a);
update_index(h, b);
}
static void bubble_up(TimeoutHeap *h, size_t i) {
// i is 1-based
while (i > 1 && h->heap[PARENT(i) - 1].expiration > h->heap[i - 1].expiration) {
swap_entries(h, PARENT(i) - 1, i - 1);
i = PARENT(i);
}
} }
int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data) { int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data, size_t *index_ptr) {
if (h->size == h->capacity) { if (h->size == h->capacity) {
size_t new_cap = h->capacity ? h->capacity * 2 : 1; size_t new_cap = h->capacity ? h->capacity * 2 : 1;
TimeoutEntry *new_heap = u_realloc(h->heap, sizeof(TimeoutEntry) * new_cap); TimeoutEntry *new_heap = u_realloc(h->heap, sizeof(TimeoutEntry) * new_cap);
if (!new_heap) return -1; // Allocation failed if (!new_heap) return -1; // Allocation failed
h->heap = new_heap; h->heap = new_heap;
h->capacity = new_cap; h->capacity = new_cap;
} }
// Insert at end (0-based) // Insert at end (0-based)
size_t idx = h->size++; size_t idx = h->size++;
h->heap[idx].expiration = expiration; h->heap[idx].expiration = expiration;
h->heap[idx].data = data; h->heap[idx].data = data;
h->heap[idx].deleted = 0; h->heap[idx].index_ptr = index_ptr;
h->heap[idx].deleted = 0;
// Bubble up (1-based) if (index_ptr)
bubble_up(h, idx + 1); *index_ptr = idx;
return 0;
// Bubble up (1-based)
bubble_up(h, idx + 1);
return 0;
} }
static void heapify_down(TimeoutHeap *h, size_t i) { static void heapify_down(TimeoutHeap *h, size_t i) {
// i is 1-based // i is 1-based
while (1) { while (1) {
size_t smallest = i; size_t smallest = i;
size_t left = LEFT_CHILD(i); size_t left = LEFT_CHILD(i);
size_t right = RIGHT_CHILD(i); size_t right = RIGHT_CHILD(i);
if (left <= h->size && h->heap[left - 1].expiration < h->heap[smallest - 1].expiration) { if (left <= h->size && h->heap[left - 1].expiration < h->heap[smallest - 1].expiration) {
smallest = left; smallest = left;
} }
if (right <= h->size && h->heap[right - 1].expiration < h->heap[smallest - 1].expiration) { if (right <= h->size && h->heap[right - 1].expiration < h->heap[smallest - 1].expiration) {
smallest = right; smallest = right;
} }
if (smallest == i) break; if (smallest == i) break;
// Swap swap_entries(h, smallest - 1, i - 1);
TimeoutEntry temp = h->heap[smallest - 1]; i = smallest;
h->heap[smallest - 1] = h->heap[i - 1]; }
h->heap[i - 1] = temp;
i = smallest;
}
} }
static void remove_root(TimeoutHeap *h) { static void remove_root(TimeoutHeap *h) {
if (h->size == 0) return; if (h->size == 0) return;
// Move last to root // Move last to root
h->heap[0] = h->heap[--h->size]; h->heap[0] = h->heap[--h->size];
update_index(h, 0);
// Heapify down (1-based)
if (h->size > 0) { // Heapify down (1-based)
heapify_down(h, 1); if (h->size > 0) {
} heapify_down(h, 1);
}
} }
int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out) { int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out) {
@ -149,14 +160,25 @@ int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out) {
return 0; return 0;
} }
int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data) { int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data) {
for (size_t i = 0; i < h->size; ++i) { for (size_t i = 0; i < h->size; ++i) {
if (h->heap[i].expiration == expiration && h->heap[i].data == data) { if (h->heap[i].expiration == expiration && h->heap[i].data == data) {
h->heap[i].deleted = 1; h->heap[i].deleted = 1;
return 0; h->heap[i].expiration = 0;
} bubble_up(h, i + 1);
} return 0;
return -1; // Not found }
}
return -1; // Not found
}
int timeout_heap_cancel_at(TimeoutHeap *h, size_t index, void *data) {
if (index >= h->size || h->heap[index].data != data) return -1;
h->heap[index].deleted = 1;
h->heap[index].expiration = 0;
bubble_up(h, index + 1);
return 0;
} }
void timeout_heap_set_free_callback(TimeoutHeap *h, void* user_data, void (*callback)(void* user_data, void* data)) { void timeout_heap_set_free_callback(TimeoutHeap *h, void* user_data, void (*callback)(void* user_data, void* data)) {

5
lib/timeout_heap.h

@ -11,6 +11,7 @@ typedef uint64_t TimeoutTime; // e.g., milliseconds since epoch or from now
typedef struct { typedef struct {
TimeoutTime expiration; // Sort key (smaller = earlier) TimeoutTime expiration; // Sort key (smaller = earlier)
void *data; // User data (e.g., callback or ID) void *data; // User data (e.g., callback or ID)
size_t *index_ptr; // Pointer to node's heap_index (NULL = no tracking)
int deleted; // 0 = active, 1 = deleted int deleted; // 0 = active, 1 = deleted
} TimeoutEntry; } TimeoutEntry;
@ -53,7 +54,7 @@ void timeout_heap_set_free_callback(TimeoutHeap *h, void* user_data, void (*call
* @param data User data associated with the timeout. * @param data User data associated with the timeout.
* @return 0 on success, -1 on allocation failure. * @return 0 on success, -1 on allocation failure.
*/ */
int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data); int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data, size_t *index_ptr);
/** /**
* Peek at the earliest non-deleted timeout without removing it. * Peek at the earliest non-deleted timeout without removing it.
@ -82,6 +83,8 @@ int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out);
*/ */
int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data); int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data);
int timeout_heap_cancel_at(TimeoutHeap *h, size_t index, void *data);
/** /**
* Get the number of freed timer nodes. * Get the number of freed timer nodes.
* @param h The heap. * @param h The heap.

843
lib/u_async.c

File diff suppressed because it is too large Load Diff

13
lib/u_async.h

@ -5,9 +5,10 @@
#ifndef UASYNC_H #ifndef UASYNC_H
#define UASYNC_H #define UASYNC_H
#include "platform_compat.h" #include "platform_compat.h"
#include <stddef.h> #include <stddef.h>
#include "timeout_heap.h" #include <signal.h>
#include "timeout_heap.h"
#include "socket_compat.h" #include "socket_compat.h"
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
@ -66,8 +67,9 @@ struct UASYNC {
#ifdef _WIN32 #ifdef _WIN32
CRITICAL_SECTION posted_lock; CRITICAL_SECTION posted_lock;
#else #else
pthread_mutex_t posted_lock; pthread_mutex_t posted_lock;
#endif #endif
volatile sig_atomic_t stop;
}; };
// Type definitions // Type definitions
@ -103,6 +105,7 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb);
// Mainloop (бесконечный цикл, __noreturn) // Mainloop (бесконечный цикл, __noreturn)
void uasync_mainloop(struct UASYNC* ua); void uasync_mainloop(struct UASYNC* ua);
void uasync_stop(struct UASYNC* ua);
// Debug statistics // 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_free, size_t* socket_alloc, size_t* socket_free);

2
tests/bench_timeout_heap.c

@ -54,7 +54,7 @@ int main(void) {
// Benchmark 1: 100 random pushes // Benchmark 1: 100 random pushes
clock_gettime(CLOCK_MONOTONIC, &start); clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < NUM_OPERATIONS; i++) { for (i = 0; i < NUM_OPERATIONS; i++) {
if (timeout_heap_push(heap, timeouts[i], data[i]) != 0) { if (timeout_heap_push(heap, timeouts[i], data[i], NULL) != 0) {
fprintf(stderr, "Push failed at iteration %d\n", i); fprintf(stderr, "Push failed at iteration %d\n", i);
timeout_heap_destroy(heap); timeout_heap_destroy(heap);
return 1; return 1;

2
tests/test_u_async_comprehensive.c

@ -267,7 +267,7 @@ static void order_immediate_cb(void* arg);
static void order_delayed_cb(void* arg) { static void order_delayed_cb(void* arg) {
struct ordering_ctx* ctx = (struct ordering_ctx*)arg; struct ordering_ctx* ctx = (struct ordering_ctx*)arg;
ctx->seq[ctx->idx++] = 1; ctx->seq[ctx->idx++] = 1;
if (ctx->idx == 1) uasync_set_timeout(ctx->ua, 0, ctx, order_immediate_cb, "test_order_imm"); if (ctx->idx == 1) uasync_call_soon(ctx->ua, ctx, order_immediate_cb);
} }
static void order_immediate_cb(void* arg) { static void order_immediate_cb(void* arg) {

Loading…
Cancel
Save