summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-01-19 22:52:52 -0800
committerVito Caputo <vcaputo@pengaru.com>2023-01-21 13:34:12 -0800
commit6d6619cc5f4f04710dd7dfccce713385924b3b06 (patch)
tree3012928a23a09910d0439ab51f90920282e62463 /src
parent9c5d17309be237f799105e252a26e2c32c2d9c50 (diff)
til_stream: introduce til_stream_hooks_t et al
There needs to be a way for a meta module like rocket to take ownership of pipes immediately upon instantiation. Since the pipes are created on demand as they become tapped by the modules using htem, the simplest way to do this is to register some callbacks with the ability to intercept the pipe creation in terms of ownership and driving tap control etc. This commit forms a minimal implementation of that, with the ability to have a single intercepter hooked into a given stream. It's a first-come-first-served situation, but that should suffice for now since the rocket meta module would be the entrypoint for such constructions. It then calls into another module to produce on the stream, after it'll already have its hooks registered. There might be a need for stacking hooks to let multiple modules control pipes. GNU Rocket for instance only deals with floats/doubles, and doesn't make it particularly easy to work on higher order concepts like say orbiting a vector around a point spatially. It might make sense to allow compositing of editors where there's rocket controlling the simple floats, and another doing dimensional/spatial stuff, with separate stacked meta modules accomodating those editors. But that's putting the cart before the horse, let's do the stupid simple thing for now and see what this is like.
Diffstat (limited to 'src')
-rw-r--r--src/til_stream.c68
-rw-r--r--src/til_stream.h11
2 files changed, 74 insertions, 5 deletions
diff --git a/src/til_stream.c b/src/til_stream.c
index fc3fc3c..da97bbf 100644
--- a/src/til_stream.c
+++ b/src/til_stream.c
@@ -80,8 +80,10 @@ struct til_stream_pipe_t {
};
typedef struct til_stream_t {
- pthread_mutex_t mutex;
- til_stream_pipe_t *buckets[TIL_STREAM_BUCKETS_COUNT];
+ pthread_mutex_t mutex;
+ const til_stream_hooks_t *hooks;
+ void *hooks_context;
+ til_stream_pipe_t *buckets[TIL_STREAM_BUCKETS_COUNT];
} til_stream_t;
@@ -119,6 +121,49 @@ til_stream_t * til_stream_free(til_stream_t *stream)
}
+/* hooks are presently implemented as a singleton per-stream, I could imagine a stack
+ * form where multiple triggers filter through until one of the callbacks
+ * claim to have processed the trigger. But let's just keep it stupid simple for now.
+ *
+ * this function is idempotent in the sense that a caller can keep resetting its hooks
+ * when it's succeeded as becoming the owner of the hooks (by being first to set them on
+ * a stream). Only when different set of hooks are attempted to be set on a stream already
+ * having hooks will it error out, to change hooks the caller must first unset them with the
+ * matching hooks pointer. There's no way to read the hooks out by a caller. This is a weak
+ * defensive mechanism to prevent multiple modules silently fighting over the hooks. As long
+ * as they're checking the return code they can say meaningful things about failing to set the
+ * hooks... and it's assumed it'd be a flawed composition attempting to use multiple hook-setting
+ * modules on the same stream (or program bug).
+ */
+int til_stream_set_hooks(til_stream_t *stream, const til_stream_hooks_t *hooks, void *context)
+{
+ assert(stream);
+ assert(hooks);
+
+ if (stream->hooks && stream->hooks != hooks)
+ return -EEXIST;
+
+ stream->hooks = hooks;
+ stream->hooks_context = context;
+
+ return 0;
+}
+
+
+int til_stream_unset_hooks(til_stream_t *stream, const til_stream_hooks_t *hooks)
+{
+ assert(stream);
+ assert(hooks);
+
+ if (stream->hooks && stream->hooks != hooks)
+ return -EINVAL;
+
+ stream->hooks = stream->hooks_context = NULL;
+
+ return 0;
+}
+
+
/* Taps the key-named type-typed pipe on the supplied stream.
* If this is the first use of the pipe on this stream, new pipe will be created.
* If the pipe exists on this stream, and the type/n_elems match, existing pipe will be used as-is.
@@ -132,6 +177,8 @@ int til_stream_tap(til_stream_t *stream, const void *owner, const void *owner_fo
{
uint32_t hash, bucket;
til_stream_pipe_t *pipe;
+ const void **p_owner = &owner, **p_owner_foo = &owner_foo;
+ const til_tap_t **p_tap = &tap;
assert(tap);
@@ -175,6 +222,14 @@ int til_stream_tap(til_stream_t *stream, const void *owner, const void *owner_fo
}
}
+ if (stream->hooks && stream->hooks->pipe_ctor) {
+ int r;
+
+ r = stream->hooks->pipe_ctor(stream->hooks_context, stream, owner, owner_foo, parent_path, parent_hash, tap, p_owner, p_owner_foo, p_tap);
+ if (r < 0)
+ return r;
+ }
+
/* matching pipe not found, create new one with tap as driver */
pipe = calloc(1, sizeof(til_stream_pipe_t));
if (!pipe) {
@@ -182,9 +237,9 @@ int til_stream_tap(til_stream_t *stream, const void *owner, const void *owner_fo
return -ENOMEM;
}
- pipe->owner = owner;
- pipe->owner_foo = owner_foo;
- pipe->driving_tap = tap;
+ pipe->owner = *p_owner;
+ pipe->owner_foo = *p_owner_foo;
+ pipe->driving_tap = *p_tap;
pipe->parent_path = strdup(parent_path);
if (!pipe->parent_path) {
@@ -216,6 +271,9 @@ void til_stream_untap_owner(til_stream_t *stream, const void *owner)
else
p_prev->next = p_next;
+ if (stream->hooks && stream->hooks->pipe_dtor)
+ stream->hooks->pipe_dtor(stream->hooks_context, stream, p->owner, p->owner_foo, p->parent_path, p->driving_tap);
+
free(p);
} else
p_prev = p;
diff --git a/src/til_stream.h b/src/til_stream.h
index 3e83c7d..3f0a618 100644
--- a/src/til_stream.h
+++ b/src/til_stream.h
@@ -32,8 +32,19 @@ typedef struct til_tap_t til_tap_t;
*/
typedef int (til_stream_iter_func_t)(void *context, til_stream_pipe_t *pipe, const void *owner, const void *owner_foo, const til_tap_t *driving_tap);
+/* this provides a way to intercept pipe creations/deletions when they occur,
+ * allowing another module to snipe ownership when they appear and cleanup
+ * its resources when they disappear.
+ */
+typedef struct til_stream_hooks_t {
+ int (*pipe_ctor)(void *context, til_stream_t *stream, const void *owner, const void *owner_foo, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap, const void **res_owner, const void **res_owner_foo, const til_tap_t **res_tap); /* called immediately *before* pipe would be created by tap using these parameters, return <0 on error, 0 on unhandled by hook, 1 on handled with desired owner/owner_foo/tap stored in res_* */
+ int (*pipe_dtor)(void *context, til_stream_t *stream, const void *owner, const void *owner_foo, const char *parent_path, const til_tap_t *tap); /* called immediately *after* pipe "destroyed" (withdrawn from stream) */
+} til_stream_hooks_t;
+
til_stream_t * til_stream_new(void);
til_stream_t * til_stream_free(til_stream_t *stream);
+int til_stream_set_hooks(til_stream_t *stream, const til_stream_hooks_t *hooks, void *context);
+int til_stream_unset_hooks(til_stream_t *stream, const til_stream_hooks_t *hooks);
/* bare interface for non-module-context owned taps */
int til_stream_tap(til_stream_t *stream, const void *owner, const void *owner_foo, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap);
© All Rights Reserved