Skip to content

Commit

Permalink
Added specialized autochunking allocator to support dynamic allocations
Browse files Browse the repository at this point in the history
The autochunking allocator has some interesting properties:
 - O(1) allocation and deallocation
 - zero fragmentation for same-sized allocations
 - no external fragmentation over time

The biggest downside of the allocator is that the allocator performs no
coalescing. This can create excessive fragmentation when used with a
large number of different-sized allocation.
  • Loading branch information
geky committed Jun 22, 2016
1 parent f83507a commit d8bdcdf
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 91 deletions.
176 changes: 91 additions & 85 deletions events.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,33 @@

#include <stdlib.h>
#include <stddef.h>
#include <string.h>


// 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;
memset(q->chunks, 0, EVENT_CHUNK_LISTS*sizeof(struct event*));
q->buffer = 0;

q->queue = 0;
q->next_id = 42;
q->break_ = (struct event){
.id = 0,
.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) {
Expand All @@ -84,56 +55,100 @@ void equeue_destroy(struct equeue *q) {
free(q->buffer);
}

// equeue mem functions
static int equeue_next_id(struct equeue *q) {
// equeue allocation functions
static inline unsigned equeue_size(unsigned size) {
size += sizeof(struct event);
unsigned alignment = offsetof(struct { char c; struct event e; }, e);
return (size + alignment-1) & ~(alignment-1);
}

static struct event *equeue_alloc(struct equeue *q, unsigned size) {
size = equeue_size(size);

events_mutex_lock(&q->freelock);

for (int i = 0; i < EVENT_CHUNK_LISTS; i++) {
if (q->chunks[i] && q->chunks[i]->size >= size) {
struct event *e = q->chunks[i];
q->chunks[i] = e->next;
events_mutex_unlock(&q->freelock);
return e;
}
}

if (q->slab.size >= size) {
struct event *e = (struct event *)q->slab.data;
q->slab.data += size;
q->slab.size -= size;
e->size = size;
events_mutex_unlock(&q->freelock);
return e;
}

events_mutex_unlock(&q->freelock);
return 0;
}

static void equeue_dealloc(struct equeue *q, struct event *e) {
int i = 0;

events_mutex_lock(&q->freelock);

for (; i < EVENT_CHUNK_LISTS-1; i++) {
if (q->chunks[i+1] && q->chunks[i+1]->size >= e->size) {
break;
}
}

e->next = q->chunks[i];
q->chunks[i] = e;

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;
}
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, 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);
}

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;
}

Expand Down Expand Up @@ -169,7 +184,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);
}
}

Expand All @@ -194,7 +209,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;
Expand All @@ -218,7 +233,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);
}
}

Expand All @@ -227,25 +242,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;
Expand Down Expand Up @@ -273,7 +269,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) {
Expand Down
19 changes: 13 additions & 6 deletions events.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ extern "C" {
#include "sys/events_sema.h"


// Number of free-chunk lists per equeue
#define EVENT_CHUNK_LISTS 4

// Event/queue structures
struct event {
unsigned size;
struct event *next;
int id;
unsigned target;
Expand All @@ -27,12 +31,16 @@ struct event {
};

struct equeue {
unsigned size;
struct event *queue;
struct event *free;
void *buffer;
int next_id;

void *buffer;
struct event *chunks[EVENT_CHUNK_LISTS];
struct {
unsigned size;
unsigned char *data;
} slab;

struct event break_;

events_sema_t eventsema;
Expand All @@ -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
Expand Down

0 comments on commit d8bdcdf

Please sign in to comment.