Age | Commit message (Collapse) | Author |
|
Enables using thunk.h in threaded programs
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
In preparation for supporting both public and private thunk defines,
convert the current assumed-private one to use a _STATIC suffix.
|
|
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.
|
|
Just bumping this limit a bit
|
|
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
|
|
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.
|
|
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.
|
|
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.
|