#include "imageri.h"
+#include <stdio.h>
+
+static volatile im_slot_t slot_count = 1;
+static im_slot_destroy_t *volatile slot_destructors;
+static volatile i_mutex_t slot_mutex;
/*
=item im_context_new()
im_context_t
im_context_new(void) {
- im_context_t ctx = mymalloc(sizeof(im_context_struct));
+ im_context_t ctx = malloc(sizeof(im_context_struct));
int i;
+ if (!slot_mutex)
+ slot_mutex = i_mutex_new();
+
+ if (!ctx)
+ return NULL;
+
ctx->error_sp = IM_ERROR_COUNT-1;
for (i = 0; i < IM_ERROR_COUNT; ++i) {
ctx->error_alloc[i] = 0;
ctx->log_level = 0;
ctx->lg_file = NULL;
#endif
+ ctx->max_width = 0;
+ ctx->max_height = 0;
+ ctx->max_bytes = DEF_BYTES_LIMIT;
+
+ ctx->slot_alloc = slot_count;
+ ctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
+ 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;
}
/*
-=item im_context_delete(ctx)
+=item im_context_refinc(ctx, where)
+X<im_context_refinc API>
+=section Context objects
+=synopsis im_context_refinc(aIMCTX, "a description");
+
+Add a new reference to the context.
+
+=cut
+*/
+
+void
+im_context_refinc(im_context_t ctx, const char *where) {
+ ++ctx->refcount;
+
+#ifdef IMAGER_TRACE_CONTEXT
+ fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where,
+ ctx, (unsigned long)ctx->refcount);
+#endif
+}
+
+/*
+=item im_context_refdec(ctx, where)
+X<im_context_refdec API>
+=section Context objects
+=synopsis im_context_refdec(aIMCTX, "a description");
-Release memory used by an Imager context object.
+Remove a reference to the context, releasing it if all references have
+been removed.
=cut
*/
void
-im_context_delete(im_context_t ctx) {
+im_context_refdec(im_context_t ctx, const char *where) {
int i;
+ im_slot_t slot;
+
+ im_assert(ctx->refcount > 0);
+
+ --ctx->refcount;
+
+#ifdef IMAGER_TRACE_CONTEXT
+ fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where,
+ ctx, (unsigned long)ctx->refcount);
+#endif
+
+ if (ctx->refcount != 0)
+ return;
+
+ /* lock here to avoid slot_destructors from being moved under us */
+ i_mutex_lock(slot_mutex);
+ for (slot = 0; slot < ctx->slot_alloc; ++slot) {
+ if (ctx->slots[slot] && slot_destructors[slot])
+ slot_destructors[slot](ctx->slots[slot]);
+ }
+ i_mutex_unlock(slot_mutex);
+
+ free(ctx->slots);
for (i = 0; i < IM_ERROR_COUNT; ++i) {
if (ctx->error_stack[i].msg)
myfree(ctx->error_stack[i].msg);
}
#ifdef IMAGER_LOG
- if (ctx->lg_file)
+ if (ctx->lg_file && ctx->own_log)
fclose(ctx->lg_file);
#endif
+
+ free(ctx);
}
/*
Clone an Imager context object, returning the result.
+The error stack is not copied from the original context.
+
=cut
*/
im_context_t
-im_context_clone(im_context_t ctx) {
- im_context_t nctx = mymalloc(sizeof(im_context_struct));
+im_context_clone(im_context_t ctx, const char *where) {
+ im_context_t nctx = malloc(sizeof(im_context_struct));
int i;
- nctx->error_sp = ctx->error_sp;
+ if (!nctx)
+ return NULL;
+
+ nctx->slot_alloc = slot_count;
+ nctx->slots = calloc(sizeof(void *), nctx->slot_alloc);
+ if (!nctx->slots) {
+ free(nctx);
+ return NULL;
+ }
+
+ nctx->error_sp = IM_ERROR_COUNT-1;
for (i = 0; i < IM_ERROR_COUNT; ++i) {
- if (ctx->error_stack[i].msg) {
- size_t sz = ctx->error_alloc[i];
- nctx->error_alloc[i] = sz;
- nctx->error_stack[i].msg = mymalloc(sz);
- memcpy(nctx->error_stack[i].msg, ctx->error_stack[i].msg, sz);
- }
- else {
- nctx->error_alloc[i] = 0;
- nctx->error_stack[i].msg = NULL;
- }
- nctx->error_stack[i].code = ctx->error_stack[i].code;
+ nctx->error_alloc[i] = 0;
+ nctx->error_stack[i].msg = NULL;
}
#ifdef IMAGER_LOG
nctx->log_level = ctx->log_level;
if (ctx->lg_file) {
- /* disable buffering, this isn't perfect */
- setvbuf(ctx->lg_file, NULL, _IONBF, 0);
-
- /* clone that and disable buffering some more */
- nctx->lg_file = fdopen(fileno(ctx->lg_file), "a");
- if (nctx->lg_file)
- setvbuf(nctx->lg_file, NULL, _IONBF, 0);
+ if (ctx->own_log) {
+ int newfd = dup(fileno(ctx->lg_file));
+ nctx->own_log = 1;
+ nctx->lg_file = fdopen(newfd, "w");
+ if (nctx->lg_file)
+ setvbuf(nctx->lg_file, NULL, _IONBF, BUFSIZ);
+ }
+ else {
+ /* stderr */
+ nctx->lg_file = ctx->lg_file;
+ nctx->own_log = 0;
+ }
}
else {
- ctx->lg_file = NULL;
+ nctx->lg_file = NULL;
}
#endif
+ nctx->max_width = ctx->max_width;
+ nctx->max_height = ctx->max_height;
+ nctx->max_bytes = ctx->max_bytes;
- return ctx;
+ nctx->refcount = 1;
+
+#ifdef IMAGER_TRACE_CONTEXT
+ fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
+#endif
+
+ return nctx;
+}
+
+/*
+=item im_context_slot_new(destructor)
+
+Allocate a new context-local-storage slot.
+
+C<desctructor> will be called when the context is destroyed if the
+corresponding slot is non-NULL.
+
+=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];
}