diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/til_stream.c | 339 | ||||
-rw-r--r-- | src/til_stream.h | 27 |
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 |