summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2021-08-17 19:32:12 -0700
committerVito Caputo <vcaputo@pengaru.com>2021-08-17 19:42:06 -0700
commit22ea32f0f945509888a3d0d5167b5d4ed001a3d9 (patch)
treee43ed439c7372eb9cde08b4647cf58456c84cfc3
parent070e8c57d5829e9b429f698c329a6d0b0d4d071c (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.h67
1 files changed, 53 insertions, 14 deletions
diff --git a/thunk.h b/thunk.h
index 2cd607f..f02aff8 100644
--- a/thunk.h
+++ b/thunk.h
@@ -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
© All Rights Reserved