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

5
lib/timeout_heap.h

@ -11,6 +11,7 @@ typedef uint64_t TimeoutTime; // e.g., milliseconds since epoch or from now
typedef struct {
TimeoutTime expiration; // Sort key (smaller = earlier)
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
} 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.
* @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.
@ -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_at(TimeoutHeap *h, size_t index, void *data);
/**
* Get the number of freed timer nodes.
* @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
#define UASYNC_H
#include "platform_compat.h"
#include <stddef.h>
#include "timeout_heap.h"
#include "platform_compat.h"
#include <stddef.h>
#include <signal.h>
#include "timeout_heap.h"
#include "socket_compat.h"
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
@ -66,8 +67,9 @@ struct UASYNC {
#ifdef _WIN32
CRITICAL_SECTION posted_lock;
#else
pthread_mutex_t posted_lock;
#endif
pthread_mutex_t posted_lock;
#endif
volatile sig_atomic_t stop;
};
// Type definitions
@ -103,6 +105,7 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb);
// Mainloop (бесконечный цикл, __noreturn)
void uasync_mainloop(struct UASYNC* ua);
void uasync_stop(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);

2
tests/bench_timeout_heap.c

@ -54,7 +54,7 @@ int main(void) {
// Benchmark 1: 100 random pushes
clock_gettime(CLOCK_MONOTONIC, &start);
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);
timeout_heap_destroy(heap);
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) {
struct ordering_ctx* ctx = (struct ordering_ctx*)arg;
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) {

Loading…
Cancel
Save