#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()
im_context_t ctx = malloc(sizeof(im_context_struct));
int i;
+ if (!slot_mutex)
+ slot_mutex = i_mutex_new();
+
if (!ctx)
return NULL;
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;
}
void
im_context_refdec(im_context_t ctx, const char *where) {
int i;
+ im_slot_t slot;
im_assert(ctx->refcount > 0);
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);
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) {
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
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];
+}
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);
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;
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
*/
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
#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
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>
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
-d "testout" or mkdir "testout";
-plan tests => 116;
+plan tests => 117;
require Inline;
Inline->import(with => 'Imager');
Inline->import("FORCE"); # force rebuild
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);
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);