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>
Sat, 24 Nov 2012 03:59:26 +0000 (14:59 +1100)
context.c
imager.h
imageri.h
imdatatypes.h
imext.c
imext.h
lib/Imager/API.pod
lib/Imager/APIRef.pod
t/t82inline.t

index 08e9a4d..cab65a9 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 6d003bb..f305fb1 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 59d333a..6ef28a7 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 af55f90..a7419ea 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 4c23d91..9b85350 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 131dd1f..6e834b2 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 9ab8e2c..2f49ef5 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 1f61bb1..e0341e1 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 82f3c80..2a23419 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
@@ -422,6 +422,27 @@ test_mutex() {
   return 1;
 }
 
+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);
@@ -640,6 +661,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);