Skip to content

Commit

Permalink
Refactor JSMallocFunctions to simplify the implementation
Browse files Browse the repository at this point in the history
Rather than having the user take care of JSMallocState, take care of the
bookkeeping internally (and make JSMallocState non-public since it's no
longer necessary) and keep the allocation functions to the bare minimum.

This has the advantage that using a different allocator is just a few
lines of code, and there is no need to copy the default implementation
just to moficy the call to the allocation function.

Fixes: #285
  • Loading branch information
saghul committed Sep 12, 2024
1 parent fb70e09 commit 1762745
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 154 deletions.
81 changes: 12 additions & 69 deletions qjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,6 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt)
return ctx;
}

#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
#define MALLOC_OVERHEAD 8
#endif

struct trace_malloc_data {
uint8_t *base;
};
Expand All @@ -188,7 +182,7 @@ __attribute__((format(gnu_printf, 2, 3)))
#else
__attribute__((format(printf, 2, 3)))
#endif
js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
js_trace_malloc_printf(void *opaque, const char *fmt, ...)
{
va_list ap;
int c;
Expand All @@ -203,7 +197,7 @@ __attribute__((format(printf, 2, 3)))
printf("NULL");
} else {
printf("H%+06lld.%zd",
js_trace_malloc_ptr_offset(ptr, s->opaque),
js_trace_malloc_ptr_offset(ptr, opaque),
js__malloc_usable_size(ptr));
}
fmt++;
Expand All @@ -226,87 +220,36 @@ static void js_trace_malloc_init(struct trace_malloc_data *s)
free(s->base = malloc(8));
}

static void *js_trace_calloc(JSMallocState *s, size_t count, size_t size)
static void *js_trace_calloc(void *opaque, size_t count, size_t size)
{
void *ptr;

/* Do not allocate zero bytes: behavior is platform dependent */
assert(count != 0 && size != 0);

if (size > 0)
if (unlikely(count != (count * size) / size))
return NULL;

/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1))
return NULL;
ptr = calloc(count, size);
js_trace_malloc_printf(s, "C %zd %zd -> %p\n", count, size, ptr);
if (ptr) {
s->malloc_count++;
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
}
js_trace_malloc_printf(opaque, "C %zd %zd -> %p\n", count, size, ptr);
return ptr;
}

static void *js_trace_malloc(JSMallocState *s, size_t size)
static void *js_trace_malloc(void *opaque, size_t size)
{
(void) opaque;
void *ptr;

/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);

/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (unlikely(s->malloc_size + size > s->malloc_limit - 1))
return NULL;
ptr = malloc(size);
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
if (ptr) {
s->malloc_count++;
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
}
js_trace_malloc_printf(opaque, "A %zd -> %p\n", size, ptr);
return ptr;
}

static void js_trace_free(JSMallocState *s, void *ptr)
static void js_trace_free(void *opaque, void *ptr)
{
if (!ptr)
return;

js_trace_malloc_printf(s, "F %p\n", ptr);
s->malloc_count--;
s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
js_trace_malloc_printf(opaque, "F %p\n", ptr);
free(ptr);
}

static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
static void *js_trace_realloc(void *opaque, void *ptr, size_t size)
{
size_t old_size;

if (!ptr) {
if (size == 0)
return NULL;
return js_trace_malloc(s, size);
}

if (unlikely(size == 0)) {
js_trace_free(s, ptr);
return NULL;
}

old_size = js__malloc_usable_size(ptr);

/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (s->malloc_size + size - old_size > s->malloc_limit - 1)
return NULL;

js_trace_malloc_printf(s, "R %zd %p", size, ptr);

js_trace_malloc_printf(opaque, "R %zd %p", size, ptr);
ptr = realloc(ptr, size);
js_trace_malloc_printf(s, " -> %p\n", ptr);
if (ptr) {
s->malloc_size += js__malloc_usable_size(ptr) - old_size;
}
js_trace_malloc_printf(opaque, " -> %p\n", ptr);
return ptr;
}

Expand Down
171 changes: 97 additions & 74 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ typedef enum {
JS_GC_PHASE_REMOVE_CYCLES,
} JSGCPhaseEnum;

typedef struct JSMallocState {
size_t malloc_count;
size_t malloc_size;
size_t malloc_limit;
void *opaque; /* user opaque */
} JSMallocState;

struct JSRuntime {
JSMallocFunctions mf;
JSMallocState malloc_state;
Expand Down Expand Up @@ -1380,22 +1387,91 @@ static size_t js_malloc_usable_size_unknown(const void *ptr)

void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size)
{
return rt->mf.js_calloc(&rt->malloc_state, count, size);
void *ptr;
JSMallocState *s;

/* Do not allocate zero bytes: behavior is platform dependent */
assert(count != 0 && size != 0);

if (size > 0)
if (unlikely(count != (count * size) / size))
return NULL;

s = &rt->malloc_state;
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1))
return NULL;

ptr = rt->mf.js_calloc(s->opaque, count, size);
if (!ptr)
return NULL;

s->malloc_count++;
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
return ptr;
}

void *js_malloc_rt(JSRuntime *rt, size_t size)
{
return rt->mf.js_malloc(&rt->malloc_state, size);
void *ptr;
JSMallocState *s;

/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);

s = &rt->malloc_state;
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (unlikely(s->malloc_size + size > s->malloc_limit - 1))
return NULL;

ptr = rt->mf.js_malloc(s->opaque, size);
if (!ptr)
return NULL;

s->malloc_count++;
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
return ptr;
}

void js_free_rt(JSRuntime *rt, void *ptr)
{
rt->mf.js_free(&rt->malloc_state, ptr);
JSMallocState *s;

if (!ptr)
return;

s = &rt->malloc_state;
s->malloc_count--;
s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
rt->mf.js_free(s->opaque, ptr);
}

void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
{
return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
size_t old_size;
JSMallocState *s;

if (!ptr) {
if (size == 0)
return NULL;
return js_malloc_rt(rt, size);
}
if (unlikely(size == 0)) {
js_free_rt(rt, ptr);
return NULL;
}
old_size = js__malloc_usable_size(ptr);
s = &rt->malloc_state;
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (s->malloc_size + size - old_size > s->malloc_limit - 1)
return NULL;

ptr = rt->mf.js_realloc(s->opaque, ptr, size);
if (!ptr)
return NULL;

s->malloc_size += js__malloc_usable_size(ptr) - old_size;
return ptr;
}

static void *js_dbuf_realloc(void *opaque, void *ptr, size_t size)
Expand Down Expand Up @@ -1651,9 +1727,12 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
ms.opaque = opaque;
ms.malloc_limit = 0;

rt = mf->js_calloc(&ms, 1, sizeof(JSRuntime));
rt = mf->js_calloc(opaque, 1, sizeof(JSRuntime));
if (!rt)
return NULL;
/* Inline what js_malloc_rt does since we cannot use it here. */
ms.malloc_count++;
ms.malloc_size += js__malloc_usable_size(rt) + MALLOC_OVERHEAD;
rt->mf = *mf;
if (!rt->mf.js_malloc_usable_size) {
/* use dummy function if none provided */
Expand Down Expand Up @@ -1718,84 +1797,28 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
rt->user_opaque = opaque;
}

static void *js_def_calloc(JSMallocState *s, size_t count, size_t size)
static void *js_def_calloc(void *opaque, size_t count, size_t size)
{
void *ptr;

/* Do not allocate zero bytes: behavior is platform dependent */
assert(count != 0 && size != 0);

if (size > 0)
if (unlikely(count != (count * size) / size))
return NULL;

/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1))
return NULL;

ptr = calloc(count, size);
if (!ptr)
return NULL;

s->malloc_count++;
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
return ptr;
(void) opaque;
return calloc(count, size);
}

static void *js_def_malloc(JSMallocState *s, size_t size)
static void *js_def_malloc(void *opaque, size_t size)
{
void *ptr;

/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);

/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (unlikely(s->malloc_size + size > s->malloc_limit - 1))
return NULL;

ptr = malloc(size);
if (!ptr)
return NULL;

s->malloc_count++;
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
return ptr;
(void) opaque;
return malloc(size);
}

static void js_def_free(JSMallocState *s, void *ptr)
static void js_def_free(void *opaque, void *ptr)
{
if (!ptr)
return;

s->malloc_count--;
s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
(void) opaque;
free(ptr);
}

static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
static void *js_def_realloc(void *opaque, void *ptr, size_t size)
{
size_t old_size;

if (!ptr) {
if (size == 0)
return NULL;
return js_def_malloc(s, size);
}
if (unlikely(size == 0)) {
js_def_free(s, ptr);
return NULL;
}
old_size = js__malloc_usable_size(ptr);
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
if (s->malloc_size + size - old_size > s->malloc_limit - 1)
return NULL;

ptr = realloc(ptr, size);
if (!ptr)
return NULL;

s->malloc_size += js__malloc_usable_size(ptr) - old_size;
return ptr;
(void) opaque;
return realloc(ptr, size);
}

static const JSMallocFunctions def_malloc_funcs = {
Expand Down Expand Up @@ -2159,8 +2182,8 @@ void JS_FreeRuntime(JSRuntime *rt)
#endif

{
JSMallocState ms = rt->malloc_state;
rt->mf.js_free(&ms, rt);
JSMallocState *ms = &rt->malloc_state;
rt->mf.js_free(ms->opaque, rt);
}
}

Expand Down
15 changes: 4 additions & 11 deletions quickjs.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,18 +283,11 @@ typedef JSValue JSCFunction(JSContext *ctx, JSValue this_val, int argc, JSValue
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic);
typedef JSValue JSCFunctionData(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic, JSValue *func_data);

typedef struct JSMallocState {
size_t malloc_count;
size_t malloc_size;
size_t malloc_limit;
void *opaque; /* user opaque */
} JSMallocState;

typedef struct JSMallocFunctions {
void *(*js_calloc)(JSMallocState *s, size_t count, size_t size);
void *(*js_malloc)(JSMallocState *s, size_t size);
void (*js_free)(JSMallocState *s, void *ptr);
void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
void *(*js_calloc)(void *opaque, size_t count, size_t size);
void *(*js_malloc)(void *opaque, size_t size);
void (*js_free)(void *opaque, void *ptr);
void *(*js_realloc)(void *opaque, void *ptr, size_t size);
size_t (*js_malloc_usable_size)(const void *ptr);
} JSMallocFunctions;

Expand Down

0 comments on commit 1762745

Please sign in to comment.