AgeCommit message (Collapse)Author
2021-09-03thunk: add some parens around truth valuesHEADmasterVito Caputo
Silencing some gcc warnings...
2021-08-24thunk: use __sync intrinsics for free listsVito Caputo
Enables using thunk.h in threaded programs
2021-08-23thunk: add some helpers for filtering thunk returnsVito Caputo
Naming may evolve in future... thunk_mid(): pass-through negative values, otherwise return 1 "mid" name is used as this is intended to return the "keep me around" value for mid-phase iterative processing. thunk_end(): pass-through negative values, otherwise return 0 "end" name is used as this indicates "don't keep me around for reuse, fin!" In experimental code using this header it became common for thunk implementations to return straight from thunk_dispatch() or bare calls to other thunkified functions. This is convenient for propagating out fatal errors, but now that return value influences lifecycle, these blind propagating returns as-is in non-error scenarios would be propagating lifecycle controls out from a totally unrelated context. These helpers are intended to wrap those call sites, blindly propagating only errors, but otherwise being opinionated about lifecycle. Now it's kind of expected that every thunk implementation will have its return paths covered with thunk_end() and/or thunk_mid() calls, or just bare non-propagated return values. This is all still very experimental, and shouldn't be considered production code, it's mostly just a curiosity.
2021-08-23thunk: deprecate thunk_dispatch_keep()Vito Caputo
Enable callees to control lifecycle via return values in thunk_dispatch() instead. The thunk_dispatch_keep() function encuraged caller-determined lifecycles which really doesn't work in practice. Now if a thunk returns <= 0 it will be freed, negative values are expected to be used for propagating out fatal errors. A positive value (conventionally will just be 1) will bypass the free, which is expected to be used in thunk reuse situations like iterators.
2021-08-17thunk: introduce and thunk_free()Vito Caputo
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 method. This commit should already significantly reduce the amount of malloc() and free() in most cases.
2020-11-19thunk: fixup expand max number of thunk parameters to 12Vito Caputo
f73822 was supposed to do this, but I messed up expanding the C99_NARGS macro fully. NARGS also needs to support 2X the desired number of parameters as they're supplied in {type, name} pairs. 12 is the maximum number of pairs supported at this time, adding more is trivial when needed.
2020-11-18thunk: clear thunk instance payloadsVito Caputo
Since thunk instances are utilized via initializer functions which always initialize all the environment members, it made sense to use plain malloc(). But with the addition of payloads, there's a component of the allocation which gets split out for the caller to make use of in a much more ad-hoc fashion. In my personal programming style, I tend to take advantage of the assumption that allocations are zeroed. The returned payload is essentially used as a heap allocation, just coupled to the thunk instance in terms of life cycle. So I prefer the payload space be zeroed on allocation. Since it still seems sensible to leave the thunk's environment part of the instance exclusively initialized by it's init function, this commit leaves the plain malloc and just memset's the payload when present.
2020-11-16thunk: mark functions w/gcc unused attributeVito Caputo
This shuts up gcc warnings w/-Wall for thunked functions. It's expected there will be some unused functions, esp. since there's the split init/alloc variants one may or may not use. It's also nice in some scenarios to write a collection of small closures a project may or may not take advantage of at any given moment in its development. No point making a bunch of noise about it.
2020-11-16thunk: add thunk_dispatch_keep() non-freeing variantVito Caputo
In scenarios where a thunk instance must be dispatched multiple times, the automatic free is a problem. This commit moves the free out of the dispatch implementations and into the thunk_dispatch() wrapper. So the behavior of thunk_dispatch() is unchanged. The new thunk_dispatch_keep() is a plain ->dispatch(). This does implicitly turn 'thunk_t *' being a free() able allocation into part of the API, which may be a problem later. But who cares, this is a .h for vendoring, not a .so w/ABI guarantees.
2020-11-04thunk: introduce split instantiation and payloadsVito Caputo
The existing THUNK() instantiation macro has really nice ergonomics taking the form of the bare function call wrapped by THUNK(). It's desirable however to sometimes enlarge the instance's allocation to accomodate some user-specified data with an instance-boound life cycle. There didn't seem to be a clean way to provide that with the existing THUNK() interface. This commit introduces a split THUNK() variant for such scenarios: THUNK_ALLOC(function_name, payload_pointer, payload_size) and THUNK_INIT(function_name(thunk_instance, [function_args] ...)) It's expected that these be used in pairs, like so: char *buf; thunk_t *t = THUNK_ALLOC(foo_func, &buf, BUFSIZ); THUNK_INIT(foo_func(t, foo_arg1, foo_arg2)); Note the THUNK_INIT's foo_func call has the thunk_t * passed as the first parameter. This isn't part of foo_func's THUNK_DEFINE signature, it's only part of the initializer for the function, followed by the real parameters from the THUNK_DEFINE's signature if there are any. This differs from the simpler THUNK() variant, where you'd do: THUNK(foo_func(foo_arg1, foo_arg2)); Also note the THUNK_ALLOC() above has stored a pointer to BUFSIZ bytes of payload memory @ *buf. This could then be supplied into foo_func, which is sometimes convenient: THUNK_INIT(foo_func(t, foo_arg1, buf)); The payloads are particularly useful when chaining up multiple thunks, and there's a need for some shared state across them for inputs/outputs, that you'd like automatically cleaned up when the instance the payload belongs to executes and returns freeing itself.
2020-11-04thunk: introduce public variant of THUNK_DEFINEVito Caputo
This is necessarily split in two: THUNK_DEFINE() THUNK_DECLARE() use is identical to the original THUNK_DEFINE (now THUNK_DEFINE_STATIC), and you supply the same input to both DEFINE and DECLARE. The THUNK_DECLARE() should go in the public header, which you also include in the implementing .c file, not just from consumers. The THUNK_DEFINE() goes in the implementing .c file, same as before. Use this variant when you wish to instantiate via THUNK() from external listings, just include the THUNK_DECLARE()-containing .h.
2020-11-04thunk: s/THUNK_DEFINE/THUNK_DEFINE_STATIC/Vito Caputo
In preparation for supporting both public and private thunk defines, convert the current assumed-private one to use a _STATIC suffix.
2020-11-04thunk: double up THUNK() macroVito Caputo
this is necessary for nested THUNK() constructions, e.g.: thunk_t * closure = THUNK(a, b, c, THUNK(d, e, f));
this internal macro just generates the initializer, just switching to a more accurate name.
2020-11-04thunk: expand max number of thunk parameters to 12Vito Caputo
Just bumping this limit a bit
2020-11-04thunk: switch from pragma to classical header guardVito Caputo
Pragma isn't standardized and it didn't seem to be working properly in a project where simply replacing it with this commit fixed compilation
2018-12-11thunk: fixup handling arity=0 thunksVito Caputo
Overlooked this, but it's also odd how the __VA_ARGS__ when empty is still being passed as a separate argument requiring the addition of the _nil placeholders.
2018-05-17thunk: default to static for everythingVito Caputo
If there comes a time when this is an issue, selector may be added and a separate declaration macro provided. For now, it appears static use is generally the appropriate thing to do.
2016-06-04thunk: initial commitVito Caputo
Something I slapped together in the interests of mechanizing the creation of thunks in C. In this context a thunk is simply a helper function that serves to normalize the calling convention of functions having potentially varying arity. This code has not been used in anything yet, I just put it together because I anticipate using it soon based on some projects I've been thinking about. example.c contains a trivial usage example.
© 2021 All Rights Reserved