diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2021-08-17 19:32:12 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2021-08-17 19:42:06 -0700 |
commit | 22ea32f0f945509888a3d0d5167b5d4ed001a3d9 (patch) | |
tree | e43ed439c7372eb9cde08b4647cf58456c84cfc3 | |
parent | 070e8c57d5829e9b429f698c329a6d0b0d4d071c (diff) |
thunk: introduce thunk_t.free and thunk_free()
While it was convenient to support bare libc free()ing of thunks
wherever appropriate, it prevented any form of caching thunk
encapsulating environments.
This commit introduces thunk_free() for freeing thunk_t
instances, and adds a .free() member to the thunk for registering
a thunk-specific free method.
For plain thunk instantiating, where the environment size is
known and fixed for a given thunk, a very simple free list has
been added. This added a free_next pointer to the thunk's
environment struct, and a thunk-specific free method putting the
instance's environment back on the thunk-specific free list. No
shrinking is ever done currently, and there's never cleanup of
any of this, it's assumed these will be relatively bounded,
leveling off at a high watermark, and simply abandoned at process
exit for now.
For split allocated-initialized thunk instances, no caching is
attempted for now. This could be trivially improved at least for
non-payload-utilizing cases, as these would be the same size as
the plain instantiated thunks so they could just use the same
free lists. For the payload-utilizing cases it'd be more tricky,
in the interests of simplicity for now, all the split cases just
use malloc()+free() as before, but via the thunk_t.free() method.
This commit should already significantly reduce the amount of
malloc() and free() in most cases.
-rw-r--r-- | thunk.h | 67 |
1 files changed, 53 insertions, 14 deletions
@@ -27,6 +27,7 @@ typedef struct thunk_t thunk_t; struct thunk_t { int (*dispatch)(thunk_t *); + void (*free)(thunk_t *); }; /* Clever macro args counting from https://gcc.gnu.org/ml/gcc/2000-09/msg00604.html */ @@ -446,12 +447,14 @@ struct thunk_t { static thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size) __attribute__ ((unused));\ static thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) __attribute__ ((unused));\ static thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__) __attribute__ ((unused));\ + static struct __thunk_environment_##_name *__thunk_environment_cache_##_name;\ \ typedef struct __thunk_environment_##_name { \ /* struct for encapsulating the calling environment */ \ - thunk_t __thunk; \ - _THUNK_GEN_STRUCT (__VA_ARGS__); \ - char __payload[]; \ + thunk_t __thunk; \ + struct __thunk_environment_##_name *next_free; \ + _THUNK_GEN_STRUCT (__VA_ARGS__); \ + char __payload[]; \ } __thunk_environment_##_name; \ \ static int __thunk_dispatch_##_name(thunk_t *thunk) { \ @@ -469,15 +472,28 @@ struct thunk_t { return r; \ } \ \ + static void __thunk_free_##_name(thunk_t *thunk) { \ + __thunk_environment_##_name *env = (__thunk_environment_##_name *)thunk;\ + \ + env->next_free = __thunk_environment_cache_##_name; \ + __thunk_environment_cache_##_name = env; \ + } \ + \ static thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) {\ /* allocate and initialize environment, return it */ \ __thunk_environment_##_name *env; \ \ - env = malloc(sizeof(*env)); \ + if (__thunk_environment_cache_##_name) { \ + env = __thunk_environment_cache_##_name; \ + __thunk_environment_cache_##_name = env->next_free; \ + } else { \ + env = malloc(sizeof(*env)); \ + } \ assert(env); \ \ _THUNK_GEN_INITIALIZE(env, __VA_ARGS__); \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ + env->__thunk.free = __thunk_free_##_name; \ \ return &env->__thunk; \ } \ @@ -495,6 +511,7 @@ struct thunk_t { } \ \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ + env->__thunk.free = (void (*)(thunk_t *))free; \ \ return &env->__thunk; \ } \ @@ -522,18 +539,23 @@ struct thunk_t { int _THUNK_GEN_PROTO(_name, __VA_ARGS__) __attribute__ ((unused)); \ typedef struct __thunk_environment_##_name { \ /* struct for encapsulating the calling environment */ \ - thunk_t __thunk; \ - _THUNK_GEN_STRUCT (__VA_ARGS__); \ - char __payload[]; \ + thunk_t __thunk; \ + struct __thunk_environment_##_name *next_free; \ + _THUNK_GEN_STRUCT (__VA_ARGS__); \ + char __payload[]; \ } __thunk_environment_##_name; \ \ int __thunk_dispatch_##_name(thunk_t *thunk) __attribute__ ((unused)); \ thunk_t * _THUNK_GEN_INIT_PROTO(__thunk_init_##_name, thunk_t *_thunk, __VA_ARGS__) __attribute__ ((unused));\ thunk_t * __thunk_alloc_##_name(void **payload_ptr, size_t payload_size) __attribute__ ((unused));\ - thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) __attribute__ ((unused)); + thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) __attribute__ ((unused));\ + \ + extern struct __thunk_environment_##_name *__thunk_environment_cache_##_name; #define THUNK_DEFINE(_name, ...) \ + struct __thunk_environment_##_name *__thunk_environment_cache_##_name = NULL;\ + \ int __thunk_dispatch_##_name(thunk_t *thunk) { \ /* dispatch thunk from associated environment */ \ __thunk_environment_##_name *env; \ @@ -549,15 +571,28 @@ struct thunk_t { return r; \ } \ \ + void __thunk_free_##_name(thunk_t *thunk) { \ + __thunk_environment_##_name *env = (__thunk_environment_##_name *)thunk;\ + \ + env->next_free = __thunk_environment_cache_##_name; \ + __thunk_environment_cache_##_name = env; \ + } \ + \ thunk_t * _THUNK_GEN_PROTO(__thunk_instantiate_##_name, __VA_ARGS__) { \ /* allocate and populate environment, return it */ \ __thunk_environment_##_name *env; \ \ - env = malloc(sizeof(*env)); \ + if (__thunk_environment_cache_##_name) { \ + env = __thunk_environment_cache_##_name; \ + __thunk_environment_cache_##_name = env->next_free; \ + } else { \ + env = malloc(sizeof(*env)); \ + } \ assert(env); \ \ _THUNK_GEN_INITIALIZE(env, __VA_ARGS__); \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ + env->__thunk.free = __thunk_free_##_name; \ \ return &env->__thunk; \ } \ @@ -575,6 +610,7 @@ struct thunk_t { } \ \ env->__thunk.dispatch = __thunk_dispatch_##_name; \ + env->__thunk.free = (void (*)(thunk_t *))free; \ \ return &env->__thunk; \ } \ @@ -675,19 +711,22 @@ static inline int thunk_dispatch(thunk_t *thunk) { int r; r = thunk->dispatch(thunk); - free(thunk); + thunk->free(thunk); return r; } /* Same as thunk_dispatch() but without the free. - * Callers can trivially free thunk instances w/free(), there's - * no fancy destructor stuff or anything. So when a thunk needs - * to be used repeatedly in a loop for instance, use this, then - * just free the thunk yourself. + * Callers can trivially free thunk instances w/thunk_free(). + * So when a thunk needs to be used repeatedly in a loop for instance, use + * this, then just free the thunk yourself. */ static inline int thunk_dispatch_keep(thunk_t *thunk) { return thunk->dispatch(thunk); } +static inline void thunk_free(thunk_t *thunk) { + return thunk->free(thunk); +} + #endif |