summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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