diff --git a/events.c b/events.c index 36fc738..48c18a4 100644 --- a/events.c +++ b/events.c @@ -1,49 +1,26 @@ #include "events.h" #include -#include +#include -// internal callback callback -struct ecallback { - void (*cb)(void*); - void *data; -}; - -static void ecallback_dispatch(void *p) { - struct ecallback *e = (struct ecallback*)p; - e->cb(e->data); -} - -// equeue functions -static inline struct event *equeue_event(struct equeue *q, unsigned i) { - return (struct event*)((char*)q->buffer + i*q->size); -} - -static inline unsigned equeue_size(unsigned size) { - if (size < sizeof(struct ecallback)) { - size = sizeof(struct ecallback); - } - - unsigned alignment = offsetof(struct { char c; struct event e; }, e); - size += sizeof(struct event); - return (size + alignment-1) & ~(alignment-1); -} - -int equeue_create(struct equeue *q, unsigned count, unsigned size) { - void *buffer = malloc(count * equeue_size(size)); +int equeue_create(struct equeue *q, unsigned size) { + void *buffer = malloc(size); if (!buffer) { return -1; } - return equeue_create_inplace(q, count, size, buffer); + int err = equeue_create_inplace(q, size, buffer); + q->buffer = buffer; + return err; } -int equeue_create_inplace(struct equeue *q, - unsigned count, unsigned size, void *buffer) { - q->size = equeue_size(size); - q->buffer = buffer; - q->free = (struct event*)buffer; +int equeue_create_inplace(struct equeue *q, unsigned size, void *buffer) { + q->slab.size = size; + q->slab.data = buffer; + q->chunks = 0; + q->buffer = 0; + q->queue = 0; q->next_id = 42; q->break_ = (struct event){ @@ -51,13 +28,6 @@ int equeue_create_inplace(struct equeue *q, .period = -1, }; - if (q->free) { - for (unsigned i = 0; i < count-1; i++) { - equeue_event(q, i)->next = equeue_event(q, i+1); - } - equeue_event(q, count-1)->next = 0; - } - int err; err = events_sema_create(&q->eventsema); if (err < 0) { @@ -84,8 +54,67 @@ void equeue_destroy(struct equeue *q) { free(q->buffer); } -// equeue mem functions -static int equeue_next_id(struct equeue *q) { +// equeue allocation functions +static void *equeue_alloc(struct equeue *q, unsigned size) { + size = size + sizeof(unsigned); + size = (size + sizeof(unsigned)-1) & ~(sizeof(unsigned)-1); + if (size < sizeof(struct equeue_chunk)) { + size = sizeof(struct equeue_chunk); + } + + events_mutex_lock(&q->freelock); + + for (struct equeue_chunk **p = &q->chunks; *p; p = &(*p)->nchunk) { + if ((*p)->size >= size) { + struct equeue_chunk *c = *p; + if (c->next) { + *p = c->next; + (*p)->nchunk = c->nchunk; + } else { + *p = c->nchunk; + } + events_mutex_unlock(&q->freelock); + return (unsigned *)c + 1; + } + } + + if (q->slab.size >= size) { + struct equeue_chunk *c = (struct equeue_chunk *)q->slab.data; + q->slab.data += size; + q->slab.size -= size; + c->size = size; + events_mutex_unlock(&q->freelock); + return (unsigned *)c + 1; + } + + events_mutex_unlock(&q->freelock); + return 0; +} + +static void equeue_dealloc(struct equeue *q, void *e) { + struct equeue_chunk *c = (struct equeue_chunk *)((unsigned *)e - 1); + + events_mutex_lock(&q->freelock); + + struct equeue_chunk **p = &q->chunks; + while (*p && (*p)->size < c->size) { + p = &(*p)->nchunk; + } + + if (*p && (*p)->size == c->size) { + c->next = *p; + c->nchunk = (*p)->nchunk; + } else { + c->next = 0; + c->nchunk = *p; + } + *p = c; + + events_mutex_unlock(&q->freelock); +} + +// event allocation functions +static inline int event_next_id(struct equeue *q) { int id = q->next_id++; if (q->next_id < 0) { q->next_id = 42; @@ -93,39 +122,32 @@ static int equeue_next_id(struct equeue *q) { return id; } -static struct event *equeue_alloc(struct equeue *q) { - struct event *e = 0; - - events_mutex_lock(&q->freelock); - if (!q->free) { - events_mutex_unlock(&q->freelock); +void *event_alloc(struct equeue *q, unsigned size) { + struct event *e = equeue_alloc(q, sizeof(struct event) + size); + if (!e) { return 0; } - e = q->free; - q->free = e->next; - events_mutex_unlock(&q->freelock); - - e->id = equeue_next_id(q); + e->id = event_next_id(q); e->target = 0; e->period = -1; e->dtor = 0; - return e; + + return e + 1; } -static void equeue_dealloc(struct equeue *q, struct event *e) { +void event_dealloc(struct equeue *q, void *p) { + struct event *e = (struct event*)p - 1; + if (e->dtor) { e->dtor(e+1); } - events_mutex_lock(&q->freelock); - e->next = q->free; - q->free = e; - events_mutex_unlock(&q->freelock); + equeue_dealloc(q, e); } // equeue scheduling functions -static inline int tickdiff(unsigned a, unsigned b) { +static inline int equeue_tickdiff(unsigned a, unsigned b) { return (int)(a - b); } @@ -133,7 +155,7 @@ static int equeue_enqueue(struct equeue *q, struct event *e, int ms) { e->target = events_tick() + (unsigned)ms; struct event **p = &q->queue; - while (*p && tickdiff((*p)->target, e->target) <= 0) { + while (*p && equeue_tickdiff((*p)->target, e->target) <= 0) { p = &(*p)->next; } @@ -169,7 +191,7 @@ static void equeue_cancel(struct equeue *q, int id) { events_mutex_unlock(&q->queuelock); if (e) { - equeue_dealloc(q, e); + event_dealloc(q, e+1); } } @@ -194,7 +216,7 @@ void equeue_dispatch(struct equeue *q, int ms) { break; } - deadline = tickdiff(q->queue->target, events_tick()); + deadline = equeue_tickdiff(q->queue->target, events_tick()); if (deadline > 0) { events_mutex_unlock(&q->queuelock); break; @@ -218,7 +240,7 @@ void equeue_dispatch(struct equeue *q, int ms) { e->cb(e + 1); if (e->period < 0) { - equeue_dealloc(q, e); + event_dealloc(q, e+1); } } @@ -227,25 +249,6 @@ void equeue_dispatch(struct equeue *q, int ms) { } // event functions -void *event_alloc(struct equeue *q, unsigned size) { - if (size > q->size - sizeof(struct event)) { - return 0; - } - - struct event *e = equeue_alloc(q); - if (!e) { - return 0; - } - - return e + 1; -} - -void event_dealloc(struct equeue *q, void *p) { - struct event *e = (struct event*)p - 1; - equeue_dealloc(q, e); -} - -// configuring events void event_delay(void *p, int ms) { struct event *e = (struct event*)p - 1; e->target = ms; @@ -273,7 +276,17 @@ void event_cancel(struct equeue *q, int id) { return equeue_cancel(q, id); } -// event helper functions +// simple callbacks +struct ecallback { + void (*cb)(void*); + void *data; +}; + +static void ecallback_dispatch(void *p) { + struct ecallback *e = (struct ecallback*)p; + e->cb(e->data); +} + int event_call(struct equeue *q, void (*cb)(void*), void *data) { struct ecallback *e = event_alloc(q, sizeof(struct ecallback)); if (!e) { diff --git a/events.h b/events.h index dcf3130..606e8e6 100644 --- a/events.h +++ b/events.h @@ -27,12 +27,20 @@ struct event { }; struct equeue { - unsigned size; struct event *queue; - struct event *free; - void *buffer; int next_id; + void *buffer; + struct equeue_chunk { + unsigned size; + struct equeue_chunk *next; + struct equeue_chunk *nchunk; + } *chunks; + struct equeue_slab { + unsigned size; + unsigned char *data; + } slab; + struct event break_; events_sema_t eventsema; @@ -43,9 +51,8 @@ struct equeue { // Queue operations // // Creation results in negative value on failure. -int equeue_create(struct equeue*, unsigned count, unsigned size); -int equeue_create_inplace(struct equeue*, - unsigned count, unsigned size, void *buffer); +int equeue_create(struct equeue*, unsigned size); +int equeue_create_inplace(struct equeue*, unsigned size, void *buffer); void equeue_destroy(struct equeue*); // Dispatch events