summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/til_stream.c339
-rw-r--r--src/til_stream.h27
3 files changed, 367 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index efb7cc0..458294c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,7 @@
SUBDIRS = libs modules
noinst_LTLIBRARIES = libtil.la
-libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_jenkins.c til_jenkins.h til_knobs.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_tap.h til_threads.c til_threads.h til_util.c til_util.h
+libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_jenkins.c til_jenkins.h til_knobs.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_stream.c til_stream.h til_tap.h til_threads.c til_threads.h til_util.c til_util.h
libtil_la_CPPFLAGS = -I@top_srcdir@/src
libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/moire/libmoire.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/roto/libroto.la modules/rtv/librtv.la modules/shapes/libshapes.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/strobe/libstrobe.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la
diff --git a/src/til_stream.c b/src/til_stream.c
new file mode 100644
index 0000000..0f13d1f
--- /dev/null
+++ b/src/til_stream.c
@@ -0,0 +1,339 @@
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "til_stream.h"
+#include "til_tap.h"
+
+/* A stream in libtil is basically a hash table for tracking dynamic
+ * information for modules to create/modify/access. The objects stored in the
+ * table are "stream pipes" and endpoints called taps, making this something
+ * like a miniature in-memory implementation of named pipes, conceptually
+ * anyways (there are no actual file descriptors).
+ */
+
+#if 0
+/* example usage: */
+typedef struct foo_t {
+ struct {
+ til_tap_t position;
+ } taps;
+ struct {
+ v2f_t position;
+ } vars;
+
+ v2f_t *position;
+} foo_t;
+
+foo_t * foo_create_context(void)
+{
+ foo_t *foo = malloc(sizeof(foo_t));
+
+ /* This creates an isolated (pipe-)tap binding our local position variable and pointer
+ * to a name for later "tapping" onto a stream.
+ */
+ foo->taps.position = til_tap_init_v2f(&foo->position, 1, &foo->vars.position, "position");
+}
+
+foo_render(foo_t *foo, til_fb_fragment_t *fragment)
+{
+ if (!til_stream_tap_context(fragment->stream, foo, &foo->taps.position)) {
+ /* got nothing, we're driving position */
+ foo->position->x = cosf(ticks);
+ foo->position->y = sinf(ticks);
+ } /* else { got something, just use foo->position as-is */
+}
+#endif
+
+
+#define TIL_STREAM_BUCKETS_COUNT 256
+
+typedef struct til_stream_pipe_t til_stream_pipe_t;
+
+struct til_stream_pipe_t {
+ til_stream_pipe_t *next;
+ const void *owner;
+ char *parent_path;
+ const til_tap_t *driving_tap;
+ uint32_t hash;
+};
+
+typedef struct til_stream_t {
+ pthread_mutex_t mutex;
+ til_stream_pipe_t *buckets[TIL_STREAM_BUCKETS_COUNT];
+} til_stream_t;
+
+
+til_stream_t * til_stream_new(void)
+{
+ til_stream_t *stream;
+
+ stream = calloc(1, sizeof(til_stream_t));
+ if (!stream)
+ return NULL;
+
+ pthread_mutex_init(&stream->mutex, NULL);
+
+ return stream;
+}
+
+
+til_stream_t * til_stream_free(til_stream_t *stream)
+{
+ if (!stream)
+ return NULL;
+
+ for (int i = 0; i < TIL_STREAM_BUCKETS_COUNT; i++) {
+ for (til_stream_pipe_t *p = stream->buckets[i], *p_next; p != NULL; p = p_next) {
+ p_next = p->next;
+ free(p->parent_path);
+ free(p);
+ }
+ }
+
+ pthread_mutex_destroy(&stream->mutex);
+ free(stream);
+
+ return NULL;
+}
+
+
+/* Taps the key-named type-typed pipe on the supplied stream.
+ * If this is the first use of the tap on this stream, new pipe will be created.
+ * If the tap exists on this stream, and the type matches, existing pipe will be used as-is.
+ * If the key exists on this stream, but the type mismatches, an error is returned.
+ *
+ * -errno is returned on error, 0 when tap is driving, 1 when tap is passenger.
+ *
+ * If stream is NULL it's treated as if the key doesn't exist without a pipe creation.
+ */
+int til_stream_tap(til_stream_t *stream, const void *owner, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap)
+{
+ uint32_t hash, bucket;
+ til_stream_pipe_t *pipe;
+
+ assert(tap);
+
+ if (!stream) {
+ *(tap->ptr) = tap->elems;
+
+ return 0;
+ }
+
+ pthread_mutex_lock(&stream->mutex);
+
+ hash = (tap->name_hash ^ parent_hash);
+ bucket = hash % TIL_STREAM_BUCKETS_COUNT;
+ for (pipe = stream->buckets[bucket]; pipe != NULL; pipe = pipe->next) {
+ if (pipe->hash == hash) {
+ if (pipe->driving_tap == tap) {
+ /* this is our pipe and we're driving */
+ *(tap->ptr) = pipe->driving_tap->elems;
+
+ pthread_mutex_unlock(&stream->mutex);
+ return 0;
+ }
+
+ if (pipe->driving_tap->elems == *(tap->ptr) ||
+ (!strcmp(pipe->driving_tap->name, tap->name) && !strcmp(pipe->parent_path, parent_path))) {
+ /* this looks to be our pipe, but we're not driving */
+ *(tap->ptr) = pipe->driving_tap->elems;
+
+ pthread_mutex_unlock(&stream->mutex);
+ return 1;
+ }
+ }
+ }
+
+ /* matching pipe not found, create new one with tap as driver */
+ pipe = calloc(1, sizeof(til_stream_pipe_t));
+ if (!pipe) {
+ pthread_mutex_unlock(&stream->mutex);
+ return -ENOMEM;
+ }
+
+ pipe->owner = owner;
+ pipe->driving_tap = tap;
+
+ pipe->parent_path = strdup(parent_path);
+ if (!pipe->parent_path) {
+ free(pipe);
+
+ pthread_mutex_unlock(&stream->mutex);
+ return -ENOMEM;
+ }
+
+ pipe->hash = hash;
+ pipe->next = stream->buckets[bucket];
+ stream->buckets[bucket] = pipe;
+
+ pthread_mutex_unlock(&stream->mutex);
+ return 0;
+}
+
+
+/* remove all pipes belonging to owner in stream */
+void til_stream_untap_owner(til_stream_t *stream, const void *owner)
+{
+ for (int i = 0; i < TIL_STREAM_BUCKETS_COUNT; i++) {
+ for (til_stream_pipe_t *p = stream->buckets[i], *p_next, *p_prev; p != NULL; p = p_next) {
+ p_next = p->next;
+
+ if (p->owner == owner) {
+ if (p == stream->buckets[i])
+ stream->buckets[i] = p_next;
+ else
+ p_prev->next = p_next;
+
+ free(p);
+ } else
+ p_prev = p;
+ }
+ }
+}
+
+
+/* We need the higher-order types defined in order to print their contents.
+ * libtil should probably just formally define these smoewhere for modules to
+ * make use of. Until now it's been very deliberate to try leave modules to be
+ * relatively self-contained, even if they often reinvent the wheel as a
+ * result... it's relatively harmless for small functionalities while keeping
+ * the listings easier to grok as a whole esp. for a newcomer who isn't
+ * necessarily comfortable jumping around a sprawling tree of files.
+ */
+typedef struct v2f_t { float x, y; } v2f_t;
+
+typedef struct v3f_t {
+ float x, y, z;
+} v3f_t;
+
+typedef struct v4f_t {
+ float x, y, z, w;
+} v4f_t;
+
+/* XXX: note that while yes, this does acquire stream->mutex to serialize access to the table/pipes,
+ * this mutex does not serialize access to the tapped variables. So if this print is performed
+ * during the threaded rendering phase of things, it will technically be racy. The only strictly
+ * correct place to perform this print race-free is in the rendering loop between submissions to
+ * the rendering threads. Arguably if careful about only printing while serial, there's no need
+ * to acquire the mutex - but it's also an uncontended lock if that's the case so just take the
+ * mutex anyways since we're accessing the underlying structure it protects.
+ */
+void til_stream_fprint(til_stream_t *stream, FILE *out)
+{
+ fprintf(out, "Pipes on stream %p:\n", stream);
+ pthread_mutex_lock(&stream->mutex);
+ for (int i = 0; i < TIL_STREAM_BUCKETS_COUNT; i++) {
+ for (til_stream_pipe_t *p = stream->buckets[i]; p != NULL; p = p->next) {
+ fprintf(out, "%s/%s: ", p->parent_path, p->driving_tap->name);
+
+ for (size_t j = 0; j < p->driving_tap->n_elems; j++) {
+ const char *sep = j ? ", " : "";
+
+ switch (p->driving_tap->type) {
+ case TIL_TAP_TYPE_I8:
+ fprintf(out, "%"PRIi8"%s",
+ *(*((int8_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_I16:
+ fprintf(out, "%"PRIi16"%s",
+ *(*((int16_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_I32:
+ fprintf(out, "%"PRIi32"%s",
+ *(*((int32_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_I64:
+ fprintf(out, "%"PRIi64"%s",
+ *(*((int64_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U8:
+ fprintf(out, "%"PRIu8"%s",
+ *(*((int8_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U16:
+ fprintf(out, "%"PRIu16"%s",
+ *(*((int16_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U32:
+ fprintf(out, "%"PRIu32"%s",
+ *(*((int32_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U64:
+ fprintf(out, "%"PRIu64"%s",
+ *(*((int64_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_FLOAT:
+ fprintf(out, "%f%s",
+ *(*((float **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_DOUBLE:
+ fprintf(out, "%f%s",
+ *(*((double **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_V2F:
+ fprintf(out, "{%f,%f}%s",
+ (*((v2f_t **)p->driving_tap->ptr))->x,
+ (*((v2f_t **)p->driving_tap->ptr))->y,
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_V3F:
+ fprintf(out, "{%f,%f,%f}%s",
+ (*((v3f_t **)p->driving_tap->ptr))->x,
+ (*((v3f_t **)p->driving_tap->ptr))->y,
+ (*((v3f_t **)p->driving_tap->ptr))->z,
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_V4F:
+ fprintf(out, "{%f,%f,%f,%f}%s",
+ (*((v4f_t **)p->driving_tap->ptr))->x,
+ (*((v4f_t **)p->driving_tap->ptr))->y,
+ (*((v4f_t **)p->driving_tap->ptr))->z,
+ (*((v4f_t **)p->driving_tap->ptr))->w,
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_M4F:
+ fprintf(out, "M4F TODO%s", sep);
+ break;
+
+ case TIL_TAP_TYPE_VOIDP:
+ fprintf(out, "%p%s", *((void **)p->driving_tap->ptr), sep);
+ break;
+
+ default:
+ assert(0);
+ }
+ fprintf(out, "\n");
+ }
+ }
+ }
+ pthread_mutex_unlock(&stream->mutex);
+ fprintf(out, "\n");
+}
diff --git a/src/til_stream.h b/src/til_stream.h
new file mode 100644
index 0000000..91eaa55
--- /dev/null
+++ b/src/til_stream.h
@@ -0,0 +1,27 @@
+#ifndef _TIL_STREAM_H
+#define _TIL_STREAM_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "til_module_context.h"
+
+typedef struct til_stream_t til_stream_t;
+typedef struct til_tap_t til_tap_t;
+
+til_stream_t * til_stream_new(void);
+til_stream_t * til_stream_free(til_stream_t *stream);
+
+/* bare interface for non-module-context owned taps */
+int til_stream_tap(til_stream_t *stream, const void *tap_owner, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap);
+
+/* convenience helper for use within modules */
+static inline int til_stream_tap_context(til_stream_t *stream, const til_module_context_t *module_context, const til_tap_t *tap)
+{
+ return til_stream_tap(stream, module_context, module_context->path, module_context->path_hash, tap);
+}
+
+void til_stream_untap_owner(til_stream_t *stream, const void *owner);
+void til_stream_fprint(til_stream_t *stream, FILE *out);
+
+#endif
© All Rights Reserved