add the context slot APIs
authorTony Cook <tony@develop-help.com>
Sun, 26 Aug 2012 05:18:27 +0000 (15:18 +1000)
committerTony Cook <tony@develop-help.com>
Sun, 26 Aug 2012 05:21:36 +0000 (15:21 +1000)
context.c
imager.h
imageri.h
imdatatypes.h
imext.c
imext.h
imexttypes.h
lib/Imager/API.pod
lib/Imager/APIRef.pod
t/t82inline.t

index 08e9a4d72c28a20ca91b3efb72c009a36cc59a94..cab65a9fc6ab7bb80e043493e43335ff93ba3622 100644 (file)
--- a/context.c
+++ b/context.c
@@ -1,6 +1,10 @@
 #include "imageri.h"
 #include <stdio.h>
 
+static im_slot_t slot_count;
+static im_slot_destroy_t *slot_destructors;
+static i_mutex_t slot_mutex;
+
 /*
 =item im_context_new()
 
@@ -14,6 +18,9 @@ im_context_new(void) {
   im_context_t ctx = malloc(sizeof(im_context_struct));
   int i;
 
+  if (!slot_mutex)
+    slot_mutex = i_mutex_new();
+
   if (!ctx)
     return NULL;
   
@@ -31,12 +38,19 @@ im_context_new(void) {
   ctx->max_height = 0;
   ctx->max_bytes = DEF_BYTES_LIMIT;
 
+  ctx->slots = calloc(sizeof(void *), slot_count);
+  if (!ctx->slots) {
+    free(ctx);
+    return NULL;
+  }
+
   ctx->refcount = 1;
 
 #ifdef IMAGER_TRACE_CONTEXT
   fprintf(stderr, "im_context: created %p\n", ctx);
 #endif
 
+
   return ctx;
 }
 
@@ -76,6 +90,7 @@ been removed.
 void
 im_context_refdec(im_context_t ctx, const char *where) {
   int i;
+  im_slot_t slot;
 
   im_assert(ctx->refcount > 0);
 
@@ -89,6 +104,13 @@ im_context_refdec(im_context_t ctx, const char *where) {
   if (ctx->refcount != 0)
     return;
 
+  for (slot = 0; slot < ctx->slot_alloc; ++slot) {
+    if (ctx->slots[slot] && slot_destructors[slot])
+      slot_destructors[slot](ctx->slots[slot]);
+  }
+
+  free(ctx->slots);
+
   for (i = 0; i < IM_ERROR_COUNT; ++i) {
     if (ctx->error_stack[i].msg)
       myfree(ctx->error_stack[i].msg);
@@ -117,6 +139,13 @@ im_context_clone(im_context_t ctx, const char *where) {
   if (!nctx)
     return NULL;
 
+  nctx->slots = calloc(sizeof(void *), slot_count);
+  if (!nctx->slots) {
+    free(nctx);
+    return NULL;
+  }
+  nctx->slot_alloc = slot_count;
+
   nctx->error_sp = ctx->error_sp;
   for (i = 0; i < IM_ERROR_COUNT; ++i) {
     if (ctx->error_stack[i].msg) {
@@ -149,6 +178,7 @@ im_context_clone(im_context_t ctx, const char *where) {
   nctx->max_width = ctx->max_width;
   nctx->max_height = ctx->max_height;
   nctx->max_bytes = ctx->max_bytes;
+
   nctx->refcount = 1;
 
 #ifdef IMAGER_TRACE_CONTEXT
@@ -157,3 +187,98 @@ im_context_clone(im_context_t ctx, const char *where) {
 
   return nctx;
 }
+
+/*
+=item im_context_slot_new(destructor)
+
+Allocate a new context-local-storage slot.
+
+=cut
+*/
+
+im_slot_t
+im_context_slot_new(im_slot_destroy_t destructor) {
+  im_slot_t new_slot;
+  im_slot_destroy_t *new_destructors;
+  if (!slot_mutex)
+    slot_mutex = i_mutex_new();
+
+  i_mutex_lock(slot_mutex);
+
+  new_slot = slot_count++;
+  new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count);
+  if (!new_destructors)
+    i_fatal(1, "Cannot allocate memory for slot destructors");
+  slot_destructors = new_destructors;
+
+  slot_destructors[new_slot] = destructor;
+
+  i_mutex_unlock(slot_mutex);
+
+  return new_slot;
+}
+
+/*
+=item im_context_slot_set(slot, value)
+
+Set the value of a slot.
+
+Returns true on success.
+
+Aborts if the slot supplied is invalid.
+
+If reallocation of slot storage fails, returns false.
+
+=cut
+*/
+
+int
+im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) {
+  if (slot < 0 || slot >= slot_count) {
+    fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
+           (int)slot, (int)slot_count-1);
+    abort();
+  }
+
+  if (slot >= ctx->slot_alloc) {
+    ssize_t i;
+    size_t new_alloc = slot_count;
+    void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc);
+
+    if (!new_slots)
+      return 0;
+
+    for (i = ctx->slot_alloc; i < new_alloc; ++i)
+      new_slots[i] = NULL;
+
+    ctx->slots = new_slots;
+    ctx->slot_alloc = new_alloc;
+  }
+
+  ctx->slots[slot] = value;
+
+  return 1;
+}
+
+/*
+=item im_context_slot_get(ctx, slot)
+
+Retrieve the value previously stored in the given slot of the context
+object.
+
+=cut
+*/
+
+void *
+im_context_slot_get(im_context_t ctx, im_slot_t slot) {
+  if (slot < 0 || slot >= slot_count) {
+    fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
+           (int)slot, (int)slot_count-1);
+    abort();
+  }
+
+  if (slot >= ctx->slot_alloc)
+    return NULL;
+
+  return ctx->slots[slot];
+}
index 6d003bbda7d0c3f7a80ff7a1f05ed8749902e03c..f305fb13138b398c50889b738571a338b0560d64 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -386,6 +386,9 @@ extern im_context_t im_context_new(void);
 extern void im_context_refinc(im_context_t ctx, const char *where);
 extern void im_context_refdec(im_context_t ctx, const char *where);
 extern im_context_t im_context_clone(im_context_t ctx, const char *where);
+extern im_slot_t im_context_slot_new(im_slot_destroy_t);
+extern void *im_context_slot_get(im_context_t ctx, im_slot_t slot);
+extern int im_context_slot_set(im_context_t ctx, im_slot_t slot, void *);
 
 extern im_context_t (*im_get_context)(void);
 
index 59d333a70f8202baa728035c94a5cc564862a1f2..6ef28a7aa2ceef5db09a1a80644e69aa3878b547 100644 (file)
--- a/imageri.h
+++ b/imageri.h
@@ -128,6 +128,10 @@ typedef struct im_context_tag {
   i_img_dim max_width, max_height;
   size_t max_bytes;
 
+  /* per context storage */
+  size_t slot_alloc;
+  void **slots;
+
   ptrdiff_t refcount;
 } im_context_struct;
 
index af55f908d9907a5692c7a0c7bb98916c2957a559..a7419ea5bc93625356446cbcfb6e56bbac602142 100644 (file)
@@ -8,6 +8,9 @@
 
 typedef struct im_context_tag *im_context_t;
 
+typedef ptrdiff_t im_slot_t;
+typedef void (*im_slot_destroy_t)(void *);
+
 /* used for palette indices in some internal code (which might be 
    exposed at some point
 */
diff --git a/imext.c b/imext.c
index 4c23d916f82dc2a5a6b309bdef07679130e3881e..9b85350eb3c194916a9015e27d6ae3a0e829f319 100644 (file)
--- a/imext.c
+++ b/imext.c
@@ -165,7 +165,10 @@ im_ext_funcs imager_function_table =
     i_mutex_new,
     i_mutex_destroy,
     i_mutex_lock,
-    i_mutex_unlock
+    i_mutex_unlock,
+    im_context_slot_new,
+    im_context_slot_set,
+    im_context_slot_get
   };
 
 /* in general these functions aren't called by Imager internally, but
diff --git a/imext.h b/imext.h
index 131dd1fa655901cd276b7761fca9d3eb569cc162..6e834b25b3cea13de297ded1e37b1b00d644a892 100644 (file)
--- a/imext.h
+++ b/imext.h
@@ -232,6 +232,10 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_mutex_lock(m) ((im_extt->f_i_mutex_lock)(m))
 #define i_mutex_unlock(m) ((im_extt->f_i_mutex_unlock)(m))
 
+#define im_context_slot_new(destructor) ((im_extt->f_im_context_slot_new)(destructor))
+#define im_context_slot_get(ctx, slot) ((im_extt->f_im_context_slot_get)((ctx), (slot)))
+#define im_context_slot_set(ctx, slot, value) ((im_extt->f_im_context_slot_set)((ctx), (slot), (value)))
+
 #define im_push_errorf (im_extt->f_im_push_errorf)
 
 #ifdef IMAGER_LOG
index 8ec8d5c9d3155fe657cdc41e362f0ccb3fd67923..7e26131ad0fcace0dd1dab241123dcb3e1e73d73 100644 (file)
@@ -232,6 +232,9 @@ typedef struct {
   void (*f_i_mutex_destroy)(i_mutex_t m);
   void (*f_i_mutex_lock)(i_mutex_t m);
   void (*f_i_mutex_unlock)(i_mutex_t m);
+  im_slot_t (*f_im_context_slot_new)(im_slot_destroy_t);
+  int (*f_im_context_slot_set)(im_context_t, im_slot_t, void *);
+  void *(*f_im_context_slot_get)(im_context_t, im_slot_t);
 
 } im_ext_funcs;
 
index 7cb5d25b7605450ab2f4675be70a7b9886a1ca53..061c1393d6a2cf05e2742332e7c8face7813aa04 100644 (file)
@@ -437,6 +437,34 @@ I most cases where you'd use these functions, your code would create
 the mutex in your BOOT section, then lock and unlock the mutex as
 needed to control access to the library.
 
+=head1 Context slots
+
+=for stopwords
+TLS APIs
+
+To avoid abstracting the platform TLS and thread clean up handling,
+Imager provides simple APIs for storing per-context information.
+
+To allocate a slot:
+
+  im_slot_t slot = im_context_slot_new(callback)
+
+where callback is a (possibly NULL) function pointer called when the
+context object is destroyed.
+
+By default, the stored value for a slot is NULL, whether for a new
+context or for a cloned context.
+
+To store a value:
+
+  im_context_slot_set(aIMCTX, slot, somevalue);
+
+where C<somevalue> can be represented as a C<void *>.
+
+To retrieve the value:
+
+  value = im_context_slot_get(aIMCTX, slot);
+
 =head1 AUTHOR
 
 Tony Cook <tonyc@cpan.org>
index 1f61bb145280aee1c7f7b1d21e189b1c33b1bfb4..e0341e1af74d265e8697b8a92f05d282aaa20d12 100644 (file)
@@ -2290,6 +2290,37 @@ X<im_context_refinc API>
 Add a new reference to the context.
 
 
+=for comment
+From: File context.c
+
+=item im_context_slot_get(ctx, slot)
+
+Retrieve the value previously stored in the given slot of the context
+object.
+
+
+=for comment
+From: File context.c
+
+=item im_context_slot_new(destructor)
+
+Allocate a new context-local-storage slot.
+
+
+=for comment
+From: File context.c
+
+=item im_context_slot_set(slot, value)
+
+Set the value of a slot.
+
+Returns true on success.
+
+Aborts if the slot supplied is invalid.
+
+If reallocation of slot storage fails, returns false.
+
+
 =for comment
 From: File context.c
 
index ff0a216e0f3e6c15777d9b7d130d44f7f5b8cdc1..2a462b5196622cf61f956e3f204d3e5d66fbcc99 100644 (file)
@@ -19,7 +19,7 @@ plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
 
 -d "testout" or mkdir "testout";
 
-plan tests => 116;
+plan tests => 117;
 require Inline;
 Inline->import(with => 'Imager');
 Inline->import("FORCE"); # force rebuild
@@ -420,6 +420,27 @@ test_mutex() {
   i_mutex_destroy(m);
 }
 
+int
+test_slots() {
+  im_slot_t slot = im_context_slot_new(NULL);
+
+  if (im_context_slot_get(aIMCTX, slot)) {
+    fprintf(stderr, "slots should default to NULL\n");
+    return 0;
+  }
+  if (!im_context_slot_set(aIMCTX, slot, &slot)) {
+    fprintf(stderr, "set slot failed\n");
+    return 0;
+  }
+
+  if (im_context_slot_get(aIMCTX, slot) != &slot) {
+    fprintf(stderr, "get slot didn't match\n");
+    return 0;
+  }
+
+  return 1;
+}
+
 EOS
 
 my $im = Imager->new(xsize=>50, ysize=>50);
@@ -638,6 +659,8 @@ for my $bits (8, 16) {
 
 ok(test_mutex(), "call mutex APIs");
 
+ok(test_slots(), "call slot APIs");
+
 sub _get_error {
   my @errors = Imager::i_errors();
   return join(": ", map $_->[0], @errors);