summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-05-11 10:11:59 -0700
committerVito Caputo <vcaputo@pengaru.com>2023-05-11 15:19:25 -0700
commit7c8086020fb8d88f662a2b33b2dabef1b62ab39c (patch)
tree00c717428263e4efc95de8d177efa51dd63c0d71
parent2b9eca8724fe6c5e0f6a4438cd398b5978283131 (diff)
til_setup: refcount til_setup_t
The whole point of til_setup_t is to represent the baked, most conveniently usable form of a setup derived from one or more settings instances. Things generally go from the serialization format "settings string" to til_settings_t eventually culminating in a til_setup_t. So the process of making a til_setup_t is rather tedious and kind of costly. Once into a til_setup_t it's desirable to just hang on to this form and reuse it if possible. The way a til_setup_t baked setup is put to use is in a read-only fashion where it basically just informs behavior, so it makes a lot of sense to enable refcounting the thing and letting whatever can make use of it bump the refcount and hold onto the pointer, accessing the contents whenever it needs to answer a question about that particular setup. The immediate impetus for this is actually rtv's snow_module setup. In rtv every channel switch may recreate the context, if the context has expired. In the case of the snow module, the context always expires, and we definitely want to discard the context while playing the next channel. But when the snow resumes, in order to recreate the context as configured, we need the same setup again. It just becomes clear that what's needed is a way to pin the snow_module's setup for this reuse to be safe. There's also plenty of other modules that have been piecemeal copying settings into their context, when what would really make more sense is to just ref it and stow the pointer, then unref on their context destroy. They can just access the setup via the pointer as needed, instead of having to duplicate the setup in their context. Indeed, some module contexts even embed the entire setup just to copy its contents over by value. In simple/small scenarios that's fine, and I'm sure in those particular cases it's perfectly safe to do. It just seems unnecessary altogether. Another small change made is supporting NULL free_func, which will default to libc's free(). Most til_setup_new() call sites are passing free() with an annoying cast, those can be changed to NULL.
-rw-r--r--src/til_setup.c50
-rw-r--r--src/til_setup.h4
2 files changed, 46 insertions, 8 deletions
diff --git a/src/til_setup.c b/src/til_setup.c
index aab196b..d819173 100644
--- a/src/til_setup.c
+++ b/src/til_setup.c
@@ -7,7 +7,8 @@
/* Allocate and initialize a new til_setup_t of size bytes.
* free_func is assigned to til_setup_t.free, and will be used for freeing the
- * instance returned when destroyed.
+ * instance returned when destroyed. If free_func is NULL, free() will be
+ * used by default.
*
* Note this returns void * despite creating a til_setup_t, this is for convenience
* as the callers are generally using it in place of calloc(), and assign it to a
@@ -18,28 +19,63 @@ void * til_setup_new(size_t size, void (*free_func)(til_setup_t *setup))
til_setup_t *setup;
assert(size >= sizeof(til_setup_t));
- assert(free_func);
setup = calloc(1, size);
if (!setup)
return NULL;
+ setup->refcount = 1;
setup->free = free_func;
return setup;
}
-/* Free the setup when non-NULL, using setup->free if non-NULL.
- * Always returns NULL for uses like foo = til_setup_free(foo);
+/* bump refcount on setup */
+void * til_setup_ref(til_setup_t *setup)
+{
+ assert(setup);
+
+ setup->refcount++;
+
+ return setup;
+}
+
+
+/* unref setup, freeing it when refcount reaches zero.
+ * returns NULL if setup is freed (including when NULL was supplied for setup)
+ * resturns setup when setup persists.
+ * the public api is to just use til_setup_free() and discard that information,
+ * but this is kept here as distinct for potential debugging purposes.
*/
-void * til_setup_free(til_setup_t *setup)
+static void * til_setup_unref(til_setup_t *setup)
{
if (!setup)
return NULL;
- if (setup->free)
- setup->free(setup);
+ assert(setup->refcount > 0);
+
+ setup->refcount--;
+ if (!setup->refcount) {
+ if (setup->free)
+ setup->free(setup);
+ else
+ free(setup);
+
+ return NULL;
+ }
+
+ return setup;
+}
+
+
+/* like til_setup_unref() except always returns NULL so you
+ * can't tell if it was actually freed or not, but this is sometimes
+ * a convenient free-style wrapper if you have to NULL-assign a placeholder.
+ */
+void * til_setup_free(til_setup_t *setup)
+{
+ (void) til_setup_unref(setup);
return NULL;
}
diff --git a/src/til_setup.h b/src/til_setup.h
index 16380d5..8cc7a88 100644
--- a/src/til_setup.h
+++ b/src/til_setup.h
@@ -4,10 +4,12 @@
typedef struct til_setup_t til_setup_t;
struct til_setup_t {
- void (*free)(til_setup_t *setup);
+ unsigned refcount;
+ void (*free)(til_setup_t *setup);
};
void * til_setup_new(size_t size, void (*free_func)(til_setup_t *setup));
+void * til_setup_ref(til_setup_t *setup);
void * til_setup_free(til_setup_t *setup);
#endif
© All Rights Reserved