This works, even with threading.
#include "imperl.h"
-static im_context_t work_context;
+/*
+
+Context object management
+
+*/
+
+typedef im_context_t Imager__Context;
+
+#define im_context_DESTROY(ctx) im_context_refdec((ctx), "DESTROY")
+
+#ifdef PERL_IMPLICIT_CONTEXT
+
+#define MY_CXT_KEY "Imager::_context" XS_VERSION
+
+typedef struct {
+ im_context_t ctx;
+} my_cxt_t;
+
+START_MY_CXT
+
+im_context_t fallback_context;
+
+static void
+start_context(pTHX) {
+ dMY_CXT;
+ MY_CXT.ctx = im_context_new();
+ sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);
+
+ /* Ideally we'd free this reference, but the error message memory
+ was never released on exit, so the associated memory here is reasonable
+ to keep.
+ With logging enabled we always need at least one context, since
+ objects may be released fairly late and attempt to get the log file.
+ */
+ im_context_refinc(MY_CXT.ctx, "start_context");
+ fallback_context = MY_CXT.ctx;
+}
+
+static im_context_t
+perl_get_context(void) {
+ dTHX;
+ dMY_CXT;
+
+ return MY_CXT.ctx ? MY_CXT.ctx : fallback_context;
+}
+
+#else
+
+static im_context_t perl_context;
+
+static void
+start_context(pTHX) {
+ perl_context = im_context_new();
+ im_context_refinc(perl_context, "start_context");
+}
static im_context_t
perl_get_context(void) {
- return work_context;
+ return perl_context;
}
+#endif
+
/* used to represent channel lists parameters */
typedef struct i_channel_list_tag {
int *channels;
}
}
-
/* I don't think ICLF_* names belong at the C interface
this makes the XS code think we have them, to let us avoid
putting function bodies in the XS code
#endif
+MODULE = Imager PACKAGE = Imager::Context PREFIX=im_context_
+
+void
+im_context_DESTROY(ctx)
+ Imager::Context ctx
+
+#ifdef PERL_IMPLICIT_CONTEXT
+
+void
+im_context_CLONE(...)
+ CODE:
+ MY_CXT_CLONE;
+ /* the following sv_setref_pv() will free this inc */
+ im_context_refinc(MY_CXT.ctx, "CLONE");
+ MY_CXT.ctx = im_context_clone(MY_CXT.ctx, "CLONE");
+ sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);
+
+#endif
+
BOOT:
PERL_SET_GLOBAL_CALLBACKS;
PERL_PL_SET_GLOBAL_CALLBACKS;
- work_context = im_context_new();
+ MY_CXT_INIT;
+ start_context(aTHX);
im_get_context = perl_get_context;
my @libpaths; # places to look for libraries
my $coverage; # build for coverage testing
my $assert; # build with assertions
+my $trace_context; # trace context management to stderr
GetOptions("help" => \$help,
"enable=s" => \@enable,
"disable=s" => \@disable,
"verbose|v" => \$VERBOSE,
"nolog" => \$NOLOG,
'coverage' => \$coverage,
- "assert|a" => \$assert);
+ "assert|a" => \$assert,
+ "tracecontext" => \$trace_context);
setenv();
unshift @typemaps, "typemap.oldperl";
}
+if ($trace_context) {
+ $CFLAGS .= " -DIMAGER_TRACE_CONTEXT";
+}
+
my %opts=
(
'NAME' => 'Imager',
#include "imageri.h"
+#include <stdio.h>
/*
=item im_context_new()
ctx->max_height = 0;
ctx->max_bytes = DEF_BYTES_LIMIT;
+ 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)
+
+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)
Release memory used by an Imager context object.
*/
void
-im_context_delete(im_context_t ctx) {
+im_context_refdec(im_context_t ctx, const char *where) {
int i;
+ 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;
+
for (i = 0; i < IM_ERROR_COUNT; ++i) {
if (ctx->error_stack[i].msg)
myfree(ctx->error_stack[i].msg);
*/
im_context_t
-im_context_clone(im_context_t ctx) {
+im_context_clone(im_context_t ctx, const char *where) {
im_context_t nctx = malloc(sizeof(im_context_struct));
int i;
nctx->max_width = ctx->max_width;
nctx->max_height = ctx->max_height;
nctx->max_bytes = ctx->max_bytes;
+ nctx->refcount = 1;
- return ctx;
+#ifdef IMAGER_TRACE_CONTEXT
+ fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
+#endif
+
+ return nctx;
}
im_img_init(pIMCTX, i_img *img) {
img->im_data = NULL;
img->context = aIMCTX;
+ im_context_refinc(aIMCTX, "img_init");
}
/*
void
i_img_destroy(i_img *im) {
+ dIMCTXim(im);
mm_log((1,"i_img_destroy(im %p)\n",im));
i_img_exorcise(im);
if (im) { myfree(im); }
+ im_context_refdec(aIMCTX, "img_destroy");
}
/*
/* context object management */
extern im_context_t im_context_new(void);
-extern void im_context_delete(im_context_t ctx);
-extern im_context_t im_context_clone(im_context_t ctx);
+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_context_t (*im_get_context)(void);
#define IMAGEI_H_
#include "imager.h"
+#include <stddef.h>
/* wrapper functions that implement the floating point sample version of a
function in terms of the 8-bit sample version
/* file size limits */
i_img_dim max_width, max_height;
size_t max_bytes;
+
+ ptrdiff_t refcount;
} im_context_struct;
#define DEF_BYTES_LIMIT 0x40000000
Imager::Internal::Hlines T_PTROBJ
+Imager::Context T_PTROBJ
+
#############################################################################
INPUT